UPDATE 13.10.2004: Corrected a bug in MDICT.INC dict_loadFile()
where an error would be generated when being passed an empty
dictionary. Redownload
I saw a couple of posts asking for the code to the associative
array routines I'd written. Here they are updated to PBCC3.x/PBWIN7.x
for future reference.
Consists of 3 files. First is to be saved as VTABLE.INC the second as IMALLOC.INC the
third as MDICT.INC. Short test code is also provided.
Vtable.inc
Imalloc.inc
Mdict.inc
some quick and dirty test code:
[This message has been edited by Florent Heyworth (edited October 13, 2004).]
where an error would be generated when being passed an empty
dictionary. Redownload
I saw a couple of posts asking for the code to the associative
array routines I'd written. Here they are updated to PBCC3.x/PBWIN7.x
for future reference.
Consists of 3 files. First is to be saved as VTABLE.INC the second as IMALLOC.INC the
third as MDICT.INC. Short test code is also provided.
Vtable.inc
Code:
'//////////////////////////////////////////////////// ' VTABLE.INC ' Build a VTABLE (Virtual table) when passed a type ' Written by Florent Heyworth '//////////////////////////////////////////////////// #IF NOT %DEF(%VTABLE_INC) %VTABLE_INC = 1 MACRO FUNCTION VTABLE_SIZE( T ) END MACRO = (SIZEOF(T)-1) \ 4 MACRO MAKE_VTABLE( T ) MACROTEMP pAddr, i LOCAL pAddr AS DWORD PTR LOCAL i AS LONG pAddr = VARPTR( T ) FOR i = 0 TO VTABLE_SIZE( T )-1 @pAddr[i+1] = @@pAddr[0] + (i * 4) NEXT END MACRO MACRO MAKE_VTABLE2( T, SIZE ) MACROTEMP pAddr, i LOCAL pAddr AS DWORD PTR LOCAL i AS LONG pAddr = VARPTR( T ) FOR i = 0 TO SIZE-1 @pAddr[i+1] = @@pAddr[0] + (i * 4) NEXT END MACRO #ENDIF 'VTABLE_INC
Code:
'//////////////////////////////////////////////// ' IMALLOC.INC ' IMALLOC memory allocator macros ' Written by Florent Heyworth '//////////////////////////////////////////////// #IF NOT %DEF(%IMALLOC_INC) %IMALLOC_INC = 1 #IF NOT %DEF(%WINAPI_INC) DECLARE FUNCTION CoGetMalloc LIB "ole32.dll" ALIAS "CoGetMalloc" ( _ BYVAL dwMemContext AS DWORD, BYVAL ppMalloc AS DWORD PTR _ ) AS LONG #ENDIF #INCLUDE "vtable.inc" #IF NOT %DEF(%S_OK) %S_OK = 0 #ENDIF MACRO VHANDLE = DWORD PTR 'IMALLOC Memory allocator function pointers TYPE T_IMALLOC MallocPtr AS VHANDLE 'IMalloc pointer 'IUnknown Methods QueryInterFace AS VHANDLE 'Returns pointers to supported interfaces AddRef AS VHANDLE 'Increments the reference count Release AS VHANDLE 'Decrements the reference count 'IMalloc Methods Alloc AS VHANDLE 'Allocates a block of memory Realloc AS VHANDLE 'Changes the size of a previously allocated block of memory Free AS VHANDLE 'Frees a previously allocated block of memory GetSize AS VHANDLE 'Returns the size in bytes of a previously allocated block of memory DidAlloc AS VHANDLE 'Determines if this instance of IMalloc was used to allocate 'the specified block of memory HeapMinimize AS VHANDLE 'Minimizes the heap by releasing unused memory to the operating system END TYPE MACRO FUNCTION IMALLOC_GETMALLOC( T ) END MACRO = CoGetMalloc( 1, VARPTR(T) ) MACRO FUNCTION IMALLOC_ALLOC( T, CB ) MACROTEMP pp LOCAL pp AS DWORD ! push CB ! push dword T CALL DWORD T.@Alloc ! mov pp, eax END MACRO = pp MACRO FUNCTION IMALLOC_REALLOC(T,PPTR,CB) MACROTEMP pp LOCAL pp AS DWORD ! push CB ! push PPTR ! push dword T CALL DWORD T.@Realloc ! mov pp, eax END MACRO = pp MACRO FUNCTION IMALLOC_GETSIZE( T, PPTR ) MACROTEMP size LOCAL size AS LONG ! push PPTR ! push dword T CALL DWORD T.@GetSize ! mov size, eax END MACRO = size MACRO FUNCTION IMALLOC_DIDALLOC( T, PPTR ) MACROTEMP isalloc LOCAL isalloc AS LONG ! push PPTR ! push dword T CALL DWORD T.@DidAlloc ! mov isalloc, eax END MACRO = isalloc MACRO IMALLOC_FREE( T, PPTR ) IF IMALLOC_DIDALLOC( T, PPTR ) THEN ! push PPTR ! push dword T CALL DWORD T.@Free ! mov pptr, 0 END IF END MACRO MACRO IMALLOC_RELEASE( T ) CALL DWORD T.@Release T.MallocPtr = 0 END MACRO MACRO IMALLOC_HEAPMINIMIZE( T ) ! push dword T CALL DWORD T.@HeapMinimize END MACRO #ENDIF 'IMALLOC_INC
Code:
#IF 0 **************************************************************************** * ' MDICT.INC - multi-purpose dictionary - slower than ENV.INC * ' due to the added overhead - Note that this can defnitely be optimised * ' * ' Written : Florent Heyworth * ' Date : 31-Mar-2000 * ' Last edit : 12-Oct-2004 * ' * ' Written by Florent Heyworth, 2000,2001,2002,2003,2004 * ' * ' Permission is hereby given to use, alter, sell and distribute * ' in commercial and non-commercial applications as long as the * ' above notice remains intact. By using, downloading, * ' distributing the code you are agreeing to hold the author, * ' Florent Heyworth, harmless from all effects, side-effects, etc * ' of this code. * ' * ' Change history (most recent change last): * ' 02-Apr-2000: changed Array dimension lHashLen-1 (off by one error) * ' in mdict_getMemKeys(),mdict_Keys() and mdict_Values() * ' 06-Apr-2000: added ability to handle ole strings, types and longs * ' as record types * ' 18-Jun-2000: added some register declarations which were omitted in * ' a couple of functions * ' 12-Oct-2004: Upgraded to PBCC3.x and PBWIN7.x * ' **************************************************************************** #ENDIF #IF NOT %DEF(%MDICT_INC) #REGISTER NONE %MDICT_INC = 1 #IF NOT %DEF(%NULL) %NULL = 0 #ENDIF #IF NOT %DEF(%TRUE) %TRUE = 1 #ENDIF #IF NOT %DEF(%FALSE) %FALSE = 0 #ENDIF #INCLUDE "win32api.inc" #INCLUDE "imalloc.inc" TYPE REC_TYPE DWORD'could have used a variant but I didn't recflag AS LONG '1 = pString, 2 = pStruct, 3 = pLong, 4 = pPtr reclen AS LONG pbyte AS BYTE PTR END TYPE TYPE DICT DWORD pnext AS DICT PTR key AS ASCIIZ PTR'pointers to string value rec AS REC_TYPE 'record type (string pointer, type pointer or long ptr) hash AS LONG 'The hash value which is translated to a memory address tablesize AS LONG 'The table size allocated inuse AS LONG 'The amount of Active (non-deleted) records active AS LONG 'Indicates a used (not NULL and not deleted) item END TYPE TYPE DICT_TABLE T_IMALLOC sequence AS LONG pdict AS DICT PTR END TYPE MACRO DICT_PTR = DWORD MACRO OS_HANDLE = DWORD MACRO PTR_STR = 1 'String or Asciiz MACRO PTR_STRUC = 2 'type MACRO PTR_LONG = 3 'long MACRO PTR_DWORD = 4 MACRO PTR_PTR = 5 'pointer type MACRO PTR_DBL = 6 'double MACRO PTR_SNG = 7 'single MACRO PTR_QUAD = 8 'quad MACRO PTR_EXT = 9 'extended MACRO PTR_CUR = 10 'currency MACRO PTR_BYTE = 11 'byte MACRO PTR_INT = 12 'integer MACRO PTR_WORD = 13 'word MACRO MAGIC_MARKER = &HBEEF?? MACRO FUNCTION DICT_HASH(tablesize, key) MACROTEMP bptr MACROTEMP accul MACROTEMP result MACROTEMP last LOCAL bptr AS BYTE PTR LOCAL accul AS DWORD LOCAL result AS LONG LOCAL last AS DWORD bptr = VARPTR(key) DO WHILE @bPtr last = accul SHIFT LEFT accul, 5 accul = accul - last + @bptr INCR bptr LOOP result = accul MOD tablesize IF result = 0 THEN result = 1 END MACRO = result MACRO MAP_WRITE(dest,src,size) MoveMemory(BYVAL dest,BYVAL src,size) dest = dest + size END MACRO MACRO MAP_READ(dest,src,size) MoveMemory(BYVAL dest,BYVAL src,size) src = src + size END MACRO FUNCTION dict_alloc ALIAS "dict_alloc" (BYREF self AS DICT_TABLE, BYVAL initial_size AS LONG) EXPORT AS LONG 'initial_size is the initial size of the dict - it's a good idea to allocate a decent size 'returns TRUE if okay FALSE if failed LOCAL alloc_size AS LONG IF self.pdict = %NULL THEN IF IMALLOC_GETMALLOC(self) <> %S_OK THEN EXIT FUNCTION IF initial_size < 100 THEN initial_size = 100 MAKE_VTABLE2( self, VTABLE_SIZE(T_IMALLOC) ) alloc_size = initial_size * SIZEOF(DICT) self.pdict = IMALLOC_ALLOC(self, alloc_size) IF self.pdict THEN self.@pdict[0].tablesize = initial_size FUNCTION = %TRUE END IF END IF END FUNCTION SUB dict_getKeyPtr(BYREF self AS DICT_TABLE, BYREF keys() AS DWORD ) PRIVATE 'internal - gets all pointers to the keys #REGISTER NONE REGISTER iter AS LONG REGISTER count AS LONG LOCAL plookup AS DICT PTR IF self.pdict = %NULL THEN EXIT SUB END IF IF self.@pdict[0].inuse <= 0 THEN EXIT SUB REDIM keys(self.@pdict[0].inuse-1) DO WHILE iter < self.@pdict[0].tablesize IF self.@pdict[iter].active THEN keys(count) = VARPTR(self.@pdict[iter]) count = count + 1 plookup = self.@pdict[iter].pnext DO WHILE plookup IF @plookup.active THEN keys(count) = plookup count = count + 1 END IF plookup = @plookup.pnext LOOP END IF iter = iter + 1 LOOP END SUB SUB dict_reset ALIAS "dict_reset"( self AS DICT_TABLE ) EXPORT 'reset all keys and records #REGISTER NONE REGISTER iter AS LONG DIM keys(0) AS DWORD LOCAL plookup AS DICT PTR LOCAL p AS BYTE PTR dict_getKeyPtr(self, keys()) FOR iter = LBOUND(keys()) TO UBOUND(keys()) plookup = keys(iter) IF plookup THEN IF @plookup.active THEN IF @plookup.rec.recflag <> PTR_STRUC THEN p = @plookup.rec.pbyte IMALLOC_FREE(self,p) ELSE @plookup.rec.pbyte = %NULL END IF @plookup.rec.recflag = 0 @plookup.active = %FALSE @plookup.hash = 0 DECR self.@pdict[0].inuse p = @plookup.key IMALLOC_FREE( self, p ) END IF END IF NEXT END SUB SUB dict_free ALIAS "dict_free" ( self AS DICT_TABLE ) EXPORT LOCAL p AS BYTE PTR IF self.pdict THEN dict_reset( self ) p = self.pdict IMALLOC_FREE( self, p ) IMALLOC_RELEASE(self) self.pdict = %NULL END IF END SUB FUNCTION dict_grow ALIAS "dict_grow"(BYREF self AS DICT_TABLE, BYVAL grow_by AS LONG ) EXPORT AS LONG 'if grow_by = 0 then dict grows by tablesize * 2 + 1 'else grow_by = actual tablesize + grow_by 'returns TRUE if okay FALSE if failed 'this operation can be very expensive as a new dict is created 'all keys and recs are copied from the old dict to the new dict 'and then the old dict is freed 'can be useful if you underestimated the size of the initial dict 'and want to increase dict size to get the keys more fairly spread 'out. A rule of thumb could be when inuse >= 200% of tablesize DIM keys(0) AS DWORD LOCAL i AS LONG LOCAL plookup AS DICT PTR LOCAL selfd AS DICT_TABLE IF self.pdict = %NULL THEN FUNCTION = dict_alloc(self, grow_by) EXIT FUNCTION END IF IF grow_by = 0 THEN grow_by = self.@pdict[0].tablesize + self.@pdict[0].tablesize + 1 ELSE grow_by = self.@pdict[0].tablesize + grow_by END IF 'alloc a new dict IF dict_alloc( selfd, grow_by ) THEN 'need to rehash all keys dict_getKeyPtr( self, keys() ) FOR i = LBOUND(keys()) TO UBOUND(keys()) plookup = keys(i) IF plookup THEN CALL dict_addKey(selfd, @plookup.@key, @plookup.rec.pbyte, @plookup.rec.recflag, @plookup.rec.reclen ) END IF NEXT 'free old dict dict_free(self) self = selfd FUNCTION = %TRUE END IF END FUNCTION FUNCTION dict_keyExists ALIAS "dict_keyExists"( self AS DICT_TABLE, key AS ASCIIZ ) EXPORT AS DICT_PTR 'return %NULL if no key DICT PTR if found LOCAL bucket AS LONG LOCAL plookup AS DICT PTR IF self.pdict = %NULL THEN EXIT FUNCTION bucket = DICT_HASH( self.@pdict[0].tablesize, key ) IF self.@pdict[bucket].key = %NULL THEN EXIT FUNCTION IF key = self.@pdict[bucket].@key THEN FUNCTION = VARPTR(self.@pdict[bucket]) ELSE plookup = self.@pdict[bucket].pnext DO WHILE plookup IF key = @plookup.@key THEN FUNCTION = plookup EXIT FUNCTION END IF plookup = @plookup.pnext LOOP END IF END FUNCTION SUB dict_deleteKey ALIAS "dict_deleteKey"( self AS DICT_TABLE, key AS ASCIIZ ) EXPORT LOCAL bucket AS LONG LOCAL plookup AS DICT PTR LOCAL pkeep AS DICT PTR LOCAL p AS BYTE PTR plookup = dict_keyExists( self, key ) IF plookup THEN pkeep = @plookup.pnext p = @plookup.key IMALLOC_FREE( self, p ) @plookup.key = 0 @plookup.active = 0 DECR self.@pdict[0].inuse @plookup.hash = 0 IF @plookup.rec.recflag <> PTR_PTR THEN p = @plookup.rec.pbyte IMALLOC_FREE( self, p ) ELSE @plookup.rec.pbyte = %NULL END IF END IF END SUB SUB dict_addKey ALIAS "dict_addKey" ( self AS DICT_TABLE, key AS ASCIIZ, BYVAL rec AS DWORD, _ BYVAL rectype AS LONG, BYVAL reclen AS LONG ) EXPORT 'NOTE: this SUB has practically no error checking LOCAL bucket AS LONG LOCAL result AS LONG LOCAL keylen AS LONG LOCAL reallen AS LONG LOCAL plookup AS DICT PTR LOCAL pnewhash AS DICT PTR LOCAL p AS BYTE PTR LOCAL size AS LONG size = SIZEOF(DICT) keylen = LEN(key) IF keylen = 0 THEN EXIT SUB bucket = DICT_HASH(self.@pdict[0].tablesize, key) IF self.@pdict[bucket].hash = bucket THEN plookup = VARPTR(self.@pdict[bucket]) IF key = @plookup.@key THEN 'overwrite IF rectype <> PTR_PTR THEN p = @plookup.rec.pbyte IF rectype = PTR_STR THEN reallen = reclen + 1 ELSE reallen = reclen END IF IF @plookup.rec.recflag <> PTR_PTR THEN @plookup.rec.pbyte = IMALLOC_REALLOC(self,p,reallen ) ELSE p = IMALLOC_ALLOC(self, reallen) END IF MoveMemory( BYVAL @plookup.rec.pbyte, BYVAL rec, reallen ) ELSE IF rectype <> @plookup.rec.recflag THEN p = @plookup.rec.pbyte IMALLOC_FREE(self, p) END IF @plookup.rec.pbyte = rec END IF @plookup.rec.recflag = rectype @plookup.rec.reclen = reclen EXIT SUB ELSE 'look for key in linked list and overwrite if found plookup = self.@pdict[bucket].pnext IF plookup THEN DO WHILE @plookup.pnext <> 0 IF key = @plookup.@key THEN IF rectype <> PTR_PTR THEN p = @plookup.rec.pbyte IF rectype = PTR_STR THEN reallen = reclen + 1 ELSE reallen = reclen END IF IF @plookup.rec.recflag <> PTR_PTR THEN @plookup.rec.pbyte = IMALLOC_REALLOC(self, p, reallen) ELSE p = IMALLOC_ALLOC(self, reallen) END IF MoveMemory( BYVAL @plookup.rec.pbyte, BYVAL rec, reallen ) ELSE IF rectype <> @plookup.rec.recflag THEN p = @plookup.rec.pbyte IMALLOC_FREE(self, p) END IF @plookup.rec.pbyte = rec END IF @plookup.rec.recflag = rectype @plookup.rec.reclen = reclen EXIT SUB END IF plookup = @plookup.pnext LOOP END IF END IF 'add a new node pnewhash = IMALLOC_ALLOC( self, size) IF pnewhash = %NULL THEN EXIT SUB IF rectype <> PTR_PTR THEN IF rectype = PTR_STR THEN reallen = reclen + 1 ELSE reallen = reclen END IF @pnewhash.rec.pbyte = IMALLOC_ALLOC( self, reallen ) MoveMemory( BYVAL @pnewhash.rec.pbyte, BYVAL rec, reallen ) ELSE @pnewhash.rec.pbyte = rec END IF @pnewhash.rec.recflag = rectype @pnewhash.rec.reclen = reclen reallen = keylen + 1 @pnewhash.key = IMALLOC_ALLOC( self, reallen ) @pnewhash.@key = PEEK$(VARPTR(key), reallen) @pnewhash.hash = bucket @pnewhash.active = %TRUE @pnewhash.pnext = 0 IF plookup THEN @plookup.pnext = pnewhash ELSE self.@pdict[bucket].pnext = pnewhash END IF INCR self.@pdict[0].inuse EXIT SUB ELSE 'new key IF rectype <> PTR_PTR THEN IF rectype = PTR_STR THEN reallen = reclen + 1 ELSE reallen = reclen END IF self.@pdict[bucket].rec.pbyte = IMALLOC_ALLOC( self, reallen ) MoveMemory( BYVAL self.@pdict[bucket].rec.pbyte, BYVAL rec, reclen ) ELSE self.@pdict[bucket].rec.pbyte = rec END IF self.@pdict[bucket].rec.recflag = rectype self.@pdict[bucket].rec.reclen = reclen reallen = keylen + 1 self.@pdict[bucket].key = IMALLOC_ALLOC( self, reallen ) MoveMemory( BYVAL self.@pdict[bucket].key, BYVAL VARPTR(key), reallen ) self.@pdict[bucket].hash = bucket self.@pdict[bucket].pnext = 0 self.@pdict[bucket].active = %TRUE INCR self.@pdict[0].inuse END IF END SUB FUNCTION dict_getKey ALIAS "dict_getKey" ( BYREF self AS DICT_TABLE, key AS ASCIIZ ) EXPORT AS DWORD 'Returns the record held under the key LOCAL plookup AS DICT PTR plookup = dict_keyExists(self, key) IF plookup THEN FUNCTION = VARPTR(@plookup.rec) END IF END FUNCTION SUB dict_keys ALIAS "dict_keys" ( BYREF self AS DICT_TABLE, keys() AS STRING ) EXPORT 'Returns all keys in dictionary in keys() REGISTER iter AS LONG LOCAL count AS LONG LOCAL plookup AS DICT PTR IF self.@pdict[0].inuse <= 0 THEN EXIT SUB REDIM keys(self.@pdict[0].inuse-1) DO WHILE iter < self.@pdict[0].tablesize IF self.@pdict[iter].active THEN keys(count) = self.@pdict[iter].@key count = count + 1 plookup = self.@pdict[iter].pnext DO WHILE plookup IF @plookup.active THEN keys(count) = @plookup.@key count = count + 1 END IF plookup = @plookup.pnext LOOP END IF iter = iter + 1 LOOP END SUB SUB dict_values ALIAS "dict_values"( BYREF self AS DICT_TABLE, BYREF values() AS DWORD, BYVAL rectype AS LONG ) EXPORT 'Returns all string values in dictionary in values() 'values are returned by reference (the allocated values stay in the hash table) 'rectype should be one of PTR_STR, PTR_LONG, PTR_STRUC or any type you registered REGISTER iter AS LONG LOCAL count AS LONG LOCAL plookup AS DICT PTR IF self.@pdict[0].inuse <= 0 THEN EXIT SUB REDIM values(self.@pdict[0].inuse-1) DO WHILE iter < self.@pdict[0].tablesize IF self.@pdict[iter].active AND self.@pdict[iter].rec.recflag = rectype THEN values(count) = self.@pdict[iter].rec.pbyte count = count + 1 plookup = self.@pdict[iter].pnext DO WHILE plookup IF @plookup.active AND @plookup.rec.recflag = rectype THEN values(count) = @plookup.rec.pbyte count = count + 1 END IF plookup = @plookup.pnext LOOP END IF iter = iter + 1 LOOP REDIM PRESERVE values(count-1) END SUB FUNCTION dict_getUseCount ALIAS "dict_getUseCount"( BYREF self AS DICT_TABLE ) EXPORT AS LONG IF self.pdict THEN FUNCTION = self.@pdict[0].inuse END IF END FUNCTION FUNCTION dict_getTablesize ALIAS "dict_getTablesize"( BYREF self AS DICT_TABLE ) EXPORT AS LONG IF self.pdict THEN FUNCTION = self.@pdict[0].tablesize END IF END FUNCTION FUNCTION dict_getRecType ALIAS "dict_getRecType"( BYVAL prec AS REC_TYPE PTR ) EXPORT AS STRING IF prec = %NULL THEN EXIT FUNCTION IF @prec.recflag < 1 OR @prec.recflag > DATACOUNT THEN EXIT FUNCTION FUNCTION = READ$( @prec.recflag ) DATA "STRING", "STRUC", "LONG", "DWORD" DATA "POINTER", "DOUBLE", "EXTENDED", "SINGLE" DATA "QUAD", "EXTENDED", "CURRENCY", "BYTE" DATA "INTEGER", "WORD" END FUNCTION FUNCTION dict_getRecLen ALIAS "dict_getRecLen"( BYVAL prec AS REC_TYPE PTR ) EXPORT AS LONG IF prec = %NULL THEN EXIT FUNCTION FUNCTION = @prec.reclen END FUNCTION FUNCTION dict_getKeyRecSize ALIAS "dict_getKeyRecSize"( BYREF self AS DICT_TABLE ) EXPORT AS LONG 'returns sum of rec and key sizes LOCAL dictsize AS LONG DIM keys(0) AS DWORD LOCAL plookup AS DICT PTR LOCAL i AS LONG dict_getKeyPtr( self, keys() ) FOR i = LBOUND(keys()) TO UBOUND(keys()) plookup = keys(i) IF plookup THEN dictsize = dictsize + @plookup.rec.reclen dictsize = dictsize + LEN(@plookup.@key) END IF NEXT FUNCTION = dictsize END FUNCTION FUNCTION dict_getTotalSize ALIAS "dict_getTotalSize"( BYREF self AS DICT_TABLE ) EXPORT AS LONG 'returns allocated memmory for dict + sum of rec and key sizes LOCAL dictsize AS LONG LOCAL p AS BYTE PTR p = self.pdict dictsize = IMALLOC_GETSIZE( self, p ) dictsize = dictsize + dict_getKeyRecSize( self ) dictsize = dictsize + SIZEOF(DICT) * self.@pdict[0].inuse FUNCTION = dictsize END FUNCTION FUNCTION dict_save ALIAS "dict_save" ( BYREF self AS DICT_TABLE, BYVAL filepath AS STRING ) EXPORT AS LONG 'saves dict to disk - it can be restore using dict_load() 'returns an error on error or 0 on success LOCAL free AS LONG DIM keys(0) AS DWORD LOCAL hfile AS OS_HANDLE LOCAL hmap AS OS_HANDLE LOCAL pview AS BYTE PTR LOCAL plookup AS DICT PTR LOCAL i AS LONG LOCAL size AS LONG LOCAL tablesize AS LONG LOCAL inuse AS LONG LOCAL sizepad AS LONG LOCAL sizelong AS LONG LOCAL tmp AS LONG LOCAL mark AS DWORD mark = MAGIC_MARKER TRY sizelong = SIZEOF(tmp) IF self.pdict = %NULL THEN ERROR %ERR_SUBSCRIPTPOINTEROUTOFRANGE END IF 'calculate needed size + padding inuse = SIZEOF(self.@pdict[0].inuse) tablesize = SIZEOF(self.@pdict[0].tablesize) sizepad = (8 * sizelong * self.@pdict[0].inuse) size = dict_getKeyRecSize( self ) + inuse + tablesize + sizepad + 4 'create file and file-mapping free = FREEFILE OPEN filepath FOR BINARY LOCK WRITE AS free hfile = FILEATTR(free, 2) hmap = CreateFileMapping( hFile, BYVAL %NULL, %PAGE_READWRITE, 0, size, BYVAL %NULL ) IF hmap = %NULL THEN ERROR %ERR_DEVICEIOERROR END IF pview = MapViewOfFile( hmap, %FILE_MAP_WRITE, 0, 0, 0 ) IF pview = %NULL THEN ERROR %ERR_DEVICEIOERROR END IF 'get active keys dict_getKeyPtr( self, keys() ) 'inuse MAP_WRITE(pview, VARPTR(self.@pdict[0].inuse), inuse) 'tablesize MAP_WRITE(pview, VARPTR(self.@pdict[0].tablesize), tablesize) FOR i = LBOUND(keys()) TO UBOUND(keys()) pLookup = keys(i) IF plookup THEN 'mark MAP_WRITE(pview, VARPTR(mark), sizelong) 'key length tmp = LEN(@plookup.@key) MAP_WRITE(pview, VARPTR(tmp), sizelong) 'mark MAP_WRITE(pview, VARPTR(mark), sizelong) 'key MAP_WRITE(pview, @plookup.key, tmp) 'mark MAP_WRITE(pview, VARPTR(mark), sizelong) 'record type MAP_WRITE(pview, VARPTR(@plookup.rec.recflag), sizelong) 'mark MAP_WRITE(pview, VARPTR(mark), sizelong) 'record length MAP_WRITE(pview, VARPTR(@plookup.rec.reclen), sizelong) 'mark MAP_WRITE(pview, VARPTR(mark), sizelong) 'record data MAP_WRITE(pview, @plookup.rec.pbyte, @plookup.rec.reclen) END IF NEXT CATCH FUNCTION = ERR FINALLY IF pview THEN UnmapViewOfFile( pview ) IF hmap THEN CloseHandle( hmap ) IF free THEN CLOSE free END TRY END FUNCTION FUNCTION dict_load ALIAS "dict_load" ( BYREF self AS DICT_TABLE, BYVAL filepath AS STRING ) EXPORT AS LONG 'saves dict to disk - it can be restore using dict_load() 'returns an error on error or 0 on success 'CAUTION: if self already has an allocated dict the dict will freed 'and a new dict allocated LOCAL free AS LONG LOCAL hfile AS OS_HANDLE LOCAL hmap AS OS_HANDLE LOCAL pview AS BYTE PTR LOCAL tablesize AS LONG LOCAL inuse AS LONG LOCAL sizelong AS LONG LOCAL tmp AS LONG LOCAL filesize AS DWORD LOCAL key AS ASCIIZ * 1024 LOCAL skey AS STRING LOCAL pkey AS ASCIIZ PTR LOCAL usestr AS LONG LOCAL recflag AS LONG LOCAL reclen AS LONG LOCAL mark AS DWORD TRY pkey = VARPTR(key) sizelong = SIZEOF(tmp) 'create file and file-mapping free = FREEFILE OPEN filepath FOR BINARY LOCK WRITE AS free hfile = FILEATTR(free, 2) filesize = GetFileSize( hfile, 0 ) IF filesize = 0 OR filesize = &HFFFFFFFF?? THEN ERROR %ERR_DEVICEIOERROR END IF hmap = CreateFileMapping( hFile, BYVAL %NULL, %PAGE_READWRITE, 0, filesize, BYVAL %NULL ) IF hmap = %NULL THEN ERROR %ERR_DEVICEIOERROR END IF pview = MapViewOfFile( hmap, %FILE_MAP_WRITE, 0, 0, 0 ) IF pview = %NULL THEN ERROR %ERR_DEVICEIOERROR END IF IF self.pdict THEN dict_free( self ) END IF 'inuse MAP_READ(VARPTR(inuse), pview, sizelong) 'tablesize MAP_READ(VARPTR(tablesize), pview, sizelong) 'allocate dict IF dict_alloc( self, tablesize ) THEN DO 'marker MAP_READ(VARPTR(mark), pview, sizelong) IF mark <> MAGIC_MARKER THEN EXIT DO 'key length MAP_READ(VARPTR(tmp), pview, sizelong) 'marker MAP_READ(VARPTR(mark), pview, sizelong) IF mark <> MAGIC_MARKER THEN EXIT DO 'key IF tmp > SIZEOF(key) THEN skey = PEEK$(pview, tmp) usestr = %TRUE ELSE MoveMemory(BYVAL pkey, BYVAL pview, tmp ) usestr = %FALSE END IF pview = pview + tmp 'mark MAP_READ(VARPTR(mark), pview, sizelong) IF mark <> MAGIC_MARKER THEN EXIT DO 'record type MAP_READ(VARPTR(recflag), pview, sizelong) 'mark MAP_READ(VARPTR(mark), pview, sizelong) IF mark <> MAGIC_MARKER THEN EXIT DO 'record length MAP_READ(VARPTR(reclen), pview, sizelong) 'mark MAP_READ(VARPTR(mark), pview, sizelong) IF mark <> MAGIC_MARKER THEN EXIT DO 'add to dict IF usestr THEN dict_addkey( self, BYVAL STRPTR(skey), BYVAL pview, recflag, reclen ) ELSE dict_addkey( self, BYVAL pkey, BYVAL pview, recflag, reclen ) 'print @pkey ZeroMemory(BYVAL pkey, LEN(@pkey)) END IF pview = pview + reclen LOOP END IF CATCH FUNCTION = ERR FINALLY IF pview THEN UnmapViewOfFile( pview ) IF hmap THEN CloseHandle( hmap ) IF free THEN CLOSE free END TRY END FUNCTION #ENDIF 'DICT_INC
Code:
#INCLUDE "mdict.inc" FUNCTION PBMAIN LOCAL tdict AS DICT_TABLE LOCAL sval AS STRING LOCAL i AS LONG LOCAL t1 AS SINGLE LOCAL t2 AS SINGLE LOCAL prec AS REC_TYPE PTR t1 = TIMER IF dict_alloc( tdict, 100000 ) THEN FOR i = 0 TO 100000 sVal = "WHAT A WORLDWHAT A WORLDWHAT A WORLDWHAT A WORLDWHAT A WORLDWHAT A WORLDWHAT A WORLD"+STR$(i) dict_addKey( tdict, "hello"+STR$(i), BYVAL STRPTR(sVal), PTR_STR, LEN(sVal) ) NEXT t2 = TIMER PRINT t2-t1 dict_grow( tdict, 50000 ) PRINT dict_getTotalSize( tdict ) FOR i = 0 TO 10 'print i dict_deletekey( tdict, "hello"+STR$(i) ) NEXT PRINT dict_getTotalSize( tdict ) dict_save( tdict, "c:\test.txt" ) IF dict_load( tdict, "c:\test.txt" ) <> 0 THEN PRINT "Failed to load dictionary from file. Exiting" dict_free(tdict) EXIT FUNCTION END IF prec = dict_getKey( tdict, "hello 40001") IF prec THEN PRINT PEEK$(@prec.pbyte, @prec.reclen) END IF 'retrieve and sort keys DIM keys(0) AS STRING LOCAL p AS ASCIIZ PTR dict_keys(tdict, keys()) ARRAY SORT keys() FOR i = 0 TO UBOUND(keys()) PRINT "'" + keys(i) +"'" NEXT END IF dict_free( tdict ) WAITKEY$ END FUNCTION
[This message has been edited by Florent Heyworth (edited October 13, 2004).]