169 lines
2.8 KiB
Go
169 lines
2.8 KiB
Go
package types
|
|
|
|
import (
|
|
"database/sql/driver"
|
|
"encoding/binary"
|
|
"errors"
|
|
fasthex "github.com/tmthrgd/go-hex"
|
|
"runtime"
|
|
"unsafe"
|
|
)
|
|
|
|
const HashSize = 32
|
|
|
|
type Hash [HashSize]byte
|
|
|
|
var ZeroHash Hash
|
|
|
|
func (h Hash) MarshalJSON() ([]byte, error) {
|
|
var buf [HashSize*2 + 2]byte
|
|
buf[0] = '"'
|
|
buf[HashSize*2+1] = '"'
|
|
fasthex.Encode(buf[1:], h[:])
|
|
return buf[:], nil
|
|
}
|
|
|
|
func MustHashFromString(s string) Hash {
|
|
if h, err := HashFromString(s); err != nil {
|
|
panic(err)
|
|
} else {
|
|
return h
|
|
}
|
|
}
|
|
|
|
func HashFromString(s string) (Hash, error) {
|
|
var h Hash
|
|
if buf, err := fasthex.DecodeString(s); err != nil {
|
|
return h, err
|
|
} else {
|
|
if len(buf) != HashSize {
|
|
return h, errors.New("wrong hash size")
|
|
}
|
|
copy(h[:], buf)
|
|
return h, nil
|
|
}
|
|
}
|
|
|
|
func HashFromBytes(buf []byte) (h Hash) {
|
|
if len(buf) != HashSize {
|
|
return
|
|
}
|
|
copy(h[:], buf)
|
|
return
|
|
}
|
|
|
|
// Compare consensus way of comparison
|
|
func (h Hash) Compare(other Hash) int {
|
|
//golang might free other otherwise
|
|
defer runtime.KeepAlive(other)
|
|
defer runtime.KeepAlive(h)
|
|
a := unsafe.Slice((*uint64)(unsafe.Pointer(&h)), len(h)/int(unsafe.Sizeof(uint64(0))))
|
|
b := unsafe.Slice((*uint64)(unsafe.Pointer(&other)), len(other)/int(unsafe.Sizeof(uint64(0))))
|
|
|
|
if a[3] < b[3] {
|
|
return -1
|
|
}
|
|
if a[3] > b[3] {
|
|
return 1
|
|
}
|
|
|
|
if a[2] < b[2] {
|
|
return -1
|
|
}
|
|
if a[2] > b[2] {
|
|
return 1
|
|
}
|
|
|
|
if a[1] < b[1] {
|
|
return -1
|
|
}
|
|
if a[1] > b[1] {
|
|
return 1
|
|
}
|
|
|
|
if a[0] < b[0] {
|
|
return -1
|
|
}
|
|
if a[0] > b[0] {
|
|
return 1
|
|
}
|
|
|
|
return 0
|
|
}
|
|
|
|
func (h Hash) String() string {
|
|
return fasthex.EncodeToString(h[:])
|
|
}
|
|
|
|
func (h Hash) Uint64() uint64 {
|
|
return binary.LittleEndian.Uint64(h[:])
|
|
}
|
|
|
|
func (h *Hash) Scan(src any) error {
|
|
if src == nil {
|
|
return nil
|
|
} else if buf, ok := src.([]byte); ok {
|
|
if len(buf) == 0 {
|
|
return nil
|
|
}
|
|
if len(buf) != HashSize {
|
|
return errors.New("invalid hash size")
|
|
}
|
|
copy((*h)[:], buf)
|
|
|
|
return nil
|
|
}
|
|
return errors.New("invalid type")
|
|
}
|
|
|
|
func (h *Hash) Value() (driver.Value, error) {
|
|
if *h == ZeroHash {
|
|
return nil, nil
|
|
}
|
|
return (*h)[:], nil
|
|
}
|
|
|
|
func (h *Hash) UnmarshalJSON(b []byte) error {
|
|
if len(b) == 0 || len(b) == 2 {
|
|
return nil
|
|
}
|
|
|
|
if len(b) != HashSize*2+2 {
|
|
return errors.New("wrong hash size")
|
|
}
|
|
|
|
if _, err := fasthex.Decode(h[:], b[1:len(b)-1]); err != nil {
|
|
return err
|
|
} else {
|
|
return nil
|
|
}
|
|
}
|
|
|
|
type Bytes []byte
|
|
|
|
func (b Bytes) MarshalJSON() ([]byte, error) {
|
|
buf := make([]byte, len(b)*2+2)
|
|
buf[0] = '"'
|
|
buf[len(buf)-1] = '"'
|
|
fasthex.Encode(buf[1:], b)
|
|
return buf, nil
|
|
}
|
|
|
|
func (b Bytes) String() string {
|
|
return fasthex.EncodeToString(b)
|
|
}
|
|
|
|
func (b *Bytes) UnmarshalJSON(buf []byte) error {
|
|
if len(buf) < 2 || (len(buf)%2) != 0 || buf[0] != '"' || buf[len(buf)-1] != '"' {
|
|
return errors.New("invalid bytes")
|
|
}
|
|
|
|
*b = make(Bytes, (len(buf)-2)/2)
|
|
|
|
if _, err := fasthex.Decode(*b, buf[1:len(buf)-1]); err != nil {
|
|
return err
|
|
} else {
|
|
return nil
|
|
}
|
|
}
|