DeclareModule ChainedHashMap EnableExplicit #InitialPower = 9 #InsertIterations = 100000 Structure Bucket *BucketData *Next.Bucket Hash.q EndStructure Structure HashMap EntryCount.i Capacity.i SizePower.a *Buckets.Bucket[0] EndStructure Prototype CbFree(*Data) Declare.i Create(SizePower.b) Declare.i GetByHash(*CHM.HashMap, Hash.q) Declare.b OverwriteByHash(*CHM.HashMap, *Data, Hash.q, *FreeingCallback.CbFree = #Null) Declare DeleteByHash(*CHM.HashMap, Hash.q) Declare Free(*CHM.HashMap) Declare NoopFree(*Data) EndDeclareModule Module ChainedHashMap EnableExplicit Procedure.l MakeBitMask(BitCount.b) EnableASM MOV eax, $00000001 MOV cl, BitCount SHL eax, cl DEC eax ProcedureReturn DisableASM EndProcedure Procedure.i Create(SizePower.b) Protected *CHM.HashMap = AllocateMemory(SizeOf(HashMap) + (SizeOf(Integer) * (MakeBitMask(SizePower) + 1))) If *CHM <> #Null *CHM\EntryCount = 0 *CHM\SizePower = SizePower Protected iBucket For iBucket = 0 To MakeBitMask(SizePower) *CHM\Buckets[iBucket] = #Null Next EndIf ProcedureReturn *CHM EndProcedure Procedure.i GetByHash(*CHM.HashMap, Hash.q) If *CHM <> #Null Protected *CurrentBucket.Bucket = *CHM\Buckets[(Hash & MakeBitMask(*CHM\SizePower))] While *CurrentBucket <> #Null If *CurrentBucket\Hash = Hash ProcedureReturn *CurrentBucket\BucketData EndIf *CurrentBucket = *CurrentBucket\Next Wend EndIf ProcedureReturn #Null EndProcedure Procedure.b OverwriteByHash(*CHM.HashMap, *Data, Hash.q, *FreeingCallback.CbFree = #Null) If *CHM <> #Null Protected *PreviousBucket.Bucket = #Null Protected *CurrentBucket.Bucket = *CHM\Buckets[(Hash & MakeBitMask(*CHM\SizePower))] ; We iterate over the bucket chain until we find a collision or an in-between. While *CurrentBucket <> #Null If *CurrentBucket\Hash >= Hash Break EndIf *PreviousBucket = *CurrentBucket *CurrentBucket = *CurrentBucket\Next Wend If *CurrentBucket = #Null Or *CurrentBucket\Hash <> Hash ; We either reached the end of the list, or no buckets was in the current slot. ; Or, we have an in-between, or we are at the start of the list. Protected *NewBucket.Bucket = AllocateMemory(SizeOf(Bucket)) If *NewBucket <> #Null *NewBucket\Next = *CurrentBucket *NewBucket\BucketData = *Data *NewBucket\Hash = Hash If *PreviousBucket <> #Null ; In-between, or we are at the start of the List. *PreviousBucket\Next = *NewBucket Else ; Start of the list. *CHM\Buckets[(Hash & MakeBitMask(*CHM\SizePower))] = *NewBucket EndIf *CHM\EntryCount = *CHM\EntryCount + 1 ProcedureReturn #True EndIf ElseIf *FreeingCallback <> #Null ; Handling collision. CallFunctionFast(*FreeingCallback, *CurrentBucket\BucketData) *CurrentBucket\BucketData = *Data ProcedureReturn #True EndIf ; Implied else: Handling collision while forbidden to overwrite => doing nothing. EndIf ProcedureReturn #False EndProcedure Procedure DeleteByHash(*CHM.HashMap, Hash.q) EndProcedure Procedure Free(*CHM.HashMap) EndProcedure Procedure NoopFree(*Data) : EndProcedure EndModule