github.com/tinygo-org/tinygo@v0.31.3-0.20240404173401-90b0bf646c27/src/runtime/memhash_tsip.go (about)

     1  //go:build runtime_memhash_tsip
     2  
     3  // This is the tsip hash developed by Damian Gryski, based on ideas from SipHash.
     4  // It is slower than leveldb's hash, but should be "stronger".
     5  
     6  // https://en.wikipedia.org/wiki/SipHash
     7  // https://github.com/veorq/SipHash
     8  // https://github.com/dgryski/tsip
     9  
    10  package runtime
    11  
    12  import (
    13  	"encoding/binary"
    14  	"math/bits"
    15  	"unsafe"
    16  )
    17  
    18  func ptrToSlice(ptr unsafe.Pointer, n uintptr) []byte {
    19  	var p []byte
    20  
    21  	type _bslice struct {
    22  		ptr *byte
    23  		len uintptr
    24  		cap uintptr
    25  	}
    26  
    27  	pslice := (*_bslice)(unsafe.Pointer(&p))
    28  	pslice.ptr = (*byte)(ptr)
    29  	pslice.cap = n
    30  	pslice.len = n
    31  
    32  	return p
    33  }
    34  
    35  type sip struct {
    36  	v0, v1 uint64
    37  }
    38  
    39  func (s *sip) round() {
    40  	s.v0 += s.v1
    41  	s.v1 = bits.RotateLeft64(s.v1, 13) ^ s.v0
    42  	s.v0 = bits.RotateLeft64(s.v0, 35) + s.v1
    43  	s.v1 = bits.RotateLeft64(s.v1, 17) ^ s.v0
    44  	s.v0 = bits.RotateLeft64(s.v0, 21)
    45  }
    46  
    47  func hash64(ptr unsafe.Pointer, n uintptr, seed uintptr) uint64 {
    48  
    49  	p := ptrToSlice(ptr, n)
    50  
    51  	k0 := uint64(seed)
    52  	k1 := uint64(0)
    53  
    54  	s := sip{
    55  		v0: k0 ^ 0x736f6d6570736575,
    56  		v1: k1 ^ 0x646f72616e646f6d,
    57  	}
    58  
    59  	b := uint64(len(p)) << 56
    60  
    61  	for len(p) >= 8 {
    62  		m := binary.LittleEndian.Uint64(p[:8])
    63  		s.v1 ^= m
    64  		s.round()
    65  		s.v0 ^= m
    66  		p = p[8:]
    67  	}
    68  
    69  	switch len(p) {
    70  	case 7:
    71  		b |= uint64(p[6]) << 48
    72  		fallthrough
    73  	case 6:
    74  		b |= uint64(p[5]) << 40
    75  		fallthrough
    76  	case 5:
    77  		b |= uint64(p[4]) << 32
    78  		fallthrough
    79  	case 4:
    80  		b |= uint64(p[3]) << 24
    81  		fallthrough
    82  	case 3:
    83  		b |= uint64(p[2]) << 16
    84  		fallthrough
    85  	case 2:
    86  		b |= uint64(p[1]) << 8
    87  		fallthrough
    88  	case 1:
    89  		b |= uint64(p[0])
    90  	}
    91  
    92  	// last block
    93  	s.v1 ^= b
    94  	s.round()
    95  	s.v0 ^= b
    96  
    97  	// finalization
    98  	s.v1 ^= 0xff
    99  	s.round()
   100  	s.v1 = bits.RotateLeft64(s.v1, 32)
   101  	s.round()
   102  	s.v1 = bits.RotateLeft64(s.v1, 32)
   103  
   104  	return s.v0 ^ s.v1
   105  }
   106  
   107  type sip32 struct {
   108  	v0, v1 uint32
   109  }
   110  
   111  func (s *sip32) round() {
   112  	s.v0 += s.v1
   113  	s.v1 = bits.RotateLeft32(s.v1, 5) ^ s.v0
   114  	s.v0 = bits.RotateLeft32(s.v0, 8) + s.v1
   115  	s.v1 = bits.RotateLeft32(s.v1, 13) ^ s.v0
   116  	s.v0 = bits.RotateLeft32(s.v0, 7)
   117  }
   118  
   119  func hash32(ptr unsafe.Pointer, n uintptr, seed uintptr) uint32 {
   120  	// TODO(dgryski): replace this messiness with unsafe.Slice when we can use 1.17 features
   121  
   122  	p := ptrToSlice(ptr, n)
   123  
   124  	k0 := uint32(seed)
   125  	k1 := uint32(seed >> 32)
   126  
   127  	s := sip32{
   128  		v0: k0 ^ 0x74656462,
   129  		v1: k1 ^ 0x6c796765,
   130  	}
   131  	b := uint32(len(p)) << 24
   132  
   133  	for len(p) >= 4 {
   134  		m := binary.LittleEndian.Uint32(p[:4])
   135  		s.v1 ^= m
   136  		s.round()
   137  		s.v0 ^= m
   138  		p = p[4:]
   139  	}
   140  
   141  	switch len(p) {
   142  	case 3:
   143  		b |= uint32(p[2]) << 16
   144  		fallthrough
   145  	case 2:
   146  		b |= uint32(p[1]) << 8
   147  		fallthrough
   148  	case 1:
   149  		b |= uint32(p[0])
   150  	}
   151  
   152  	// last block
   153  	s.v1 ^= b
   154  	s.round()
   155  	s.v0 ^= b
   156  
   157  	// finalization
   158  	s.v1 ^= 0xff
   159  	s.round()
   160  	s.v1 = bits.RotateLeft32(s.v1, 16)
   161  	s.round()
   162  	s.v1 = bits.RotateLeft32(s.v1, 16)
   163  
   164  	return s.v0 ^ s.v1
   165  }