Przeglądaj źródła

Fixed issues with tree and null pointer access.

Refactored tree based maps.
Added unit tests.
Brucey 2 lat temu
rodzic
commit
ac4c2501a6
9 zmienionych plików z 464 dodań i 106 usunięć
  1. 3 5
      blitz.mod/tree/tree.h
  2. 8 0
      map.mod/common.bmx
  3. 27 20
      map.mod/intmap.bmx
  4. 4 1
      map.mod/map.bmx
  5. 30 25
      map.mod/map.c
  6. 25 18
      map.mod/objectmap.bmx
  7. 25 18
      map.mod/ptrmap.bmx
  8. 26 19
      map.mod/stringmap.bmx
  9. 316 0
      map.mod/tests/test.bmx

+ 3 - 5
blitz.mod/tree/tree.h

@@ -222,7 +222,7 @@ void avl_del(struct avl_root *entry, struct avl_root **root);
  * @ptr is the pointer to the embedded struct of which the name is
  * provided by @member, and @type indicates the host struct type. */
 #define tree_entry(ptr, type, member)					\
-	((type *)((char *)(ptr)-(uintptr_t)(&((type *)0)->member)))
+	(ptr != NULL ? ((type *)((char *)(ptr)-(uintptr_t)(offsetof(type, member)))) : NULL)
 
 #define avl_entry(ptr, type, member) tree_entry(ptr, type, member)
 
@@ -274,10 +274,8 @@ void avl_del(struct avl_root *entry, struct avl_root **root);
 #define avl_for_each_entry_safe(pos, n, root, member)			\
 	for (pos = tree_entry(TREE_MIN(root), typeof(*pos), member),	\
 		     n = tree_entry(TREE_SUCCESSOR(&pos->member), typeof(*pos), member); \
-	     &pos->member != NULL;					\
-	     pos = n, n = tree_entry(&n->member != NULL?		\
-				     TREE_SUCCESSOR(&n->member): NULL,	\
-				     typeof(*n), member))
+	     pos != NULL && &pos->member != NULL;					\
+	     pos = n, n = (n != NULL && &n->member != NULL) ? tree_entry(TREE_SUCCESSOR(&n->member), typeof(*n), member) : NULL)
 
 #ifdef __cplusplus
 #undef new

+ 8 - 0
map.mod/common.bmx

@@ -0,0 +1,8 @@
+SuperStrict
+
+Struct SavlRoot
+	Field left:SavlRoot Ptr
+	Field right:SavlRoot Ptr
+	Field parent:SavlRoot Ptr
+	Field balance:Int
+End Struct

+ 27 - 20
map.mod/intmap.bmx

@@ -1,21 +1,28 @@
 SuperStrict
 
+Import "common.bmx"
 
 Extern
-	Function bmx_map_intmap_clear(root:Byte Ptr Ptr)
-	Function bmx_map_intmap_isempty:Int(root:Byte Ptr Ptr)
-	Function bmx_map_intmap_insert(key:Int, value:Object, root:Byte Ptr Ptr)
-	Function bmx_map_intmap_contains:Int(key:Int, root:Byte Ptr Ptr)
-	Function bmx_map_intmap_valueforkey:Object(key:Int, root:Byte Ptr Ptr)
-	Function bmx_map_intmap_remove:Int(key:Int, root:Byte Ptr Ptr)
-	Function bmx_map_intmap_firstnode:Byte Ptr(root:Byte Ptr)
-	Function bmx_map_intmap_nextnode:Byte Ptr(node:Byte Ptr)
-	Function bmx_map_intmap_key:Int(node:Byte Ptr)
-	Function bmx_map_intmap_value:Object(node:Byte Ptr)
-	Function bmx_map_intmap_hasnext:Int(node:Byte Ptr, root:Byte Ptr)
-	Function bmx_map_intmap_copy(dst:Byte Ptr Ptr, _root:Byte Ptr)
+	Function bmx_map_intmap_clear(root:SavlRoot Ptr Ptr)
+	Function bmx_map_intmap_isempty:Int(root:SavlRoot Ptr)
+	Function bmx_map_intmap_insert(key:Int, value:Object, root:SavlRoot Ptr Ptr)
+	Function bmx_map_intmap_contains:Int(key:Int, root:SavlRoot Ptr)
+	Function bmx_map_intmap_valueforkey:Object(key:Int, root:SavlRoot Ptr)
+	Function bmx_map_intmap_remove:Int(key:Int, root:SavlRoot Ptr)
+	Function bmx_map_intmap_firstnode:SIntMapNode Ptr(root:SavlRoot Ptr)
+	Function bmx_map_intmap_nextnode:SIntMapNode Ptr(node:SIntMapNode Ptr)
+	Function bmx_map_intmap_key:Int(node:SIntMapNode Ptr)
+	Function bmx_map_intmap_value:Object(node:SIntMapNode Ptr)
+	Function bmx_map_intmap_hasnext:Int(node:SIntMapNode Ptr, root:SavlRoot Ptr)
+	Function bmx_map_intmap_copy(dst:SavlRoot Ptr Ptr, _root:SavlRoot Ptr)
 End Extern
 
