DNA.
wyrm-assoc
Version.
2.1.9
Namespace.
::wyrm
Command.
::wyrm::assoc
Language.
c
Manpage.
assoc (1WY)
Manpage.
wyrm_assocNew (3WY)
Manpage.
wyrm_assocEmpty (3WY)
Manpage.
wyrm_assocFirst (3WY)
Manpage.
wyrm_assocGet (3WY)
Manpage.
wyrm_assocFilter (3WY)
Manpage.
WyrmAssocMapType (4WY)
Testbase.
Test Script
Test Report
Import.
Interface.
wyrm-assoc-btree
wyrm-assoc-hash
wyrm-assoc-splay
Object.
wyrm-assoc-btree
wyrm-assoc-hash
wyrm-assoc-splay
wyrm-oav
Package.
wyrmwif
Export.
Implementation.
wyrm-assoc.c
Interface.
wyrm-assoc.h
Object.
wyrm-assoc.o
Package.
wyrmassoc.dylib
System.
wyrm-assoc.sys

Associative Maps

Sections.
WyrmAssocMapType
Assign Mapping Type
Pass Through Interface
Auxillary I/O Routines
Wyrmassoc Tcl Package
Man Pages
Safe Interpretters
Test Base
Make.
Object.
set declares ""
set calls ""
foreach header [import interface] {
  set header [file tail [file root $header]]
  if {![string match wyrm-assoc-* $header]} continue
  set init wyrm_assoc[string totitle [string map {wyrm-assoc- ""} $header]]Init
  append declares "int ${init}(Intr intr); "
  append calls "if (${init}(intr)!=TCL_OK) return TCL_ERROR; "
}
compile "'-DASSOCDECLARES=$declares'" "'-DASSOCCALLS=$calls'" -c -o [
  export object
] [
  export implementation
] -- -list [
  import interface
] [
  export interface
] $include/unix/$base.sys
Package.
compile -ld -o [export package] -list [import object] [export object]
Script.
rule clean :: {} "
  -rm [export object] [import object]
  -rm $test/wyrm-assoc.TESTING
  -rm $test/wyrm-assoc-btree.TESTING
  -rm $test/wyrm-assoc-hash.TESTING
  -rm $test/wyrm-assoc-splay.TESTING
  -rm $test/wyrm-oav.TESTING
"
   

WyrmAssocMapType

   
top

1 :: Generic interface to associative maps.

Copyright (C) 2002, 2004 SM Ryan

Wyrmwif Tcl extensions. For non-profit uses only, provided this copyright is preserved on all copies, this work may be freely copied, modified, redistributed, compiled, and incorporated in other works. This work is distributed with no warranty of any kind; no author or distributor accepts any responsibility for the consequences of using it, or for whether it serves any particular purpose or works at all, unless he or she says so in writing.

 
section    top

2. Description :: Associations map an arbitrary key string into a data string. The map may be internal, stored entirely in main memory, or external, in a file read and written as needed. An internal map is represented by a list of an even number of elements, key value pairs, such as produced by [array get arrayname] (with optional intermingled flags). Such a list can be passed to associative map routines, and it will be converted internally to a map. An external map representation is a list of exactly one element which is the path to the file. A string representation which cannot be a list, or has an odd number of elements greater than one is an invalid representation.

Unlike lists, associative map objects in the C api can be shared; modifications to a map in one variable can change the value of another variable without notice. The Tcl assoc and vassoc commands duplicate a shared mapping before modifying it, so that the Tcl api does not permit sharing.

 
section    top

3. Associative map type :: The string representation of an associative map is a one element or even numberred element list. The internal representation is specific to the map implementation. The object type is a pointer to the WyrmAssocMapType structure for the implementation which includes the Tcl object type as a prefix. The type name always begins with "wyrm.assoc. ...".

wyrm-assoc does not implement a mapping itself. Rather it provides a common interface to the variety of implementations.

Properties of associative mappings:

