1
2
3
4
5 package maps
6
7 import (
8 "internal/abi"
9 "internal/goarch"
10 "internal/goexperiment"
11 "internal/race"
12 "internal/runtime/sys"
13 "unsafe"
14 )
15
16 func (m *Map) getWithoutKeySmallFastStr(typ *abi.MapType, key string) unsafe.Pointer {
17 g := groupReference{
18 data: m.dirPtr,
19 }
20
21 ctrls := *g.ctrls()
22 slotKey := g.key(typ, 0)
23 var keyStride uintptr
24 if goexperiment.MapSplitGroup {
25 keyStride = 2 * goarch.PtrSize
26 } else {
27 keyStride = typ.KeyStride
28 }
29
30
31
32
33
34 if len(key) > 64 {
35
36 j := abi.MapGroupSlots
37 for i := range abi.MapGroupSlots {
38 if ctrls&(1<<7) == 0 && longStringQuickEqualityTest(key, *(*string)(slotKey)) {
39 if j < abi.MapGroupSlots {
40
41
42 goto dohash
43 }
44 j = i
45 }
46 slotKey = unsafe.Pointer(uintptr(slotKey) + keyStride)
47 ctrls >>= 8
48 }
49 if j == abi.MapGroupSlots {
50
51 return nil
52 }
53
54 slotKey = g.key(typ, uintptr(j))
55 if key == *(*string)(slotKey) {
56 if goexperiment.MapSplitGroup {
57 return g.elem(typ, uintptr(j))
58 } else {
59 return unsafe.Pointer(uintptr(slotKey) + 2*goarch.PtrSize)
60 }
61 }
62 return nil
63 }
64
65 dohash:
66
67
68
69
70 k := key
71 hash := strhash(unsafe.Pointer(&k), m.seed)
72 h2 := uint8(h2(hash))
73 ctrls = *g.ctrls()
74 slotKey = g.key(typ, 0)
75
76 for i := range uintptr(abi.MapGroupSlots) {
77 if uint8(ctrls) == h2 && key == *(*string)(slotKey) {
78 if goexperiment.MapSplitGroup {
79 return g.elem(typ, i)
80 } else {
81 return unsafe.Pointer(uintptr(slotKey) + 2*goarch.PtrSize)
82 }
83 }
84 slotKey = unsafe.Pointer(uintptr(slotKey) + keyStride)
85 ctrls >>= 8
86 }
87 return nil
88 }
89
90
91
92
93 func longStringQuickEqualityTest(a, b string) bool {
94 if len(a) != len(b) {
95 return false
96 }
97 x, y := stringPtr(a), stringPtr(b)
98
99 if *(*[8]byte)(x) != *(*[8]byte)(y) {
100 return false
101 }
102
103 x = unsafe.Pointer(uintptr(x) + uintptr(len(a)) - 8)
104 y = unsafe.Pointer(uintptr(y) + uintptr(len(a)) - 8)
105 if *(*[8]byte)(x) != *(*[8]byte)(y) {
106 return false
107 }
108 return true
109 }
110 func stringPtr(s string) unsafe.Pointer {
111 type stringStruct struct {
112 ptr unsafe.Pointer
113 len int
114 }
115 return (*stringStruct)(unsafe.Pointer(&s)).ptr
116 }
117
118
119 func runtime_mapaccess1_faststr(typ *abi.MapType, m *Map, key string) unsafe.Pointer {
120 p, _ := runtime_mapaccess2_faststr(typ, m, key)
121 return p
122 }
123
124
125 func runtime_mapaccess2_faststr(typ *abi.MapType, m *Map, key string) (unsafe.Pointer, bool) {
126 if race.Enabled && m != nil {
127 callerpc := sys.GetCallerPC()
128 pc := abi.FuncPCABIInternal(runtime_mapaccess2_faststr)
129 race.ReadPC(unsafe.Pointer(m), callerpc, pc)
130 }
131
132 if m == nil || m.Used() == 0 {
133 return unsafe.Pointer(&zeroVal[0]), false
134 }
135
136 if m.writing != 0 {
137 fatal("concurrent map read and map write")
138 return nil, false
139 }
140
141 if m.dirLen <= 0 {
142 elem := m.getWithoutKeySmallFastStr(typ, key)
143 if elem == nil {
144 return unsafe.Pointer(&zeroVal[0]), false
145 }
146 return elem, true
147 }
148
149
150
151 k := key
152 hash := strhash(unsafe.Pointer(&k), m.seed)
153
154
155 idx := m.directoryIndex(hash)
156 t := m.directoryAt(idx)
157
158
159 seq := makeProbeSeq(h1(hash), t.groups.lengthMask)
160 h2Hash := h2(hash)
161 for ; ; seq = seq.next() {
162 g := t.groups.group(typ, seq.offset)
163
164 match := g.ctrls().matchH2(h2Hash)
165
166 for match != 0 {
167 i := match.first()
168
169 slotKey := g.key(typ, i)
170 if key == *(*string)(slotKey) {
171 if goexperiment.MapSplitGroup {
172 return g.elem(typ, i), true
173 } else {
174 return unsafe.Pointer(uintptr(slotKey) + 2*goarch.PtrSize), true
175 }
176 }
177 match = match.removeFirst()
178 }
179
180 match = g.ctrls().matchEmpty()
181 if match != 0 {
182
183
184 return unsafe.Pointer(&zeroVal[0]), false
185 }
186 }
187 }
188
189 func (m *Map) putSlotSmallFastStr(typ *abi.MapType, hash uintptr, key string) unsafe.Pointer {
190 g := groupReference{
191 data: m.dirPtr,
192 }
193
194 match := g.ctrls().matchH2(h2(hash))
195
196
197 for match != 0 {
198 i := match.first()
199
200 slotKey := g.key(typ, i)
201 if key == *(*string)(slotKey) {
202
203 *(*string)(slotKey) = key
204 slotElem := g.elem(typ, i)
205 return slotElem
206 }
207 match = match.removeFirst()
208 }
209
210
211
212
213 match = g.ctrls().matchEmptyOrDeleted()
214 if match == 0 {
215 fatal("small map with no empty slot (concurrent map writes?)")
216 }
217
218 i := match.first()
219
220 slotKey := g.key(typ, i)
221 *(*string)(slotKey) = key
222
223 slotElem := g.elem(typ, i)
224
225 g.ctrls().set(i, ctrl(h2(hash)))
226 m.used++
227
228 return slotElem
229 }
230
231
232 func runtime_mapassign_faststr(typ *abi.MapType, m *Map, key string) unsafe.Pointer {
233 if m == nil {
234 panic(errNilAssign)
235 }
236 if race.Enabled {
237 callerpc := sys.GetCallerPC()
238 pc := abi.FuncPCABIInternal(runtime_mapassign_faststr)
239 race.WritePC(unsafe.Pointer(m), callerpc, pc)
240 }
241 if m.writing != 0 {
242 fatal("concurrent map writes")
243 }
244
245
246
247 k := key
248 hash := strhash(unsafe.Pointer(&k), m.seed)
249
250
251
252 m.writing ^= 1
253
254 if m.dirPtr == nil {
255 m.growToSmall(typ)
256 }
257
258 if m.dirLen == 0 {
259 if m.used < abi.MapGroupSlots {
260 elem := m.putSlotSmallFastStr(typ, hash, key)
261
262 if m.writing == 0 {
263 fatal("concurrent map writes")
264 }
265 m.writing ^= 1
266
267 return elem
268 }
269
270
271 m.growToTable(typ)
272 }
273
274 var slotElem unsafe.Pointer
275 outer:
276 for {
277
278 idx := m.directoryIndex(hash)
279 t := m.directoryAt(idx)
280
281 seq := makeProbeSeq(h1(hash), t.groups.lengthMask)
282
283
284
285
286 var firstDeletedGroup groupReference
287 var firstDeletedSlot uintptr
288
289 h2Hash := h2(hash)
290 for ; ; seq = seq.next() {
291 g := t.groups.group(typ, seq.offset)
292 match := g.ctrls().matchH2(h2Hash)
293
294
295 for match != 0 {
296 i := match.first()
297
298 slotKey := g.key(typ, i)
299 if key == *(*string)(slotKey) {
300
301
302 *(*string)(slotKey) = key
303 slotElem = g.elem(typ, i)
304
305 t.checkInvariants(typ, m)
306 break outer
307 }
308 match = match.removeFirst()
309 }
310
311
312
313 match = g.ctrls().matchEmptyOrDeleted()
314 if match == 0 {
315 continue
316 }
317 i := match.first()
318 if g.ctrls().get(i) == ctrlDeleted {
319
320
321 if firstDeletedGroup.data == nil {
322 firstDeletedGroup = g
323 firstDeletedSlot = i
324 }
325 continue
326 }
327
328
329
330
331
332 if firstDeletedGroup.data != nil {
333 g = firstDeletedGroup
334 i = firstDeletedSlot
335 t.growthLeft++
336 }
337
338
339 if t.growthLeft == 0 {
340 t.pruneTombstones(typ, m)
341 }
342
343
344 if t.growthLeft > 0 {
345 slotKey := g.key(typ, i)
346 *(*string)(slotKey) = key
347
348 slotElem = g.elem(typ, i)
349
350 g.ctrls().set(i, ctrl(h2Hash))
351 t.growthLeft--
352 t.used++
353 m.used++
354
355 t.checkInvariants(typ, m)
356 break outer
357 }
358
359 t.rehash(typ, m)
360 continue outer
361 }
362 }
363
364 if m.writing == 0 {
365 fatal("concurrent map writes")
366 }
367 m.writing ^= 1
368
369 return slotElem
370 }
371
372
373 func runtime_mapdelete_faststr(typ *abi.MapType, m *Map, key string) {
374 if race.Enabled {
375 callerpc := sys.GetCallerPC()
376 pc := abi.FuncPCABIInternal(runtime_mapdelete_faststr)
377 race.WritePC(unsafe.Pointer(m), callerpc, pc)
378 }
379
380 if m == nil || m.Used() == 0 {
381 return
382 }
383
384 m.Delete(typ, abi.NoEscape(unsafe.Pointer(&key)))
385 }
386
View as plain text