diff options
Diffstat (limited to 'vendor/github.com/bytedance/sonic/internal/caching')
4 files changed, 328 insertions, 0 deletions
diff --git a/vendor/github.com/bytedance/sonic/internal/caching/asm.s b/vendor/github.com/bytedance/sonic/internal/caching/asm.s new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/vendor/github.com/bytedance/sonic/internal/caching/asm.s diff --git a/vendor/github.com/bytedance/sonic/internal/caching/fcache.go b/vendor/github.com/bytedance/sonic/internal/caching/fcache.go new file mode 100644 index 000000000..8cf62ff44 --- /dev/null +++ b/vendor/github.com/bytedance/sonic/internal/caching/fcache.go @@ -0,0 +1,115 @@ +/* + * Copyright 2021 ByteDance Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package caching + +import ( + `strings` + `unsafe` + + `github.com/bytedance/sonic/internal/rt` +) + +type FieldMap struct { + N uint64 + b unsafe.Pointer + m map[string]int +} + +type FieldEntry struct { + ID int + Name string + Hash uint64 +} + +const ( + FieldMap_N = int64(unsafe.Offsetof(FieldMap{}.N)) + FieldMap_b = int64(unsafe.Offsetof(FieldMap{}.b)) + FieldEntrySize = int64(unsafe.Sizeof(FieldEntry{})) +) + +func newBucket(n int) unsafe.Pointer { + v := make([]FieldEntry, n) + return (*rt.GoSlice)(unsafe.Pointer(&v)).Ptr +} + +func CreateFieldMap(n int) *FieldMap { + return &FieldMap { + N: uint64(n * 2), + b: newBucket(n * 2), // LoadFactor = 0.5 + m: make(map[string]int, n * 2), + } +} + +func (self *FieldMap) At(p uint64) *FieldEntry { + off := uintptr(p) * uintptr(FieldEntrySize) + return (*FieldEntry)(unsafe.Pointer(uintptr(self.b) + off)) +} + +// Get searches FieldMap by name. JIT generated assembly does NOT call this +// function, rather it implements its own version directly in assembly. So +// we must ensure this function stays in sync with the JIT generated one. +func (self *FieldMap) Get(name string) int { + h := StrHash(name) + p := h % self.N + s := self.At(p) + + /* find the element; + * the hash map is never full, so the loop will always terminate */ + for s.Hash != 0 { + if s.Hash == h && s.Name == name { + return s.ID + } else { + p = (p + 1) % self.N + s = self.At(p) + } + } + + /* not found */ + return -1 +} + +func (self *FieldMap) Set(name string, i int) { + h := StrHash(name) + p := h % self.N + s := self.At(p) + + /* searching for an empty slot; + * the hash map is never full, so the loop will always terminate */ + for s.Hash != 0 { + p = (p + 1) % self.N + s = self.At(p) + } + + /* set the value */ + s.ID = i + s.Hash = h + s.Name = name + + /* add the case-insensitive version, prefer the one with smaller field ID */ + key := strings.ToLower(name) + if v, ok := self.m[key]; !ok || i < v { + self.m[key] = i + } +} + +func (self *FieldMap) GetCaseInsensitive(name string) int { + if i, ok := self.m[strings.ToLower(name)]; ok { + return i + } else { + return -1 + } +} diff --git a/vendor/github.com/bytedance/sonic/internal/caching/hashing.go b/vendor/github.com/bytedance/sonic/internal/caching/hashing.go new file mode 100644 index 000000000..b8876a410 --- /dev/null +++ b/vendor/github.com/bytedance/sonic/internal/caching/hashing.go @@ -0,0 +1,40 @@ +/* + * Copyright 2021 ByteDance Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package caching + +import ( + `unsafe` + + `github.com/bytedance/sonic/internal/rt` +) + +var ( + V_strhash = rt.UnpackEface(strhash) + S_strhash = *(*uintptr)(V_strhash.Value) +) + +//go:noescape +//go:linkname strhash runtime.strhash +func strhash(_ unsafe.Pointer, _ uintptr) uintptr + +func StrHash(s string) uint64 { + if v := strhash(unsafe.Pointer(&s), 0); v == 0 { + return 1 + } else { + return uint64(v) + } +} diff --git a/vendor/github.com/bytedance/sonic/internal/caching/pcache.go b/vendor/github.com/bytedance/sonic/internal/caching/pcache.go new file mode 100644 index 000000000..8c1224d9c --- /dev/null +++ b/vendor/github.com/bytedance/sonic/internal/caching/pcache.go @@ -0,0 +1,173 @@ +/* + * Copyright 2021 ByteDance Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package caching + +import ( + `sync` + `sync/atomic` + `unsafe` + + `github.com/bytedance/sonic/internal/rt` +) + +/** Program Map **/ + +const ( + _LoadFactor = 0.5 + _InitCapacity = 4096 // must be a power of 2 +) + +type _ProgramMap struct { + n uint64 + m uint32 + b []_ProgramEntry +} + +type _ProgramEntry struct { + vt *rt.GoType + fn interface{} +} + +func newProgramMap() *_ProgramMap { + return &_ProgramMap { + n: 0, + m: _InitCapacity - 1, + b: make([]_ProgramEntry, _InitCapacity), + } +} + +func (self *_ProgramMap) copy() *_ProgramMap { + fork := &_ProgramMap{ + n: self.n, + m: self.m, + b: make([]_ProgramEntry, len(self.b)), + } + for i, f := range self.b { + fork.b[i] = f + } + return fork +} + +func (self *_ProgramMap) get(vt *rt.GoType) interface{} { + i := self.m + 1 + p := vt.Hash & self.m + + /* linear probing */ + for ; i > 0; i-- { + if b := self.b[p]; b.vt == vt { + return b.fn + } else if b.vt == nil { + break + } else { + p = (p + 1) & self.m + } + } + + /* not found */ + return nil +} + +func (self *_ProgramMap) add(vt *rt.GoType, fn interface{}) *_ProgramMap { + p := self.copy() + f := float64(atomic.LoadUint64(&p.n) + 1) / float64(p.m + 1) + + /* check for load factor */ + if f > _LoadFactor { + p = p.rehash() + } + + /* insert the value */ + p.insert(vt, fn) + return p +} + +func (self *_ProgramMap) rehash() *_ProgramMap { + c := (self.m + 1) << 1 + r := &_ProgramMap{m: c - 1, b: make([]_ProgramEntry, int(c))} + + /* rehash every entry */ + for i := uint32(0); i <= self.m; i++ { + if b := self.b[i]; b.vt != nil { + r.insert(b.vt, b.fn) + } + } + + /* rebuild successful */ + return r +} + +func (self *_ProgramMap) insert(vt *rt.GoType, fn interface{}) { + h := vt.Hash + p := h & self.m + + /* linear probing */ + for i := uint32(0); i <= self.m; i++ { + if b := &self.b[p]; b.vt != nil { + p += 1 + p &= self.m + } else { + b.vt = vt + b.fn = fn + atomic.AddUint64(&self.n, 1) + return + } + } + + /* should never happens */ + panic("no available slots") +} + +/** RCU Program Cache **/ + +type ProgramCache struct { + m sync.Mutex + p unsafe.Pointer +} + +func CreateProgramCache() *ProgramCache { + return &ProgramCache { + m: sync.Mutex{}, + p: unsafe.Pointer(newProgramMap()), + } +} + +func (self *ProgramCache) Get(vt *rt.GoType) interface{} { + return (*_ProgramMap)(atomic.LoadPointer(&self.p)).get(vt) +} + +func (self *ProgramCache) Compute(vt *rt.GoType, compute func(*rt.GoType, ... interface{}) (interface{}, error), ex ...interface{}) (interface{}, error) { + var err error + var val interface{} + + /* use defer to prevent inlining of this function */ + self.m.Lock() + defer self.m.Unlock() + + /* double check with write lock held */ + if val = self.Get(vt); val != nil { + return val, nil + } + + /* compute the value */ + if val, err = compute(vt, ex...); err != nil { + return nil, err + } + + /* update the RCU cache */ + atomic.StorePointer(&self.p, unsafe.Pointer((*_ProgramMap)(atomic.LoadPointer(&self.p)).add(vt, val))) + return val, nil +} |