-
Notifications
You must be signed in to change notification settings - Fork 32
/
short.go
124 lines (102 loc) · 3.28 KB
/
short.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
// Copyright (C) 2019-2021, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package ids
import (
"bytes"
"encoding/hex"
"fmt"
"sort"
"strings"
"github.com/flare-foundation/flare/utils/formatting"
"github.com/flare-foundation/flare/utils/hashing"
)
// ShortEmpty is a useful all zero value
var ShortEmpty = ShortID{}
// ShortID wraps a 20 byte hash as an identifier
type ShortID [20]byte
// ToShortID attempt to convert a byte slice into an id
func ToShortID(bytes []byte) (ShortID, error) {
return hashing.ToHash160(bytes)
}
// ShortFromString is the inverse of ShortID.String()
func ShortFromString(idStr string) (ShortID, error) {
bytes, err := formatting.Decode(defaultEncoding, idStr)
if err != nil {
return ShortID{}, err
}
return ToShortID(bytes)
}
// ShortFromPrefixedString returns a ShortID assuming the cb58 format is
// prefixed
func ShortFromPrefixedString(idStr, prefix string) (ShortID, error) {
if !strings.HasPrefix(idStr, prefix) {
return ShortID{}, fmt.Errorf("ID: %s is missing the prefix: %s", idStr, prefix)
}
return ShortFromString(strings.TrimPrefix(idStr, prefix))
}
func (id ShortID) MarshalJSON() ([]byte, error) {
str, err := formatting.EncodeWithChecksum(defaultEncoding, id[:])
if err != nil {
return nil, err
}
return []byte("\"" + str + "\""), nil
}
func (id *ShortID) UnmarshalJSON(b []byte) error {
str := string(b)
if str == "null" { // If "null", do nothing
return nil
} else if len(str) < 2 {
return errMissingQuotes
}
lastIndex := len(str) - 1
if str[0] != '"' || str[lastIndex] != '"' {
return errMissingQuotes
}
// Parse CB58 formatted string to bytes
bytes, err := formatting.Decode(defaultEncoding, str[1:lastIndex])
if err != nil {
return fmt.Errorf("couldn't decode ID to bytes: %w", err)
}
*id, err = ToShortID(bytes)
return err
}
// Bytes returns the 20 byte hash as a slice. It is assumed this slice is not
// modified.
func (id ShortID) Bytes() []byte { return id[:] }
// Hex returns a hex encoded string of this id.
func (id ShortID) Hex() string { return hex.EncodeToString(id.Bytes()) }
func (id ShortID) String() string {
// We assume that the maximum size of a byte slice that
// can be stringified is at least the length of an ID
str, _ := formatting.EncodeWithChecksum(defaultEncoding, id.Bytes())
return str
}
// PrefixedString returns the String representation with a prefix added
func (id ShortID) PrefixedString(prefix string) string {
return prefix + id.String()
}
type sortShortIDData []ShortID
func (ids sortShortIDData) Less(i, j int) bool {
return bytes.Compare(
ids[i].Bytes(),
ids[j].Bytes()) == -1
}
func (ids sortShortIDData) Len() int { return len(ids) }
func (ids sortShortIDData) Swap(i, j int) { ids[j], ids[i] = ids[i], ids[j] }
// SortShortIDs sorts the ids lexicographically
func SortShortIDs(ids []ShortID) { sort.Sort(sortShortIDData(ids)) }
// IsSortedAndUniqueShortIDs returns true if the ids are sorted and unique
func IsSortedAndUniqueShortIDs(ids []ShortID) bool {
for i := 0; i < len(ids)-1; i++ {
if bytes.Compare(ids[i].Bytes(), ids[i+1].Bytes()) != -1 {
return false
}
}
return true
}
// IsUniqueShortIDs returns true iff [ids] are unique
func IsUniqueShortIDs(ids []ShortID) bool {
set := ShortSet{}
set.Add(ids...)
return set.Len() == len(ids)
}