fcache.go (2894B)
1 /* 2 * Copyright 2021 ByteDance Inc. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package caching 18 19 import ( 20 `strings` 21 `unsafe` 22 23 `github.com/bytedance/sonic/internal/rt` 24 ) 25 26 type FieldMap struct { 27 N uint64 28 b unsafe.Pointer 29 m map[string]int 30 } 31 32 type FieldEntry struct { 33 ID int 34 Name string 35 Hash uint64 36 } 37 38 const ( 39 FieldMap_N = int64(unsafe.Offsetof(FieldMap{}.N)) 40 FieldMap_b = int64(unsafe.Offsetof(FieldMap{}.b)) 41 FieldEntrySize = int64(unsafe.Sizeof(FieldEntry{})) 42 ) 43 44 func newBucket(n int) unsafe.Pointer { 45 v := make([]FieldEntry, n) 46 return (*rt.GoSlice)(unsafe.Pointer(&v)).Ptr 47 } 48 49 func CreateFieldMap(n int) *FieldMap { 50 return &FieldMap { 51 N: uint64(n * 2), 52 b: newBucket(n * 2), // LoadFactor = 0.5 53 m: make(map[string]int, n * 2), 54 } 55 } 56 57 func (self *FieldMap) At(p uint64) *FieldEntry { 58 off := uintptr(p) * uintptr(FieldEntrySize) 59 return (*FieldEntry)(unsafe.Pointer(uintptr(self.b) + off)) 60 } 61 62 // Get searches FieldMap by name. JIT generated assembly does NOT call this 63 // function, rather it implements its own version directly in assembly. So 64 // we must ensure this function stays in sync with the JIT generated one. 65 func (self *FieldMap) Get(name string) int { 66 h := StrHash(name) 67 p := h % self.N 68 s := self.At(p) 69 70 /* find the element; 71 * the hash map is never full, so the loop will always terminate */ 72 for s.Hash != 0 { 73 if s.Hash == h && s.Name == name { 74 return s.ID 75 } else { 76 p = (p + 1) % self.N 77 s = self.At(p) 78 } 79 } 80 81 /* not found */ 82 return -1 83 } 84 85 func (self *FieldMap) Set(name string, i int) { 86 h := StrHash(name) 87 p := h % self.N 88 s := self.At(p) 89 90 /* searching for an empty slot; 91 * the hash map is never full, so the loop will always terminate */ 92 for s.Hash != 0 { 93 p = (p + 1) % self.N 94 s = self.At(p) 95 } 96 97 /* set the value */ 98 s.ID = i 99 s.Hash = h 100 s.Name = name 101 102 /* add the case-insensitive version, prefer the one with smaller field ID */ 103 key := strings.ToLower(name) 104 if v, ok := self.m[key]; !ok || i < v { 105 self.m[key] = i 106 } 107 } 108 109 func (self *FieldMap) GetCaseInsensitive(name string) int { 110 if i, ok := self.m[strings.ToLower(name)]; ok { 111 return i 112 } else { 113 return -1 114 } 115 }