github.com/authzed/spicedb@v1.32.1-0.20240520085336-ebda56537386/internal/dispatch/keys/hasher_ristretto.go (about)

     1  //go:build !wasm
     2  // +build !wasm
     3  
     4  package keys
     5  
     6  import (
     7  	"fmt"
     8  	"unsafe"
     9  
    10  	"github.com/cespare/xxhash/v2"
    11  )
    12  
    13  // dispatchCacheKeyHash computres a DispatchCheckKey for the given prefix and any hashable values.
    14  func dispatchCacheKeyHash(prefix cachePrefix, atRevision string, computeOption dispatchCacheKeyHashComputeOption, args ...hashableValue) DispatchCacheKey {
    15  	hasher := newDispatchCacheKeyHasher(prefix, computeOption)
    16  
    17  	for _, arg := range args {
    18  		arg.AppendToHash(hasher)
    19  		hasher.WriteString("@")
    20  	}
    21  
    22  	hasher.WriteString(atRevision)
    23  	return hasher.BuildKey()
    24  }
    25  
    26  type dispatchCacheKeyHasher struct {
    27  	stableHasher       *xxhash.Digest
    28  	computeOption      dispatchCacheKeyHashComputeOption
    29  	processSpecificSum uint64
    30  }
    31  
    32  func newDispatchCacheKeyHasher(prefix cachePrefix, computeOption dispatchCacheKeyHashComputeOption) *dispatchCacheKeyHasher {
    33  	h := &dispatchCacheKeyHasher{
    34  		stableHasher:  xxhash.New(),
    35  		computeOption: computeOption,
    36  	}
    37  
    38  	prefixString := string(prefix)
    39  	h.WriteString(prefixString)
    40  	h.WriteString("/")
    41  	return h
    42  }
    43  
    44  // WriteString writes a single string to the hasher.
    45  func (h *dispatchCacheKeyHasher) WriteString(value string) {
    46  	h.mustWriteString(value)
    47  }
    48  
    49  func (h *dispatchCacheKeyHasher) mustWriteString(value string) {
    50  	// NOTE: xxhash doesn't seem to ever return an error for WriteString, but we check it just
    51  	// to be on the safe side.
    52  	_, err := h.stableHasher.WriteString(value)
    53  	if err != nil {
    54  		panic(fmt.Errorf("got an error from writing to the stable hasher: %w", err))
    55  	}
    56  
    57  	if h.computeOption == computeBothHashes {
    58  		h.processSpecificSum = runMemHash(h.processSpecificSum, []byte(value))
    59  	}
    60  }
    61  
    62  // From: https://github.com/outcaste-io/ristretto/blob/master/z/rtutil.go
    63  type stringStruct struct {
    64  	str unsafe.Pointer
    65  	len int
    66  }
    67  
    68  //go:noescape
    69  //go:linkname memhash runtime.memhash
    70  func memhash(p unsafe.Pointer, h, s uintptr) uintptr
    71  
    72  func runMemHash(seed uint64, data []byte) uint64 {
    73  	ss := (*stringStruct)(unsafe.Pointer(&data))
    74  	return uint64(memhash(ss.str, uintptr(seed), uintptr(ss.len)))
    75  }
    76  
    77  // BuildKey returns the constructed DispatchCheckKey.
    78  func (h *dispatchCacheKeyHasher) BuildKey() DispatchCacheKey {
    79  	return DispatchCacheKey{
    80  		stableSum:          h.stableHasher.Sum64(),
    81  		processSpecificSum: h.processSpecificSum,
    82  	}
    83  }