Black Lives Matter. Support the Equal Justice Initiative.

Source file src/hash/maphash/maphash.go

Documentation: hash/maphash

     1  // Copyright 2019 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  // Package maphash provides hash functions on byte sequences.
     6  // These hash functions are intended to be used to implement hash tables or
     7  // other data structures that need to map arbitrary strings or byte
     8  // sequences to a uniform distribution on unsigned 64-bit integers.
     9  // Each different instance of a hash table or data structure should use its own Seed.
    10  //
    11  // The hash functions are not cryptographically secure.
    12  // (See crypto/sha256 and crypto/sha512 for cryptographic use.)
    13  //
    14  package maphash
    15  
    16  import (
    17  	"internal/unsafeheader"
    18  	"unsafe"
    19  )
    20  
    21  // A Seed is a random value that selects the specific hash function
    22  // computed by a Hash. If two Hashes use the same Seeds, they
    23  // will compute the same hash values for any given input.
    24  // If two Hashes use different Seeds, they are very likely to compute
    25  // distinct hash values for any given input.
    26  //
    27  // A Seed must be initialized by calling MakeSeed.
    28  // The zero seed is uninitialized and not valid for use with Hash's SetSeed method.
    29  //
    30  // Each Seed value is local to a single process and cannot be serialized
    31  // or otherwise recreated in a different process.
    32  type Seed struct {
    33  	s uint64
    34  }
    35  
    36  // A Hash computes a seeded hash of a byte sequence.
    37  //
    38  // The zero Hash is a valid Hash ready to use.
    39  // A zero Hash chooses a random seed for itself during
    40  // the first call to a Reset, Write, Seed, or Sum64 method.
    41  // For control over the seed, use SetSeed.
    42  //
    43  // The computed hash values depend only on the initial seed and
    44  // the sequence of bytes provided to the Hash object, not on the way
    45  // in which the bytes are provided. For example, the three sequences
    46  //
    47  //     h.Write([]byte{'f','o','o'})
    48  //     h.WriteByte('f'); h.WriteByte('o'); h.WriteByte('o')
    49  //     h.WriteString("foo")
    50  //
    51  // all have the same effect.
    52  //
    53  // Hashes are intended to be collision-resistant, even for situations
    54  // where an adversary controls the byte sequences being hashed.
    55  //
    56  // A Hash is not safe for concurrent use by multiple goroutines, but a Seed is.
    57  // If multiple goroutines must compute the same seeded hash,
    58  // each can declare its own Hash and call SetSeed with a common Seed.
    59  type Hash struct {
    60  	_     [0]func()     // not comparable
    61  	seed  Seed          // initial seed used for this hash
    62  	state Seed          // current hash of all flushed bytes
    63  	buf   [bufSize]byte // unflushed byte buffer
    64  	n     int           // number of unflushed bytes
    65  }
    66  
    67  // bufSize is the size of the Hash write buffer.
    68  // The buffer ensures that writes depend only on the sequence of bytes,
    69  // not the sequence of WriteByte/Write/WriteString calls,
    70  // by always calling rthash with a full buffer (except for the tail).
    71  const bufSize = 128
    72  
    73  // initSeed seeds the hash if necessary.
    74  // initSeed is called lazily before any operation that actually uses h.seed/h.state.
    75  // Note that this does not include Write/WriteByte/WriteString in the case
    76  // where they only add to h.buf. (If they write too much, they call h.flush,
    77  // which does call h.initSeed.)
    78  func (h *Hash) initSeed() {
    79  	if h.seed.s == 0 {
    80  		seed := MakeSeed()
    81  		h.seed = seed
    82  		h.state = seed
    83  	}
    84  }
    85  
    86  // WriteByte adds b to the sequence of bytes hashed by h.
    87  // It never fails; the error result is for implementing io.ByteWriter.
    88  func (h *Hash) WriteByte(b byte) error {
    89  	if h.n == len(h.buf) {
    90  		h.flush()
    91  	}
    92  	h.buf[h.n] = b
    93  	h.n++
    94  	return nil
    95  }
    96  
    97  // Write adds b to the sequence of bytes hashed by h.
    98  // It always writes all of b and never fails; the count and error result are for implementing io.Writer.
    99  func (h *Hash) Write(b []byte) (int, error) {
   100  	size := len(b)
   101  	// Deal with bytes left over in h.buf.
   102  	// h.n <= bufSize is always true.
   103  	// Checking it is ~free and it lets the compiler eliminate a bounds check.
   104  	if h.n > 0 && h.n <= bufSize {
   105  		k := copy(h.buf[h.n:], b)
   106  		h.n += k
   107  		if h.n < bufSize {
   108  			// Copied the entirety of b to h.buf.
   109  			return size, nil
   110  		}
   111  		b = b[k:]
   112  		h.flush()
   113  		// No need to set h.n = 0 here; it happens just before exit.
   114  	}
   115  	// Process as many full buffers as possible, without copying, and calling initSeed only once.
   116  	if len(b) > bufSize {
   117  		h.initSeed()
   118  		for len(b) > bufSize {
   119  			h.state.s = rthash(&b[0], bufSize, h.state.s)
   120  			b = b[bufSize:]
   121  		}
   122  	}
   123  	// Copy the tail.
   124  	copy(h.buf[:], b)
   125  	h.n = len(b)
   126  	return size, nil
   127  }
   128  
   129  // WriteString adds the bytes of s to the sequence of bytes hashed by h.
   130  // It always writes all of s and never fails; the count and error result are for implementing io.StringWriter.
   131  func (h *Hash) WriteString(s string) (int, error) {
   132  	// WriteString mirrors Write. See Write for comments.
   133  	size := len(s)
   134  	if h.n > 0 && h.n <= bufSize {
   135  		k := copy(h.buf[h.n:], s)
   136  		h.n += k
   137  		if h.n < bufSize {
   138  			return size, nil
   139  		}
   140  		s = s[k:]
   141  		h.flush()
   142  	}
   143  	if len(s) > bufSize {
   144  		h.initSeed()
   145  		for len(s) > bufSize {
   146  			ptr := (*byte)((*unsafeheader.String)(unsafe.Pointer(&s)).Data)
   147  			h.state.s = rthash(ptr, bufSize, h.state.s)
   148  			s = s[bufSize:]
   149  		}
   150  	}
   151  	copy(h.buf[:], s)
   152  	h.n = len(s)
   153  	return size, nil
   154  }
   155  
   156  // Seed returns h's seed value.
   157  func (h *Hash) Seed() Seed {
   158  	h.initSeed()
   159  	return h.seed
   160  }
   161  
   162  // SetSeed sets h to use seed, which must have been returned by MakeSeed
   163  // or by another Hash's Seed method.
   164  // Two Hash objects with the same seed behave identically.
   165  // Two Hash objects with different seeds will very likely behave differently.
   166  // Any bytes added to h before this call will be discarded.
   167  func (h *Hash) SetSeed(seed Seed) {
   168  	if seed.s == 0 {
   169  		panic("maphash: use of uninitialized Seed")
   170  	}
   171  	h.seed = seed
   172  	h.state = seed
   173  	h.n = 0
   174  }
   175  
   176  // Reset discards all bytes added to h.
   177  // (The seed remains the same.)
   178  func (h *Hash) Reset() {
   179  	h.initSeed()
   180  	h.state = h.seed
   181  	h.n = 0
   182  }
   183  
   184  // precondition: buffer is full.
   185  func (h *Hash) flush() {
   186  	if h.n != len(h.buf) {
   187  		panic("maphash: flush of partially full buffer")
   188  	}
   189  	h.initSeed()
   190  	h.state.s = rthash(&h.buf[0], h.n, h.state.s)
   191  	h.n = 0
   192  }
   193  
   194  // Sum64 returns h's current 64-bit value, which depends on
   195  // h's seed and the sequence of bytes added to h since the
   196  // last call to Reset or SetSeed.
   197  //
   198  // All bits of the Sum64 result are close to uniformly and
   199  // independently distributed, so it can be safely reduced
   200  // by using bit masking, shifting, or modular arithmetic.
   201  func (h *Hash) Sum64() uint64 {
   202  	h.initSeed()
   203  	return rthash(&h.buf[0], h.n, h.state.s)
   204  }
   205  
   206  // MakeSeed returns a new random seed.
   207  func MakeSeed() Seed {
   208  	var s1, s2 uint64
   209  	for {
   210  		s1 = uint64(runtime_fastrand())
   211  		s2 = uint64(runtime_fastrand())
   212  		// We use seed 0 to indicate an uninitialized seed/hash,
   213  		// so keep trying until we get a non-zero seed.
   214  		if s1|s2 != 0 {
   215  			break
   216  		}
   217  	}
   218  	return Seed{s: s1<<32 + s2}
   219  }
   220  
   221  //go:linkname runtime_fastrand runtime.fastrand
   222  func runtime_fastrand() uint32
   223  
   224  func rthash(ptr *byte, len int, seed uint64) uint64 {
   225  	if len == 0 {
   226  		return seed
   227  	}
   228  	// The runtime hasher only works on uintptr. For 64-bit
   229  	// architectures, we use the hasher directly. Otherwise,
   230  	// we use two parallel hashers on the lower and upper 32 bits.
   231  	if unsafe.Sizeof(uintptr(0)) == 8 {
   232  		return uint64(runtime_memhash(unsafe.Pointer(ptr), uintptr(seed), uintptr(len)))
   233  	}
   234  	lo := runtime_memhash(unsafe.Pointer(ptr), uintptr(seed), uintptr(len))
   235  	hi := runtime_memhash(unsafe.Pointer(ptr), uintptr(seed>>32), uintptr(len))
   236  	return uint64(hi)<<32 | uint64(lo)
   237  }
   238  
   239  //go:linkname runtime_memhash runtime.memhash
   240  //go:noescape
   241  func runtime_memhash(p unsafe.Pointer, seed, s uintptr) uintptr
   242  
   243  // Sum appends the hash's current 64-bit value to b.
   244  // It exists for implementing hash.Hash.
   245  // For direct calls, it is more efficient to use Sum64.
   246  func (h *Hash) Sum(b []byte) []byte {
   247  	x := h.Sum64()
   248  	return append(b,
   249  		byte(x>>0),
   250  		byte(x>>8),
   251  		byte(x>>16),
   252  		byte(x>>24),
   253  		byte(x>>32),
   254  		byte(x>>40),
   255  		byte(x>>48),
   256  		byte(x>>56))
   257  }
   258  
   259  // Size returns h's hash value size, 8 bytes.
   260  func (h *Hash) Size() int { return 8 }
   261  
   262  // BlockSize returns h's block size.
   263  func (h *Hash) BlockSize() int { return len(h.buf) }
   264  

View as plain text