mirror of https://github.com/poanetwork/gecko.git
156 lines
3.8 KiB
Go
156 lines
3.8 KiB
Go
|
// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
|
||
|
// See the file LICENSE for licensing terms.
|
||
|
|
||
|
package ids
|
||
|
|
||
|
import (
|
||
|
"bytes"
|
||
|
"encoding/hex"
|
||
|
"sort"
|
||
|
|
||
|
"github.com/ava-labs/gecko/utils"
|
||
|
"github.com/ava-labs/gecko/utils/formatting"
|
||
|
"github.com/ava-labs/gecko/utils/hashing"
|
||
|
"github.com/ava-labs/gecko/utils/wrappers"
|
||
|
)
|
||
|
|
||
|
// Empty is a useful all zero value
|
||
|
var Empty = ID{ID: &[32]byte{}}
|
||
|
|
||
|
// ID wraps a 32 byte hash as an identifier
|
||
|
// Internal field [ID] should never be modified
|
||
|
// from outside ids package
|
||
|
type ID struct {
|
||
|
ID *[32]byte `serialize:"true"`
|
||
|
}
|
||
|
|
||
|
// NewID creates an identifer from a 32 byte hash
|
||
|
func NewID(id [32]byte) ID { return ID{ID: &id} }
|
||
|
|
||
|
// ToID attempt to convert a byte slice into an id
|
||
|
func ToID(bytes []byte) (ID, error) {
|
||
|
addrHash, err := hashing.ToHash256(bytes)
|
||
|
return NewID(addrHash), err
|
||
|
}
|
||
|
|
||
|
// FromString is the inverse of ID.String()
|
||
|
func FromString(idStr string) (ID, error) {
|
||
|
cb58 := formatting.CB58{}
|
||
|
err := cb58.FromString(idStr)
|
||
|
if err != nil {
|
||
|
return ID{}, err
|
||
|
}
|
||
|
return ToID(cb58.Bytes)
|
||
|
}
|
||
|
|
||
|
// MarshalJSON ...
|
||
|
func (id ID) MarshalJSON() ([]byte, error) {
|
||
|
if id.IsZero() {
|
||
|
return []byte("null"), nil
|
||
|
}
|
||
|
cb58 := formatting.CB58{Bytes: id.ID[:]}
|
||
|
return cb58.MarshalJSON()
|
||
|
}
|
||
|
|
||
|
// UnmarshalJSON ...
|
||
|
func (id *ID) UnmarshalJSON(b []byte) error {
|
||
|
if string(b) == "null" {
|
||
|
return nil
|
||
|
}
|
||
|
cb58 := formatting.CB58{}
|
||
|
if err := cb58.UnmarshalJSON(b); err != nil {
|
||
|
return err
|
||
|
}
|
||
|
newID, err := ToID(cb58.Bytes)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
*id = newID
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// IsZero returns true if the value has not been initialized
|
||
|
func (id ID) IsZero() bool { return id.ID == nil }
|
||
|
|
||
|
// Key returns a 32 byte hash that this id represents. This is useful to allow
|
||
|
// for this id to be used as keys in maps.
|
||
|
func (id ID) Key() [32]byte { return *id.ID }
|
||
|
|
||
|
// Prefix this id to create a more selective id. This can be used to store
|
||
|
// multiple values under the same key. For example:
|
||
|
// prefix1(id) -> confidence
|
||
|
// prefix2(id) -> vertex
|
||
|
// This will return a new id and not modify the original id.
|
||
|
func (id ID) Prefix(prefixes ...uint64) ID {
|
||
|
packer := wrappers.Packer{
|
||
|
Bytes: make([]byte, len(prefixes)*wrappers.LongLen+hashing.HashLen),
|
||
|
}
|
||
|
|
||
|
for _, prefix := range prefixes {
|
||
|
packer.PackLong(prefix)
|
||
|
}
|
||
|
packer.PackFixedBytes(id.Bytes())
|
||
|
|
||
|
return NewID(hashing.ComputeHash256Array(packer.Bytes))
|
||
|
}
|
||
|
|
||
|
// Equals returns true if the ids have the same byte representation
|
||
|
func (id ID) Equals(oID ID) bool {
|
||
|
return id.ID == oID.ID ||
|
||
|
(id.ID != nil && oID.ID != nil && bytes.Equal(id.Bytes(), oID.Bytes()))
|
||
|
}
|
||
|
|
||
|
// Bytes returns the 32 byte hash as a slice. It is assumed this slice is not
|
||
|
// modified.
|
||
|
func (id ID) Bytes() []byte { return id.ID[:] }
|
||
|
|
||
|
// Bit returns the bit value at the ith index of the byte array. Returns 0 or 1
|
||
|
func (id ID) Bit(i uint) int {
|
||
|
byteIndex := i / BitsPerByte
|
||
|
bitIndex := i % BitsPerByte
|
||
|
|
||
|
bytes := id.Bytes()
|
||
|
b := bytes[byteIndex]
|
||
|
|
||
|
// b = [7, 6, 5, 4, 3, 2, 1, 0]
|
||
|
|
||
|
b = b >> bitIndex
|
||
|
|
||
|
// b = [0, ..., bitIndex + 1, bitIndex]
|
||
|
// 1 = [0, 0, 0, 0, 0, 0, 0, 1]
|
||
|
|
||
|
b = b & 1
|
||
|
|
||
|
// b = [0, 0, 0, 0, 0, 0, 0, bitIndex]
|
||
|
|
||
|
return int(b)
|
||
|
}
|
||
|
|
||
|
// Hex returns a hex encoded string of this id.
|
||
|
func (id ID) Hex() string { return hex.EncodeToString(id.Bytes()) }
|
||
|
|
||
|
func (id ID) String() string {
|
||
|
if id.IsZero() {
|
||
|
return "nil"
|
||
|
}
|
||
|
bytes := id.Bytes()
|
||
|
cb58 := formatting.CB58{Bytes: bytes}
|
||
|
return cb58.String()
|
||
|
}
|
||
|
|
||
|
type sortIDData []ID
|
||
|
|
||
|
func (ids sortIDData) Less(i, j int) bool {
|
||
|
return bytes.Compare(
|
||
|
ids[i].Bytes(),
|
||
|
ids[j].Bytes()) == -1
|
||
|
}
|
||
|
func (ids sortIDData) Len() int { return len(ids) }
|
||
|
func (ids sortIDData) Swap(i, j int) { ids[j], ids[i] = ids[i], ids[j] }
|
||
|
|
||
|
// SortIDs sorts the ids lexicographically
|
||
|
func SortIDs(ids []ID) { sort.Sort(sortIDData(ids)) }
|
||
|
|
||
|
// IsSortedAndUniqueIDs returns true if the ids are sorted and unique
|
||
|
func IsSortedAndUniqueIDs(ids []ID) bool { return utils.IsSortedAndUnique(sortIDData(ids)) }
|