mirror of
				https://github.com/superseriousbusiness/gotosocial.git
				synced 2025-10-31 12:12:25 -05:00 
			
		
		
		
	
		
			
				
	
	
		
			404 lines
		
	
	
	
		
			7.6 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			404 lines
		
	
	
	
		
			7.6 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package structr
 | |
| 
 | |
| import (
 | |
| 	"reflect"
 | |
| 	"sync"
 | |
| 	"unsafe"
 | |
| 
 | |
| 	"github.com/zeebo/xxh3"
 | |
| )
 | |
| 
 | |
| var hash_pool sync.Pool
 | |
| 
 | |
| func get_hasher() *xxh3.Hasher {
 | |
| 	v := hash_pool.Get()
 | |
| 	if v == nil {
 | |
| 		v = new(xxh3.Hasher)
 | |
| 	}
 | |
| 	return v.(*xxh3.Hasher)
 | |
| }
 | |
| 
 | |
| func hash_sum(fields []structfield, h *xxh3.Hasher, key []any) (Hash, bool) {
 | |
| 	if len(key) != len(fields) {
 | |
| 		panicf("incorrect number key parts: want=%d received=%d",
 | |
| 			len(key),
 | |
| 			len(fields),
 | |
| 		)
 | |
| 	}
 | |
| 	var zero bool
 | |
| 	h.Reset()
 | |
| 	for i, part := range key {
 | |
| 		zero = fields[i].hasher(h, part) || zero
 | |
| 	}
 | |
| 	// See: https://github.com/Cyan4973/xxHash/issues/453#issuecomment-696838445
 | |
| 	//
 | |
| 	// In order to extract 32-bit from a good 64-bit hash result,
 | |
| 	// there are many possible choices, which are all valid.
 | |
| 	// I would typically grab the lower 32-bit and call it a day.
 | |
| 	//
 | |
| 	// Grabbing any other 32-bit (the upper part for example) is fine too.
 | |
| 	//
 | |
| 	// xoring higher and lower bits makes more sense whenever the produced hash offers dubious quality.
 | |
| 	// FNV, for example, has poor mixing in its lower bits, so it's better to mix with the higher bits.
 | |
| 	//
 | |
| 	// XXH3 already performs significant output mixing before returning the data,
 | |
| 	// so it's not beneficial to add another xorfold stage.
 | |
| 	return uint64ToHash(h.Sum64()), zero
 | |
| }
 | |
| 
 | |
