...

Source file src/github.com/zeebo/xxh3/hasher.go

Documentation: github.com/zeebo/xxh3

     1  package xxh3
     2  
     3  import (
     4  	"encoding/binary"
     5  	"hash"
     6  )
     7  
     8  // Hasher implements the hash.Hash interface
     9  type Hasher struct {
    10  	acc  [8]u64
    11  	blk  u64
    12  	len  u64
    13  	key  ptr
    14  	buf  [_block + _stripe]byte
    15  	seed u64
    16  }
    17  
    18  var (
    19  	_ hash.Hash   = (*Hasher)(nil)
    20  	_ hash.Hash64 = (*Hasher)(nil)
    21  )
    22  
    23  // New returns a new Hasher that implements the hash.Hash interface.
    24  func New() *Hasher {
    25  	return new(Hasher)
    26  }
    27  
    28  // NewSeed returns a new Hasher that implements the hash.Hash interface.
    29  func NewSeed(seed uint64) *Hasher {
    30  	var h Hasher
    31  	h.Reset()
    32  	h.seed = seed
    33  	h.key = key
    34  
    35  	// Only initiate once, not on reset.
    36  	if seed != 0 {
    37  		h.key = ptr(&[secretSize]byte{})
    38  		initSecret(h.key, seed)
    39  	}
    40  	return &h
    41  }
    42  
    43  // Reset resets the Hash to its initial state.
    44  func (h *Hasher) Reset() {
    45  	h.acc = [8]u64{
    46  		prime32_3, prime64_1, prime64_2, prime64_3,
    47  		prime64_4, prime32_2, prime64_5, prime32_1,
    48  	}
    49  	h.blk = 0
    50  	h.len = 0
    51  }
    52  
    53  // BlockSize returns the hash's underlying block size.
    54  // The Write method will accept any amount of data, but
    55  // it may operate more efficiently if all writes are a
    56  // multiple of the block size.
    57  func (h *Hasher) BlockSize() int { return _stripe }
    58  
    59  // Size returns the number of bytes Sum will return.
    60  func (h *Hasher) Size() int { return 8 }
    61  
    62  // Sum appends the current hash to b and returns the resulting slice.
    63  // It does not change the underlying hash state.
    64  func (h *Hasher) Sum(b []byte) []byte {
    65  	var tmp [8]byte
    66  	binary.BigEndian.PutUint64(tmp[:], h.Sum64())
    67  	return append(b, tmp[:]...)
    68  }
    69  
    70  // Write adds more data to the running hash.
    71  // It never returns an error.
    72  func (h *Hasher) Write(buf []byte) (int, error) {
    73  	h.update(buf)
    74  	return len(buf), nil
    75  }
    76  
    77  // WriteString adds more data to the running hash.
    78  // It never returns an error.
    79  func (h *Hasher) WriteString(buf string) (int, error) {
    80  	h.updateString(buf)
    81  	return len(buf), nil
    82  }
    83  
    84  func (h *Hasher) update(buf []byte) {
    85  	// relies on the data pointer being the first word in the string header
    86  	h.updateString(*(*string)(ptr(&buf)))
    87  }
    88  
    89  func (h *Hasher) updateString(buf string) {
    90  	if h.key == nil {
    91  		h.key = key
    92  		h.Reset()
    93  	}
    94  
    95  	// On first write, if more than 1 block, process without copy.
    96  	for h.len == 0 && len(buf) > len(h.buf) {
    97  		if hasAVX2 {
    98  			accumBlockAVX2(&h.acc, *(*ptr)(ptr(&buf)), h.key)
    99  		} else if hasSSE2 {
   100  			accumBlockSSE(&h.acc, *(*ptr)(ptr(&buf)), h.key)
   101  		} else {
   102  			accumBlockScalar(&h.acc, *(*ptr)(ptr(&buf)), h.key)
   103  		}
   104  		buf = buf[_block:]
   105  		h.blk++
   106  	}
   107  
   108  	for len(buf) > 0 {
   109  		if h.len < u64(len(h.buf)) {
   110  			n := copy(h.buf[h.len:], buf)
   111  			h.len += u64(n)
   112  			buf = buf[n:]
   113  			continue
   114  		}
   115  
   116  		if hasAVX2 {
   117  			accumBlockAVX2(&h.acc, ptr(&h.buf), h.key)
   118  		} else if hasSSE2 {
   119  			accumBlockSSE(&h.acc, ptr(&h.buf), h.key)
   120  		} else {
   121  			accumBlockScalar(&h.acc, ptr(&h.buf), h.key)
   122  		}
   123  
   124  		h.blk++
   125  		h.len = _stripe
   126  		copy(h.buf[:_stripe], h.buf[_block:])
   127  	}
   128  }
   129  
   130  // Sum64 returns the 64-bit hash of the written data.
   131  func (h *Hasher) Sum64() uint64 {
   132  	if h.key == nil {
   133  		h.key = key
   134  		h.Reset()
   135  	}
   136  
   137  	if h.blk == 0 {
   138  		if h.seed == 0 {
   139  			return Hash(h.buf[:h.len])
   140  		}
   141  		return HashSeed(h.buf[:h.len], h.seed)
   142  	}
   143  
   144  	l := h.blk*_block + h.len
   145  	acc := l * prime64_1
   146  	accs := h.acc
   147  
   148  	if h.len > 0 {
   149  		// We are only ever doing 1 block here, so no avx512.
   150  		if hasAVX2 {
   151  			accumAVX2(&accs, ptr(&h.buf[0]), h.key, h.len)
   152  		} else if hasSSE2 {
   153  			accumSSE(&accs, ptr(&h.buf[0]), h.key, h.len)
   154  		} else {
   155  			accumScalar(&accs, ptr(&h.buf[0]), h.key, h.len)
   156  		}
   157  	}
   158  
   159  	if h.seed == 0 {
   160  		acc += mulFold64(accs[0]^key64_011, accs[1]^key64_019)
   161  		acc += mulFold64(accs[2]^key64_027, accs[3]^key64_035)
   162  		acc += mulFold64(accs[4]^key64_043, accs[5]^key64_051)
   163  		acc += mulFold64(accs[6]^key64_059, accs[7]^key64_067)
   164  	} else {
   165  		secret := h.key
   166  		acc += mulFold64(accs[0]^readU64(secret, 11), accs[1]^readU64(secret, 19))
   167  		acc += mulFold64(accs[2]^readU64(secret, 27), accs[3]^readU64(secret, 35))
   168  		acc += mulFold64(accs[4]^readU64(secret, 43), accs[5]^readU64(secret, 51))
   169  		acc += mulFold64(accs[6]^readU64(secret, 59), accs[7]^readU64(secret, 67))
   170  	}
   171  
   172  	acc = xxh3Avalanche(acc)
   173  
   174  	return acc
   175  }
   176  
   177  // Sum128 returns the 128-bit hash of the written data.
   178  func (h *Hasher) Sum128() Uint128 {
   179  	if h.key == nil {
   180  		h.key = key
   181  		h.Reset()
   182  	}
   183  
   184  	if h.blk == 0 {
   185  		if h.seed == 0 {
   186  			return Hash128(h.buf[:h.len])
   187  		}
   188  		return Hash128Seed(h.buf[:h.len], h.seed)
   189  	}
   190  
   191  	l := h.blk*_block + h.len
   192  	acc := Uint128{Lo: l * prime64_1, Hi: ^(l * prime64_2)}
   193  	accs := h.acc
   194  
   195  	if h.len > 0 {
   196  		// We are only ever doing 1 block here, so no avx512.
   197  		if hasAVX2 {
   198  			accumAVX2(&accs, ptr(&h.buf[0]), h.key, h.len)
   199  		} else if hasSSE2 {
   200  			accumSSE(&accs, ptr(&h.buf[0]), h.key, h.len)
   201  		} else {
   202  			accumScalar(&accs, ptr(&h.buf[0]), h.key, h.len)
   203  		}
   204  	}
   205  
   206  	if h.seed == 0 {
   207  		acc.Lo += mulFold64(accs[0]^key64_011, accs[1]^key64_019)
   208  		acc.Hi += mulFold64(accs[0]^key64_117, accs[1]^key64_125)
   209  
   210  		acc.Lo += mulFold64(accs[2]^key64_027, accs[3]^key64_035)
   211  		acc.Hi += mulFold64(accs[2]^key64_133, accs[3]^key64_141)
   212  
   213  		acc.Lo += mulFold64(accs[4]^key64_043, accs[5]^key64_051)
   214  		acc.Hi += mulFold64(accs[4]^key64_149, accs[5]^key64_157)
   215  
   216  		acc.Lo += mulFold64(accs[6]^key64_059, accs[7]^key64_067)
   217  		acc.Hi += mulFold64(accs[6]^key64_165, accs[7]^key64_173)
   218  	} else {
   219  		secret := h.key
   220  		const hi_off = 117 - 11
   221  
   222  		acc.Lo += mulFold64(accs[0]^readU64(secret, 11), accs[1]^readU64(secret, 19))
   223  		acc.Hi += mulFold64(accs[0]^readU64(secret, 11+hi_off), accs[1]^readU64(secret, 19+hi_off))
   224  
   225  		acc.Lo += mulFold64(accs[2]^readU64(secret, 27), accs[3]^readU64(secret, 35))
   226  		acc.Hi += mulFold64(accs[2]^readU64(secret, 27+hi_off), accs[3]^readU64(secret, 35+hi_off))
   227  
   228  		acc.Lo += mulFold64(accs[4]^readU64(secret, 43), accs[5]^readU64(secret, 51))
   229  		acc.Hi += mulFold64(accs[4]^readU64(secret, 43+hi_off), accs[5]^readU64(secret, 51+hi_off))
   230  
   231  		acc.Lo += mulFold64(accs[6]^readU64(secret, 59), accs[7]^readU64(secret, 67))
   232  		acc.Hi += mulFold64(accs[6]^readU64(secret, 59+hi_off), accs[7]^readU64(secret, 67+hi_off))
   233  	}
   234  
   235  	acc.Lo = xxh3Avalanche(acc.Lo)
   236  	acc.Hi = xxh3Avalanche(acc.Hi)
   237  
   238  	return acc
   239  }
   240  

View as plain text