github.com/songzhibin97/go-baseutils@v0.0.2-0.20240302024150-487d8ce9c082/internal/wyhash/digest.go (about) 1 package wyhash 2 3 import ( 4 "reflect" 5 "unsafe" 6 7 "github.com/songzhibin97/go-baseutils/internal/runtimex" 8 ) 9 10 type Digest struct { 11 initseed uint64 12 seed uint64 13 see1 uint64 14 data [64]byte 15 length int 16 total int 17 } 18 19 // New creates a new Digest that computes the 64-bit wyhash algorithm. 20 func New(seed uint64) *Digest { 21 return &Digest{ 22 seed: seed, 23 initseed: seed, 24 see1: seed, 25 } 26 } 27 28 func NewDefault() *Digest { 29 return New(DefaultSeed) 30 } 31 32 // Size always returns 8 bytes. 33 func (d *Digest) Size() int { return 8 } 34 35 // BlockSize always returns 64 bytes. 36 func (d *Digest) BlockSize() int { return 64 } 37 38 // Reset the digest, and the seed will be reset to initseed. 39 // The initseed is the seed when digest has been created. 40 func (d *Digest) Reset() { 41 d.seed = d.initseed 42 d.see1 = d.initseed 43 d.total = 0 44 d.length = 0 45 } 46 47 func (d *Digest) SetSeed(seed uint64) { d.seed = seed } 48 func (d *Digest) Seed() uint64 { return d.seed } 49 func (d *Digest) SetInitSeed(seed uint64) { d.initseed = seed } 50 func (d *Digest) InitSeed() uint64 { return d.initseed } 51 52 // Write (via the embedded io.Writer interface) adds more data to the running hash. 53 // It never returns an error. 54 func (d *Digest) Write(input []byte) (int, error) { 55 ilen := len(input) 56 d.total += ilen 57 58 if d.length+ilen <= 64 { 59 copy(d.data[d.length:d.length+ilen], input) 60 d.length += ilen 61 return ilen, nil 62 } 63 64 // Consume first 64 bytes if possible. 65 if d.length != 0 { 66 inputpre := 64 - d.length // the data from input to d.data 67 copy(d.data[d.length:], input[:inputpre]) 68 input = input[inputpre:] // free preceding data 69 70 paddr := unsafe.Pointer(&d.data) 71 d.seed = _wymix(runtimex.ReadUnaligned64(paddr)^s1, runtimex.ReadUnaligned64(add(paddr, 8))^d.seed) ^ _wymix(runtimex.ReadUnaligned64(add(paddr, 16))^s2, runtimex.ReadUnaligned64(add(paddr, 24))^d.seed) 72 d.see1 = _wymix(runtimex.ReadUnaligned64(add(paddr, 32))^s3, runtimex.ReadUnaligned64(add(paddr, 40))^d.see1) ^ _wymix(runtimex.ReadUnaligned64(add(paddr, 48))^s4, runtimex.ReadUnaligned64(add(paddr, 56))^d.see1) 73 74 d.length = 0 // free d.data, since it has been consumed 75 } 76 77 // If remain input still greater than 64. 78 seed, see1 := d.seed, d.see1 79 for len(input) > 64 { 80 paddr := *(*unsafe.Pointer)(unsafe.Pointer(&input)) 81 seed = _wymix(runtimex.ReadUnaligned64(paddr)^s1, runtimex.ReadUnaligned64(add(paddr, 8))^seed) ^ _wymix(runtimex.ReadUnaligned64(add(paddr, 16))^s2, runtimex.ReadUnaligned64(add(paddr, 24))^seed) 82 see1 = _wymix(runtimex.ReadUnaligned64(add(paddr, 32))^s3, runtimex.ReadUnaligned64(add(paddr, 40))^see1) ^ _wymix(runtimex.ReadUnaligned64(add(paddr, 48))^s4, runtimex.ReadUnaligned64(add(paddr, 56))^see1) 83 input = input[64:] 84 } 85 d.seed, d.see1 = seed, see1 86 87 // Store remain data(< 64 bytes), d.length == 0 for now. 88 if len(input) > 0 { 89 copy(d.data[:len(input)], input) 90 d.length = len(input) 91 } 92 93 return ilen, nil 94 } 95 96 // Sum64 returns the current hash. 97 func (d *Digest) Sum64() uint64 { 98 if d.total <= 64 { 99 return Sum64WithSeed(byteToSlice(d.data, d.length), d.seed) 100 } 101 102 var ( 103 i = uintptr(d.length) 104 paddr = unsafe.Pointer(&d.data) 105 seed = d.seed ^ d.see1 106 length = d.total 107 a, b uint64 108 ) 109 110 for i > 16 { 111 seed = _wymix(runtimex.ReadUnaligned64(paddr)^s1, runtimex.ReadUnaligned64(add(paddr, 8))^seed) 112 paddr = add(paddr, 16) 113 i -= 16 114 } 115 116 // i <= 16 117 switch { 118 case i == 0: 119 return _wymix(s1, _wymix(s1, seed)) 120 case i < 4: 121 a = uint64(*(*byte)(paddr))<<16 | uint64(*(*byte)(add(paddr, uintptr(i>>1))))<<8 | uint64(*(*byte)(add(paddr, uintptr(i-1)))) 122 // b = 0 123 return _wymix(s1^uint64(length), _wymix(a^s1, seed)) 124 case i == 4: 125 a = runtimex.ReadUnaligned32(paddr) 126 // b = 0 127 return _wymix(s1^uint64(length), _wymix(a^s1, seed)) 128 case i < 8: 129 a = runtimex.ReadUnaligned32(paddr) 130 b = runtimex.ReadUnaligned32(add(paddr, i-4)) 131 return _wymix(s1^uint64(length), _wymix(a^s1, b^seed)) 132 case i == 8: 133 a = runtimex.ReadUnaligned64(paddr) 134 // b = 0 135 return _wymix(s1^uint64(length), _wymix(a^s1, seed)) 136 default: // 8 < i <= 16 137 a = runtimex.ReadUnaligned64(paddr) 138 b = runtimex.ReadUnaligned64(add(paddr, i-8)) 139 return _wymix(s1^uint64(length), _wymix(a^s1, b^seed)) 140 } 141 } 142 143 // Sum appends the current hash to b and returns the resulting slice. 144 func (d *Digest) Sum(b []byte) []byte { 145 s := d.Sum64() 146 return append( 147 b, 148 byte(s>>56), 149 byte(s>>48), 150 byte(s>>40), 151 byte(s>>32), 152 byte(s>>24), 153 byte(s>>16), 154 byte(s>>8), 155 byte(s), 156 ) 157 } 158 159 func byteToSlice(b [64]byte, length int) []byte { 160 var res []byte 161 bh := (*reflect.SliceHeader)(unsafe.Pointer(&res)) 162 bh.Data = uintptr(unsafe.Pointer(&b)) 163 bh.Len = length 164 bh.Cap = length 165 return res 166 }