Internal or external.
An internal mapping exist only memory and one process; an external mapping is in file and can be shared by other processes.
String representation.
An internal mapping string representation of an even number of elements; an external mapping string representation is a one element list which is the file path.
Entries.
A mapping has zero or more entries. An entry is a unique key string, a data string, and flags packed into an integer. The string representation of an internal mapping is a list of key-value pairs. If an element has nonzero flags, that is included in the string representation with the pseudo key '[]'.
Sorting and Traversal.
A mapping can be unsorted, sorted, or sorted and reverse sorted. A nonempty sorted mapping has a first and last key. Given any key except the last key, a sorted mapping can return the next key; a reverse sorted mapping can also give the previous key of any key except the first. An sorted mapping can find approximate keys: the greatest key, if any, less than or equal a given key, otherwise the first key. Unsorted mappings do not have to define first, last, next, and previous keys, but if they do, first-next-last must traverse all keys in an implementation defined order; if previous is defined, last-previous-first must also traverse all keys.
Enumeration.
Sorted and unsorted mappings must provide an enumerator of all the elements. (Sorting mappings may use a generic first-next-last enumeration.) During an enumeration only the enumerate and enumerateEnd methods will be called; the mapping is allowed to place itself in a special enumeration state that is incompatiable with normal operations.
Modifiable.
Internal mappings are modifiable. External mappings can be modifiable or read only, depending on the implementation and how the mapping is created.
type struct WyrmAssocMapType—Associative map type
type struct tcl—Tcl object type
chars name—Type name, must begin with "wyrm.assoc."...
proc void freeIntRepProc—Release all storage, close files, etc.
io Obj objPtr—The mapping being freed.
Memory. All internal allocation not accessible any other way are freed.
proc void dupIntRepProc—Duplicate internal storage from srcPtr to dupPtr.
input Obj srcPtr—Original mapping.
output Obj dupPtr—Duplicate mapping.
proc void updateStringProc—Update the string representation.
io Obj mapping—The mapping being displayed.
Memory. String representation is heap allocated.
proc int setFromAnyProc—Construct the internal representation from the string.
output Intr intr—Error message return; may be NULL.
io Obj objPtr—The mapping being synthesised.
proc Obj update—Update string from wyrm_assocUpdate. NULL if updateStringProc is not wyrm_assocUpdate.
input Obj obj—The mapping being displayed.
output Tcl_DString* S—Receives string representation.
proc Obj internal—Update internal representation from wyrm_assocInternal. NULL if setFromAnyProc does not call wyrm_assocInternal.
output Intr intr—Error message return; may be NULL.
io Obj mapping—The mapping being updated.
io ptr cookie—Passed through to wyrm_assocInternalEntry.
proc Obj dump—Format the mapping in some implementation fashion.
Memory. Caller decrements reference count when done.
output Intr intr—Error message return; may be NULL.
input Obj base—The mapping being dumped.
input int N—Number of additional arguments. May be zero.
input Obj* P—Additional arguments. May be null if N is zero.
long magic—Type positive magic number if an external mapping, used to identify files. Must be zero for an internal mapping.
proc Obj new—Construct the internal representation from the base and return it; return NULL on error.
Memory. Caller decrements reference count when done.
output Intr intr—Error message return; may be NULL.
io Obj base—The base object mapping being synthesised.
input int N—Number of additional arguments. May be zero.
input Obj* P—Additional arguments. May be null if N is zero.
proc int empty—If the mapping is currently empty; TCL_OK if true, TCL_BREAK if not, or TCL_ERROR.
output Intr intr—Error message return; may be NULL.
input Obj mapping—The mapping being queried.
proc int first—Return the key of the first element, or NULL if error.
Memory. Caller decrements.
output Intr intr—Error message return; may be NULL.
input Obj mapping—The mapping being queried.
proc int last—Return the key of the last element, or NULL if error.
Memory. Caller decrements.
output Intr intr—Error message return; may be NULL.
input Obj mapping—The mapping being queried.
proc int next—Return the key of the next element after the given one, or NULL if error.
Memory. Caller decrements.
output Intr intr—Error message return; may be NULL.
input Obj mapping—The mapping being queried.
input Obj key—Key to search for.
proc int previous—Return the key of the previous element before the given one, or NULL if error.
Memory. Caller decrements.
output Intr intr—Error message return; may be NULL.
input Obj mapping—The mapping being queried.
input Obj key—Key to search for.
proc int enumerateBegin—Begin an enumeration of the mapping; returns an enumeration cooke or NULL if error.
output Intr intr—Error message return; may be NULL.
input Obj mapping—The mapping being queried.
input Obj firstHint—Suggested first key if nonnull. The enumerator expects no key less than this.
output bool* sorted—Keys are enumerated strictly increasing.
proc int enumerate—Get the next element of the mapping; return TCL_OK. TCL_ERROR, or TCL_BREAK at end.
output Intr intr—Error message return; may be NULL.
input Obj mapping—The mapping being queried.
io ptr cookie—Enumeration magic cookie.
output Obj* key—Enumerated key if TCL_OK.
Memory. Caller decrements refcount.
output Obj* data—Enumerated data if TCL_OK.
Memory. Caller decrements refcount.
output int* flags—Enumerated flags if TCL_OK.
proc int enumerateEnd—End an enumeration of the mapping; returns TCL_ERROR or TCL_OK.
output Intr intr—Error message return; may be NULL.
input Obj mapping—The mapping being queried.
input ptr cookie—Enumeration magic cookie.
proc int get—Get information from the keyed element; TCL_OK or TCL_ERROR.
output Intr intr—Error message return; may be NULL.
input Obj mapping—The mapping being queried.
input Obj seek—The key of the element to get. If the mapping is sorted, this may be an approximate key.
output Obj *key—The key of the position if not NULL.
Memory. Decrement reference count to release.
output Obj *data—The data of the position or NULL.
Memory. Decrement reference count to release.
output int *flags—The flags of the position or -1.
proc int put—Put information into the mapping; TCL_OK or TCL_ERROR.
output Intr intr—Error message return; may be NULL.
io Obj mapping—The mapping being modified.
input Obj key—Store in this key's position, adding the key if necessary. If null, store in the current position; NULL if error.
Memory. Reference count incremented if the mapping retains this object.
input Obj data—If nonnull, store this data.
Memory. Reference count incremented if the mapping retains this object.
input int flags—If nonnegative, store these flags.
proc int delete—Delete the key and data; TCL_OK or TCL_ERROR.
output Intr intr—Error message return; may be NULL.
io Obj mapping—The mapping being modified.
input Obj key—Key to search for.
typedef int (*WyrmAssocEnumerator)(ptr context,Obj key,Obj data,int flags,bool sorted);
typedef struct {
	<Tcl object type prefix>
	<Mapping identification protocol>
	<Mapping status>
	<Traversing the map protocol>
	<Enumeration protocol>
	<Input/output protocol>
} WyrmAssocMapType;
<Flags>
 
section    top
type enum WyrmAssocFlags
wyrm_assocFlagCompressKey—Request key compression.
Enumeration. 0x01
wyrm_assocFlagCompressData—Request data compression.
Enumeration. 0x02
wyrm_oavFlagDelegate—OAV delegation.
Enumeration. 0x04
wyrm_oavFlagSubst—Do OAV %-substitutions.
Enumeration. 0x08
wyrm_oavFlagActive—Data is active string.
Enumeration. 0x10
wyrm_oavResumption—Data is an OAV resumption.
Enumeration. 0x20
enum WyrmAssocFlags {
	wyrm_assocFlagCompressKey = 0x01,
	wyrm_assocFlagCompressData = 0x02,
	wyrm_oavFlagDelegate = 0x04,
	wyrm_oavFlagSubst = 0x08,
	wyrm_oavFlagActive = 0x010,
	wyrm_oavResumption = 0x20,
};
 
