github.com/fluhus/gostuff@v0.4.1-0.20240331134726-be71864f2b5d/rhash/rabin64.go (about)

     1  package rhash
     2  
     3  import (
     4  	"fmt"
     5  	"hash"
     6  )
     7  
     8  const rabinPrime = 16777619
     9  
    10  var _ hash.Hash64 = &RabinFingerprint64{}
    11  
    12  // RabinFingerprint64 implements a Rabin fingerprint rolling-hash.
    13  // Implements [hash.Hash64].
    14  type RabinFingerprint64 struct {
    15  	h, pow uint64
    16  	i      int
    17  	hist   []byte
    18  }
    19  
    20  // NewRabinFingerprint64 returns a new rolling hash with a window size of n.
    21  func NewRabinFingerprint64(n int) *RabinFingerprint64 {
    22  	if n < 1 {
    23  		panic(fmt.Sprintf("bad n: %d", n))
    24  	}
    25  	return &RabinFingerprint64{0, 1, 0, make([]byte, n)}
    26  }
    27  
    28  // Write updates the hash with the given bytes. Always returns len(data), nil.
    29  func (h *RabinFingerprint64) Write(data []byte) (int, error) {
    30  	for _, b := range data {
    31  		h.WriteByte(b)
    32  	}
    33  	return len(data), nil
    34  }
    35  
    36  // WriteByte updates the hash with the given byte. Always returns nil.
    37  func (h *RabinFingerprint64) WriteByte(b byte) error {
    38  	h.h = h.h*rabinPrime + uint64(b)
    39  	i := h.i % len(h.hist)
    40  	h.h -= h.pow * uint64(h.hist[i])
    41  	h.hist[i] = b
    42  	if h.i < len(h.hist) {
    43  		h.pow *= rabinPrime
    44  	}
    45  	h.i++
    46  	return nil
    47  }
    48  
    49  // Sum appends the current hash to b and returns the resulting slice.
    50  func (h *RabinFingerprint64) Sum(b []byte) []byte {
    51  	s := h.Sum64()
    52  	n := h.Size()
    53  	for i := 0; i < n; i++ {
    54  		b = append(b, byte(s))
    55  		s >>= 8
    56  	}
    57  	return b
    58  }
    59  
    60  // Sum64 returns the current hash.
    61  func (h *RabinFingerprint64) Sum64() uint64 {
    62  	return h.h
    63  }
    64  
    65  // Size returns the number of bytes Sum will return, which is eight.
    66  func (h *RabinFingerprint64) Size() int {
    67  	return 8
    68  }
    69  
    70  // BlockSize returns the hash's block size, which is one.
    71  func (h *RabinFingerprint64) BlockSize() int {
    72  	return 1
    73  }
    74  
    75  // Reset resets the hash to its initial state.
    76  func (h *RabinFingerprint64) Reset() {
    77  	h.h = 0
    78  	h.i = 0
    79  	h.pow = 1
    80  	for i := range h.hist {
    81  		h.hist[i] = 0
    82  	}
    83  }
    84  
    85  // RabinFingerprintSum64 returns the Rabin fingerprint of data.
    86  func RabinFingerprintSum64(data []byte) uint64 {
    87  	if len(data) == 0 {
    88  		return 0
    89  	}
    90  	h := NewRabinFingerprint64(len(data))
    91  	h.Write(data)
    92  	return h.Sum64()
    93  }