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  }