github.com/higress-group/nottinygc@v0.0.0-20231101025119-e93c4c2f8520/intmap.go (about) 1 // Copyright wasilibs authors 2 // SPDX-License-Identifier: MIT 3 4 package nottinygc 5 6 import "C" 7 import "unsafe" 8 9 // Aim for initial over on the order of a few kilobytes. 10 11 const ( 12 initialBuckets = 512 13 numEmbedded = 8 14 ) 15 16 type item struct { 17 key uintptr 18 val uintptr 19 } 20 21 type extraNode struct { 22 next *extraNode 23 item item 24 } 25 26 type bucket struct { 27 embedded [numEmbedded]item 28 extra *extraNode 29 count byte 30 } 31 32 // intMap is a map from int to int. As it is used to cache descriptors within 33 // allocation, it cannot itself allocate using the Go heap and uses malloc 34 // instead. It also takes advantage of knowing we never replace values, so it 35 // does not support replacement. 36 type intMap struct { 37 buckets []bucket 38 count int 39 } 40 41 func newIntMap() intMap { 42 return intMap{ 43 buckets: newBuckets(initialBuckets), 44 count: 0, 45 } 46 } 47 48 func (m *intMap) put(key uintptr, val uintptr) { 49 if float64(m.count+1) > float64(len(m.buckets))*0.75 { 50 m.resize() 51 } 52 doPut(m.buckets, key, val) 53 m.count++ 54 } 55 56 func doPut(buckets []bucket, key uintptr, val uintptr) { 57 pos := hash(key) % uintptr(len(buckets)) 58 b := &buckets[pos] 59 if b.count < numEmbedded { 60 b.embedded[b.count] = item{key: key, val: val} 61 } else { 62 e := newExtraNode() 63 e.item = item{key: key, val: val} 64 e.next = b.extra 65 b.extra = e 66 } 67 b.count++ 68 } 69 70 func (m *intMap) resize() { 71 newSz := len(m.buckets) * 2 72 newBkts := newBuckets(newSz) 73 for i := 0; i < len(m.buckets); i++ { 74 b := &m.buckets[i] 75 for j := 0; j < int(b.count); j++ { 76 if j < numEmbedded { 77 doPut(newBkts, b.embedded[j].key, b.embedded[j].val) 78 } else { 79 for n := b.extra; n != nil; { 80 doPut(newBkts, n.item.key, n.item.val) 81 next := n.next 82 cfree(unsafe.Pointer(n)) 83 n = next 84 } 85 } 86 } 87 } 88 cfree(unsafe.Pointer(&m.buckets[0])) 89 m.buckets = newBkts 90 } 91 92 func (m *intMap) get(key uintptr) (uintptr, bool) { 93 pos := hash(key) % uintptr(len(m.buckets)) 94 b := &m.buckets[pos] 95 for i := 0; i < int(b.count); i++ { 96 if i < numEmbedded { 97 if b.embedded[i].key == key { 98 return b.embedded[i].val, true 99 } 100 } else { 101 for n := b.extra; n != nil; n = n.next { 102 if n.item.key == key { 103 return n.item.val, true 104 } 105 } 106 break 107 } 108 } 109 return 0, false 110 } 111 112 func hash(key uintptr) uintptr { 113 // Use Java algorithm for cheap and easy handling of aligned values. 114 // There are better ones with more operations, we can try later if needed. 115 return key ^ (key >> 16) 116 } 117 118 func newBuckets(size int) []bucket { 119 sz := unsafe.Sizeof(bucket{}) * uintptr(size) 120 bucketsArr := cmalloc(sz) 121 for i := uintptr(0); i < sz; i++ { 122 *(*byte)(unsafe.Pointer(uintptr(bucketsArr) + i)) = 0 123 } 124 buckets := unsafe.Slice((*bucket)(bucketsArr), size) 125 return buckets 126 } 127 128 func newExtraNode() *extraNode { 129 sz := unsafe.Sizeof(extraNode{}) 130 arr := cmalloc(sz) 131 for i := uintptr(0); i < sz; i++ { 132 *(*byte)(unsafe.Pointer(uintptr(arr) + i)) = 0 133 } 134 return (*extraNode)(arr) 135 }