| func hasher(t reflect.Type) func(*xxh3.Hasher, any) bool {
 | |
| 	switch t.Kind() {
 | |
| 	case reflect.Int,
 | |
| 		reflect.Uint,
 | |
| 		reflect.Uintptr:
 | |
| 		switch unsafe.Sizeof(int(0)) {
 | |
| 		case 4:
 | |
| 			return hash32bit
 | |
| 		case 8:
 | |
| 			return hash64bit
 | |
| 		default:
 | |
| 			panic("unexpected platform int size")
 | |
| 		}
 | |
| 
 | |
| 	case reflect.Int8,
 | |
| 		reflect.Uint8:
 | |
| 		return hash8bit
 | |
| 
 | |
| 	case reflect.Int16,
 | |
| 		reflect.Uint16:
 | |
| 		return hash16bit
 | |
| 
 | |
| 	case reflect.Int32,
 | |
| 		reflect.Uint32,
 | |
| 		reflect.Float32:
 | |
| 		return hash32bit
 | |
| 
 | |
| 	case reflect.Int64,
 | |
| 		reflect.Uint64,
 | |
| 		reflect.Float64,
 | |
| 		reflect.Complex64:
 | |
| 		return hash64bit
 | |
| 
 | |
| 	case reflect.String:
 | |
| 		return hashstring
 | |
| 
 | |
| 	case reflect.Pointer:
 | |
| 		switch t.Elem().Kind() {
 | |
| 		case reflect.Int,
 | |
| 			reflect.Uint,
 | |
| 			reflect.Uintptr:
 | |
| 			switch unsafe.Sizeof(int(0)) {
 | |
| 			case 4:
 | |
| 				return hash32bitptr
 | |
| 			case 8:
 | |
| 				return hash64bitptr
 | |
| 			default:
 | |
| 				panic("unexpected platform int size")
 | |
| 			}
 | |
| 
 | |
| 		case reflect.Int8,
 | |
| 			reflect.Uint8:
 | |
| 			return hash8bitptr
 | |
| 
 | |
| 		case reflect.Int16,
 | |
| 			reflect.Uint16:
 | |
| 			return hash16bitptr
 | |
| 
 | |
| 		case reflect.Int32,
 | |
| 			reflect.Uint32,
 | |
| 			reflect.Float32:
 | |
| 			return hash32bitptr
 | |
| 
 | |
| 		case reflect.Int64,
 | |
| 			reflect.Uint64,
 | |
| 			reflect.Float64,
 | |
| 			reflect.Complex64:
 | |
| 			return hash64bitptr
 | |
| 
 | |
| 		case reflect.String:
 | |
| 			return hashstringptr
 | |
| 		}
 | |
| 
 | |
| 	case reflect.Slice:
 | |
| 		switch t.Elem().Kind() {
 | |
| 		case reflect.Int,
 | |
| 			reflect.Uint,
 | |
| 			reflect.Uintptr:
 | |
| 			switch unsafe.Sizeof(int(0)) {
 | |
| 			case 4:
 | |
| 				return hash32bitslice
 | |
| 			case 8:
 | |
| 				return hash64bitslice
 | |
| 			default:
 | |
| 				panic("unexpected platform int size")
 | |
| 			}
 | |
| 
 | |
| 		case reflect.Int8,
 | |
| 			reflect.Uint8:
 | |
| 			return hash8bitslice
 | |
| 
 | |
| 		case reflect.Int16,
 | |
| 			reflect.Uint16:
 | |
| 			return hash16bitslice
 | |
| 
 | |
| 		case reflect.Int32,
 | |
| 			reflect.Uint32,
 | |
| 			reflect.Float32:
 | |
| 			return hash32bitslice
 | |
| 
 | |
| 		case reflect.Int64,
 | |
| 			reflect.Uint64,
 | |
| 			reflect.Float64,
 | |
| 			reflect.Complex64:
 | |
| 			return hash64bitslice
 | |
| 
 | |
| 		case reflect.String:
 | |
| 			return hashstringslice
 | |
| 		}
 | |
| 	}
 | |
| 	switch {
 | |
| 	case t.Implements(reflect.TypeOf((*interface{ MarshalBinary() ([]byte, error) })(nil)).Elem()):
 | |
| 		return hashbinarymarshaler
 | |
| 
 | |
| 	case t.Implements(reflect.TypeOf((*interface{ Bytes() []byte })(nil)).Elem()):
 | |
| 		return hashbytesmethod
 | |
| 
 | |
| 	case t.Implements(reflect.TypeOf((*interface{ String() string })(nil)).Elem()):
 | |
| 		return hashstringmethod
 | |
| 
 | |
| 	case t.Implements(reflect.TypeOf((*interface{ MarshalText() ([]byte, error) })(nil)).Elem()):
 | |
| 		return hashtextmarshaler
 | |
| 
 | |
| 	case t.Implements(reflect.TypeOf((*interface{ MarshalJSON() ([]byte, error) })(nil)).Elem()):
 | |
| 		return hashjsonmarshaler
 | |
| 	}
 | |
| 	panic("unhashable type")
 | |
| }
 | |
| 
 | |
| func hash8bit(h *xxh3.Hasher, a any) bool {
 | |
| 	u := *(*uint8)(data_ptr(a))
 | |
| 	_, _ = h.Write([]byte{u})
 | |
| 	return u == 0
 | |
| }
 | |
| 
 | |
