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 }
116