github.com/evanw/esbuild@v0.21.4/internal/xxhash/xxhash.go (about)

     1  // Package xxhash implements the 64-bit variant of xxHash (XXH64) as described
     2  // at http://cyan4973.github.io/xxHash/.
     3  package xxhash
     4  
     5  import (
     6  	"encoding/binary"
     7  	"errors"
     8  	"math/bits"
     9  )
    10  
    11  const (
    12  	prime1 uint64 = 11400714785074694791
    13  	prime2 uint64 = 14029467366897019727
    14  	prime3 uint64 = 1609587929392839161
    15  	prime4 uint64 = 9650029242287828579
    16  	prime5 uint64 = 2870177450012600261
    17  )
    18  
    19  // NOTE(caleb): I'm using both consts and vars of the primes. Using consts where
    20  // possible in the Go code is worth a small (but measurable) performance boost
    21  // by avoiding some MOVQs. Vars are needed for the asm and also are useful for
    22  // convenience in the Go code in a few places where we need to intentionally
    23  // avoid constant arithmetic (e.g., v1 := prime1 + prime2 fails because the
    24  // result overflows a uint64).
    25  var (
    26  	prime1v = prime1
    27  	prime2v = prime2
    28  	prime3v = prime3
    29  	prime4v = prime4
    30  	prime5v = prime5
    31  )
    32  
    33  // Digest implements hash.Hash64.
    34  type Digest struct {
    35  	v1    uint64
    36  	v2    uint64
    37  	v3    uint64
    38  	v4    uint64
    39  	total uint64
    40  	mem   [32]byte
    41  	n     int // how much of mem is used
    42  }
    43  
    44  // New creates a new Digest that computes the 64-bit xxHash algorithm.
    45  func New() *Digest {
    46  	var d Digest
    47  	d.Reset()
    48  	return &d
    49  }
    50  
    51  // Reset clears the Digest's state so that it can be reused.
    52  func (d *Digest) Reset() {
    53  	d.v1 = prime1v + prime2
    54  	d.v2 = prime2
    55  	d.v3 = 0
    56  	d.v4 = -prime1v
    57  	d.total = 0
    58  	d.n = 0
    59  }
    60  
    61  // Size always returns 8 bytes.
    62  func (d *Digest) Size() int { return 8 }
    63  
    64  // BlockSize always returns 32 bytes.
    65  func (d *Digest) BlockSize() int { return 32 }
    66  
    67  // Write adds more data to d. It always returns len(b), nil.
    68  func (d *Digest) Write(b []byte) (n int, err error) {
    69  	n = len(b)
    70  	d.total += uint64(n)
    71  
    72  	if d.n+n < 32 {
    73  		// This new data doesn't even fill the current block.
    74  		copy(d.mem[d.n:], b)
    75  		d.n += n
    76  		return
    77  	}
    78  
    79  	if d.n > 0 {
    80  		// Finish off the partial block.
    81  		copy(d.mem[d.n:], b)
    82  		d.v1 = round(d.v1, u64(d.mem[0:8]))
    83  		d.v2 = round(d.v2, u64(d.mem[8:16]))
    84  		d.v3 = round(d.v3, u64(d.mem[16:24]))
    85  		d.v4 = round(d.v4, u64(d.mem[24:32]))
    86  		b = b[32-d.n:]
    87  		d.n = 0
    88  	}
    89  
    90  	if len(b) >= 32 {
    91  		// One or more full blocks left.
    92  		nw := writeBlocks(d, b)
    93  		b = b[nw:]
    94  	}
    95  
    96  	// Store any remaining partial block.
    97  	copy(d.mem[:], b)
    98  	d.n = len(b)
    99  
   100  	return
   101  }
   102  
   103  // Sum appends the current hash to b and returns the resulting slice.
   104  func (d *Digest) Sum(b []byte) []byte {
   105  	s := d.Sum64()
   106  	return append(
   107  		b,
   108  		byte(s>>56),
   109  		byte(s>>48),
   110  		byte(s>>40),
   111  		byte(s>>32),
   112  		byte(s>>24),
   113  		byte(s>>16),
   114  		byte(s>>8),
   115  		byte(s),
   116  	)
   117  }
   118  
   119  // Sum64 returns the current hash.
   120  func (d *Digest) Sum64() uint64 {
   121  	var h uint64
   122  
   123  	if d.total >= 32 {
   124  		v1, v2, v3, v4 := d.v1, d.v2, d.v3, d.v4
   125  		h = rol1(v1) + rol7(v2) + rol12(v3) + rol18(v4)
   126  		h = mergeRound(h, v1)
   127  		h = mergeRound(h, v2)
   128  		h = mergeRound(h, v3)
   129  		h = mergeRound(h, v4)
   130  	} else {
   131  		h = d.v3 + prime5
   132  	}
   133  
   134  	h += d.total
   135  
   136  	i, end := 0, d.n
   137  	for ; i+8 <= end; i += 8 {
   138  		k1 := round(0, u64(d.mem[i:i+8]))
   139  		h ^= k1
   140  		h = rol27(h)*prime1 + prime4
   141  	}
   142  	if i+4 <= end {
   143  		h ^= uint64(u32(d.mem[i:i+4])) * prime1
   144  		h = rol23(h)*prime2 + prime3
   145  		i += 4
   146  	}
   147  	for i < end {
   148  		h ^= uint64(d.mem[i]) * prime5
   149  		h = rol11(h) * prime1
   150  		i++
   151  	}
   152  
   153  	h ^= h >> 33
   154  	h *= prime2
   155  	h ^= h >> 29
   156  	h *= prime3
   157  	h ^= h >> 32
   158  
   159  	return h
   160  }
   161  
   162  const (
   163  	magic         = "xxh\x06"
   164  	marshaledSize = len(magic) + 8*5 + 32
   165  )
   166  
   167  // MarshalBinary implements the encoding.BinaryMarshaler interface.
   168  func (d *Digest) MarshalBinary() ([]byte, error) {
   169  	b := make([]byte, 0, marshaledSize)
   170  	b = append(b, magic...)
   171  	b = appendUint64(b, d.v1)
   172  	b = appendUint64(b, d.v2)
   173  	b = appendUint64(b, d.v3)
   174  	b = appendUint64(b, d.v4)
   175  	b = appendUint64(b, d.total)
   176  	b = append(b, d.mem[:d.n]...)
   177  	b = b[:len(b)+len(d.mem)-d.n]
   178  	return b, nil
   179  }
   180  
   181  // UnmarshalBinary implements the encoding.BinaryUnmarshaler interface.
   182  func (d *Digest) UnmarshalBinary(b []byte) error {
   183  	if len(b) < len(magic) || string(b[:len(magic)]) != magic {
   184  		return errors.New("xxhash: invalid hash state identifier")
   185  	}
   186  	if len(b) != marshaledSize {
   187  		return errors.New("xxhash: invalid hash state size")
   188  	}
   189  	b = b[len(magic):]
   190  	b, d.v1 = consumeUint64(b)
   191  	b, d.v2 = consumeUint64(b)
   192  	b, d.v3 = consumeUint64(b)
   193  	b, d.v4 = consumeUint64(b)
   194  	b, d.total = consumeUint64(b)
   195  	copy(d.mem[:], b)
   196  	d.n = int(d.total % uint64(len(d.mem)))
   197  	return nil
   198  }
   199  
   200  func appendUint64(b []byte, x uint64) []byte {
   201  	var a [8]byte
   202  	binary.LittleEndian.PutUint64(a[:], x)
   203  	return append(b, a[:]...)
   204  }
   205  
   206  func consumeUint64(b []byte) ([]byte, uint64) {
   207  	x := u64(b)
   208  	return b[8:], x
   209  }
   210  
   211  func u64(b []byte) uint64 { return binary.LittleEndian.Uint64(b) }
   212  func u32(b []byte) uint32 { return binary.LittleEndian.Uint32(b) }
   213  
   214  func round(acc, input uint64) uint64 {
   215  	acc += input * prime2
   216  	acc = rol31(acc)
   217  	acc *= prime1
   218  	return acc
   219  }
   220  
   221  func mergeRound(acc, val uint64) uint64 {
   222  	val = round(0, val)
   223  	acc ^= val
   224  	acc = acc*prime1 + prime4
   225  	return acc
   226  }
   227  
   228  func rol1(x uint64) uint64  { return bits.RotateLeft64(x, 1) }
   229  func rol7(x uint64) uint64  { return bits.RotateLeft64(x, 7) }
   230  func rol11(x uint64) uint64 { return bits.RotateLeft64(x, 11) }
   231  func rol12(x uint64) uint64 { return bits.RotateLeft64(x, 12) }
   232  func rol18(x uint64) uint64 { return bits.RotateLeft64(x, 18) }
   233  func rol23(x uint64) uint64 { return bits.RotateLeft64(x, 23) }
   234  func rol27(x uint64) uint64 { return bits.RotateLeft64(x, 27) }
   235  func rol31(x uint64) uint64 { return bits.RotateLeft64(x, 31) }