@@ -89,13 +89,20 @@ func hashmapLen(m *hashmap) int {
89
89
// Set a specified key to a given value. Grow the map if necessary.
90
90
//go:nobounds
91
91
func hashmapSet (m * hashmap , key unsafe.Pointer , value unsafe.Pointer , hash uint32 , keyEqual func (x , y unsafe.Pointer , n uintptr ) bool ) {
92
+ tophash := hashmapTopHash (hash )
93
+
94
+ if m .buckets == nil {
95
+ // No bucket was allocated yet, do so now.
96
+ m .buckets = unsafe .Pointer (hashmapInsertIntoNewBucket (m , key , value , tophash ))
97
+ return
98
+ }
99
+
92
100
numBuckets := uintptr (1 ) << m .bucketBits
93
101
bucketNumber := (uintptr (hash ) & (numBuckets - 1 ))
94
102
bucketSize := unsafe .Sizeof (hashmapBucket {}) + uintptr (m .keySize )* 8 + uintptr (m .valueSize )* 8
95
103
bucketAddr := uintptr (m .buckets ) + bucketSize * bucketNumber
96
104
bucket := (* hashmapBucket )(unsafe .Pointer (bucketAddr ))
97
-
98
- tophash := hashmapTopHash (hash )
105
+ var lastBucket * hashmapBucket
99
106
100
107
// See whether the key already exists somewhere.
101
108
var emptySlotKey unsafe.Pointer
@@ -104,9 +111,9 @@ func hashmapSet(m *hashmap, key unsafe.Pointer, value unsafe.Pointer, hash uint3
104
111
for bucket != nil {
105
112
for i := uintptr (0 ); i < 8 ; i ++ {
106
113
slotKeyOffset := unsafe .Sizeof (hashmapBucket {}) + uintptr (m .keySize )* uintptr (i )
107
- slotKey := unsafe .Pointer (bucketAddr + slotKeyOffset )
114
+ slotKey := unsafe .Pointer (uintptr ( unsafe . Pointer ( bucket )) + slotKeyOffset )
108
115
slotValueOffset := unsafe .Sizeof (hashmapBucket {}) + uintptr (m .keySize )* 8 + uintptr (m .valueSize )* uintptr (i )
109
- slotValue := unsafe .Pointer (bucketAddr + slotValueOffset )
116
+ slotValue := unsafe .Pointer (uintptr ( unsafe . Pointer ( bucket )) + slotValueOffset )
110
117
if bucket .tophash [i ] == 0 && emptySlotKey == nil {
111
118
// Found an empty slot, store it for if we couldn't find an
112
119
// existing slot.
@@ -115,24 +122,45 @@ func hashmapSet(m *hashmap, key unsafe.Pointer, value unsafe.Pointer, hash uint3
115
122
emptySlotTophash = & bucket .tophash [i ]
116
123
}
117
124
if bucket .tophash [i ] == tophash {
118
- // Could be an existing value that's the same.
125
+ // Could be an existing key that's the same.
119
126
if keyEqual (key , slotKey , uintptr (m .keySize )) {
120
127
// found same key, replace it
121
128
memcpy (slotValue , value , uintptr (m .valueSize ))
122
129
return
123
130
}
124
131
}
125
132
}
133
+ lastBucket = bucket
126
134
bucket = bucket .next
127
135
}
128
- if emptySlotKey != nil {
129
- m .count ++
130
- memcpy (emptySlotKey , key , uintptr (m .keySize ))
131
- memcpy (emptySlotValue , value , uintptr (m .valueSize ))
132
- * emptySlotTophash = tophash
136
+ if emptySlotKey == nil {
137
+ // Add a new bucket to the bucket chain.
138
+ // TODO: rebalance if necessary to avoid O(n) insert and lookup time.
139
+ lastBucket .next = (* hashmapBucket )(hashmapInsertIntoNewBucket (m , key , value , tophash ))
133
140
return
134
141
}
135
- panic ("todo: hashmap: grow bucket" )
142
+ m .count ++
143
+ memcpy (emptySlotKey , key , uintptr (m .keySize ))
144
+ memcpy (emptySlotValue , value , uintptr (m .valueSize ))
145
+ * emptySlotTophash = tophash
146
+ }
147
+
148
+ // hashmapInsertIntoNewBucket creates a new bucket, inserts the given key and
149
+ // value into the bucket, and returns a pointer to this bucket.
150
+ func hashmapInsertIntoNewBucket (m * hashmap , key , value unsafe.Pointer , tophash uint8 ) * hashmapBucket {
151
+ bucketBufSize := unsafe .Sizeof (hashmapBucket {}) + uintptr (m .keySize )* 8 + uintptr (m .valueSize )* 8
152
+ bucketBuf := alloc (bucketBufSize )
153
+ // Insert into the first slot, which is empty as it has just been allocated.
154
+ slotKeyOffset := unsafe .Sizeof (hashmapBucket {})
155
+ slotKey := unsafe .Pointer (uintptr (bucketBuf ) + slotKeyOffset )
156
+ slotValueOffset := unsafe .Sizeof (hashmapBucket {}) + uintptr (m .keySize )* 8
157
+ slotValue := unsafe .Pointer (uintptr (bucketBuf ) + slotValueOffset )
158
+ m .count ++
159
+ memcpy (slotKey , key , uintptr (m .keySize ))
160
+ memcpy (slotValue , value , uintptr (m .valueSize ))
161
+ bucket := (* hashmapBucket )(bucketBuf )
162
+ bucket .tophash [0 ] = tophash
163
+ return bucket
136
164
}
137
165
138
166
// Get the value of a specified key, or zero the value if not found.
0 commit comments