github.com/cloudwego/dynamicgo@v0.2.6-0.20240519101509-707f41b6b834/proto/utils.go (about) 1 package proto 2 3 import ( 4 "unsafe" 5 6 "github.com/cloudwego/dynamicgo/internal/caching" 7 ) 8 9 const ( 10 defaultMaxBucketSize float64 = 10 11 defaultMapSize int = 4 12 defaultHashMapLoadFactor int = 4 13 defaultMaxFieldID = 256 14 defaultMaxNestedDepth = 1024 15 ) 16 17 // FieldNameMap is a map for field name and field descriptor 18 type FieldNameMap struct { 19 maxKeyLength int 20 all []caching.Pair 21 trie *caching.TrieTree 22 hash *caching.HashMap 23 } 24 25 // Set sets the field descriptor for the given key 26 func (ft *FieldNameMap) Set(key string, field *FieldDescriptor) (exist bool) { 27 if len(key) > ft.maxKeyLength { 28 ft.maxKeyLength = len(key) 29 } 30 for i, v := range ft.all { 31 if v.Key == key { 32 exist = true 33 ft.all[i].Val = unsafe.Pointer(field) 34 return 35 } 36 } 37 ft.all = append(ft.all, caching.Pair{Val: unsafe.Pointer(field), Key: key}) 38 return 39 } 40 41 // Get gets the field descriptor for the given key 42 func (ft FieldNameMap) Get(k string) *FieldDescriptor { 43 if ft.trie != nil { 44 return (*FieldDescriptor)(ft.trie.Get(k)) 45 } else if ft.hash != nil { 46 return (*FieldDescriptor)(ft.hash.Get(k)) 47 } 48 return nil 49 } 50 51 // All returns all field descriptors 52 func (ft FieldNameMap) All() []*FieldDescriptor { 53 return *(*[]*FieldDescriptor)(unsafe.Pointer(&ft.all)) 54 } 55 56 // Size returns the size of the map 57 func (ft FieldNameMap) Size() int { 58 if ft.hash != nil { 59 return ft.hash.Size() 60 } else { 61 return ft.trie.Size() 62 } 63 } 64 65 // Build builds the map. 66 // It will try to build a trie tree if the dispersion of keys is higher enough (min). 67 func (ft *FieldNameMap) Build() { 68 var empty unsafe.Pointer 69 70 // statistics the distrubution for each position: 71 // - primary slice store the position as its index 72 // - secondary map used to merge values with same char at the same position 73 var positionDispersion = make([]map[byte][]int, ft.maxKeyLength) 74 75 for i, v := range ft.all { 76 for j := ft.maxKeyLength - 1; j >= 0; j-- { 77 if v.Key == "" { 78 // empty key, especially store 79 empty = v.Val 80 } 81 // get the char at the position, defualt (position beyonds key range) is ASCII 0 82 var c = byte(0) 83 if j < len(v.Key) { 84 c = v.Key[j] 85 } 86 87 if positionDispersion[j] == nil { 88 positionDispersion[j] = make(map[byte][]int, 16) 89 } 90 // recoder the index i of the value with same char c at the same position j 91 positionDispersion[j][c] = append(positionDispersion[j][c], i) 92 } 93 } 94 95 // calculate the best position which has the highest dispersion 96 var idealPos = -1 97 var min = defaultMaxBucketSize 98 var count = len(ft.all) 99 100 for i := ft.maxKeyLength - 1; i >= 0; i-- { 101 cd := positionDispersion[i] 102 l := len(cd) 103 // calculate the dispersion (average bucket size) 104 f := float64(count) / float64(l) 105 if f < min { 106 min = f 107 idealPos = i 108 } 109 // 1 means all the value store in different bucket, no need to continue calulating 110 if min == 1 { 111 break 112 } 113 } 114 115 if idealPos != -1 { 116 // find the best position, build a trie tree 117 ft.hash = nil 118 ft.trie = &caching.TrieTree{} 119 // NOTICE: we only use a two-layer tree here, for better performance 120 ft.trie.Positions = append(ft.trie.Positions, idealPos) 121 // set all key-values to the trie tree 122 for _, v := range ft.all { 123 ft.trie.Set(v.Key, v.Val) 124 } 125 if empty != nil { 126 ft.trie.Empty = empty 127 } 128 129 } else { 130 // no ideal position, build a hash map 131 ft.trie = nil 132 ft.hash = caching.NewHashMap(len(ft.all), defaultHashMapLoadFactor) 133 // set all key-values to the trie tree 134 for _, v := range ft.all { 135 // caching.HashMap does not support duplicate key, so must check if the key exists before set 136 // WARN: if the key exists, the value WON'T be replaced 137 o := ft.hash.Get(v.Key) 138 if o == nil { 139 ft.hash.Set(v.Key, v.Val) 140 } 141 } 142 if empty != nil { 143 ft.hash.Set("", empty) 144 } 145 } 146 } 147 148 // FieldIDMap is a map from field id to field descriptor 149 type FieldNumberMap struct { 150 m []*FieldDescriptor 151 all []*FieldDescriptor 152 } 153 154 // All returns all field descriptors 155 func (fd FieldNumberMap) All() (ret []*FieldDescriptor) { 156 return fd.all 157 } 158 159 // Size returns the size of the map 160 func (fd FieldNumberMap) Size() int { 161 return len(fd.m) 162 } 163 164 // Get gets the field descriptor for the given id 165 func (fd FieldNumberMap) Get(id FieldNumber) *FieldDescriptor { 166 if int(id) >= len(fd.m) { 167 return nil 168 } 169 return fd.m[id] 170 } 171 172 // Set sets the field descriptor for the given id 173 func (fd *FieldNumberMap) Set(id FieldNumber, f *FieldDescriptor) { 174 if int(id) >= len(fd.m) { 175 len := int(id) + 1 176 tmp := make([]*FieldDescriptor, len) 177 copy(tmp, fd.m) 178 fd.m = tmp 179 } 180 o := (fd.m)[id] 181 if o == nil { 182 fd.all = append(fd.all, f) 183 } else { 184 for i, v := range fd.all { 185 if v == o { 186 fd.all[i] = f 187 break 188 } 189 } 190 } 191 fd.m[id] = f 192 }