http://www.purebasic.fr/german/viewtopic.php?t=17659
Da, wie in dem Thread angesprochen, eine Hashtable meist am besten ist, desto spezieller man sie an das jeweilige Einsatztgebiet anpasst, hab ich mir selbst eine gebaut.
Ich habe das ganze auf Speed optimiert, das ist wohl die schnellste Hashtable die je in PB geschrieben worden ist.
Als Basis diente mir dabei diese geniale Hashtable von Inc:
http://www.purebasic-lounge.com/viewtop ... c&start=12
Details:
Die einzelnen Hashtables sind autark, und in der Größe dynamisch.
Die Startblockgröße der Hashtables lässt sich beliebig festelegen, wenn die Table zu 2/3 voll ist (einstellbar) reallociert sie sich von selbst.
Es können Strings und GanzZahlen in der Table gespeichert werden.
Einmal gemachte Einträge können NICHT wieder gelöscht oder geändert werden (im Orginal vorhanden, ich benötige das nicht kostet nur Speed, kann aber leicht wieder eingebaut werden).
Code:
Code: Alles auswählen
; ------------------------------------------------------------------------------------
; Fast Hashtable Include
; Core taken from: http://www.purebasic-lounge.com/viewtopic.php?t=5464&postdays=0&postorder=asc&start=12
; ------------------------------------------------------------------------------------
; ------------------------------------------------------------------------------------
; Structures & Enumerations
; ------------------------------------------------------------------------------------
Enumeration
#InitialTableSize = 2503
#CHASHMAP_TYPE_STRING = 1
#CHASHMAP_TYPE_VALUE
#CHASHMAP_MAP_OVERFLOW = -1
EndEnumeration
Structure sHASHMAP
Object.l
Blocksize.l
Mapsize.l
count.l
*map
EndStructure
Structure sElement
key.s
type.l
used.l
StructureUnion
Value.l
string.s
EndStructureUnion
EndStructure
Structure MapArray
map.sElement[0]
EndStructure
; ------------------------------------------------------------------------------------
; Internal
; ------------------------------------------------------------------------------------
Macro Mem_Alloc(_mem)
AllocateMemory(_mem)
EndMacro
Macro Mem_ReAlloc(_mem, _size)
ReAllocateMemory(_mem, _size)
EndMacro
Macro Mem_Free(_mem)
FreeMemory(_mem)
EndMacro
Macro _DoResize(_State, _Maximum)
_State>(2*_Maximum/3)
EndMacro
Macro _ResizeTo(_current, _blocksize)
_current + _blocksize
EndMacro
Procedure _CreateHash(*pKey.CHARACTER, Mapsize.l)
!MOV EAX,5381 ;Startwert hash
!MOV EBX,EAX ;für erste Addition in Schleife
!MOV ESI,dword[p.p_pKey] ;oder LEA ESI,[v_pKey], aber wenn p_pKey eh schon da ist...
!XOR EDX,EDX ;EDX auf Null setzen
!MOV CL,5 ;sollte so schneller sein
!@@:
!MOV DL,[ESI] ;also kein Unicode!
!OR DL,DL ;Test auf Null
!JZ @f ;ist Null, Sprung vorwärts
!SHL EAX,CL ;SHL = SAL; SAL wird selten angegeben
!ADD EAX,EBX
!ADD EAX,EDX
!MOV EBX,EAX ;"alten" hash sichern
!INC ESI
!JMP @b ;Sprung rückwärts, nächsten Wert einlesen
!@@:
!MOV dword[p.p_pKey],esi ;wirklich Pointer erhöhen? Sonst weg
!XOR EDX,EDX ;schneller und richtiger (!) als CDQ ich gehe hier mal von vorzeichenlosen Werten aus
!DIV dword[p.v_Mapsize] ;EAX ist noch hash
!MOV EAX,EDX ;Divisions-Rest
ProcedureReturn ;Gibt Hash in EAX zurück
EndProcedure
Procedure _NextPrime(num.l)
Protected prime.l, i.l, start.l
num + 1
Repeat
i = 2
While i * i < num
If num%i = 0 : prime = #False : Break
Else : prime = #True : EndIf
i + 1
Wend
If prime = #False : num + 1
Else
Break : EndIf
ForEver
ProcedureReturn num
EndProcedure
Procedure _ReDimHashmap(*object.sHASHMAP)
Protected NewSize.l = _NextPrime(_ResizeTo(*object\Mapsize, *object\Blocksize))
Protected *pNewMap.MapArray = Mem_Alloc(NewSize*SizeOf(sElement))
Protected *p.MapArray = *object\map, i.l, counter.l, r.l, hash.l
For i = 0 To *object\Mapsize-1
If *p\map[i]\used = #True
hash = _CreateHash(@*p\map[i]\key, NewSize)
counter = 0 : r = 0
While counter<NewSize
If *pNewMap\map[hash]\used = #False
CopyMemory(*p\map[i], *pNewMap\map[hash], SizeOf(sElement))
Break
Else
counter + 1
hash + ((counter/2)*(counter/2))*(-1 + (r*2))
hash%NewSize
If hash<0 : hash + NewSize : EndIf
r ! 1
EndIf
Wend
EndIf
Next i
Mem_Free(*object\map)
*object\map = *pNewMap
*object\Mapsize = NewSize
EndProcedure
; ------------------------------------------------------------------------------------
; Public
; ------------------------------------------------------------------------------------
Procedure HT_AddString(*object.sHASHMAP, key.s, string.s)
Protected hash.l, counter.l, r.l, *p.MapArray
hash = _CreateHash(@key, *object\Mapsize)
*p = *object\map
While counter<*object\Mapsize And Not *object\count = *object\Mapsize
If *p\map[hash]\used = #False
*p\map[hash]\key = key
*p\map[hash]\type = #CHASHMAP_TYPE_STRING
*p\map[hash]\string = string
*p\map[hash]\used = #True
*object\count + 1
If _DoResize(*object\count, *object\Mapsize)
_ReDimHashmap(Object)
EndIf
ProcedureReturn #True
Else
counter + 1
hash + ((counter/2)*(counter/2))*(-1 + (r*2))
hash%*object\Mapsize
If hash<0 : hash + *object\Mapsize : EndIf
r ! 1
EndIf
Wend
ProcedureReturn #CHASHMAP_MAP_OVERFLOW
EndProcedure
Procedure HT_AddValue(*object.sHASHMAP, key.s, Value.l)
Protected hash.l, counter.l, r.l, *p.MapArray
hash = _CreateHash(@key, *object\Mapsize)
*p = *object\map
While counter<*object\Mapsize And Not *object\count = *object\Mapsize
If *p\map[hash]\used = #False
*p\map[hash]\key = key
*p\map[hash]\type = #CHASHMAP_TYPE_VALUE
*p\map[hash]\Value = Value
*p\map[hash]\used = #True
*object\count + 1
If _DoResize(*object\count, *object\Mapsize)
_ReDimHashmap(Object)
EndIf
ProcedureReturn #True
Else
counter + 1
hash + ((counter/2)*(counter/2))*(-1 + (r*2))
hash%*object\Mapsize
If hash<0 : hash + *object\Mapsize : EndIf
r ! 1
EndIf
Wend
ProcedureReturn #CHASHMAP_MAP_OVERFLOW
EndProcedure
Procedure.s HT_GetString(*object.sHASHMAP, key.s)
Protected ht.l, hash.l, counter.l, r.l, *p.MapArray
hash = _CreateHash(@key, *object\Mapsize)
*p = *object\map
While counter<*object\Mapsize
If Not *p\map[hash]\used
ProcedureReturn ""
ElseIf *p\map[hash]\key = key And *p\map[hash]\type = #CHASHMAP_TYPE_STRING And *p\map[hash]\used = #True
ProcedureReturn *p\map[hash]\string
Else; // quadratic probe
counter + 1
hash + ((counter/2)*(counter/2))*(-1 + (r*2))
hash%*object\Mapsize
If hash<0 : hash + *object\Mapsize : EndIf
r ! 1 ; // Swap bool
EndIf
Wend
ProcedureReturn ""
EndProcedure
Procedure.l HT_GetValue(*object.sHASHMAP, key.s)
Protected hash.l, counter.l, r.l, *p.MapArray
hash = _CreateHash(@key, *object\Mapsize)
*p = *object\map
While counter<*object\Mapsize
If Not *p\map[hash]\used
ProcedureReturn #False
ElseIf *p\map[hash]\key = key And *p\map[hash]\type = #CHASHMAP_TYPE_VALUE And *p\map[hash]\used = #True
ProcedureReturn *p\map[hash]\Value
Else ; // quadratic probe
counter + 1
hash + ((counter/2)*(counter/2))*(-1 + (r*2))
hash%*object\Mapsize
If hash<0 : hash + *object\Mapsize : EndIf
r ! 1 ; // Swap bool
EndIf
Wend
EndProcedure
Procedure HT_Exists(*object.sHASHMAP, key.s)
Protected hash.l, counter.l, r.l, *p.MapArray
hash = _CreateHash(@key, *object\Mapsize)
*p = *object\map
While counter<*object\Mapsize
If Not *p\map[hash]\used
ProcedureReturn #False
ElseIf *p\map[hash]\key = key And *p\map[hash]\used = #True
ProcedureReturn #True
Else ; // quadratic probe
counter + 1
hash + ((counter/2)*(counter/2))*(-1 + (r*2))
hash%*object\Mapsize
If hash<0 : hash + *object\Mapsize : EndIf
r ! 1 ; // Swap bool
EndIf
Wend
EndProcedure
Procedure HT_FreeHashmap(*object.sHASHMAP)
Protected hash.l, i.l, *ptrStr, *p.MapArray
*p = *object\map
For i = 0 To *object\Mapsize-1
If *p\map[hash]\type = #CHASHMAP_TYPE_STRING
*ptrStr = @*p\map[hash]\string
*p\map[hash]\string = ""
!PUSH dword [p.p_ptrStr]
!CALL _SYS_FreeString@4
EndIf
Next
Mem_Free(*object\map)
ProcedureReturn #True
EndProcedure
Procedure HT_New(BlockSize = #InitialTableSize)
Protected *object.sHASHMAP, i.l, prime.l
BlockSize = _NextPrime(BlockSize)
*object = Mem_Alloc(SizeOf(sHASHMAP))
If *object
*object\Object = #True
*object\BlockSize = Blocksize
*object\Mapsize = Blocksize
*object\map = Mem_Alloc(Blocksize*SizeOf(sElement))
ProcedureReturn *object
EndIf
EndProcedure