| func hash8bitptr(h *xxh3.Hasher, a any) bool {
 | |
| 	u := (*uint8)(data_ptr(a))
 | |
| 	if u == nil {
 | |
| 		_, _ = h.Write([]byte{
 | |
| 			0,
 | |
| 		})
 | |
| 		return true
 | |
| 	} else {
 | |
| 		_, _ = h.Write([]byte{
 | |
| 			1,
 | |
| 			byte(*u),
 | |
| 		})
 | |
| 		return false
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func hash8bitslice(h *xxh3.Hasher, a any) bool {
 | |
| 	b := *(*[]byte)(data_ptr(a))
 | |
| 	_, _ = h.Write(b)
 | |
| 	return b == nil
 | |
| }
 | |
| 
 | |
| func hash16bit(h *xxh3.Hasher, a any) bool {
 | |
| 	u := *(*uint16)(data_ptr(a))
 | |
| 	_, _ = h.Write([]byte{
 | |
| 		byte(u),
 | |
| 		byte(u >> 8),
 | |
| 	})
 | |
| 	return u == 0
 | |
| }
 | |
| 
 | |
| func hash16bitptr(h *xxh3.Hasher, a any) bool {
 | |
| 	u := (*uint16)(data_ptr(a))
 | |
| 	if u == nil {
 | |
| 		_, _ = h.Write([]byte{
 | |
| 			0,
 | |
| 		})
 | |
| 		return true
 | |
| 	} else {
 | |
| 		_, _ = h.Write([]byte{
 | |
| 			1,
 | |
| 			byte(*u),
 | |
| 			byte(*u >> 8),
 | |
| 		})
 | |
| 		return false
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func hash16bitslice(h *xxh3.Hasher, a any) bool {
 | |
| 	u := *(*[]uint16)(data_ptr(a))
 | |
| 	for i := range u {
 | |
| 		_, _ = h.Write([]byte{
 | |
| 			byte(u[i]),
 | |
| 			byte(u[i] >> 8),
 | |
| 		})
 | |
| 	}
 | |
| 	return u == nil
 | |
| }
 | |
| 
 | |
| func hash32bit(h *xxh3.Hasher, a any) bool {
 | |
| 	u := *(*uint32)(data_ptr(a))
 | |
| 	_, _ = h.Write([]byte{
 | |
| 		byte(u),
 | |
| 		byte(u >> 8),
 | |
| 		byte(u >> 16),
 | |
| 		byte(u >> 24),
 | |
| 	})
 | |
| 	return u == 0
 | |
| }
 | |
| 
 | |
| func hash32bitptr(h *xxh3.Hasher, a any) bool {
 | |
| 	u := (*uint32)(data_ptr(a))
 | |
| 	if u == nil {
 | |
| 		_, _ = h.Write([]byte{
 | |
| 			0,
 | |
| 		})
 | |
| 		return true
 | |
| 	} else {
 | |
| 		_, _ = h.Write([]byte{
 | |
| 			1,
 | |
| 			byte(*u),
 | |
| 			byte(*u >> 8),
 | |
| 			byte(*u >> 16),
 | |
| 			byte(*u >> 24),
 | |
| 		})
 | |
| 		return false
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func hash32bitslice(h *xxh3.Hasher, a any) bool {
 | |
| 	u := *(*[]uint32)(data_ptr(a))
 | |
| 	for i := range u {
 | |
| 		_, _ = h.Write([]byte{
 | |
| 			byte(u[i]),
 | |
| 			byte(u[i] >> 8),
 | |
| 			byte(u[i] >> 16),
 | |
| 			byte(u[i] >> 24),
 | |
| 		})
 | |
| 	}
 | |
| 	return u == nil
 | |
| }
 | |
| 
 | |
| func hash64bit(h *xxh3.Hasher, a any) bool {
 | |
| 	u := *(*uint64)(data_ptr(a))
 | |
| 	_, _ = h.Write([]byte{
 | |
| 		byte(u),
 | |
| 		byte(u >> 8),
 | |
| 		byte(u >> 16),
 | |
| 		byte(u >> 24),
 | |
| 		byte(u >> 32),
 | |
| 		byte(u >> 40),
 | |
| 		byte(u >> 48),
 | |
| 		byte(u >> 56),
 | |
| 	})
 | |
| 	return u == 0
 | |
| }
 | |
| 
 | |
| func hash64bitptr(h *xxh3.Hasher, a any) bool {
 | |
| 	u := (*uint64)(data_ptr(a))
 | |
| 	if u == nil {
 | |
| 		_, _ = h.Write([]byte{
 | |
| 			0,
 | |
| 		})
 | |
| 		return true
 | |
| 	} else {
 | |
| 		_, _ = h.Write([]byte{
 | |
| 			1,
 | |
| 			byte(*u),
 | |
| 			byte(*u >> 8),
 | |
| 			byte(*u >> 16),
 | |
| 			byte(*u >> 24),
 | |
| 			byte(*u >> 32),
 | |
| 			byte(*u >> 40),
 | |
| 			byte(*u >> 48),
 | |
| 			byte(*u >> 56),
 | |
| 		})
 | |
| 		return false
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func hash64bitslice(h *xxh3.Hasher, a any) bool {
 | |
| 	u := *(*[]uint64)(data_ptr(a))
 | |
| 	for i := range u {
 | |
| 		_, _ = h.Write([]byte{
 | |
| 			byte(u[i]),
 | |
| 			byte(u[i] >> 8),
 | |
| 			byte(u[i] >> 16),
 | |
| 			byte(u[i] >> 24),
 | |
| 			byte(u[i] >> 32),
 | |
| 			byte(u[i] >> 40),
 | |
| 			byte(u[i] >> 48),
 | |
| 			byte(u[i] >> 56),
 | |
| 		})
 | |
| 	}
 | |
| 	return u == nil
 | |
| }
 | |
| 
 | |
| func hashstring(h *xxh3.Hasher, a any) bool {
 | |
| 	s := *(*string)(data_ptr(a))
 | |
| 	_, _ = h.WriteString(s)
 | |
| 	return s == ""
 | |
| }
 | |
| 
 | |
| func hashstringptr(h *xxh3.Hasher, a any) bool {
 | |
| 	s := (*string)(data_ptr(a))
 | |
| 	if s == nil {
 | |
| 		_, _ = h.Write([]byte{
 | |
| 			0,
 | |
| 		})
 | |
| 		return true
 | |
| 	} else {
 | |
| 		_, _ = h.Write([]byte{
 | |
| 			1,
 | |
| 		})
 | |
| 		_, _ = h.WriteString(*s)
 | |
| 		return false
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func hashstringslice(h *xxh3.Hasher, a any) bool {
 | |
| 	s := *(*[]string)(data_ptr(a))
 | |
| 	for i := range s {
 | |
| 		_, _ = h.WriteString(s[i])
 | |
| 	}
 | |
| 	return s == nil
 | |
| }
 | |
| 
 | |
| func hashbinarymarshaler(h *xxh3.Hasher, a any) bool {
 | |
| 	i := a.(interface{ MarshalBinary() ([]byte, error) })
 | |
| 	b, _ := i.MarshalBinary()
 | |
| 	_, _ = h.Write(b)
 | |
| 	return b == nil
 | |
| }
 | |
| 
 | |
| func hashbytesmethod(h *xxh3.Hasher, a any) bool {
 | |
| 	i := a.(interface{ Bytes() []byte })
 | |
| 	b := i.Bytes()
 | |
| 	_, _ = h.Write(b)
 | |
| 	return b == nil
 | |
| }
 | |
| 
 | |
| func hashstringmethod(h *xxh3.Hasher, a any) bool {
 | |
| 	i := a.(interface{ String() string })
 | |
| 	s := i.String()
 | |
| 	_, _ = h.WriteString(s)
 | |
| 	return s == ""
 | |
| }
 | |
| 
 | |
| func hashtextmarshaler(h *xxh3.Hasher, a any) bool {
 | |
| 	i := a.(interface{ MarshalText() ([]byte, error) })
 | |
| 	b, _ := i.MarshalText()
 | |
| 	_, _ = h.Write(b)
 | |
| 	return b == nil
 | |
| }
 | |
| 
 | |
| func hashjsonmarshaler(h *xxh3.Hasher, a any) bool {
 | |
| 	i := a.(interface{ MarshalJSON() ([]byte, error) })
 | |
| 	b, _ := i.MarshalJSON()
 | |
| 	_, _ = h.Write(b)
 | |
| 	return b == nil
 | |
| }
 |