section    top
Tcl_ObjType tcl;
void (*update)(Obj obj,Tcl_DString *S);
int (*internal)(Intr intr,Obj obj,ptr cookie,ptr*);
Obj (*dump)(Intr intr,Obj mapping,int N,Obj *P);
 
section    top

6. Mapping identification protocol :: wyrm-assoc requires a mapping to have associative map type, coerced from the string representation if necessary. Internal mappings are coerced to splay trees by default. New external mappings (the file does not exist), are created as (a,b)-trees. if the file in the external mapping already exists, the first sizeof(long) bytes are the magic number for the file; each external mapping implementation has its own unique magic number.

A mapping can also be openned explicitly for string representation for a specific mapping implementation. In this case additional parameters can be specified, such open modes or node sizes. The implementation is responsible for parsing additional arguments after the base value.

The names of type can omit the initial "wyrm.assoc."; it is prefixed to given names if omitted.

The magic number for internal mappings must be zero, and positive for external mappings. (Four ASCII characters will be a positive number.) Negative values are used internally.

protocol mapping identification
protocol internal mapping identification—when an even numbered length list is used as a mapping
proc wyrm_assoc...
mapping—Key/data pairs list
proc (wyrm.assoc.splay).setFromAnyProc—Find the type
protocol external mapping identification—when a one element list is used as a mapping
proc wyrm_assoc...
mapping—Path in list
proc (wyrm.assoc.btree).setFromAnyProc—Find the type
protocol external mapping identification—when a one element list is used as a mapping
proc wyrm_assoc...
mapping—Path in list
proc (wyrm.assoc.btree).setFromAnyProc
protocol open mapping—explicitly open a mapping
proc wyrm_assocNew
name—Type name.
base—The object to convert to a mapping.
proc type.open
objPtr—base object
long magic;
Obj (*new)(Intr intr,Obj base,int N,Obj *P);
 
section    top

7 :: Mappings are not explicitly closed, but are released when the last reference is cleared. What exactly is released is specific to the implementation.

protocol release mapping
proc Tcl_DecrRefCount
proc obj->typePtr->freeIntRepProc
 
section    top

8 :: As part of the Tcl type, the implementation must include a method to rebuild the string implementation. Internal mappings can define the method update and use the routine wyrm_assocUpdate in the Tcl type. wyrm_assocUpdate handles the mechanics of the string representation. The update method can then use wyrm_assocUpdateEntry to format a key, data, and flags into the string representation.

protocol update mapping with wyrm_assocUpdateEntry
proc Tcl_GetStringFromObj
proc obj->typePtr->updateStringProc is wyrm_assocUpdate
proc obj->wyrm assoc typePtr->update
proc wyrm_assocUpdateEntry
 
section    top

9 :: As part of the Tcl type, the implementation must include a method to build the internal implementation from the string. Internal mappings can define the method internal and use the routine wyrm_assocInternal called from its setFromAnyProc. wyrm_assocInternal handles the mechanics of the internal representations. The internal method then uses wyrm_assocInternalEntry to get the key, data, and flags of each entry.

protocol update mapping with wyrm_assocInternalEntry
proc obj->typePtr->setFromAnyProc calls wyrm_assocInternal
proc obj->wyrm assoc typePtr->internal
proc wyrm_assocInternalEntry
 
section    top
int (*empty)(Intr intr,Obj mapping);
 
section    top
protocol traverse mapping
protocol random positionning—All mappings.
proc get
protocol approximate random positionning—The mapping is sorted.
proc get
proc next
proc previous
protocol forward scan
proc first
proc next
protocol reverse scan—The mapping is reverse sorted.
proc last
proc previous
Obj (*first)(Intr intr,Obj mapping);
Obj (*last)(Intr intr,Obj mapping);
Obj (*next)(Intr intr,Obj mapping,Obj key);
Obj (*previous)(Intr intr,Obj mapping,Obj key);
 
section    top
protocol enumerate mapping
protocol enumerate to end—All mappings.
proc enumerateBegin—Nonnull cookie: begin enumeration; all other protocols may be disabled.
proc enumerate—Returns TCL_OK.
cookie
key—Nonnull.
data—Nonnull.
flags—Nonnull.
proc enumerate—Continued....
proc enumerate—Returns TCL_BREAK.
cookie
key—Null.
data—Null.
flags—Null.
proc enumerateEnd—All other protocols are enabled.
cookie
protocol enumerate to error—All mappings.
proc enumerateBegin—Nonnull cookie: begin enumeration; all other protocols may be disabled.
proc enumerate—Returns TCL_OK.
cookie
key—Nonnull.
data—Nonnull.
flags—Nonnull.
proc enumerate—Continued....
proc enumerate—Returns TCL_ERROR.
cookie
key—Null.
data—Null.
flags—Null.
proc enumerateEnd—All other protocols are enabled.
cookie
protocol enumeration failure—All mappings.
proc enumerateBegin—Null cookie: enumeration failed; all other protocols remain enabled.
ptr (*enumerateBegin)(Intr intr,Obj mapping,Obj firstHint,bool *sorted);
int (*enumerate)(Intr intr,Obj mapping,ptr cookie,Obj *key,Obj *data,int *flags);
int (*enumerateEnd)(Intr intr,Obj mapping,ptr cookie);
 
section    top

13. Input/output protocol :: An associative mapping is always readable, but it might not be writable.