+Struct SIntMapNode
+	Field link:SavlRoot
+	Field key:Int
+	Field value:Object
+End Struct
+
 Rem
 bbdoc: A key/value (Int/Object) map.
 End Rem
@@ -43,7 +50,7 @@ Type TIntMap
 	about: #True if @map is empty, otherwise #False.
 	End Rem
 	Method IsEmpty:Int()
-		Return bmx_map_intmap_isempty(Varptr _root)
+		Return bmx_map_intmap_isempty(_root)
 	End Method
 	
 	Rem
@@ -62,7 +69,7 @@ Type TIntMap
 	returns: #True if the map contains @key.
 	End Rem
 	Method Contains:Int( key:Int )
-		Return bmx_map_intmap_contains(key, Varptr _root)
+		Return bmx_map_intmap_contains(key, _root)
 	End Method
 	
 	Rem
@@ -71,7 +78,7 @@ Type TIntMap
 	about: If the map does not contain @key, a #Null object is returned.
 	End Rem
 	Method ValueForKey:Object( key:Int )
-		Return bmx_map_intmap_valueforkey(key, Varptr _root)
+		Return bmx_map_intmap_valueforkey(key, _root)
 	End Method
 	
 	Rem
@@ -170,7 +177,7 @@ Type TIntMap
 	about: If the map does not contain @key, a #Null object is returned.
 	End Rem
 	Method Operator[]:Object(key:Int)
-		Return bmx_map_intmap_valueforkey(key, Varptr _root)
+		Return bmx_map_intmap_valueforkey(key, _root)
 	End Method
 	
 	Rem
@@ -181,7 +188,7 @@ Type TIntMap
 		bmx_map_intmap_insert(key, value, Varptr _root)
 	End Method
 
-	Field _root:Byte Ptr
+	Field _root:SavlRoot Ptr
 
 ?ngcmod
 	Field _modCount:Int
@@ -190,10 +197,10 @@ Type TIntMap
 End Type
 
 Type TIntNode
-	Field _root:Byte Ptr
-	Field _nodePtr:Byte Ptr
+	Field _root:SavlRoot Ptr
+	Field _nodePtr:SIntMapNode Ptr
 	
-	Field _nextNode:Byte Ptr
+	Field _nextNode:SIntMapNode Ptr
 	
 	Method Key:Int()
 		Return bmx_map_intmap_key(_nodePtr)

+ 4 - 1
map.mod/map.bmx

@@ -6,12 +6,15 @@ bbdoc: Data structures/Maps
 End Rem
 Module BRL.Map
 
-ModuleInfo "Version: 1.10"
+ModuleInfo "Version: 1.11"
 ModuleInfo "Author: Mark Sibly"
 ModuleInfo "License: zlib/libpng"
 ModuleInfo "Copyright: Blitz Research Ltd"
 ModuleInfo "Modserver: BRL"
 
+ModuleInfo "History: 1.11"
+ModuleInfo "History: Refactored tree based maps."
+ModuleInfo "History: Added unit tests."
 ModuleInfo "History: 1.10"
 ModuleInfo "History: Changed to SuperStrict."
 ModuleInfo "History: 1.09"

+ 30 - 25
map.mod/map.c

