1
2
3
4
5
6 package maps
7
8 import (
9 "internal/abi"
10 "internal/goarch"
11 "internal/runtime/math"
12 "internal/runtime/sys"
13 "unsafe"
14 )
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183 func h1(h uintptr) uintptr {
184 return h >> 7
185 }
186
187
188
189
190 func h2(h uintptr) uintptr {
191 return h & 0x7f
192 }
193
194
195 type Map struct {
196
197
198
199 used uint64
200
201
202 seed uintptr
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223 dirPtr unsafe.Pointer
224 dirLen int
225
226
227 globalDepth uint8
228
229
230
231 globalShift uint8
232
233
234
235
236
237 writing uint8
238
239
240
241 tombstonePossible bool
242
243
244
245 clearSeq uint64
246 }
247
248 func depthToShift(depth uint8) uint8 {
249 if goarch.PtrSize == 4 {
250 return 32 - depth
251 }
252 return 64 - depth
253 }
254
255
256
257
258
259
260 func NewMap(mt *abi.SwissMapType, hint uintptr, m *Map, maxAlloc uintptr) *Map {
261 if m == nil {
262 m = new(Map)
263 }
264
265 m.seed = uintptr(rand())
266
267 if hint <= abi.SwissMapGroupSlots {
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282 return m
283 }
284
285
286
287
288
289 targetCapacity := (hint * abi.SwissMapGroupSlots) / maxAvgGroupLoad
290 if targetCapacity < hint {
291 return m
292 }
293
294 dirSize := (uint64(targetCapacity) + maxTableCapacity - 1) / maxTableCapacity
295 dirSize, overflow := alignUpPow2(dirSize)
296 if overflow || dirSize > uint64(math.MaxUintptr) {
297 return m
298 }
299
300
301 groups, overflow := math.MulUintptr(uintptr(dirSize), maxTableCapacity)
302 if overflow {
303 return m
304 } else {
305 mem, overflow := math.MulUintptr(groups, mt.GroupSize)
306 if overflow || mem > maxAlloc {
307 return m
308 }
309 }
310
311 m.globalDepth = uint8(sys.TrailingZeros64(dirSize))
312 m.globalShift = depthToShift(m.globalDepth)
313
314 directory := make([]*table, dirSize)
315
316 for i := range directory {
317
318 directory[i] = newTable(mt, uint64(targetCapacity)/dirSize, i, m.globalDepth)
319 }
320
321 m.dirPtr = unsafe.Pointer(&directory[0])
322 m.dirLen = len(directory)
323
324 return m
325 }
326
327 func NewEmptyMap() *Map {
328 m := new(Map)
329 m.seed = uintptr(rand())
330
331 return m
332 }
333
334 func (m *Map) directoryIndex(hash uintptr) uintptr {
335 if m.dirLen == 1 {
336 return 0
337 }
338 return hash >> (m.globalShift & 63)
339 }
340
341 func (m *Map) directoryAt(i uintptr) *table {
342 return *(**table)(unsafe.Pointer(uintptr(m.dirPtr) + goarch.PtrSize*i))
343 }
344
345 func (m *Map) directorySet(i uintptr, nt *table) {
346 *(**table)(unsafe.Pointer(uintptr(m.dirPtr) + goarch.PtrSize*i)) = nt
347 }
348
349 func (m *Map) replaceTable(nt *table) {
350
351
352 entries := 1 << (m.globalDepth - nt.localDepth)
353 for i := 0; i < entries; i++ {
354
355 m.directorySet(uintptr(nt.index+i), nt)
356 }
357 }
358
359 func (m *Map) installTableSplit(old, left, right *table) {
360 if old.localDepth == m.globalDepth {
361
362
363 newDir := make([]*table, m.dirLen*2)
364 for i := range m.dirLen {
365 t := m.directoryAt(uintptr(i))
366 newDir[2*i] = t
367 newDir[2*i+1] = t
368
369
370
371
372 if t.index == i {
373 t.index = 2 * i
374 }
375 }
376 m.globalDepth++
377 m.globalShift--
378
379 m.dirPtr = unsafe.Pointer(&newDir[0])
380 m.dirLen = len(newDir)
381 }
382
383
384
385 left.index = old.index
386 m.replaceTable(left)
387
388 entries := 1 << (m.globalDepth - left.localDepth)
389 right.index = left.index + entries
390 m.replaceTable(right)
391 }
392
393 func (m *Map) Used() uint64 {
394 return m.used
395 }
396
397
398
399 func (m *Map) Get(typ *abi.SwissMapType, key unsafe.Pointer) (unsafe.Pointer, bool) {
400 return m.getWithoutKey(typ, key)
401 }
402
403 func (m *Map) getWithKey(typ *abi.SwissMapType, key unsafe.Pointer) (unsafe.Pointer, unsafe.Pointer, bool) {
404 if m.Used() == 0 {
405 return nil, nil, false
406 }
407
408 if m.writing != 0 {
409 fatal("concurrent map read and map write")
410 }
411
412 hash := typ.Hasher(key, m.seed)
413
414 if m.dirLen == 0 {
415 return m.getWithKeySmall(typ, hash, key)
416 }
417
418 idx := m.directoryIndex(hash)
419 return m.directoryAt(idx).getWithKey(typ, hash, key)
420 }
421
422 func (m *Map) getWithoutKey(typ *abi.SwissMapType, key unsafe.Pointer) (unsafe.Pointer, bool) {
423 if m.Used() == 0 {
424 return nil, false
425 }
426
427 if m.writing != 0 {
428 fatal("concurrent map read and map write")
429 }
430
431 hash := typ.Hasher(key, m.seed)
432
433 if m.dirLen == 0 {
434 _, elem, ok := m.getWithKeySmall(typ, hash, key)
435 return elem, ok
436 }
437
438 idx := m.directoryIndex(hash)
439 return m.directoryAt(idx).getWithoutKey(typ, hash, key)
440 }
441
442 func (m *Map) getWithKeySmall(typ *abi.SwissMapType, hash uintptr, key unsafe.Pointer) (unsafe.Pointer, unsafe.Pointer, bool) {
443 g := groupReference{
444 data: m.dirPtr,
445 }
446
447 match := g.ctrls().matchH2(h2(hash))
448
449 for match != 0 {
450 i := match.first()
451
452 slotKey := g.key(typ, i)
453 if typ.IndirectKey() {
454 slotKey = *((*unsafe.Pointer)(slotKey))
455 }
456
457 if typ.Key.Equal(key, slotKey) {
458 slotElem := g.elem(typ, i)
459 if typ.IndirectElem() {
460 slotElem = *((*unsafe.Pointer)(slotElem))
461 }
462 return slotKey, slotElem, true
463 }
464
465 match = match.removeFirst()
466 }
467
468
469
470 return nil, nil, false
471 }
472
473 func (m *Map) Put(typ *abi.SwissMapType, key, elem unsafe.Pointer) {
474 slotElem := m.PutSlot(typ, key)
475 typedmemmove(typ.Elem, slotElem, elem)
476 }
477
478
479
480
481
482 func (m *Map) PutSlot(typ *abi.SwissMapType, key unsafe.Pointer) unsafe.Pointer {
483 if m.writing != 0 {
484 fatal("concurrent map writes")
485 }
486
487 hash := typ.Hasher(key, m.seed)
488
489
490
491 m.writing ^= 1
492
493 if m.dirPtr == nil {
494 m.growToSmall(typ)
495 }
496
497 if m.dirLen == 0 {
498 if m.used < abi.SwissMapGroupSlots {
499 elem := m.putSlotSmall(typ, hash, key)
500
501 if m.writing == 0 {
502 fatal("concurrent map writes")
503 }
504 m.writing ^= 1
505
506 return elem
507 }
508
509
510
511
512
513 m.growToTable(typ)
514 }
515
516 for {
517 idx := m.directoryIndex(hash)
518 elem, ok := m.directoryAt(idx).PutSlot(typ, m, hash, key)
519 if !ok {
520 continue
521 }
522
523 if m.writing == 0 {
524 fatal("concurrent map writes")
525 }
526 m.writing ^= 1
527
528 return elem
529 }
530 }
531
532 func (m *Map) putSlotSmall(typ *abi.SwissMapType, hash uintptr, key unsafe.Pointer) unsafe.Pointer {
533 g := groupReference{
534 data: m.dirPtr,
535 }
536
537 match := g.ctrls().matchH2(h2(hash))
538
539
540 for match != 0 {
541 i := match.first()
542
543 slotKey := g.key(typ, i)
544 if typ.IndirectKey() {
545 slotKey = *((*unsafe.Pointer)(slotKey))
546 }
547 if typ.Key.Equal(key, slotKey) {
548 if typ.NeedKeyUpdate() {
549 typedmemmove(typ.Key, slotKey, key)
550 }
551
552 slotElem := g.elem(typ, i)
553 if typ.IndirectElem() {
554 slotElem = *((*unsafe.Pointer)(slotElem))
555 }
556
557 return slotElem
558 }
559 match = match.removeFirst()
560 }
561
562
563
564
565 match = g.ctrls().matchEmptyOrDeleted()
566 if match == 0 {
567 fatal("small map with no empty slot (concurrent map writes?)")
568 return nil
569 }
570
571 i := match.first()
572
573 slotKey := g.key(typ, i)
574 if typ.IndirectKey() {
575 kmem := newobject(typ.Key)
576 *(*unsafe.Pointer)(slotKey) = kmem
577 slotKey = kmem
578 }
579 typedmemmove(typ.Key, slotKey, key)
580
581 slotElem := g.elem(typ, i)
582 if typ.IndirectElem() {
583 emem := newobject(typ.Elem)
584 *(*unsafe.Pointer)(slotElem) = emem
585 slotElem = emem
586 }
587
588 g.ctrls().set(i, ctrl(h2(hash)))
589 m.used++
590
591 return slotElem
592 }
593
594 func (m *Map) growToSmall(typ *abi.SwissMapType) {
595 grp := newGroups(typ, 1)
596 m.dirPtr = grp.data
597
598 g := groupReference{
599 data: m.dirPtr,
600 }
601 g.ctrls().setEmpty()
602 }
603
604 func (m *Map) growToTable(typ *abi.SwissMapType) {
605 tab := newTable(typ, 2*abi.SwissMapGroupSlots, 0, 0)
606
607 g := groupReference{
608 data: m.dirPtr,
609 }
610
611 for i := uintptr(0); i < abi.SwissMapGroupSlots; i++ {
612 if (g.ctrls().get(i) & ctrlEmpty) == ctrlEmpty {
613
614 continue
615 }
616
617 key := g.key(typ, i)
618 if typ.IndirectKey() {
619 key = *((*unsafe.Pointer)(key))
620 }
621
622 elem := g.elem(typ, i)
623 if typ.IndirectElem() {
624 elem = *((*unsafe.Pointer)(elem))
625 }
626
627 hash := typ.Hasher(key, m.seed)
628
629 tab.uncheckedPutSlot(typ, hash, key, elem)
630 }
631
632 directory := make([]*table, 1)
633
634 directory[0] = tab
635
636 m.dirPtr = unsafe.Pointer(&directory[0])
637 m.dirLen = len(directory)
638
639 m.globalDepth = 0
640 m.globalShift = depthToShift(m.globalDepth)
641 }
642
643 func (m *Map) Delete(typ *abi.SwissMapType, key unsafe.Pointer) {
644 if m == nil || m.Used() == 0 {
645 if err := mapKeyError(typ, key); err != nil {
646 panic(err)
647 }
648 return
649 }
650
651 if m.writing != 0 {
652 fatal("concurrent map writes")
653 }
654
655 hash := typ.Hasher(key, m.seed)
656
657
658
659 m.writing ^= 1
660
661 if m.dirLen == 0 {
662 m.deleteSmall(typ, hash, key)
663 } else {
664 idx := m.directoryIndex(hash)
665 if m.directoryAt(idx).Delete(typ, m, hash, key) {
666 m.tombstonePossible = true
667 }
668 }
669
670 if m.used == 0 {
671
672
673
674 m.seed = uintptr(rand())
675 }
676
677 if m.writing == 0 {
678 fatal("concurrent map writes")
679 }
680 m.writing ^= 1
681 }
682
683 func (m *Map) deleteSmall(typ *abi.SwissMapType, hash uintptr, key unsafe.Pointer) {
684 g := groupReference{
685 data: m.dirPtr,
686 }
687
688 match := g.ctrls().matchH2(h2(hash))
689
690 for match != 0 {
691 i := match.first()
692 slotKey := g.key(typ, i)
693 origSlotKey := slotKey
694 if typ.IndirectKey() {
695 slotKey = *((*unsafe.Pointer)(slotKey))
696 }
697 if typ.Key.Equal(key, slotKey) {
698 m.used--
699
700 if typ.IndirectKey() {
701
702 *(*unsafe.Pointer)(origSlotKey) = nil
703 } else if typ.Key.Pointers() {
704
705 typedmemclr(typ.Key, slotKey)
706 }
707
708 slotElem := g.elem(typ, i)
709 if typ.IndirectElem() {
710
711 *(*unsafe.Pointer)(slotElem) = nil
712 } else {
713
714
715
716
717
718 typedmemclr(typ.Elem, slotElem)
719 }
720
721
722
723 g.ctrls().set(i, ctrlEmpty)
724 return
725 }
726 match = match.removeFirst()
727 }
728 }
729
730
731 func (m *Map) Clear(typ *abi.SwissMapType) {
732 if m == nil || m.Used() == 0 && !m.tombstonePossible {
733 return
734 }
735
736 if m.writing != 0 {
737 fatal("concurrent map writes")
738 }
739 m.writing ^= 1
740
741 if m.dirLen == 0 {
742 m.clearSmall(typ)
743 } else {
744 var lastTab *table
745 for i := range m.dirLen {
746 t := m.directoryAt(uintptr(i))
747 if t == lastTab {
748 continue
749 }
750 t.Clear(typ)
751 lastTab = t
752 }
753 m.used = 0
754 m.tombstonePossible = false
755
756 }
757 m.clearSeq++
758
759
760
761 m.seed = uintptr(rand())
762
763 if m.writing == 0 {
764 fatal("concurrent map writes")
765 }
766 m.writing ^= 1
767 }
768
769 func (m *Map) clearSmall(typ *abi.SwissMapType) {
770 g := groupReference{
771 data: m.dirPtr,
772 }
773
774 typedmemclr(typ.Group, g.data)
775 g.ctrls().setEmpty()
776
777 m.used = 0
778 }
779
780 func (m *Map) Clone(typ *abi.SwissMapType) *Map {
781
782 if m.writing != 0 {
783 fatal("concurrent map clone and map write")
784 }
785
786
787 m2 := new(Map)
788 *m2 = *m
789 m = m2
790
791
792 if m.dirPtr == nil {
793
794 } else if m.dirLen == 0 {
795
796 oldGroup := groupReference{data: m.dirPtr}
797 newGroup := groupReference{data: newGroups(typ, 1).data}
798 cloneGroup(typ, newGroup, oldGroup)
799 m.dirPtr = newGroup.data
800 } else {
801
802 oldDir := unsafe.Slice((**table)(m.dirPtr), m.dirLen)
803 newDir := make([]*table, m.dirLen)
804 for i, t := range oldDir {
805 if i > 0 && t == oldDir[i-1] {
806 newDir[i] = newDir[i-1]
807 continue
808 }
809 newDir[i] = t.clone(typ)
810 }
811 m.dirPtr = unsafe.Pointer(&newDir[0])
812 }
813
814 return m
815 }
816
817 func OldMapKeyError(t *abi.OldMapType, p unsafe.Pointer) error {
818 if !t.HashMightPanic() {
819 return nil
820 }
821 return mapKeyError2(t.Key, p)
822 }
823
824 func mapKeyError(t *abi.SwissMapType, p unsafe.Pointer) error {
825 if !t.HashMightPanic() {
826 return nil
827 }
828 return mapKeyError2(t.Key, p)
829 }
830
831 func mapKeyError2(t *abi.Type, p unsafe.Pointer) error {
832 if t.TFlag&abi.TFlagRegularMemory != 0 {
833 return nil
834 }
835 switch t.Kind() {
836 case abi.Float32, abi.Float64, abi.Complex64, abi.Complex128, abi.String:
837 return nil
838 case abi.Interface:
839 i := (*abi.InterfaceType)(unsafe.Pointer(t))
840 var t *abi.Type
841 var pdata *unsafe.Pointer
842 if len(i.Methods) == 0 {
843 a := (*abi.EmptyInterface)(p)
844 t = a.Type
845 if t == nil {
846 return nil
847 }
848 pdata = &a.Data
849 } else {
850 a := (*abi.NonEmptyInterface)(p)
851 if a.ITab == nil {
852 return nil
853 }
854 t = a.ITab.Type
855 pdata = &a.Data
856 }
857
858 if t.Equal == nil {
859 return unhashableTypeError{t}
860 }
861
862 if t.Kind_&abi.KindDirectIface != 0 {
863 return mapKeyError2(t, unsafe.Pointer(pdata))
864 } else {
865 return mapKeyError2(t, *pdata)
866 }
867 case abi.Array:
868 a := (*abi.ArrayType)(unsafe.Pointer(t))
869 for i := uintptr(0); i < a.Len; i++ {
870 if err := mapKeyError2(a.Elem, unsafe.Pointer(uintptr(p)+i*a.Elem.Size_)); err != nil {
871 return err
872 }
873 }
874 return nil
875 case abi.Struct:
876 s := (*abi.StructType)(unsafe.Pointer(t))
877 for _, f := range s.Fields {
878 if f.Name.IsBlank() {
879 continue
880 }
881 if err := mapKeyError2(f.Typ, unsafe.Pointer(uintptr(p)+f.Offset)); err != nil {
882 return err
883 }
884 }
885 return nil
886 default:
887
888 return unhashableTypeError{t}
889 }
890 }
891
892 type unhashableTypeError struct{ typ *abi.Type }
893
894 func (unhashableTypeError) RuntimeError() {}
895
896 func (e unhashableTypeError) Error() string { return "hash of unhashable type: " + typeString(e.typ) }
897
898
899
900
901 func typeString(typ *abi.Type) string
902
View as plain text