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 }