The contents of a mapping can be gotten if the mapping is non-empty. Each I/O access has the key of the mapped item that accessed; for a sorted get, if the key does not exactly exist, a nearby key can be returned instead. A put will modify an existing item with the same key, or it will create a new item with the key; a delete can only refer to an existing item. The get may return the same object as is already in the mapping; in this case its reference count will be at least two, once for owned by the mapping, and once for the returned value. The Tcl_IsShared will detect these cases, and modifiers of Tcl object can duplicate it and then modift the duplicate.

If a Tcl shared object is modified, wyrm-assoc does not guarentee the modifications will be retained. If the mapping is read-only, it is not guarenteed the modification will even be possible.

On a put the collection will first position itself at the given key, and modify the data at that position. If the key is not in the mapping, it will be added, and position of that added key modified. The mapping may retain the put object or a duplicate ofit, in which case the object will have a reference count of at least two (for the mapping and the caller) which signals it is shared.

To update data, it should be gotten, duplicated as necessary, and then put, rather than trying to modify the gotten data and hoping the changes propagate into the mapping.

An item can be deleted; the key will no longer appear in a traversal, a get of the key will fail, and if the key or data were objects their reference counts are decremented to note they are no longer shared by the mapping.

protocol input/output
protocol read approximate—The mapping is sorted.
proc get
input seek—Approximate sought key.
output key—key = seek or nearby
protocol get exact
proc get
input seek—Sought key
output key—key = seek
protocol put at key
proc put
input key—Put key.
input data—Put data.
input flags—Put flags.
protocol delete key
proc delete
input key—Delete key.
int (*get)(Intr intr,Obj mapping,Obj seek,Obj *key,Obj *data,int *flags);
int (*put)(Intr intr,Obj mapping,Obj key,Obj data,int flags);
int (*delete)(Intr intr,Obj mapping,Obj key);
 
section    top

#ifndef WYRM_ASSOC_H
#define WYRM_ASSOC_H

	//	wyrm-assoc.dna - Copyright (C) 2002, 2004 SM Ryan.  All rights reserved.'

	#include "wyrmwif.h"

	<Associative map type>
	<New mapping interface>
	<Mapping status interface>
	<Mapping traversal interface>
	<Mapping I/O interface>
	<Mapping enumeration interface>
	<Package initialisation interface>
	<wyrm-assoc.h for implementors>
#endif

		
 
section    top

#ifdef WYRM_ASSOC_H_IMPLEMENTORS
	<Declare implementor interface>
#endif

		
 
section    top

static const char COPYRIGHT[] = "wyrm-assoc.dna - Copyright (C) 2002, 2004 SM Ryan.  All rights reserved.";

#define WYRM_ASSOC_H_IMPLEMENTORS
#include "wyrm-assoc.h"
#include "wyrm-assoc.sys"
#include "wyrm-io.h"

<Static declarations>
static bool RW = 1,RO = 0;
<wyrm_assocUpdate>
<wyrm_assocInternal>
<wyrm_sortedEnumerate>
<wyrm_assocNew>
<coerceToMapping>
<Pass through interface routines>
<wyrm_assocKey>
<wyrm_assocData>
<wyrm_assocFlags>
<wyrm_assocFilter>
<wyrm_assocFilterGlob>
<wyrm_assocFilterRE>
<Verify a path is allowed in a slave interpretter>
<package>
<assoc command>

		
 
section    top
void wyrm_assocUpdate(Obj mapping);
void wyrm_assocUpdateEntry(Tcl_DString *S,chars key,Obj data,int flags);
 
section    top

void wyrm_assocUpdate(Obj mapping) {
	Tcl_DString D; Tcl_DStringInit(&D);
	((WyrmAssocMapType*)(mapping->typePtr))->update(mapping,&D);
	mapping->length = Tcl_DStringLength(&D);
	mapping->bytes = nheap(mapping->length+1,char);
	strcpy(mapping->bytes,Tcl_DStringValue(&D));
	Tcl_DStringFree(&D);
}

void wyrm_assocUpdateEntry(Tcl_DString *S,chars key,Obj data,int flags) {
	chars d = data ? Tcl_GetString(data) : "";
	bool flagged = flags>0 || streq(key,"[]");
	if (flagged) {
		char B[20];
		Tcl_DStringAppendElement(S,"[]");
		sprintf(B,"%d",flags<0?0:flags);
		Tcl_DStringAppendElement(S,B);
	}
	Tcl_DStringAppendElement(S,key);
	Tcl_DStringAppendElement(S,d);
}

		
 
section    top
typedef int (*WyrmAssocScanner)(ptr context,ptr cookie);

int wyrm_assocInternal(Intr intr,Obj mapping,Tcl_ObjType *requiredType);
int wyrm_assocParseList(Intr intr,int N,Obj *P,WyrmAssocScanner scanner,ptr context);
bool wyrm_assocInternalEntry(ptr cookie,Obj *key,Obj *data,int *flags);
 
section    top

typedef struct {
	int N; chars *P; Obj *Q; int i;
} InternalCookie;

int wyrm_assocInternal(Intr intr,Obj mapping,Tcl_ObjType *requiredType) {
	int rc;
	if (mapping->typePtr!=requiredType) {
		InternalCookie cookie; zero(1,InternalCookie,&cookie);
		if ((rc=Tcl_SplitList(intr,Tcl_GetString(mapping),&cookie.N,(CONST char***)&cookie.P))!=TCL_OK) {
			;
		}else if (cookie.N%2) {
			rc = TCL_ERROR;
			rprintf(intr,"mapping has an odd number of elements");
		}else {
			ptr internal; rc = ((WyrmAssocMapType*)requiredType)->internal(intr,mapping,&cookie,&internal);
			if (rc==TCL_OK) {
				if ((mapping)->typePtr && (mapping)->typePtr->freeIntRepProc)
					(mapping)->typePtr->freeIntRepProc((mapping));
				(mapping)->typePtr = requiredType;
				mapping->internalRep.otherValuePtr = internal;
			}
		}
		dispose(cookie.P);
	}else {
		rc = TCL_OK;
	}
	return rc;
}

