github.com/primecitizens/pcz/std@v0.2.1/core/hash/hash.go (about)

     1  // SPDX-License-Identifier: Apache-2.0
     2  // Copyright 2023 The Prime Citizens
     3  
     4  package hash
     5  
     6  import (
     7  	"unsafe"
     8  
     9  	stdstring "github.com/primecitizens/pcz/std/builtin/string"
    10  	stdtype "github.com/primecitizens/pcz/std/builtin/type"
    11  	"github.com/primecitizens/pcz/std/core/abi"
    12  	"github.com/primecitizens/pcz/std/core/arch"
    13  	"github.com/primecitizens/pcz/std/core/assert"
    14  	"github.com/primecitizens/pcz/std/core/thread"
    15  )
    16  
    17  var (
    18  	// used in hash{32,64}.go to seed the hash function
    19  	hashkey [4]uintptr
    20  )
    21  
    22  const (
    23  	c0 = uintptr((8-arch.PtrSize)/4*2860486313 + (arch.PtrSize-4)/4*33054211828000289)
    24  	c1 = uintptr((8-arch.PtrSize)/4*3267000013 + (arch.PtrSize-4)/4*23344194077549503)
    25  )
    26  
    27  func StringHash(p unsafe.Pointer, h uintptr) uintptr {
    28  	str := (*stdstring.Header)(p)
    29  	return MemHash(str.Str, h, uintptr(str.Len))
    30  }
    31  
    32  func Float32Hash(p unsafe.Pointer, h uintptr) uintptr {
    33  	f := *(*float32)(p)
    34  	switch {
    35  	case f == 0:
    36  		return c1 * (c0 ^ h) // +0, -0
    37  	case f != f:
    38  		return c1 * (c0 ^ h ^ uintptr(thread.G().G().Rand32())) // any kind of NaN
    39  	default:
    40  		return MemHash(p, h, 4)
    41  	}
    42  }
    43  
    44  func Float64Hash(p unsafe.Pointer, h uintptr) uintptr {
    45  	f := *(*float64)(p)
    46  	switch {
    47  	case f == 0:
    48  		return c1 * (c0 ^ h) // +0, -0
    49  	case f != f:
    50  		return c1 * (c0 ^ h ^ uintptr(thread.G().G().Rand32())) // any kind of NaN
    51  	default:
    52  		return MemHash(p, h, 8)
    53  	}
    54  }
    55  
    56  func Complex64Hash(p unsafe.Pointer, h uintptr) uintptr {
    57  	x := (*[2]float32)(p)
    58  	return Float32Hash(unsafe.Pointer(&x[1]), Float32Hash(unsafe.Pointer(&x[0]), h))
    59  }
    60  
    61  func Complex128Hash(p unsafe.Pointer, h uintptr) uintptr {
    62  	x := (*[2]float64)(p)
    63  	return Float64Hash(unsafe.Pointer(&x[1]), Float64Hash(unsafe.Pointer(&x[0]), h))
    64  }
    65  
    66  func InterfaceHash(p unsafe.Pointer, h uintptr) uintptr {
    67  	a := (*stdtype.Iface)(p)
    68  	tab := a.Itab
    69  	if tab == nil {
    70  		return h
    71  	}
    72  	t := tab.Type
    73  	if t.Equal == nil {
    74  		// Check hashability here. We could do this check inside
    75  		// typehash, but we want to report the topmost type in
    76  		// the error text (e.g. in a struct with a field of slice type
    77  		// we want to report the struct, not the slice).
    78  		assert.Panic("hash", "of", "unhashable", "type ", t.String())
    79  		return h
    80  	}
    81  
    82  	if t.IsDirectIface() {
    83  		return c1 * TypeHash(t, unsafe.Pointer(&a.Data), h^c0)
    84  	} else {
    85  		return c1 * TypeHash(t, a.Data, h^c0)
    86  	}
    87  }
    88  
    89  func NilInterfaceHash(p unsafe.Pointer, h uintptr) uintptr {
    90  	a := (*stdtype.Eface)(p)
    91  	t := a.Type
    92  	if t == nil {
    93  		return h
    94  	}
    95  	if t.Equal == nil {
    96  		// See comment in interhash above.
    97  		assert.Panic("hash", "of", "unhashable", "type ", t.String())
    98  		return h
    99  	}
   100  
   101  	if t.IsDirectIface() {
   102  		return c1 * TypeHash(t, unsafe.Pointer(&a.Data), h^c0)
   103  	} else {
   104  		return c1 * TypeHash(t, a.Data, h^c0)
   105  	}
   106  }
   107  
   108  // TypeHash computes the hash of the object of type t at address p.
   109  // h is the seed.
   110  // This function is seldom used. Most maps use for hashing either
   111  // fixed functions (e.g. f32hash) or compiler-generated functions
   112  // (e.g. for a type like struct { x, y string }). This implementation
   113  // is slower but more general and is used for hashing interface types
   114  // (called from interhash or nilinterhash, above) or for hashing in
   115  // maps generated by reflect.MapOf (reflect_typehash, below).
   116  // Note: this function must match the compiler generated
   117  // functions exactly. See issue 37716.
   118  func TypeHash(t *abi.Type, p unsafe.Pointer, h uintptr) uintptr {
   119  	if t.TFlag&abi.TFlagRegularMemory != 0 {
   120  		// Handle ptr sizes specially, see issue 37086.
   121  		switch t.Size_ {
   122  		case 4:
   123  			return MemHash32(p, h)
   124  		case 8:
   125  			return MemHash64(p, h)
   126  		default:
   127  			return MemHash(p, h, t.Size_)
   128  		}
   129  	}
   130  
   131  	switch t.Kind() {
   132  	case abi.KindFloat32:
   133  		return Float32Hash(p, h)
   134  	case abi.KindFloat64:
   135  		return Float64Hash(p, h)
   136  	case abi.KindComplex64:
   137  		return Complex64Hash(p, h)
   138  	case abi.KindComplex128:
   139  		return Complex128Hash(p, h)
   140  	case abi.KindString:
   141  		return StringHash(p, h)
   142  	case abi.KindInterface:
   143  		if len(t.InterfaceType().Methods) == 0 {
   144  			return NilInterfaceHash(p, h)
   145  		}
   146  		return InterfaceHash(p, h)
   147  	case abi.KindArray:
   148  		for a, i := t.ArrayType(), uintptr(0); i < a.Len; i++ {
   149  			h = TypeHash(a.Elem, unsafe.Add(p, i*a.Elem.Size_), h)
   150  		}
   151  		return h
   152  	case abi.KindStruct:
   153  		s := t.StructType()
   154  		for _, f := range s.Fields {
   155  			if f.Name.IsBlank() {
   156  				continue
   157  			}
   158  			h = TypeHash(f.Typ, unsafe.Add(p, f.Offset), h)
   159  		}
   160  		return h
   161  	default:
   162  		// Should never happen, as typehash should only be called
   163  		// with comparable types.
   164  		assert.Panic("hash", "of", "unhashable", "type ", t.String())
   165  		return h
   166  	}
   167  }