forked from TrueCloudLab/restic
This skips an allocation. internal/archiver benchmarks, Linux/amd64: name old time/op new time/op delta ArchiverSaveFileSmall-8 3.94ms ± 6% 3.91ms ± 6% ~ (p=0.947 n=20+20) ArchiverSaveFileLarge-8 304ms ± 3% 301ms ± 4% ~ (p=0.265 n=18+18) name old speed new speed delta ArchiverSaveFileSmall-8 1.04MB/s ± 6% 1.05MB/s ± 6% ~ (p=0.803 n=20+20) ArchiverSaveFileLarge-8 142MB/s ± 3% 143MB/s ± 4% ~ (p=0.421 n=18+19) name old alloc/op new alloc/op delta ArchiverSaveFileSmall-8 17.9MB ± 0% 17.9MB ± 0% -0.01% (p=0.000 n=19+19) ArchiverSaveFileLarge-8 382MB ± 2% 382MB ± 1% ~ (p=0.687 n=20+19) name old allocs/op new allocs/op delta ArchiverSaveFileSmall-8 540 ± 1% 528 ± 0% -2.19% (p=0.000 n=19+19) ArchiverSaveFileLarge-8 1.93k ± 3% 1.79k ± 4% -7.06% (p=0.000 n=20+20)
153 lines
2.9 KiB
Go
153 lines
2.9 KiB
Go
package restic
|
|
|
|
import (
|
|
"crypto/rand"
|
|
"encoding/hex"
|
|
"fmt"
|
|
"io"
|
|
|
|
"github.com/restic/restic/internal/errors"
|
|
|
|
"github.com/minio/sha256-simd"
|
|
)
|
|
|
|
// Hash returns the ID for data.
|
|
func Hash(data []byte) ID {
|
|
return sha256.Sum256(data)
|
|
}
|
|
|
|
// idSize contains the size of an ID, in bytes.
|
|
const idSize = sha256.Size
|
|
|
|
// ID references content within a repository.
|
|
type ID [idSize]byte
|
|
|
|
// ParseID converts the given string to an ID.
|
|
func ParseID(s string) (ID, error) {
|
|
b, err := hex.DecodeString(s)
|
|
|
|
if err != nil {
|
|
return ID{}, errors.Wrap(err, "hex.DecodeString")
|
|
}
|
|
|
|
if len(b) != idSize {
|
|
return ID{}, errors.New("invalid length for hash")
|
|
}
|
|
|
|
id := ID{}
|
|
copy(id[:], b)
|
|
|
|
return id, nil
|
|
}
|
|
|
|
func (id ID) String() string {
|
|
return hex.EncodeToString(id[:])
|
|
}
|
|
|
|
// NewRandomID returns a randomly generated ID. When reading from rand fails,
|
|
// the function panics.
|
|
func NewRandomID() ID {
|
|
id := ID{}
|
|
_, err := io.ReadFull(rand.Reader, id[:])
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
return id
|
|
}
|
|
|
|
const shortStr = 4
|
|
|
|
// Str returns the shortened string version of id.
|
|
func (id *ID) Str() string {
|
|
if id == nil {
|
|
return "[nil]"
|
|
}
|
|
|
|
if id.IsNull() {
|
|
return "[null]"
|
|
}
|
|
|
|
return hex.EncodeToString(id[:shortStr])
|
|
}
|
|
|
|
// IsNull returns true iff id only consists of null bytes.
|
|
func (id ID) IsNull() bool {
|
|
var nullID ID
|
|
|
|
return id == nullID
|
|
}
|
|
|
|
// Equal compares an ID to another other.
|
|
func (id ID) Equal(other ID) bool {
|
|
return id == other
|
|
}
|
|
|
|
// EqualString compares this ID to another one, given as a string.
|
|
func (id ID) EqualString(other string) (bool, error) {
|
|
s, err := hex.DecodeString(other)
|
|
if err != nil {
|
|
return false, errors.Wrap(err, "hex.DecodeString")
|
|
}
|
|
|
|
id2 := ID{}
|
|
copy(id2[:], s)
|
|
|
|
return id == id2, nil
|
|
}
|
|
|
|
// MarshalJSON returns the JSON encoding of id.
|
|
func (id ID) MarshalJSON() ([]byte, error) {
|
|
buf := make([]byte, 2+hex.EncodedLen(len(id)))
|
|
|
|
buf[0] = '"'
|
|
hex.Encode(buf[1:], id[:])
|
|
buf[len(buf)-1] = '"'
|
|
|
|
return buf, nil
|
|
}
|
|
|
|
// UnmarshalJSON parses the JSON-encoded data and stores the result in id.
|
|
func (id *ID) UnmarshalJSON(b []byte) error {
|
|
// check string length
|
|
if len(b) < 2 {
|
|
return fmt.Errorf("invalid ID: %q", b)
|
|
}
|
|
|
|
if len(b)%2 != 0 {
|
|
return fmt.Errorf("invalid ID length: %q", b)
|
|
}
|
|
|
|
// check string delimiters
|
|
if b[0] != '"' && b[0] != '\'' {
|
|
return fmt.Errorf("invalid start of string: %q", b[0])
|
|
}
|
|
|
|
last := len(b) - 1
|
|
if b[0] != b[last] {
|
|
return fmt.Errorf("starting string delimiter (%q) does not match end (%q)", b[0], b[last])
|
|
}
|
|
|
|
// strip JSON string delimiters
|
|
b = b[1:last]
|
|
|
|
if len(b) != 2*len(id) {
|
|
return fmt.Errorf("invalid length for ID")
|
|
}
|
|
|
|
_, err := hex.Decode(id[:], b)
|
|
if err != nil {
|
|
return errors.Wrap(err, "hex.Decode")
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// IDFromHash returns the ID for the hash.
|
|
func IDFromHash(hash []byte) (id ID) {
|
|
if len(hash) != idSize {
|
|
panic("invalid hash type, not enough/too many bytes")
|
|
}
|
|
|
|
copy(id[:], hash)
|
|
return id
|
|
}
|