Announcement

Collapse

Forum Guidelines

This forum is for finished source code that is working properly. If you have questions about this or any other source code, please post it in one of the Discussion Forums, not here.
See more
See less

Associative array

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

    Associative array

    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
    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
    Imalloc.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
    Mdict.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
    some quick and dirty test code:

    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).]
Working...
X
😀
🥰
🤢
😎
😡
👍
👎