-
Notifications
You must be signed in to change notification settings - Fork 59
/
Copy pathregistry.go
123 lines (113 loc) · 5.06 KB
/
registry.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
package multihash
import (
"crypto/md5"
"crypto/sha1"
"crypto/sha256"
"crypto/sha512"
"fmt"
"hash"
)
// registry is a simple map which maps a multihash indicator number
// to a function : (size:int) -> ((hasher:hash.Hash), (bool:success))
// The function may error (i.e., return (nil, false)) to signify that the hasher can't return that many bytes.
//
// Multihash indicator numbers are reserved and described in
// https://github.com/multiformats/multicodec/blob/master/table.csv .
// The keys used in this map must match those reservations.
//
// Hashers which are available in the golang stdlib will be registered automatically.
// Others can be added using the Register function.
var registry = make(map[uint64]func(int) (h hash.Hash, ok bool))
// Register adds a new hash to the set available from GetHasher and Sum.
//
// Register has a global effect and should only be used at package init time to avoid data races.
//
// The indicator code should be per the numbers reserved and described in
// https://github.com/multiformats/multicodec/blob/master/table.csv .
//
// If Register is called with the same indicator code more than once, the last call wins.
// In practice, this means that if an application has a strong opinion about what implementation to use for a certain hash
// (e.g., perhaps they want to override the sha256 implementation to use a special hand-rolled assembly variant
// rather than the stdlib one which is registered by default),
// then this can be done by making a Register call with that effect at init time in the application's main package.
// This should have the desired effect because the root of the import tree has its init time effect last.
func Register(indicator uint64, hasherFactory func() hash.Hash) {
if hasherFactory == nil {
panic("not sensible to attempt to register a nil function")
}
maxSize := hasherFactory().Size()
registry[indicator] = func(size int) (hash.Hash, bool) {
if size > maxSize {
return nil, false
}
return hasherFactory(), true
}
DefaultLengths[indicator] = maxSize
}
// RegisterVariableSize is like Register, but adds a new variable-sized hasher factory that takes a
// size hint.
//
// When passed -1, the hasher should produce digests with the hash-function's default length. When
// passed a non-negative integer, the hasher should try to produce digests of at least the specified
// size.
func RegisterVariableSize(indicator uint64, hasherFactory func(sizeHint int) (hash.Hash, bool)) {
if hasherFactory == nil {
panic("not sensible to attempt to register a nil function")
}
if hasher, ok := hasherFactory(-1); !ok {
panic("failed to determine default hash length for hasher")
} else {
DefaultLengths[indicator] = hasher.Size()
}
registry[indicator] = hasherFactory
}
// GetHasher returns a new hash.Hash according to the indicator code number provided.
//
// The indicator code should be per the numbers reserved and described in
// https://github.com/multiformats/multicodec/blob/master/table.csv .
//
// The actual hashers available are determined by what has been registered.
// The registry automatically contains those hashers which are available in the golang standard libraries
// (which includes md5, sha1, sha256, sha384, sha512, and the "identity" multihash, among others).
// Other hash implementations can be made available by using the Register function.
// The 'go-multihash/register/*' packages can also be imported to gain more common hash functions.
//
// If an error is returned, it will match `errors.Is(err, ErrSumNotSupported)`.
func GetHasher(indicator uint64) (hash.Hash, error) {
return GetVariableHasher(indicator, -1)
}
// GetVariableHasher returns a new hash.Hash according to the indicator code number provided, with
// the specified size hint.
//
// NOTE: The size hint is only a hint. Hashers will attempt to produce at least the number of requested bytes, but may not.
//
// This function can fail if either the hash code is not registered, or the passed size hint is
// statically incompatible with the specified hash function.
func GetVariableHasher(indicator uint64, sizeHint int) (hash.Hash, error) {
factory, exists := registry[indicator]
if !exists {
return nil, fmt.Errorf("unknown multihash code %d (0x%x): %w", indicator, indicator, ErrSumNotSupported)
}
hasher, ok := factory(sizeHint)
if !ok {
return nil, ErrLenTooLarge
}
return hasher, nil
}
// DefaultLengths maps a multihash indicator code to the output size for that hash, in units of bytes.
//
// This map is populated when a hash function is registered by the Register function.
// It's effectively a shortcut for asking Size() on the hash.Hash.
var DefaultLengths = map[uint64]int{}
func init() {
RegisterVariableSize(IDENTITY, func(_ int) (hash.Hash, bool) { return &identityMultihash{}, true })
Register(MD5, md5.New)
Register(SHA1, sha1.New)
Register(SHA2_224, sha256.New224)
Register(SHA2_256, sha256.New)
Register(SHA2_384, sha512.New384)
Register(SHA2_512, sha512.New)
Register(SHA2_512_224, sha512.New512_224)
Register(SHA2_512_256, sha512.New512_256)
Register(DBL_SHA2_256, func() hash.Hash { return &doubleSha256{sha256.New()} })
}