int wyrm_assocParseList(Intr intr,int N,Obj *P,WyrmAssocScanner scanner,ptr context) {
	if (N%2) {
		rprintf(intr,"mapping has an odd number of elements");
		return TCL_ERROR;
	}else {
		InternalCookie cookie; zero(1,InternalCookie,&cookie);
		cookie.N = N; cookie.Q = P; cookie.i = 0;
		return scanner(context,&cookie);
	}
}

bool wyrm_assocInternalEntry(ptr cookie0,Obj *key,Obj *data,int *flags) {
	InternalCookie *cookie = cookie0;
	if (cookie->i>=cookie->N) return false;
	if (cookie->P) {
		if (streq(cookie->P[cookie->i],"[]")) {
			*flags = strtol(cookie->P[cookie->i],0,0);
			cookie->i += 2;
		}else
			*flags = 0;
	}else {
		chars q = Tcl_GetString(cookie->Q[cookie->i]);
		if (streq(q,"[]")) {
			*flags = strtol(q,0,0);
			cookie->i += 2;
		}else
			*flags = 0;
	}
	if (cookie->i>=cookie->N) {
		*key = incr(Tcl_NewObj());
		*data = incr(Tcl_NewObj());
	}else if (cookie->P) {
		*key = incr(Tcl_NewStringObj(cookie->P[cookie->i],-1));
		*data = incr(Tcl_NewStringObj(cookie->P[cookie->i+1],-1));
		cookie->i += 2;
	}else {
		*key = incr(cookie->Q[cookie->i]);
		*data = incr(cookie->Q[cookie->i+1]);
		cookie->i += 2;
	}
	return true;
}

		
 
section    top
ptr wyrm_sortedEnumerateBegin(Intr intr,Obj mapping,Obj firstHint,bool *sorted);
int wyrm_sortedEnumerate(Intr intr,Obj mapping,ptr cookie,Obj *key,Obj *data,int *flags);
int wyrm_sortedEnumerateEnd(Intr intr,Obj mapping,ptr cookie);
 
section    top

ptr wyrm_sortedEnumerateBegin(Intr intr,Obj mapping,Obj firstHint,bool *sorted) {
	Obj first,last;
	*sorted = true;
	if (firstHint) {
		if (wyrm_assocGet(intr,mapping,firstHint,&first,0,0)!=TCL_OK) first = 0;
	}else {
		first = wyrm_assocFirst(intr,mapping);
	}
	last = first ? wyrm_assocLast(intr,mapping) : 0;
	if (first) {
		Obj *context = nheap(2,Obj); context[0] = first; context[1] = last;
		return context;
	}else {
		return 0;
	}
}
int wyrm_sortedEnumerate(Intr intr,Obj mapping,ptr cookie,Obj *key,Obj *data,int *flags) {
	Obj *context = cookie;
	*key = *data = 0; *flags = -1;
	if (!context || !context[0]) {
		return TCL_BREAK;
	}else {
		int rc = wyrm_assocGet(intr,mapping,context[0],key,data,flags); Obj next;
		if (rc==TCL_ERROR) next = 0;
		else if (streq(Tcl_GetString(context[0]),Tcl_GetString(context[1]))) next = 0;
		else {next = wyrm_assocNext(intr,mapping,context[0]); if (!next) rc = TCL_ERROR;}
		decr(context[0]); context[0] = next;
		return rc;
	}
}
int wyrm_sortedEnumerateEnd(Intr intr,Obj mapping,ptr cookie) {
	Obj *context = cookie;
	if (context) {decr(context[0]); decr(context[1]);}
	return TCL_OK;
}

		
   
   

Assign Mapping Type

   
top
Obj wyrm_assocNew(Intr intr,chars typename,Obj base,int N,Obj *P);
 
section    top
proc Obj wyrm_assocNew—Construct the internal representation from the base and return it; return NULL on error.
Memory. Caller decrements reference count when done.
output Intr intr—Error message return; may be NULL.
io Obj base—The base object mapping being synthesised.
input int N—Number of additional arguments. May be zero.
input Obj* P—Additional arguments. May be null if N is zero.

Obj wyrm_assocNew(Intr intr,chars typename,Obj base,int N,Obj *P) {
	static char prefix[] = "wyrm.assoc.";
	chars typename0 = typename;
	WyrmAssocMapType *type;
	bool inventedBase = base==0;
	Obj result = 0;
	if (!strbegins(prefix,typename)) {
		typename0 = nheap(strlen(typename)+sizeof prefix,char);
		sprintf(typename0,"%s%s",prefix,typename);
	}
	type = (WyrmAssocMapType*)Tcl_GetObjType(typename0);
	if (!type) {
		rprintf(intr,"%s type not found",typename);
	}else {
		if (inventedBase) {
			base = incr(Tcl_NewObj());
		}else if (Tcl_IsShared(base)) {
			base = incr(Tcl_DuplicateObj(base));
			inventedBase = true;
		}
		result = type->new(intr,base,N,P);
	}
	if (inventedBase) decr(base);
	if (typename!=typename0) dispose(typename0);
	return result;
}

		
 
section    top
protocol
input obj—Object being queried.
input obj—Object being queried.

