EnableExplicit #HashMap_InitialPower = 9 #HashMap_InsertIterations = 100000 ;{ Structure ChainedHashMapBucket *BucketData *Next.ChainedHashMapBucket Hash.q EndStructure Structure ChainedHashMap EntryCount.i Capacity.i SizePower.a *Buckets.ChainedHashMapBucket[0] EndStructure Prototype CbFree(*Data) Procedure.l MakeBitMask(BitCount.b) EnableASM MOV eax, $00000001 MOV cl, BitCount SHL eax, cl DEC eax ProcedureReturn DisableASM EndProcedure Procedure.i CHM_Create(SizePower.b) Protected *CHM.ChainedHashMap = AllocateMemory(SizeOf(ChainedHashMap) + (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 CHM_GetByHash(*CHM.ChainedHashMap, Hash.q) If *CHM <> #Null Protected *CurrentBucket.ChainedHashMapBucket = *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 CHM_OverwriteByHash(*CHM.ChainedHashMap, *Data, Hash.q, *FreeingCallback.CbFree = #Null) If *CHM <> #Null Protected *PreviousBucket.ChainedHashMapBucket = #Null Protected *CurrentBucket.ChainedHashMapBucket = *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.ChainedHashMapBucket = AllocateMemory(SizeOf(ChainedHashMapBucket)) 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 CHM_DeleteByHash(*CHM.ChainedHashMap, Hash.q) EndProcedure Procedure CHM_Free(*CHM.ChainedHashMap) EndProcedure Procedure NoopFree(*Data) : EndProcedure