github.com/jxskiss/gopkg@v0.17.3/rthash/hash.go (about)

     1  // Package rthash exposes the various hash functions in runtime package.
     2  //
     3  // The idea mainly comes from https://github.com/golang/go/issues/21195.
     4  package rthash
     5  
     6  import (
     7  	"github.com/jxskiss/gopkg/internal/linkname"
     8  	"unsafe"
     9  )
    10  
    11  // Hash exposes the various hash functions in runtime package.
    12  // The idea mainly comes from https://github.com/golang/go/issues/21195.
    13  //
    14  // See also: hash/maphash.Hash.
    15  //
    16  // Unlike hash.Hash or hash/maphash.Hash, this Hash does not provide
    17  // the ability to reset seed, the seed must be provided when creating the
    18  // Hash instance and will be used during the lifetime.
    19  //
    20  // This Hash type is intended to be used to do fast sharding, when
    21  // implementing hash tables or other data structures, it's recommended
    22  // to consider using hash/maphash.Hash as a proper choice.
    23  //
    24  // The hash functions are not cryptographically secure.
    25  // (See crypto/sha256 and crypto/sha512 for cryptographic use.)
    26  //
    27  // A Hash must be initialized by calling New().
    28  // After initialized, a Hash is safe for concurrent use by multiple goroutines.
    29  //
    30  // Each call to a same method with the same value will return the same
    31  // result for a Hash instance, but it may and supposed to return different
    32  // hash results from each Hash instance.
    33  type Hash struct {
    34  	seed uintptr
    35  }
    36  
    37  // New returns a new Hash instance, which exposes the various hash functions
    38  // in runtime package. The returned Hash instance is safe for concurrent use
    39  // by multiple goroutines.
    40  func New() Hash {
    41  	seed := uintptr(makeseed())
    42  	return Hash{seed: seed}
    43  }
    44  
    45  // Hash returns a hash code for a comparable argument.
    46  //
    47  // Note this function calls the hash functions for the concrete type if
    48  // x is of type string, int8, uint8, int16, uint16, int32, uint32,
    49  // int64, uint64, int, uint, uintptr, float32, float64, complex64,
    50  // or complex128, else it calls
    51  func (h Hash) Hash(x interface{}) uintptr {
    52  	switch v := x.(type) {
    53  	case string:
    54  		return h.String(v)
    55  	case int8:
    56  		return h.Int8(v)
    57  	case uint8:
    58  		return h.Uint8(v)
    59  	case int16:
    60  		return h.Int16(v)
    61  	case uint16:
    62  		return h.Uint16(v)
    63  	case int32:
    64  		return h.Int32(v)
    65  	case uint32:
    66  		return h.Uint32(v)
    67  	case int64:
    68  		return h.Int64(v)
    69  	case uint64:
    70  		return h.Uint64(v)
    71  	case int:
    72  		return h.Int(v)
    73  	case uint:
    74  		return h.Uint(v)
    75  	case uintptr:
    76  		return h.Uintptr(v)
    77  	case float32:
    78  		return h.Float32(v)
    79  	case float64:
    80  		return h.Float64(v)
    81  	case complex64:
    82  		return h.Complex64(v)
    83  	case complex128:
    84  		return h.Complex128(v)
    85  	default:
    86  		return h.Interface(v)
    87  	}
    88  }
    89  
    90  // String exposes the stringHash function from runtime package.
    91  func (h Hash) String(x string) uintptr {
    92  	return linkname.Runtime_stringHash(x, h.seed)
    93  }
    94  
    95  // Bytes exposes the bytesHash function from runtime package.
    96  func (h Hash) Bytes(x []byte) uintptr {
    97  	return linkname.Runtime_bytesHash(x, h.seed)
    98  }
    99  
   100  // Int8 exposes the memhash8 function from runtime package.
   101  func (h Hash) Int8(x int8) uintptr {
   102  	return linkname.Runtime_memhash8(unsafe.Pointer(&x), h.seed)
   103  }
   104  
   105  // Uint8 exposes the memhash8 function from runtime package.
   106  func (h Hash) Uint8(x uint8) uintptr {
   107  	return linkname.Runtime_memhash8(unsafe.Pointer(&x), h.seed)
   108  }
   109  
   110  // Int16 exposes the memhash16 function from runtime package.
   111  func (h Hash) Int16(x int16) uintptr {
   112  	return linkname.Runtime_memhash16(unsafe.Pointer(&x), h.seed)
   113  }
   114  
   115  // Uint16 exposes the memhash16 function from runtime package.
   116  func (h Hash) Uint16(x uint16) uintptr {
   117  	return linkname.Runtime_memhash16(unsafe.Pointer(&x), h.seed)
   118  }
   119  
   120  // Int32 exposes the int32Hash function from runtime package.
   121  func (h Hash) Int32(x int32) uintptr {
   122  	return linkname.Runtime_int32Hash(uint32(x), h.seed)
   123  }
   124  
   125  // Uint32 exposes the int32Hash function from runtime package.
   126  func (h Hash) Uint32(x uint32) uintptr {
   127  	return linkname.Runtime_int32Hash(x, h.seed)
   128  }
   129  
   130  // Int64 exposes the int64Hash function from runtime package.
   131  func (h Hash) Int64(x int64) uintptr {
   132  	return linkname.Runtime_int64Hash(uint64(x), h.seed)
   133  }
   134  
   135  // Uint64 exposes the int64Hash function from runtime package.
   136  func (h Hash) Uint64(x uint64) uintptr {
   137  	return linkname.Runtime_int64Hash(x, h.seed)
   138  }
   139  
   140  // Int calculates hash of x using either int32Hash or int64Hash
   141  // according to the pointer size of the platform.
   142  func (h Hash) Int(x int) uintptr {
   143  	if ptrSize == 32 {
   144  		return linkname.Runtime_int32Hash(uint32(x), h.seed)
   145  	}
   146  	return linkname.Runtime_int64Hash(uint64(x), h.seed)
   147  }
   148  
   149  // Uint calculates hash of x using either int32Hash or int64Hash
   150  // according the pointer size of the platform.
   151  func (h Hash) Uint(x uint) uintptr {
   152  	if ptrSize == 32 {
   153  		return linkname.Runtime_int32Hash(uint32(x), h.seed)
   154  	}
   155  	return linkname.Runtime_int64Hash(uint64(x), h.seed)
   156  }
   157  
   158  // Uintptr calculates hash of x using either int32Hash or int64Hash
   159  // according to the pointer size of the platform.
   160  func (h Hash) Uintptr(x uintptr) uintptr {
   161  	if ptrSize == 32 {
   162  		return linkname.Runtime_int32Hash(uint32(x), h.seed)
   163  	}
   164  	return linkname.Runtime_int64Hash(uint64(x), h.seed)
   165  }
   166  
   167  // Float32 exposes the f32hash function from runtime package.
   168  func (h Hash) Float32(x float32) uintptr {
   169  	return linkname.Runtime_f32hash(unsafe.Pointer(&x), h.seed)
   170  }
   171  
   172  // Float64 exposes the f64hash function from runtime package.
   173  func (h Hash) Float64(x float64) uintptr {
   174  	return linkname.Runtime_f64hash(unsafe.Pointer(&x), h.seed)
   175  }
   176  
   177  // Complex64 exposes the c64hash function from runtime package.
   178  func (h Hash) Complex64(x complex64) uintptr {
   179  	return linkname.Runtime_c64hash(unsafe.Pointer(&x), h.seed)
   180  }
   181  
   182  // Complex128 exposes the c128hash function from runtime package.
   183  func (h Hash) Complex128(x complex128) uintptr {
   184  	return linkname.Runtime_c128hash(unsafe.Pointer(&x), h.seed)
   185  }
   186  
   187  // Interface exposes the efaceHash function from runtime package.
   188  func (h Hash) Interface(x interface{}) uintptr {
   189  	return linkname.Runtime_efaceHash(x, h.seed)
   190  }
   191  
   192  // ptrSize is the size in bits of an int or uint value.
   193  const ptrSize = 32 << (^uint(0) >> 63)
   194  
   195  func makeseed() uint64 {
   196  	var s1, s2 uint64
   197  	for {
   198  		s1 = uint64(linkname.Runtime_fastrand())
   199  		s2 = uint64(linkname.Runtime_fastrand())
   200  		// We use seed 0 to indicate an uninitialized seed/hash,
   201  		// so keep trying until we get a non-zero seed.
   202  		if s1|s2 != 0 {
   203  			break
   204  		}
   205  	}
   206  	return s1<<32 + s2
   207  }