static Obj coerceToMapping(Intr intr,Obj obj) {
	int N; chars *P = 0; Obj fn = 0; Tcl_Channel channel = 0; int rc;
	long magic; int n; Obj *q,T=0; Tcl_ObjType *type;
	
	if (obj->typePtr && strbegins("wyrm.assoc.",obj->typePtr->name)) {
		type = (obj->typePtr);
	}else if ((rc=Tcl_SplitList(0,Tcl_GetString(obj),&N,(CONST char***)&P))!=TCL_OK) {
		rprintf(intr,"mapping is not a list");
		type = 0;
	}else if (N%2==0) {
		type = Tcl_GetObjType("wyrm.assoc.splay");
		if (!type) rprintf(intr,"wyrm.assoc.splay type not found");
	}else if (N>1) {
		rprintf(intr,"mapping has an odd number of elements");
		type = 0;
	}else if (fn=incr(Tcl_NewStringObj(P[0],-1)), Tcl_FSAccess(fn,0)!=0) {
		type = Tcl_GetObjType("wyrm.assoc.btree");
		if (!type) rprintf(intr,"wyrm.assoc.btree type not found");
	}else if ((channel=Tcl_OpenFileChannel(intr,*P,"r",0))==0) {
		rprintf(intr,"could not open file for reading: %s",*P);
		type = 0;
	}else if ((n=cread(channel,&magic,sizeof(long)))<sizeof(long)) {
		rprintf(intr,"cannot read file magic number: %s",*P);
		type = 0;
	}else if (T=incr(Tcl_NewObj()),Tcl_AppendAllObjTypes(0,T)==TCL_OK
			&& Tcl_ListObjGetElements(0,T,&n,&q)==TCL_OK
	) {
		magic = ntohl(magic);
		for (; n>0; n--,q++) {
			if (strbegins("wyrm.assoc.",Tcl_GetString(*q))) {
				type = Tcl_GetObjType(Tcl_GetString(*q));
				if (((WyrmAssocMapType*)type)->magic==magic) break;
				type = 0;
			}
		}
		if (!type) rprintf(intr,"cannot identify the magic: %04X",magic);
	}else {
		rprintf(intr,"could not get tcl types");
		type = 0;
	}
	
	dispose(P); decr(fn); decr(T);
	if (channel) Tcl_Close(intr,channel);

	if (!type) return 0;
	else if (type==obj->typePtr) return obj;
	else if (Tcl_ConvertToType(intr,obj,type)==TCL_OK) return obj;
	else return 0;
}

		
   
   

Pass Through Interface

   
top
int wyrm_assocEmpty(Intr intr,Obj mapping);
Obj wyrm_assocDump(Intr intr,Obj mapping,int N,Obj *P);
 
section    top
Obj wyrm_assocFirst(Intr intr,Obj mapping);
Obj wyrm_assocLast(Intr intr,Obj mapping);
Obj wyrm_assocNext(Intr intr,Obj mapping,Obj key);
Obj wyrm_assocPrevious(Intr intr,Obj mapping,Obj key);
 
section    top
int wyrm_assocGet(Intr intr,Obj mapping,Obj seek,Obj *key,Obj *data,int *flags);
int wyrm_assocPut(Intr intr,Obj mapping,Obj key,Obj data,int flags);
int wyrm_assocDelete(Intr intr,Obj mapping,Obj key);
 
section    top

Obj wyrm_assocDump(Intr intr,Obj mapping,int N,Obj *P) {
	return (mapping=coerceToMapping(intr,mapping))
		? ((WyrmAssocMapType*)(mapping->typePtr))->dump(intr,mapping,N,P)
		: 0;
}

int wyrm_assocEmpty(Intr intr,Obj mapping) {
	if (!(mapping=coerceToMapping(intr,mapping))) {
		return TCL_ERROR;
	}else {
		return ((WyrmAssocMapType*)(mapping->typePtr))->empty(intr,mapping);
	}
}
Obj wyrm_assocFirst(Intr intr,Obj mapping) {
	if (!(mapping=coerceToMapping(intr,mapping))) {
		return 0;
	}else {
		return ((WyrmAssocMapType*)(mapping->typePtr))->first(intr,mapping);
	}
}
Obj wyrm_assocLast(Intr intr,Obj mapping) {
	if (!(mapping=coerceToMapping(intr,mapping))) {
		return 0;
	}else {
		return ((WyrmAssocMapType*)(mapping->typePtr))->last(intr,mapping);
	}
}
Obj wyrm_assocNext(Intr intr,Obj mapping,Obj key) {
	if (!(mapping=coerceToMapping(intr,mapping))) {
		return 0;
	}else {
		return ((WyrmAssocMapType*)(mapping->typePtr))->next(intr,mapping,key);
	}
}
Obj wyrm_assocPrevious(Intr intr,Obj mapping,Obj key) {
	if (!(mapping=coerceToMapping(intr,mapping))) {
		return 0;
	}else {
		return ((WyrmAssocMapType*)(mapping->typePtr))->previous(intr,mapping,key);
	}
}

int wyrm_assocGet(Intr intr,Obj mapping,Obj seek,Obj *key,Obj *data,int *flags) {
	if (!(mapping=coerceToMapping(intr,mapping))) {
		return TCL_ERROR;
	}else {
		return ((WyrmAssocMapType*)(mapping->typePtr))->get(intr,mapping,seek,key,data,flags);
	}
}
int wyrm_assocPut(Intr intr,Obj mapping,Obj key,Obj data,int flags) {
	if (!(mapping=coerceToMapping(intr,mapping))) {
		return TCL_ERROR;
	}else {
		return ((WyrmAssocMapType*)(mapping->typePtr))->put(intr,mapping,key,data,flags);
	}
}
int wyrm_assocDelete(Intr intr,Obj mapping,Obj key) {
	if (!(mapping=coerceToMapping(intr,mapping))) {
		return TCL_ERROR;
	}else {
		return ((WyrmAssocMapType*)(mapping->typePtr))->delete(intr,mapping,key);
	}
}

		
   
   

