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
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202 func h1(h uintptr) uintptr {
203 return h >> 7
204 }
205
206
207
208
209 func h2(h uintptr) uintptr {
210 return h & 0x7f
211 }
212
213
214 type Map struct {
215
216
217
218 used uint64
219
220
221 seed uintptr
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242 dirPtr unsafe.Pointer
243 dirLen int
244
245
246 globalDepth uint8
247
248
249
250 globalShift uint8
251
252
253
254
255
256 writing uint8
257
258
259
260 tombstonePossible bool
261
262
263
264 clearSeq uint64
265 }
266
267
268
269 const Use64BitHash = goarch.PtrSize == 8 && goarch.IsWasm == 0
270
271 func depthToShift(depth uint8) uint8 {
272 if !Use64BitHash {
273 return 32 - depth
274 }
275 return 64 - depth
276 }
277
278
279 func NewMap(mt *abi.MapType, hint uintptr, m *Map) *Map {
280 return newMap(mt, hint, m, sys.MaxAlloc)
281 }
282
283
284 func newMap(mt *abi.MapType, hint uintptr, m *Map, maxAlloc uintptr) *Map {
285 if m == nil {
286 m = new(Map)
287 }
288
289 m.seed = uintptr(rand())
290
291 if hint <= abi.MapGroupSlots {
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306 return m
307 }
308
309
310
311 dirSize, targetCapacity, ok := mapCapacityFor(mt, hint, maxAlloc)
312 if !ok {
313 return m
314 }
315
316 m.globalDepth = uint8(sys.TrailingZeros64(dirSize))
317 m.globalShift = depthToShift(m.globalDepth)
318
319 directory := make([]*table, dirSize)
320
321 for i := range directory {
322
323 directory[i] = newTable(mt, uint64(targetCapacity)/dirSize, i, m.globalDepth)
324 }
325
326 m.dirPtr = unsafe.Pointer(&directory[0])
327 m.dirLen = len(directory)
328
329 return m
330 }
331
332
333
334
335
336 func mapCapacityFor(mt *abi.MapType, hint uintptr, maxAlloc uintptr) (dirSize uint64, targetCapacity uintptr, ok bool) {
337
338
339 targetCapacity = (hint * abi.MapGroupSlots) / maxAvgGroupLoad
340 if targetCapacity < hint {
341 return 0, 0, false
342 }
343
344 dirSize = (uint64(targetCapacity) + maxTableCapacity - 1) / maxTableCapacity
345 var overflow bool
346 dirSize, overflow = alignUpPow2(dirSize)
347 if overflow || dirSize > uint64(math.MaxUintptr) {
348 return 0, 0, false
349 }
350
351
352 groups, overflow := math.MulUintptr(uintptr(dirSize), maxTableCapacity)
353 if overflow {
354 return 0, 0, false
355 }
356 mem, overflow := math.MulUintptr(groups, mt.GroupSize)
357 if overflow || mem > maxAlloc {
358 return 0, 0, false
359 }
360
361 return dirSize, targetCapacity, true
362 }
363
364 func NewEmptyMap() *Map {
365 m := new(Map)
366 m.seed = uintptr(rand())
367
368 return m
369 }
370
371
372
373 func (m *Map) totalCapacity() uint64 {
374 if m.dirLen == 0 {
375 return abi.MapGroupSlots
376 }
377
378 var total uint64
379 var lastTab *table
380 for i := range m.dirLen {
381 t := m.directoryAt(uintptr(i))
382 if t == lastTab {
383 continue
384 }
385 lastTab = t
386 total += uint64(t.capacity)
387 }
388 return total
389 }
390
391
392
393
394 func (m *Map) Grow(typ *abi.MapType, n uintptr) {
395 m.grow(typ, n, sys.MaxAlloc)
396 }
397
398
399 func (m *Map) grow(typ *abi.MapType, n uintptr, maxAlloc uintptr) {
400 if m.writing != 0 {
401 fatal("concurrent map writes")
402 }
403
404 if n == 0 {
405 return
406 }
407
408 used := uintptr(m.Used())
409 hint := used + n
410 if hint < used {
411 hint = math.MaxUintptr
412 }
413
414 if hint <= abi.MapGroupSlots {
415 return
416 }
417
418 dirSize, targetCapacity, ok := mapCapacityFor(typ, hint, maxAlloc)
419 if !ok {
420 return
421 }
422
423 if m.totalCapacity() >= uint64(targetCapacity) {
424 return
425 }
426
427
428
429 if oldDirLen := uint64(m.dirLen); dirSize < oldDirLen {
430 dirSize = oldDirLen
431 }
432
433 m.writing ^= 1
434
435 m.growTo(typ, dirSize, targetCapacity)
436
437 if m.writing == 0 {
438 fatal("concurrent map writes")
439 }
440 m.writing ^= 1
441 }
442
443
444
445
446 func (m *Map) growTo(typ *abi.MapType, dirSize uint64, targetCapacity uintptr) {
447 newGlobalDepth := uint8(sys.TrailingZeros64(dirSize))
448 newGlobalShift := depthToShift(newGlobalDepth)
449
450
451
452
453 tableCapacity := uint64(targetCapacity)
454 if dirSize > 1 {
455 tableCapacity = maxTableCapacity
456 }
457
458 newDir := make([]*table, dirSize)
459 for i := range newDir {
460 newDir[i] = newTable(typ, tableCapacity, i, newGlobalDepth)
461 }
462
463
464
465
466
467 orders := newGlobalDepth - m.globalDepth
468
469 putGroup := func(g groupReference, oldIndex int, oldDepth uint8) {
470 base := uintptr(oldIndex) << orders
471 mask := uintptr(1)<<(newGlobalDepth-oldDepth) - 1
472 for j := uintptr(0); j < abi.MapGroupSlots; j++ {
473 if (g.ctrls().get(j) & ctrlEmpty) == ctrlEmpty {
474
475 continue
476 }
477
478 key := g.key(typ, j)
479 if typ.IndirectKey() {
480 key = *((*unsafe.Pointer)(key))
481 }
482
483 elem := g.elem(typ, j)
484 if typ.IndirectElem() {
485 elem = *((*unsafe.Pointer)(elem))
486 }
487
488 hash := typ.Hasher(key, m.seed)
489 idx := base | hash>>(newGlobalShift&63)&mask
490 newDir[idx].uncheckedPutSlot(typ, hash, key, elem)
491 }
492 }
493
494 switch {
495 case m.dirPtr == nil:
496
497 case m.dirLen == 0:
498
499 putGroup(groupReference{data: m.dirPtr}, 0, 0)
500 default:
501 var lastTab *table
502 for i := range m.dirLen {
503 t := m.directoryAt(uintptr(i))
504 if t == lastTab {
505 continue
506 }
507 lastTab = t
508
509 for gi := uint64(0); gi <= t.groups.lengthMask; gi++ {
510 putGroup(t.groups.group(typ, gi), t.index, t.localDepth)
511 }
512
513
514
515 t.index = -1
516 }
517 }
518
519 for _, t := range newDir {
520 t.checkInvariants(typ, m)
521 }
522
523 m.dirPtr = unsafe.Pointer(&newDir[0])
524 m.dirLen = len(newDir)
525 m.globalDepth = newGlobalDepth
526 m.globalShift = newGlobalShift
527 m.tombstonePossible = false
528 }
529
530 func (m *Map) directoryIndex(hash uintptr) uintptr {
531 if m.dirLen == 1 {
532 return 0
533 }
534 return hash >> (m.globalShift & 63)
535 }
536
537 func (m *Map) directoryAt(i uintptr) *table {
538 return *(**table)(unsafe.Pointer(uintptr(m.dirPtr) + goarch.PtrSize*i))
539 }
540
541 func (m *Map) directorySet(i uintptr, nt *table) {
542 *(**table)(unsafe.Pointer(uintptr(m.dirPtr) + goarch.PtrSize*i)) = nt
543 }
544
545 func (m *Map) replaceTable(nt *table) {
546
547
548 entries := 1 << (m.globalDepth - nt.localDepth)
549 for i := 0; i < entries; i++ {
550
551 m.directorySet(uintptr(nt.index+i), nt)
552 }
553 }
554
555 func (m *Map) installTableSplit(old, left, right *table) {
556 if old.localDepth == m.globalDepth {
557
558
559 newDir := make([]*table, m.dirLen*2)
560 for i := range m.dirLen {
561 t := m.directoryAt(uintptr(i))
562 newDir[2*i] = t
563 newDir[2*i+1] = t
564
565
566
567
568 if t.index == i {
569 t.index = 2 * i
570 }
571 }
572 m.globalDepth++
573 m.globalShift--
574
575 m.dirPtr = unsafe.Pointer(&newDir[0])
576 m.dirLen = len(newDir)
577 }
578
579
580
581 left.index = old.index
582 m.replaceTable(left)
583
584 entries := 1 << (m.globalDepth - left.localDepth)
585 right.index = left.index + entries
586 m.replaceTable(right)
587 }
588
589 func (m *Map) Used() uint64 {
590 return m.used
591 }
592
593
594
595 func (m *Map) Get(typ *abi.MapType, key unsafe.Pointer) (unsafe.Pointer, bool) {
596 return m.getWithoutKey(typ, key)
597 }
598
599 func (m *Map) getWithKey(typ *abi.MapType, key unsafe.Pointer) (unsafe.Pointer, unsafe.Pointer, bool) {
600 if m.Used() == 0 {
601 return nil, nil, false
602 }
603
604 if m.writing != 0 {
605 fatal("concurrent map read and map write")
606 }
607
608 hash := typ.Hasher(key, m.seed)
609
610 if m.dirLen == 0 {
611 return m.getWithKeySmall(typ, hash, key)
612 }
613
614 idx := m.directoryIndex(hash)
615 return m.directoryAt(idx).getWithKey(typ, hash, key)
616 }
617
618 func (m *Map) getWithoutKey(typ *abi.MapType, key unsafe.Pointer) (unsafe.Pointer, bool) {
619 if m.Used() == 0 {
620 return nil, false
621 }
622
623 if m.writing != 0 {
624 fatal("concurrent map read and map write")
625 }
626
627 hash := typ.Hasher(key, m.seed)
628
629 if m.dirLen == 0 {
630 _, elem, ok := m.getWithKeySmall(typ, hash, key)
631 return elem, ok
632 }
633
634 idx := m.directoryIndex(hash)
635 return m.directoryAt(idx).getWithoutKey(typ, hash, key)
636 }
637
638 func (m *Map) getWithKeySmall(typ *abi.MapType, hash uintptr, key unsafe.Pointer) (unsafe.Pointer, unsafe.Pointer, bool) {
639 g := groupReference{
640 data: m.dirPtr,
641 }
642
643 match := g.ctrls().matchH2(h2(hash))
644
645 for match != 0 {
646 i := match.first()
647
648 slotKey := g.key(typ, i)
649 if typ.IndirectKey() {
650 slotKey = *((*unsafe.Pointer)(slotKey))
651 }
652
653 if typ.Key.Equal(key, slotKey) {
654 slotElem := g.elem(typ, i)
655 if typ.IndirectElem() {
656 slotElem = *((*unsafe.Pointer)(slotElem))
657 }
658 return slotKey, slotElem, true
659 }
660
661 match = match.removeFirst()
662 }
663
664
665
666 return nil, nil, false
667 }
668
669 func (m *Map) Put(typ *abi.MapType, key, elem unsafe.Pointer) {
670 slotElem := m.PutSlot(typ, key)
671 typedmemmove(typ.Elem, slotElem, elem)
672 }
673
674
675
676
677
678 func (m *Map) PutSlot(typ *abi.MapType, key unsafe.Pointer) unsafe.Pointer {
679 if m.writing != 0 {
680 fatal("concurrent map writes")
681 }
682
683 hash := typ.Hasher(key, m.seed)
684
685
686
687 m.writing ^= 1
688
689 if m.dirPtr == nil {
690 m.growToSmall(typ)
691 }
692
693 if m.dirLen == 0 {
694 elem := m.putSlotSmall(typ, hash, key)
695 if elem == nil {
696
697 tab := m.growToTable(typ)
698
699 elem = tab.uncheckedPutSlotForAssign(typ, hash, key)
700 m.used++
701
702 tab.checkInvariants(typ, m)
703 }
704
705 if m.writing == 0 {
706 fatal("concurrent map writes")
707 }
708 m.writing ^= 1
709
710 return elem
711 }
712
713 for {
714 idx := m.directoryIndex(hash)
715 elem, ok := m.directoryAt(idx).PutSlot(typ, m, hash, key)
716 if !ok {
717 continue
718 }
719
720 if m.writing == 0 {
721 fatal("concurrent map writes")
722 }
723 m.writing ^= 1
724
725 return elem
726 }
727 }
728
729 func (m *Map) putSlotSmall(typ *abi.MapType, hash uintptr, key unsafe.Pointer) unsafe.Pointer {
730 g := groupReference{
731 data: m.dirPtr,
732 }
733
734 match := g.ctrls().matchH2(h2(hash))
735
736
737 for match != 0 {
738 i := match.first()
739
740 slotKey := g.key(typ, i)
741 if typ.IndirectKey() {
742 slotKey = *((*unsafe.Pointer)(slotKey))
743 }
744 if typ.Key.Equal(key, slotKey) {
745 if typ.NeedKeyUpdate() {
746 typedmemmove(typ.Key, slotKey, key)
747 }
748
749 slotElem := g.elem(typ, i)
750 if typ.IndirectElem() {
751 slotElem = *((*unsafe.Pointer)(slotElem))
752 }
753
754 return slotElem
755 }
756 match = match.removeFirst()
757 }
758
759
760
761
762 match = g.ctrls().matchEmptyOrDeleted()
763 if match == 0 {
764
765 return nil
766 }
767
768 i := match.first()
769
770 slotKey := g.key(typ, i)
771 if typ.IndirectKey() {
772 kmem := newobject(typ.Key)
773 *(*unsafe.Pointer)(slotKey) = kmem
774 slotKey = kmem
775 }
776 typedmemmove(typ.Key, slotKey, key)
777
778 slotElem := g.elem(typ, i)
779 if typ.IndirectElem() {
780 emem := newobject(typ.Elem)
781 *(*unsafe.Pointer)(slotElem) = emem
782 slotElem = emem
783 }
784
785 g.ctrls().set(i, ctrl(h2(hash)))
786 m.used++
787
788 return slotElem
789 }
790
791 func (m *Map) growToSmall(typ *abi.MapType) {
792 grp := newGroups(typ, 1)
793 m.dirPtr = grp.data
794
795 g := groupReference{
796 data: m.dirPtr,
797 }
798 g.ctrls().setEmpty()
799 }
800
801 func (m *Map) growToTable(typ *abi.MapType) *table {
802 tab := newTable(typ, 2*abi.MapGroupSlots, 0, 0)
803
804 g := groupReference{
805 data: m.dirPtr,
806 }
807
808 for i := uintptr(0); i < abi.MapGroupSlots; i++ {
809 if (g.ctrls().get(i) & ctrlEmpty) == ctrlEmpty {
810
811 continue
812 }
813
814 key := g.key(typ, i)
815 if typ.IndirectKey() {
816 key = *((*unsafe.Pointer)(key))
817 }
818
819 elem := g.elem(typ, i)
820 if typ.IndirectElem() {
821 elem = *((*unsafe.Pointer)(elem))
822 }
823
824 hash := typ.Hasher(key, m.seed)
825
826 tab.uncheckedPutSlot(typ, hash, key, elem)
827 }
828
829 directory := make([]*table, 1)
830
831 directory[0] = tab
832
833 m.dirPtr = unsafe.Pointer(&directory[0])
834 m.dirLen = len(directory)
835
836 m.globalDepth = 0
837 m.globalShift = depthToShift(m.globalDepth)
838 return tab
839 }
840
841 func (m *Map) Delete(typ *abi.MapType, key unsafe.Pointer) {
842 if m == nil || m.Used() == 0 {
843 if err := mapKeyError(typ, key); err != nil {
844 panic(err)
845 }
846 return
847 }
848
849 if m.writing != 0 {
850 fatal("concurrent map writes")
851 }
852
853 hash := typ.Hasher(key, m.seed)
854
855
856
857 m.writing ^= 1
858
859 if m.dirLen == 0 {
860 m.deleteSmall(typ, hash, key)
861 } else {
862 idx := m.directoryIndex(hash)
863 if m.directoryAt(idx).Delete(typ, m, hash, key) {
864 m.tombstonePossible = true
865 }
866 }
867
868 if m.used == 0 {
869
870
871
872 m.seed = uintptr(rand())
873 }
874
875 if m.writing == 0 {
876 fatal("concurrent map writes")
877 }
878 m.writing ^= 1
879 }
880
881 func (m *Map) deleteSmall(typ *abi.MapType, hash uintptr, key unsafe.Pointer) {
882 g := groupReference{
883 data: m.dirPtr,
884 }
885
886 match := g.ctrls().matchH2(h2(hash))
887
888 for match != 0 {
889 i := match.first()
890 slotKey := g.key(typ, i)
891 origSlotKey := slotKey
892 if typ.IndirectKey() {
893 slotKey = *((*unsafe.Pointer)(slotKey))
894 }
895 if typ.Key.Equal(key, slotKey) {
896 m.used--
897
898 if typ.IndirectKey() {
899
900 *(*unsafe.Pointer)(origSlotKey) = nil
901 } else if typ.Key.Pointers() {
902
903 typedmemclr(typ.Key, slotKey)
904 }
905
906 slotElem := g.elem(typ, i)
907 if typ.IndirectElem() {
908
909 *(*unsafe.Pointer)(slotElem) = nil
910 } else {
911
912
913
914
915
916 typedmemclr(typ.Elem, slotElem)
917 }
918
919
920
921 g.ctrls().set(i, ctrlEmpty)
922 return
923 }
924 match = match.removeFirst()
925 }
926 }
927
928
929 func (m *Map) Clear(typ *abi.MapType) {
930 if m == nil || m.Used() == 0 && !m.tombstonePossible {
931 return
932 }
933
934 if m.writing != 0 {
935 fatal("concurrent map writes")
936 }
937 m.writing ^= 1
938
939 if m.dirLen == 0 {
940 m.clearSmall(typ)
941 } else {
942 var lastTab *table
943 for i := range m.dirLen {
944 t := m.directoryAt(uintptr(i))
945 if t == lastTab {
946 continue
947 }
948 t.Clear(typ)
949 lastTab = t
950 }
951 m.used = 0
952 m.tombstonePossible = false
953
954 }
955 m.clearSeq++
956
957
958
959 m.seed = uintptr(rand())
960
961 if m.writing == 0 {
962 fatal("concurrent map writes")
963 }
964 m.writing ^= 1
965 }
966
967 func (m *Map) clearSmall(typ *abi.MapType) {
968 g := groupReference{
969 data: m.dirPtr,
970 }
971
972 typedmemclr(typ.Group, g.data)
973 g.ctrls().setEmpty()
974
975 m.used = 0
976 }
977
978 func (m *Map) Clone(typ *abi.MapType) *Map {
979
980 if m.writing != 0 {
981 fatal("concurrent map clone and map write")
982 }
983
984
985 m2 := new(Map)
986 *m2 = *m
987 m = m2
988
989
990 if m.dirPtr == nil {
991
992 } else if m.dirLen == 0 {
993
994 oldGroup := groupReference{data: m.dirPtr}
995 newGroup := groupReference{data: newGroups(typ, 1).data}
996 cloneGroup(typ, newGroup, oldGroup)
997 m.dirPtr = newGroup.data
998 } else {
999
1000 oldDir := unsafe.Slice((**table)(m.dirPtr), m.dirLen)
1001 newDir := make([]*table, m.dirLen)
1002 for i, t := range oldDir {
1003 if i > 0 && t == oldDir[i-1] {
1004 newDir[i] = newDir[i-1]
1005 continue
1006 }
1007 newDir[i] = t.clone(typ)
1008 }
1009 m.dirPtr = unsafe.Pointer(&newDir[0])
1010 }
1011
1012 return m
1013 }
1014
1015 func mapKeyError(t *abi.MapType, p unsafe.Pointer) error {
1016 if !t.HashMightPanic() {
1017 return nil
1018 }
1019 return mapKeyError2(t.Key, p)
1020 }
1021
1022 func mapKeyError2(t *abi.Type, p unsafe.Pointer) error {
1023 if t.TFlag&abi.TFlagRegularMemory != 0 {
1024 return nil
1025 }
1026 switch t.Kind() {
1027 case abi.Float32, abi.Float64, abi.Complex64, abi.Complex128, abi.String:
1028 return nil
1029 case abi.Interface:
1030 i := (*abi.InterfaceType)(unsafe.Pointer(t))
1031 var t *abi.Type
1032 var pdata *unsafe.Pointer
1033 if len(i.Methods) == 0 {
1034 a := (*abi.EmptyInterface)(p)
1035 t = a.Type
1036 if t == nil {
1037 return nil
1038 }
1039 pdata = &a.Data
1040 } else {
1041 a := (*abi.NonEmptyInterface)(p)
1042 if a.ITab == nil {
1043 return nil
1044 }
1045 t = a.ITab.Type
1046 pdata = &a.Data
1047 }
1048
1049 if t.Equal == nil {
1050 return unhashableTypeError{t}
1051 }
1052
1053 if t.IsDirectIface() {
1054 return mapKeyError2(t, unsafe.Pointer(pdata))
1055 } else {
1056 return mapKeyError2(t, *pdata)
1057 }
1058 case abi.Array:
1059 a := (*abi.ArrayType)(unsafe.Pointer(t))
1060 for i := uintptr(0); i < a.Len; i++ {
1061 if err := mapKeyError2(a.Elem, unsafe.Pointer(uintptr(p)+i*a.Elem.Size_)); err != nil {
1062 return err
1063 }
1064 }
1065 return nil
1066 case abi.Struct:
1067 s := (*abi.StructType)(unsafe.Pointer(t))
1068 for _, f := range s.Fields {
1069 if f.Name.IsBlank() {
1070 continue
1071 }
1072 if err := mapKeyError2(f.Typ, unsafe.Pointer(uintptr(p)+f.Offset)); err != nil {
1073 return err
1074 }
1075 }
1076 return nil
1077 default:
1078
1079 return unhashableTypeError{t}
1080 }
1081 }
1082
1083 type unhashableTypeError struct{ typ *abi.Type }
1084
1085 func (unhashableTypeError) RuntimeError() {}
1086
1087 func (e unhashableTypeError) Error() string { return "hash of unhashable type: " + typeString(e.typ) }
1088
1089
1090
1091
1092 func typeString(typ *abi.Type) string
1093
View as plain text