github.com/cloudwego/dynamicgo@v0.2.6-0.20240519101509-707f41b6b834/internal/caching/map.go (about) 1 /* 2 * Copyright 2023 CloudWeGo Authors. 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 "unsafe" 21 22 "github.com/cloudwego/dynamicgo/internal/rt" 23 ) 24 25 type HashMap struct { 26 b unsafe.Pointer 27 c uint32 28 N uint32 29 } 30 31 type Entry struct { 32 Hash uint32 33 Key string 34 Val unsafe.Pointer 35 } 36 37 const ( 38 FieldMap_N = int64(unsafe.Offsetof(HashMap{}.N)) 39 FieldMap_b = int64(unsafe.Offsetof(HashMap{}.b)) 40 FieldEntrySize = int64(unsafe.Sizeof(Entry{})) 41 ) 42 43 func newBucket(n int) unsafe.Pointer { 44 v := make([]Entry, n) 45 return (*rt.GoSlice)(unsafe.Pointer(&v)).Ptr 46 } 47 48 func NewHashMap(n int, loadFactor int) *HashMap { 49 return &HashMap{ 50 N: uint32(n * loadFactor), 51 c: 0, 52 b: newBucket(n * loadFactor), 53 } 54 } 55 56 func (self *HashMap) At(p uint32) *Entry { 57 return (*Entry)(unsafe.Pointer(uintptr(self.b) + uintptr(p)*uintptr(FieldEntrySize))) 58 } 59 60 // Get searches FieldMap by name. JIT generated assembly does NOT call this 61 // function, rather it implements its own version directly in assembly. So 62 // we must ensure this function stays in sync with the JIT generated one. 63 func (self *HashMap) Get(name string) unsafe.Pointer { 64 h := DJBHash32(name) 65 p := h % self.N 66 s := self.At(p) 67 68 /* find the element; 69 * the hash map is never full, so the loop will always terminate */ 70 for s.Hash != 0 { 71 if s.Hash == h && s.Key == name { 72 return s.Val 73 } else { 74 p = (p + 1) % self.N 75 s = self.At(p) 76 } 77 } 78 79 /* not found */ 80 return nil 81 } 82 83 // WARN: this function is not idempotent, set same key twice will cause incorrect result. 84 func (self *HashMap) Set(name string, val unsafe.Pointer) { 85 h := DJBHash32(name) 86 p := h % self.N 87 s := self.At(p) 88 89 /* searching for an empty slot; 90 * the hash map is never full, so the loop will always terminate */ 91 for s.Hash != 0 { 92 p = (p + 1) % self.N 93 s = self.At(p) 94 } 95 96 /* set the value */ 97 s.Val = val 98 s.Hash = h 99 s.Key = name 100 self.c++ 101 } 102 103 func (self *HashMap) Size() int { 104 return int(self.c) 105 }