Auxillary I/O Routines

   
top
Obj wyrm_assocKey(Intr intr,Obj mapping,Obj key);
Obj wyrm_assocData(Intr intr,Obj mapping,Obj key);
int wyrm_assocFlags(Intr intr,Obj mapping,Obj key);
		
 
section    top
int wyrm_assocFilter(Intr intr,Obj mapping,bool name,Obj first,Obj *list,int *N,Obj **P);
int wyrm_assocFilterGlob(Intr intr,Obj mapping,bool name,Obj pattern,Obj *list,int *N,Obj **P);
int wyrm_assocFilterRE(Intr intr,Obj mapping,bool name,Obj pattern,Obj *list,int *N,Obj **P);
 
section    top
proc Obj wyrm_assocKey—Return the actual key at or near the key.
Memory. Caller decrements reference count when done.
input Obj mapping—Mapping being queried.
input Obj key—Key to seek.

Obj wyrm_assocKey(Intr intr,Obj mapping,Obj key) {
	Obj actualkey = 0;
	int rc = wyrm_assocGet(intr,mapping,key,&actualkey,0,0);
	if (rc==TCL_ERROR) {decr(actualkey); return 0;}
	else return actualkey;
}

		
 
section    top
proc Obj wyrm_assocData—Return the actual data at the key or NULL.
Memory. Caller decrements reference count when done.
input Obj mapping—Mapping being queried.
input Obj key—Key to seek, or NULL for current position.

Obj wyrm_assocData(Intr intr,Obj mapping,Obj key) {
	Obj actualkey = 0;
	Obj data = 0;
	int rc = wyrm_assocGet(intr,mapping,key,&actualkey,&data,0);
	if (rc==TCL_ERROR) {
		decr(actualkey); decr(data); return 0;
	}else if (!streq(Tcl_GetString(key),Tcl_GetString(actualkey))) {
		rprintf(intr,"missing: %{y}s",key);
		decr(actualkey); decr(data); return 0;
	} else {
		decr(actualkey); return data;
	}
}

		
 
section    top
proc Obj wyrm_assocFlags—Return the actual flags at the key or -1.
input Obj mapping—Mapping being queried.
input Obj key—Key to seek.

int wyrm_assocFlags(Intr intr,Obj mapping,Obj key) {
	Obj actualkey = 0;
	int flags = -1;
	int rc = wyrm_assocGet(intr,mapping,key,&actualkey,0,&flags);
	if (rc==TCL_ERROR) {
		decr(actualkey); return -1;
	}else if (!streq(Tcl_GetString(key),Tcl_GetString(actualkey))) {
		rprintf(intr,"missing: %{y}s",key);
		decr(actualkey); return -1;
	} else {
		decr(actualkey); return flags;
	}
}

		
 
section    top
proc int wyrm_assocFilter—Return a list of keys and data or TCL_ERROR if error.
output Intr intr—For error messages.
input Obj mapping—Mapping being queried.
input bool name—Only names.
input Obj* list—Returned list.
input int* N—Returned list length.
input Obj** P—Returned list elements.

int wyrm_assocFilter(Intr intr,Obj mapping,bool name,Obj first,Obj *list,int *N,Obj **P) {
	*list = 0;
	if (N) *N = 0;
	if (P) *P = 0;
	if (!(mapping=coerceToMapping(intr,mapping))) {
		return TCL_ERROR;
	}else {
		Obj result = 0;
		ptr cookie = 0;
		bool sorted;
		int rc;
		cookie = ((WyrmAssocMapType*)(mapping->typePtr))->enumerateBegin(intr,mapping,first,&sorted);
		if (cookie) {
			Obj key,data; int flags;
			result = incr(Tcl_NewObj());
			while ((rc=((WyrmAssocMapType*)(mapping->typePtr))->
						enumerate(intr,mapping,cookie,&key,&data,&flags))==TCL_OK) {
				if (Tcl_ListObjAppendElement(intr,result,key)!=TCL_OK
						|| !name && Tcl_ListObjAppendElement(intr,result,data)!=TCL_OK
				) {
					rc = TCL_ERROR;
				}
				decr(key); decr(data);
				if (rc==TCL_ERROR) break;
			}
			if (rc==TCL_BREAK) rc = TCL_OK;
			if (((WyrmAssocMapType*)(mapping->typePtr))->enumerateEnd(intr,mapping,cookie)!=TCL_OK)
				rc = TCL_ERROR;
		}else {
			rc = TCL_ERROR;
		}
		if (rc!=TCL_OK) {decr(result); result = 0;}
		*list = result;
		if (result && (N || P))  {
			int N1; Obj *P1; if (!N) N = &N1; if (!P) P = &P1;
			Tcl_ListObjGetElements(0,result,N,P);
		}
		return rc;
	}
}

		
 
section    top
proc Obj wyrm_assocFilterGlob—Return a list of keys and data or TCL_ERROR if error.
output Intr intr—For error messages.
input Obj mapping—Mapping being queried.
input bool name—Only names.
input Obj pattern—Filter pattern.
input Obj* list—Returned list.
input int* N—Returned list length.
input Obj** P—Returned list elements.

