fcache.go raw

   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