@@ -22,14 +22,16 @@ static int compare_intmap_nodes(const void *x, const void *y) {
 void bmx_map_intmap_clear(struct avl_root ** root) {
 	struct intmap_node *node;
 	struct intmap_node *tmp;
-	avl_for_each_entry_safe(node, tmp, *root, link) {
+	if (*root == 0) return; // already cleared?
+	avl_for_each_entry_safe(node, tmp, *root, link)
+	{
 		avl_del(&node->link, root);
 		GC_FREE(node);
 	}
 }
 
-int bmx_map_intmap_isempty(struct avl_root ** root) {
-	return *root == 0;
+int bmx_map_intmap_isempty(struct avl_root * root) {
+	return root == 0;
 }
 
 void bmx_map_intmap_insert( int key, BBObject *value, struct avl_root ** root ) {
@@ -47,11 +49,11 @@ void bmx_map_intmap_insert( int key, BBObject *value, struct avl_root ** root )
 	}
 }
 
-int bmx_map_intmap_contains(int key, struct avl_root ** root) {
+int bmx_map_intmap_contains(int key, struct avl_root * root) {
 	struct intmap_node node;
 	node.key = key;
 	
-	struct intmap_node * found = (struct intmap_node *)TREE_SEARCH(&node, compare_intmap_nodes, *root);
+	struct intmap_node * found = (struct intmap_node *)TREE_SEARCH(&node, compare_intmap_nodes, root);
 	if (found) {
 		return 1;
 	} else {
@@ -59,11 +61,11 @@ int bmx_map_intmap_contains(int key, struct avl_root ** root) {
 	}
 }
 
-BBObject * bmx_map_intmap_valueforkey(int key, struct avl_root ** root) {
+BBObject * bmx_map_intmap_valueforkey(int key, struct avl_root * root) {
 	struct intmap_node node;
 	node.key = key;
 	
-	struct intmap_node * found = (struct intmap_node *)TREE_SEARCH(&node, compare_intmap_nodes, *root);
+	struct intmap_node * found = (struct intmap_node *)TREE_SEARCH(&node, compare_intmap_nodes, root);
 	
 	if (found) {
 		return found->value;
@@ -142,14 +144,15 @@ static int compare_ptrmap_nodes(const void *x, const void *y) {
 void bmx_map_ptrmap_clear(struct avl_root ** root) {
 	struct ptrmap_node *node;
 	struct ptrmap_node *tmp;
+	if (*root == 0) return; // already cleared?
 	avl_for_each_entry_safe(node, tmp, *root, link) {
 		avl_del(&node->link, root);
 		GC_FREE(node);
 	}
 }
 
-int bmx_map_ptrmap_isempty(struct avl_root ** root) {
-	return *root == 0;
+int bmx_map_ptrmap_isempty(struct avl_root * root) {
+	return root == 0;
 }
 
 void bmx_map_ptrmap_insert( void * key, BBObject *value, struct avl_root ** root ) {
@@ -167,11 +170,11 @@ void bmx_map_ptrmap_insert( void * key, BBObject *value, struct avl_root ** root
 	}
 }
 
-int bmx_map_ptrmap_contains(void * key, struct avl_root ** root) {
+int bmx_map_ptrmap_contains(void * key, struct avl_root * root) {
 	struct ptrmap_node node;
 	node.key = key;
 	
-	struct ptrmap_node * found = (struct ptrmap_node *)TREE_SEARCH(&node, compare_ptrmap_nodes, *root);
+	struct ptrmap_node * found = (struct ptrmap_node *)TREE_SEARCH(&node, compare_ptrmap_nodes, root);
 	if (found) {
 		return 1;
 	} else {
@@ -179,11 +182,11 @@ int bmx_map_ptrmap_contains(void * key, struct avl_root ** root) {
 	}
 }
 
-BBObject * bmx_map_ptrmap_valueforkey(void * key, struct avl_root ** root) {
+BBObject * bmx_map_ptrmap_valueforkey(void * key, struct avl_root * root) {
 	struct ptrmap_node node;
 	node.key = key;
 	
-	struct ptrmap_node * found = (struct ptrmap_node *) TREE_SEARCH(&node, compare_ptrmap_nodes, *root);
+	struct ptrmap_node * found = (struct ptrmap_node *) TREE_SEARCH(&node, compare_ptrmap_nodes, root);
 	
 	if (found) {
 		return found->value;
@@ -261,14 +264,15 @@ static int compare_stringmap_nodes(const void *x, const void *y) {
 void bmx_map_stringmap_clear(struct avl_root ** root) {
 	struct stringmap_node *node;
 	struct stringmap_node *tmp;
+	if (*root == 0) return; // already cleared?
 	avl_for_each_entry_safe(node, tmp, *root, link) {
 		avl_del(&node->link, root);
 		GC_FREE(node);
 	}
 }
 
-int bmx_map_stringmap_isempty(struct avl_root ** root) {
-	return *root == 0;
+int bmx_map_stringmap_isempty(struct avl_root * root) {
+	return root == 0;
 }
 
 void bmx_map_stringmap_insert( BBString * key, BBObject *value, struct avl_root ** root) {
@@ -286,11 +290,11 @@ void bmx_map_stringmap_insert( BBString * key, BBObject *value, struct avl_root
 	}
 }
 
-int bmx_map_stringmap_contains(BBString * key, struct avl_root ** root) {
+int bmx_map_stringmap_contains(BBString * key, struct avl_root * root) {
 	struct stringmap_node node;
 	node.key = key;
 	
-	struct stringmap_node * found = (struct stringmap_node *)TREE_SEARCH(&node, compare_stringmap_nodes, *root);
+	struct stringmap_node * found = (struct stringmap_node *)TREE_SEARCH(&node, compare_stringmap_nodes, root);
 	if (found) {
 		return 1;
 	} else {
@@ -298,11 +302,11 @@ int bmx_map_stringmap_contains(BBString * key, struct avl_root ** root) {
 	}
 }
 
-BBObject * bmx_map_stringmap_valueforkey(BBString * key, struct avl_root ** root) {
+BBObject * bmx_map_stringmap_valueforkey(BBString * key, struct avl_root * root) {
 	struct stringmap_node node;
 	node.key = key;
 	
-	struct stringmap_node * found = (struct stringmap_node *)TREE_SEARCH(&node, compare_stringmap_nodes, *root);
+	struct stringmap_node * found = (struct stringmap_node *)TREE_SEARCH(&node, compare_stringmap_nodes, root);
 	
 	if (found) {
 		return found->value;
@@ -380,14 +384,15 @@ static int compare_objectmap_nodes(const void *x, const void *y) {
 void bmx_map_objectmap_clear(struct avl_root ** root) {
 	struct objectmap_node *node;
 	struct objectmap_node *tmp;
+	if (*root == 0) return; // already cleared?
 	avl_for_each_entry_safe(node, tmp, *root, link) {
 		avl_del(&node->link, root);
 		GC_FREE(node);
 	}
 }
 
-int bmx_map_objectmap_isempty(struct avl_root ** root) {
-	return *root == 0;
+int bmx_map_objectmap_isempty(struct avl_root * root) {
+	return root == 0;
 }
 
 void bmx_map_objectmap_insert( BBObject * key, BBObject *value, struct avl_root ** root) {
@@ -405,11 +410,11 @@ void bmx_map_objectmap_insert( BBObject * key, BBObject *value, struct avl_root
 	}
 }
 
-int bmx_map_objectmap_contains(BBObject * key, struct avl_root ** root) {
+int bmx_map_objectmap_contains(BBObject * key, struct avl_root * root) {
 	struct objectmap_node node;
 	node.key = key;
 	
-	struct objectmap_node * found = (struct objectmap_node *)TREE_SEARCH(&node, compare_objectmap_nodes, *root);
+	struct objectmap_node * found = (struct objectmap_node *)TREE_SEARCH(&node, compare_objectmap_nodes, root);
 	if (found) {
 		return 1;
 	} else {
@@ -417,11 +422,11 @@ int bmx_map_objectmap_contains(BBObject * key, struct avl_root ** root) {
 	}
 }
 
-BBObject * bmx_map_objectmap_valueforkey(BBObject * key, struct avl_root ** root) {
+BBObject * bmx_map_objectmap_valueforkey(BBObject * key, struct avl_root * root) {
 	struct objectmap_node node;
 	node.key = key;
 	
-	struct objectmap_node * found = (struct objectmap_node *)TREE_SEARCH(&node, compare_objectmap_nodes, *root);
+	struct objectmap_node * found = (struct objectmap_node *)TREE_SEARCH(&node, compare_objectmap_nodes, root);
 	
 	if (found) {
 		return found->value;

+ 25 - 18
map.mod/objectmap.bmx

@@ -1,21 +1,28 @@
 SuperStrict
 
+Import "common.bmx"
 
 Extern
-	Function bmx_map_objectmap_clear(root:Byte Ptr Ptr)
-	Function bmx_map_objectmap_isempty:Int(root:Byte Ptr Ptr)
-	Function bmx_map_objectmap_insert(key:Object, value:Object, root:Byte Ptr Ptr)
-	Function bmx_map_objectmap_contains:Int(key:Object, root:Byte Ptr Ptr)
-	Function bmx_map_objectmap_valueforkey:Object(key:Object, root:Byte Ptr Ptr)
-	Function bmx_map_objectmap_remove:Int(key:Object, root:Byte Ptr Ptr)
-	Function bmx_map_objectmap_firstnode:Byte Ptr(root:Byte Ptr)
-	Function bmx_map_objectmap_nextnode:Byte Ptr(node:Byte Ptr)
-	Function bmx_map_objectmap_key:Object(node:Byte Ptr)
-	Function bmx_map_objectmap_value:Object(node:Byte Ptr)
-	Function bmx_map_objectmap_hasnext:Int(node:Byte Ptr, root:Byte Ptr)
-	Function bmx_map_objectmap_copy(dst:Byte Ptr Ptr, _root:Byte Ptr)
+	Function bmx_map_objectmap_clear(root:SavlRoot Ptr Ptr)
+	Function bmx_map_objectmap_isempty:Int(root:SavlRoot Ptr)
+	Function bmx_map_objectmap_insert(key:Object, value:Object, root:SavlRoot Ptr Ptr)
+	Function bmx_map_objectmap_contains:Int(key:Object, root:SavlRoot Ptr)
+	Function bmx_map_objectmap_valueforkey:Object(key:Object, root:SavlRoot Ptr)
+	Function bmx_map_objectmap_remove:Int(key:Object, root:SavlRoot Ptr Ptr)
+	Function bmx_map_objectmap_firstnode:SObjectMapNode Ptr(root:SavlRoot Ptr)
+	Function bmx_map_objectmap_nextnode:SObjectMapNode Ptr(node:SObjectMapNode Ptr)
+	Function bmx_map_objectmap_key:Object(node:SObjectMapNode Ptr)
+	Function bmx_map_objectmap_value:Object(node:SObjectMapNode Ptr)
+	Function bmx_map_objectmap_hasnext:Int(node:SObjectMapNode Ptr, root:SavlRoot Ptr)
+	Function bmx_map_objectmap_copy(dst:SavlRoot Ptr Ptr, _root:SavlRoot Ptr)
 End Extern
 
+Struct SObjectMapNode
+	Field link:SavlRoot
+	Field key:Object
+	Field value:Object
+End Struct
+
 Type TObjectMap
 
 	Method Delete()
@@ -32,7 +39,7 @@ Type TObjectMap
 	End Method
 	
 	Method IsEmpty:Int()
-		Return bmx_map_objectmap_isempty(Varptr _root)
+		Return bmx_map_objectmap_isempty(_root)
 	End Method
 	
 	Method Insert( key:Object,value:Object )
@@ -43,11 +50,11 @@ Type TObjectMap
 	End Method
 
 	Method Contains:Int( key:Object )
-		Return bmx_map_objectmap_contains(key, Varptr _root)
+		Return bmx_map_objectmap_contains(key, _root)
 	End Method
 	
 	Method ValueForKey:Object( key:Object )
-		Return bmx_map_objectmap_valueforkey(key, Varptr _root)
+		Return bmx_map_objectmap_valueforkey(key, _root)
 	End Method
 	
 	Method Remove:Int( key:Object )
@@ -119,7 +126,7 @@ Type TObjectMap
 		Return nodeenum
 	End Method
 
-	Field _root:Byte Ptr
+	Field _root:SavlRoot Ptr
 
 ?ngcmod
 	Field _modCount:Int
@@ -128,8 +135,8 @@ Type TObjectMap
 End Type
 
 Type TObjectNode
-	Field _root:Byte Ptr
-	Field _nodePtr:Byte Ptr
+	Field _root:SavlRoot Ptr
+	Field _nodePtr:SObjectMapNode Ptr
 	
 	Method Key:Object()
 		Return bmx_map_objectmap_key(_nodePtr)

+ 25 - 18
map.mod/ptrmap.bmx

@@ -1,21 +1,28 @@
 SuperStrict
 
+Import "common.bmx"
 
 Extern
-	Function bmx_map_ptrmap_clear(root:Byte Ptr Ptr)
-	Function bmx_map_ptrmap_isempty:Int(root:Byte Ptr Ptr)
-	Function bmx_map_ptrmap_insert(key:Byte Ptr, value:Object, root:Byte Ptr Ptr)
-	Function bmx_map_ptrmap_contains:Int(key:Byte Ptr, root:Byte Ptr Ptr)
-	Function bmx_map_ptrmap_valueforkey:Object(key:Byte Ptr, root:Byte Ptr Ptr)
-	Function bmx_map_ptrmap_remove:Int(key:Byte Ptr, root:Byte Ptr Ptr)
-	Function bmx_map_ptrmap_firstnode:Byte Ptr(root:Byte Ptr)
-	Function bmx_map_ptrmap_nextnode:Byte Ptr(node:Byte Ptr)
-	Function bmx_map_ptrmap_key:Byte Ptr(node:Byte Ptr)
-	Function bmx_map_ptrmap_value:Object(node:Byte Ptr)
-	Function bmx_map_ptrmap_hasnext:Int(node:Byte Ptr, root:Byte Ptr)
-	Function bmx_map_ptrmap_copy(dst:Byte Ptr Ptr, _root:Byte Ptr)
+	Function bmx_map_ptrmap_clear(root:SavlRoot Ptr Ptr)
+	Function bmx_map_ptrmap_isempty:Int(root:SavlRoot Ptr)
+	Function bmx_map_ptrmap_insert(key:Byte Ptr, value:Object, root:SavlRoot Ptr Ptr)
+	Function bmx_map_ptrmap_contains:Int(key:Byte Ptr, root:SavlRoot Ptr)
+	Function bmx_map_ptrmap_valueforkey:Object(key:Byte Ptr, root:SavlRoot Ptr)
+	Function bmx_map_ptrmap_remove:Int(key:Byte Ptr, root:SavlRoot Ptr Ptr)
+	Function bmx_map_ptrmap_firstnode:SPtrMapNode Ptr(root:SavlRoot Ptr)
+	Function bmx_map_ptrmap_nextnode:SPtrMapNode Ptr(node:SPtrMapNode Ptr)
+	Function bmx_map_ptrmap_key:Byte Ptr(node:SPtrMapNode Ptr)
+	Function bmx_map_ptrmap_value:Object(node:SPtrMapNode Ptr)
+	Function bmx_map_ptrmap_hasnext:Int(node:SPtrMapNode Ptr, root:SavlRoot Ptr)
+	Function bmx_map_ptrmap_copy(dst:SavlRoot Ptr Ptr, _root:SavlRoot Ptr)
 End Extern
 
+Struct SPtrMapNode
+	Field link:SavlRoot
+	Field key:Byte Ptr
+	Field value:Object
+End Struct
+
 Rem
 bbdoc: A key/value (Byte Ptr/Object) map.
 End Rem
@@ -43,7 +50,7 @@ Type TPtrMap
 	about: #True if @map is empty, otherwise #False.
 	End Rem
 	Method IsEmpty:Int()
-		Return bmx_map_ptrmap_isempty(Varptr _root)
+		Return bmx_map_ptrmap_isempty(_root)
 	End Method
 	
 	Rem
@@ -62,7 +69,7 @@ Type TPtrMap
 	returns: #True if the map contains @key.
 	End Rem
 	Method Contains:Int( key:Byte Ptr )
-		Return bmx_map_ptrmap_contains(key, Varptr _root)
+		Return bmx_map_ptrmap_contains(key, _root)
 	End Method
 	
 	Rem
@@ -71,7 +78,7 @@ Type TPtrMap
 	about: If the map does not contain @key, a #Null object is returned.
 	End Rem
 	Method ValueForKey:Object( key:Byte Ptr )
-		Return bmx_map_ptrmap_valueforkey(key, Varptr _root)
+		Return bmx_map_ptrmap_valueforkey(key, _root)
 	End Method
 	
 	Rem
@@ -181,7 +188,7 @@ Type TPtrMap
 		bmx_map_ptrmap_insert(key, value, Varptr _root)
 	End Method
 
-	Field _root:Byte Ptr
+	Field _root:SavlRoot Ptr
 	
 ?ngcmod
 	Field _modCount:Int
@@ -190,8 +197,8 @@ Type TPtrMap
 End Type
 
 Type TPtrNode
-	Field _root:Byte Ptr
-	Field _nodePtr:Byte Ptr
+	Field _root:SavlRoot Ptr
+	Field _nodePtr:SPtrMapNode Ptr
 	
 	Method Key:Byte Ptr()
 		Return bmx_map_ptrmap_key(_nodePtr)

+ 26 - 19
map.mod/stringmap.bmx

@@ -1,21 +1,28 @@
 SuperStrict
 
+Import "common.bmx"
 
 Extern
-	Function bmx_map_stringmap_clear(root:Byte Ptr Ptr)
-	Function bmx_map_stringmap_isempty:Int(root:Byte Ptr Ptr)
-	Function bmx_map_stringmap_insert(key:String, value:Object, root:Byte Ptr Ptr)
-	Function bmx_map_stringmap_contains:Int(key:String, root:Byte Ptr Ptr)
-	Function bmx_map_stringmap_valueforkey:Object(key:String, root:Byte Ptr Ptr)
-	Function bmx_map_stringmap_remove:Int(key:String, root:Byte Ptr Ptr)
-	Function bmx_map_stringmap_firstnode:Byte Ptr(root:Byte Ptr)
-	Function bmx_map_stringmap_nextnode:Byte Ptr(node:Byte Ptr)
-	Function bmx_map_stringmap_key:String(node:Byte Ptr)
-	Function bmx_map_stringmap_value:Object(node:Byte Ptr)
-	Function bmx_map_stringmap_hasnext:Int(node:Byte Ptr, root:Byte Ptr)
-	Function bmx_map_stringmap_copy(dst:Byte Ptr Ptr, _root:Byte Ptr)
+	Function bmx_map_stringmap_clear(root:SavlRoot Ptr Ptr)
+	Function bmx_map_stringmap_isempty:Int(root:SavlRoot Ptr)
+	Function bmx_map_stringmap_insert(key:String, value:Object, root:SavlRoot Ptr Ptr)
+	Function bmx_map_stringmap_contains:Int(key:String, root:SavlRoot Ptr)
+	Function bmx_map_stringmap_valueforkey:Object(key:String, root:SavlRoot Ptr)
+	Function bmx_map_stringmap_remove:Int(key:String, root:SavlRoot Ptr Ptr)
+	Function bmx_map_stringmap_firstnode:SStringMapNode Ptr(root:SavlRoot Ptr)
+	Function bmx_map_stringmap_nextnode:SStringMapNode Ptr(node:Byte Ptr)
+	Function bmx_map_stringmap_key:String(node:SStringMapNode Ptr)
+	Function bmx_map_stringmap_value:Object(node:SStringMapNode Ptr)
+	Function bmx_map_stringmap_hasnext:Int(node:SStringMapNode Ptr, root:SavlRoot Ptr)
+	Function bmx_map_stringmap_copy(dst:SavlRoot Ptr Ptr, _root:SavlRoot Ptr)
 End Extern
 
+Struct SStringMapNode
+	Field link:SavlRoot
+	Field key:String
+	Field value:Object
+End Struct
+
 Rem
 bbdoc: A key/value (String/Object) map.
 End Rem
@@ -38,7 +45,7 @@ Type TStringMap
 	about: #True if @map is empty, otherwise #False.
 	End Rem
 	Method IsEmpty:Int()
-		Return bmx_map_stringmap_isempty(Varptr _root)
+		Return bmx_map_stringmap_isempty(_root)
 	End Method
 	
 	Rem
@@ -56,7 +63,7 @@ Type TStringMap
 	End Rem
 	Method Contains:Int( key:String )
 		key.Hash()
-		Return bmx_map_stringmap_contains(key, Varptr _root)
+		Return bmx_map_stringmap_contains(key, _root)
 	End Method
 	
 	Rem
@@ -66,7 +73,7 @@ Type TStringMap
 	End Rem
 	Method ValueForKey:Object( key:String )
 		key.Hash()
-		Return bmx_map_stringmap_valueforkey(key, Varptr _root)
+		Return bmx_map_stringmap_valueforkey(key, _root)
 	End Method
 	
 	Rem
@@ -158,7 +165,7 @@ Type TStringMap
 	End Rem
 	Method Operator[]:Object(key:String)
 		key.Hash()
-		Return bmx_map_stringmap_valueforkey(key, Varptr _root)
+		Return bmx_map_stringmap_valueforkey(key, _root)
 	End Method
 	
 	Rem
@@ -170,13 +177,13 @@ Type TStringMap
 		bmx_map_stringmap_insert(key, value, Varptr _root)
 	End Method
 
-	Field _root:Byte Ptr
+	Field _root:SavlRoot Ptr
 
 End Type
 
 Type TStringNode
-	Field _root:Byte Ptr
-	Field _nodePtr:Byte Ptr
+	Field _root:SavlRoot Ptr
+	Field _nodePtr:SStringMapNode Ptr
 	
 	Method Key:String()
 		Return bmx_map_stringmap_key(_nodePtr)

+ 316 - 0
map.mod/tests/test.bmx

@@ -0,0 +1,316 @@
+SuperStrict
+
+Framework brl.standardio
+Import brl.map
+Import BRL.MaxUnit
+
+New TTestSuite.run()
+
+Type TIntMapTest Extends TTest
+
+	Method test() { test }
+
+		Local map:TIntMap = New TIntMap
+
+		Local count:Int
+
+		For Local key:TIntKey = Eachin map.Keys()
+			count :+ 1
+		Next
+
+		AssertEquals(0, count, "initial empty count")
+
+		map.Insert(1, "One")
+		map.Insert(-1, "Two")
+		map.Insert($7fffffff, "Three")
+
+		count = 0
+
+		For Local key:TIntKey = Eachin map.Keys()
+			count :+ 1
+		Next
+
+		AssertEquals(3, count, "count after inserts")
+
+		AssertNotNull(map.ValueForKey($7fffffff))
+		AssertNotNull(map.ValueForKey(-1))
+		AssertNull(map.ValueForKey(0))
+		AssertNull(map.ValueForKey(2))
+
+		AssertTrue(map.Remove($7fffffff), "key removed")
+		AssertFalse(map.Remove($7fffffff), "key not found")
+
+		count = 0
+
+		map.Clear()
+		map.Clear()
+
+		For Local key:TIntKey = Eachin map.Keys()
+			count :+ 1
+		Next
+
+		AssertEquals(0, count, "count after clear")
+
+		map.Insert(4, "Four")
+
+		count = 0
+
+		For Local key:TIntKey = Eachin map.Keys()
+			count :+ 1
+		Next
+
+		AssertEquals(1, count, "count after insert")
+
+		AssertTrue(map.Remove(4))
+
+		count = 0
+
+		For Local key:TIntKey = Eachin map.Keys()
+			count :+ 1
+		Next
+
+		AssertEquals(0, count, "count after last remove")
+
+	End Method
+
+
+End Type
+
+Type TStringMapTest Extends TTest
+
+	Method test() { test }
+
+		Local map:TStringMap = New TStringMap
+
+		Local count:Int
+
+		For Local key:TIntKey = Eachin map.Keys()
+			count :+ 1
+		Next
+
+		AssertEquals(0, count, "initial empty count")
+
+		map.Insert("one", "One")
+		map.Insert("two", "Two")
+		map.Insert("three", "Three")
+
+		count = 0
+
+		For Local key:String = Eachin map.Keys()
+			count :+ 1
+		Next
+
+		AssertEquals(3, count, "count after inserts")
+
+		AssertNotNull(map.ValueForKey("one"))
+		AssertNotNull(map.ValueForKey("three"))
+		AssertNull(map.ValueForKey("six"))
+		AssertNull(map.ValueForKey("123"))
+
+		AssertTrue(map.Remove("two"), "key removed")
+		AssertFalse(map.Remove("two"), "key not found")
+
+		count = 0
+
+		map.Clear()
+		map.Clear()
+
+		For Local key:String = Eachin map.Keys()
+			count :+ 1
+		Next
+
+		AssertEquals(0, count, "count after clear")
+
+		map.Insert("four", "Four")
+
+		count = 0
+
+		For Local key:String = Eachin map.Keys()
+			count :+ 1
+		Next
+
+		AssertEquals(1, count, "count after insert")
+
+		AssertTrue(map.Remove("four"))
+
+		count = 0
+
+		For Local key:String = Eachin map.Keys()
+			count :+ 1
+		Next
+
+		AssertEquals(0, count, "count after last remove")
+
+	End Method
+
+End Type
+
+Type TPtrMapTest Extends TTest
+
+	Method test() { test }
+
+		Local map:TPtrMap = New TPtrMap
+
+		Local count:Int
+
+		For Local key:TPtrKey = Eachin map.Keys()
+			count :+ 1
+		Next
+
+		AssertEquals(0, count, "initial empty count")
+
+		map.Insert(Byte Ptr(12345), "One")
+		map.Insert(Byte Ptr(2222), "Two")
+		map.Insert(Byte Ptr(100), "Three")
+
+		count = 0
+
+		For Local key:TPtrKey = Eachin map.Keys()
+			count :+ 1
+		Next
+
+		AssertEquals(3, count, "count after inserts")
+
+		AssertNotNull(map.ValueForKey(Byte Ptr(12345)))
+		AssertNotNull(map.ValueForKey(Byte Ptr(100)))
+		AssertNull(map.ValueForKey(Byte Ptr(42)))
+		AssertNull(map.ValueForKey(Byte Ptr(900000)))
+
+		AssertTrue(map.Remove(Byte Ptr(2222)), "key removed")
+		AssertFalse(map.Remove(Byte Ptr(2222)), "key not found")
+
+		count = 0
+
+		map.Clear()
+		map.Clear()
+
+		For Local key:TPtrKey = Eachin map.Keys()
+			count :+ 1
+		Next
+
+		AssertEquals(0, count, "count after clear")
+
+		map.Insert(Byte Ptr(440000), "Four")
+
+		count = 0
+
+		For Local key:TPtrKey = Eachin map.Keys()
+			count :+ 1
+		Next
+
+		AssertEquals(1, count, "count after insert")
+
+		AssertTrue(map.Remove(Byte Ptr(440000)))
+
+		count = 0
+
+		For Local key:TPtrKey = Eachin map.Keys()
+			count :+ 1
+		Next
+
+		AssertEquals(0, count, "count after last remove")
+
+	End Method
+
+
+End Type
+
+Type TObj
+	Field value:Int
+	Method New(value:Int)
+		Self.value = value
+	End Method
+
+	Method Compare:Int(other:Object)
+		Local obj:TObj = TObj(other)
+		If Not obj Then
+			Return -1
+		End If
+		If value < obj.value Then
+			Return -1
+		Else if value > obj.value Then
+			Return 1
+		End If
+		Return 0
+	End Method
+End Type
+
+Type TObjectMapTest Extends TTest
+
+	Method test() { test }
+
+		Local map:TObjectMap = New TObjectMap
+
+		Local count:Int
+
+		For Local key:Object = Eachin map.Keys()
+			count :+ 1
+		Next
+
+		AssertEquals(0, count, "initial empty count")
+
+		Local obj1:TObj = New TObj(1)
+		Local obj2:TObj = New TObj(2)
+		Local s3:String = "three"
+		Local obj4:TObj = New TObj(4)
+		Local obj1a:TObj = New TObj(1)
+
+		Local obj11:TObj = New TObj(11)
+		Local obj12:TObj = New TObj(12)
+
+		map.Insert(obj1, "One")
+		map.Insert(obj2, "Two")
+		map.Insert(s3, "Three") ' mixed objects/Strings
+
+		count = 0
+
+		For Local key:Object = Eachin map.Keys()
+			count :+ 1
+		Next
+
+		AssertEquals(3, count, "count after inserts")
+
+		AssertNotNull(map.ValueForKey(obj1))
+		AssertNotNull(map.ValueForKey(obj1a)) ' different obj same compare
+		AssertNotNull(map.ValueForKey("three"))
+		AssertNull(map.ValueForKey(obj11))
+		AssertNull(map.ValueForKey(obj12))
+
+		AssertTrue(map.Remove(obj2), "key removed")
+		AssertFalse(map.Remove(obj2), "key not found")
+
+		count = 0
+
+		map.Clear()
+
+		For Local key:Object = Eachin map.Keys()
+			count :+ 1
+		Next
+
+		AssertEquals(0, count, "count after clear")
+
+		map.Clear() ' multiple cleans are ok
+
+		map.Insert(obj4, "Four")
+
+		count = 0
+
+		For Local key:Object = Eachin map.Keys()
+			count :+ 1
+		Next
+
+		AssertEquals(1, count, "count after insert")
+
+		AssertTrue(map.Remove(obj4))
+
+		count = 0
+
+		For Local key:Object = Eachin map.Keys()
+			count :+ 1
+		Next
+
+		AssertEquals(0, count, "count after last remove")
+
+	End Method
+
+End Type