int wyrm_assocFilterGlob(Intr intr,Obj mapping,bool name,Obj pattern,Obj *list,int *N,Obj **P) {
	*list = 0;
	if (N) *N = 0;
	if (P) *P = 0;
	if (!(mapping=coerceToMapping(intr,mapping))) {
		return TCL_ERROR;
	}else {
		chars glob = Tcl_GetString(pattern);
		Obj result = 0;
		Obj prefix = 0;
		ptr cookie = 0;
		bool sorted;
		int rc;
		chars p; for (p=glob; *p; p++) if (*p=='*' || *p=='?' || *p=='\\' || *p=='[' || *p==']') break;
		if (p>glob) prefix = incr(Tcl_NewStringObj(glob,p-glob));
		cookie = ((WyrmAssocMapType*)(mapping->typePtr))->enumerateBegin(intr,mapping,prefix,&sorted);
		if (cookie) {
			Obj key,data; int flags;
			if (!sorted) {decr(prefix); prefix = 0;}
			result = incr(Tcl_NewObj());
			while ((rc=((WyrmAssocMapType*)(mapping->typePtr))->
						enumerate(intr,mapping,cookie,&key,&data,&flags))==TCL_OK) {
				if (sorted && prefix && strcmp(Tcl_GetString(prefix),Tcl_GetString(key))>0) break;
				rc = Tcl_StringMatch(Tcl_GetString(key),glob);
				if (rc>0 && Tcl_ListObjAppendElement(intr,result,key)!=TCL_OK) rc = -1;
				if (!name && rc>0 && Tcl_ListObjAppendElement(intr,result,data)!=TCL_OK) rc = -1;
				decr(key); decr(data);
				rc = rc<0 ? TCL_ERROR : TCL_OK;
				if (rc==TCL_ERROR) break;
			}
			if (rc==TCL_BREAK) rc = TCL_OK;
			if (((WyrmAssocMapType*)(mapping->typePtr))->enumerateEnd(intr,mapping,cookie)!=TCL_OK)
				rc = TCL_ERROR;
		}else {
			rc = TCL_ERROR;
		}
		decr(prefix);
		if (rc!=TCL_OK) {decr(result); result = 0;}
		*list = result;
		if (result && (N || P))  {
			int N1; Obj *P1; if (!N) N = &N1; if (!P) P = &P1;
			Tcl_ListObjGetElements(0,result,N,P);
		}
		return rc;
	}
}

		
 
section    top
proc Obj wyrm_assocFilterRE—Return a list of keys and data or TCL_ERROR if error.
output Intr intr—For error messages.
input Obj mapping—Mapping being queried.
input bool name—Only names.
input Obj pattern—Filter pattern.
input Obj* list—Returned list.
input int* N—Returned list length.
input Obj** P—Returned list elements.

int wyrm_assocFilterRE(Intr intr,Obj mapping,bool name,Obj regexp,Obj *list,int *N,Obj **P) {
	*list = 0;
	if (N) *N = 0;
	if (P) *P = 0;
	if (!(mapping=coerceToMapping(intr,mapping))) {
		return TCL_ERROR;
	}else {
		Obj result = 0;
		Obj prefix = 0;
		ptr cookie = 0;
		bool sorted;
		int rc;
		chars re = Tcl_GetString(regexp);
		if (*re=='^') {
			chars p; for (p= re; *p; p++)
				if (
						*p=='*' || *p=='+' || *p=='?' || *p=='\\' || *p=='.' || *p=='$'
						|| *p=='[' || *p==']' || *p=='{' || *p=='}' || *p=='(' || *p==')'
				) break;
			if (p>re+1) prefix = incr(Tcl_NewStringObj(re+1,p-re));
		}
		cookie = ((WyrmAssocMapType*)(mapping->typePtr))->enumerateBegin(intr,mapping,prefix,&sorted);
		if (cookie) {
			Obj key,data; int flags;
			if (!sorted) {decr(prefix); prefix = 0;}
			result = incr(Tcl_NewObj());
			while ((rc=((WyrmAssocMapType*)(mapping->typePtr))->
						enumerate(intr,mapping,cookie,&key,&data,&flags))==TCL_OK) {
				if (sorted && prefix && !strbegins(Tcl_GetString(prefix),Tcl_GetString(key))) break;
				rc =Tcl_RegExpMatchObj(intr,key,regexp);
				if (rc>0 && Tcl_ListObjAppendElement(intr,result,key)!=TCL_OK) rc = -1;
				if (!name && rc>0 && Tcl_ListObjAppendElement(intr,result,data)!=TCL_OK) rc = -1;
				decr(key); decr(data);
				rc = rc<0 ? TCL_ERROR : TCL_OK;
				if (rc==TCL_ERROR) break;
			}
			if (rc==TCL_BREAK) rc = TCL_OK;
			if (((WyrmAssocMapType*)(mapping->typePtr))->enumerateEnd(intr,mapping,cookie)!=TCL_OK)
				rc = TCL_ERROR;
		}else {
			rc = TCL_ERROR;
		}
		decr(prefix);
		if (rc!=TCL_OK) {decr(result); result = 0;}
		*list = result;
		if (result && (N || P))  {
			int N1; Obj *P1; if (!N) N = &N1; if (!P) P = &P1;
			Tcl_ListObjGetElements(0,result,N,P);
		}
		return rc;
	}
}

		
   
   

Wyrmassoc Tcl Package

   
top
proc int Wyrmassoc_Init—Initialise the wyrm-assoc package; returns TCL_OK or TCL_ERROR.
io Intr intr—Package initialised in the interpretter.

int Wyrmassoc_Init(Intr intr) {
	ASSOCDECLARES;
	if (Tcl_PkgProvide(intr,"wyrmassoc",VERSION)!=TCL_OK) return TCL_ERROR;
	if (!Tcl_PkgRequire(intr,"wyrmwif","1",false)) return TCL_ERROR;
	Tcl_CreateObjCommand(intr,"::wyrm::assoc",assoc,0,0);
	Tcl_CreateObjCommand(intr,"::wyrm::vassoc",assoc,"vassoc",0);
	ASSOCCALLS;
	if (Tcl_VarEval(intr,
			"namespace eval ::wyrm {namespace export assoc vassoc}\n",
			0)!=TCL_OK) return TCL_ERROR;
	if (wyrm_oavCommandInit(intr)!=TCL_OK) return TCL_ERROR;
	return TCL_OK;
}

int Wyrmassoc_SafeInit(Intr intr) {
	return Wyrmassoc_Init(intr);
}

		
 
section    top
int Wyrmassoc_Init(Intr intr);
int Wyrmassoc_SafeInit(Intr intr);
		
 
section    top
static int assoc(ClientData clientData,Intr intr,int N,Tcl_Obj *const P[]);