|
| 1 | +package multihash |
| 2 | + |
| 3 | +import ( |
| 4 | + "crypto/md5" |
| 5 | + "crypto/sha1" |
| 6 | + "crypto/sha256" |
| 7 | + "crypto/sha512" |
| 8 | + "fmt" |
| 9 | + "hash" |
| 10 | +) |
| 11 | + |
| 12 | +// registry is a simple map which maps a multihash indicator number |
| 13 | +// to a standard golang Hash interface. |
| 14 | +// |
| 15 | +// Multihash indicator numbers are reserved and described in |
| 16 | +// https://github.com/multiformats/multicodec/blob/master/table.csv . |
| 17 | +// The keys used in this map must match those reservations. |
| 18 | +// |
| 19 | +// Hashers which are available in the golang stdlib will be registered automatically. |
| 20 | +// Others can be added using the Register function. |
| 21 | +var registry = make(map[uint64]func() hash.Hash) |
| 22 | + |
| 23 | +// Register adds a new hash to the set available from GetHasher and Sum. |
| 24 | +// |
| 25 | +// Register has a global effect and should only be used at package init time to avoid data races. |
| 26 | +// |
| 27 | +// The indicator code should be per the numbers reserved and described in |
| 28 | +// https://github.com/multiformats/multicodec/blob/master/table.csv . |
| 29 | +// |
| 30 | +// If Register is called with the same indicator code more than once, the last call wins. |
| 31 | +// In practice, this means that if an application has a strong opinion about what implementation to use for a certain hash |
| 32 | +// (e.g., perhaps they want to override the sha256 implementation to use a special hand-rolled assembly variant |
| 33 | +// rather than the stdlib one which is registered by default), |
| 34 | +// then this can be done by making a Register call with that effect at init time in the application's main package. |
| 35 | +// This should have the desired effect because the root of the import tree has its init time effect last. |
| 36 | +func Register(indicator uint64, hasherFactory func() hash.Hash) { |
| 37 | + if hasherFactory == nil { |
| 38 | + panic("not sensible to attempt to register a nil function") |
| 39 | + } |
| 40 | + registry[indicator] = hasherFactory |
| 41 | + DefaultLengths[indicator] = hasherFactory().Size() |
| 42 | +} |
| 43 | + |
| 44 | +// GetHasher returns a new hash.Hash according to the indicator code number provided. |
| 45 | +// |
| 46 | +// The indicator code should be per the numbers reserved and described in |
| 47 | +// https://github.com/multiformats/multicodec/blob/master/table.csv . |
| 48 | +// |
| 49 | +// The actual hashers available are determined by what has been registered. |
| 50 | +// The registry automatically contains those hashers which are available in the golang standard libraries |
| 51 | +// (which includes md5, sha1, sha256, sha384, sha512, and the "identity" mulithash, among others). |
| 52 | +// Other hash implementations can be made available by using the Register function. |
| 53 | +// The 'go-mulithash/register/*' packages can also be imported to gain more common hash functions. |
| 54 | +// |
| 55 | +// If an error is returned, it will match `errors.Is(err, ErrSumNotSupported)`. |
| 56 | +func GetHasher(indicator uint64) (hash.Hash, error) { |
| 57 | + factory, exists := registry[indicator] |
| 58 | + if !exists { |
| 59 | + return nil, fmt.Errorf("unknown multihash code %d (0x%x): %w", indicator, indicator, ErrSumNotSupported) |
| 60 | + } |
| 61 | + return factory(), nil |
| 62 | +} |
| 63 | + |
| 64 | +// DefaultLengths maps a multihash indicator code to the output size for that hash, in units of bytes. |
| 65 | +// |
| 66 | +// This map is populated when a hash function is registered by the Register function. |
| 67 | +// It's effectively a shortcut for asking Size() on the hash.Hash. |
| 68 | +var DefaultLengths = map[uint64]int{} |
| 69 | + |
| 70 | +func init() { |
| 71 | + Register(IDENTITY, func() hash.Hash { return &identityMultihash{} }) |
| 72 | + Register(MD5, md5.New) |
| 73 | + Register(SHA1, sha1.New) |
| 74 | + Register(SHA2_256, sha256.New) |
| 75 | + Register(SHA2_512, sha512.New) |
| 76 | + Register(DBL_SHA2_256, func() hash.Hash { return &doubleSha256{sha256.New()} }) |
| 77 | +} |
0 commit comments