github.com/bytedance/gopkg@v0.0.0-20240514070511-01b2cbcf35e1/internal/wyhash/digest.go (about) 1 // Copyright 2021 ByteDance Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package wyhash 16 17 import ( 18 "reflect" 19 "unsafe" 20 21 "github.com/bytedance/gopkg/internal/runtimex" 22 ) 23 24 type Digest struct { 25 initseed uint64 26 seed uint64 27 see1 uint64 28 data [64]byte 29 length int 30 total int 31 } 32 33 // New creates a new Digest that computes the 64-bit wyhash algorithm. 34 func New(seed uint64) *Digest { 35 return &Digest{ 36 seed: seed, 37 initseed: seed, 38 see1: seed, 39 } 40 } 41 42 func NewDefault() *Digest { 43 return New(DefaultSeed) 44 } 45 46 // Size always returns 8 bytes. 47 func (d *Digest) Size() int { return 8 } 48 49 // BlockSize always returns 64 bytes. 50 func (d *Digest) BlockSize() int { return 64 } 51 52 // Reset the digest, and the seed will be reset to initseed. 53 // The initseed is the seed when digest has been created. 54 func (d *Digest) Reset() { 55 d.seed = d.initseed 56 d.see1 = d.initseed 57 d.total = 0 58 d.length = 0 59 } 60 61 func (d *Digest) SetSeed(seed uint64) { d.seed = seed } 62 func (d *Digest) Seed() uint64 { return d.seed } 63 func (d *Digest) SetInitSeed(seed uint64) { d.initseed = seed } 64 func (d *Digest) InitSeed() uint64 { return d.initseed } 65 66 // Write (via the embedded io.Writer interface) adds more data to the running hash. 67 // It never returns an error. 68 func (d *Digest) Write(input []byte) (int, error) { 69 ilen := len(input) 70 d.total += ilen 71 72 if d.length+ilen <= 64 { 73 copy(d.data[d.length:d.length+ilen], input) 74 d.length += ilen 75 return ilen, nil 76 } 77 78 // Consume first 64 bytes if possible. 79 if d.length != 0 { 80 inputpre := 64 - d.length // the data from input to d.data 81 copy(d.data[d.length:], input[:inputpre]) 82 input = input[inputpre:] // free preceding data 83 84 paddr := unsafe.Pointer(&d.data) 85 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) 86 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) 87 88 d.length = 0 // free d.data, since it has been consumed 89 } 90 91 // If remain input still greater than 64. 92 seed, see1 := d.seed, d.see1 93 for len(input) > 64 { 94 paddr := *(*unsafe.Pointer)(unsafe.Pointer(&input)) 95 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) 96 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) 97 input = input[64:] 98 } 99 d.seed, d.see1 = seed, see1 100 101 // Store remain data(< 64 bytes), d.length == 0 for now. 102 if len(input) > 0 { 103 copy(d.data[:len(input)], input) 104 d.length = len(input) 105 } 106 107 return ilen, nil 108 } 109 110 // Sum64 returns the current hash. 111 func (d *Digest) Sum64() uint64 { 112 if d.total <= 64 { 113 return Sum64WithSeed(byteToSlice(d.data, d.length), d.seed) 114 } 115 116 var ( 117 i = uintptr(d.length) 118 paddr = unsafe.Pointer(&d.data) 119 seed = d.seed ^ d.see1 120 length = d.total 121 a, b uint64 122 ) 123 124 for i > 16 { 125 seed = _wymix(runtimex.ReadUnaligned64(paddr)^s1, runtimex.ReadUnaligned64(add(paddr, 8))^seed) 126 paddr = add(paddr, 16) 127 i -= 16 128 } 129 130 // i <= 16 131 switch { 132 case i == 0: 133 return _wymix(s1, _wymix(s1, seed)) 134 case i < 4: 135 a = uint64(*(*byte)(paddr))<<16 | uint64(*(*byte)(add(paddr, uintptr(i>>1))))<<8 | uint64(*(*byte)(add(paddr, uintptr(i-1)))) 136 // b = 0 137 return _wymix(s1^uint64(length), _wymix(a^s1, seed)) 138 case i == 4: 139 a = runtimex.ReadUnaligned32(paddr) 140 // b = 0 141 return _wymix(s1^uint64(length), _wymix(a^s1, seed)) 142 case i < 8: 143 a = runtimex.ReadUnaligned32(paddr) 144 b = runtimex.ReadUnaligned32(add(paddr, i-4)) 145 return _wymix(s1^uint64(length), _wymix(a^s1, b^seed)) 146 case i == 8: 147 a = runtimex.ReadUnaligned64(paddr) 148 // b = 0 149 return _wymix(s1^uint64(length), _wymix(a^s1, seed)) 150 default: // 8 < i <= 16 151 a = runtimex.ReadUnaligned64(paddr) 152 b = runtimex.ReadUnaligned64(add(paddr, i-8)) 153 return _wymix(s1^uint64(length), _wymix(a^s1, b^seed)) 154 } 155 } 156 157 // Sum appends the current hash to b and returns the resulting slice. 158 func (d *Digest) Sum(b []byte) []byte { 159 s := d.Sum64() 160 return append( 161 b, 162 byte(s>>56), 163 byte(s>>48), 164 byte(s>>40), 165 byte(s>>32), 166 byte(s>>24), 167 byte(s>>16), 168 byte(s>>8), 169 byte(s), 170 ) 171 } 172 173 func byteToSlice(b [64]byte, length int) []byte { 174 var res []byte 175 bh := (*reflect.SliceHeader)(unsafe.Pointer(&res)) 176 bh.Data = uintptr(unsafe.Pointer(&b)) 177 bh.Len = length 178 bh.Cap = length 179 return res 180 }