experimental/digest.go (83 lines of code) (raw):
package experimental
import (
"bytes"
"crypto/md5"
"crypto/sha256"
"encoding/hex"
"fmt"
"path/filepath"
)
const digestAlgorithm = "sha256"
const digestReferenceAlgorithm = "sha256:"
var digestEmpty [sha256.Size]byte
const digestReferenceSize = int64(len(digestReferenceAlgorithm) + sha256.Size*2)
type digest struct {
hash [sha256.Size]byte
}
func newDigestFromPath(components []string) (d digest, err error) {
if len(components) != 2 {
return digest{}, fmt.Errorf("digest components should contain exactly two items: %v", components)
}
if components[0] != digestAlgorithm {
return digest{}, fmt.Errorf("only %v is supported: %v", digestAlgorithm, components[0])
}
err = d.decode([]byte(components[1]))
return
}
func newDigestFromScopedPath(components []string) (d digest, err error) {
if len(components) != 3 {
return digest{}, fmt.Errorf("digest components should contain exactly three items: %v", components)
}
if components[0] != digestAlgorithm {
return digest{}, fmt.Errorf("only %v is supported: %v", digestAlgorithm, components[0])
}
if components[1] != components[2][0:2] {
return digest{}, fmt.Errorf("digest needs to be prefixed with %v: %v", components[2][0:2], components)
}
err = d.decode([]byte(components[2]))
if err != nil {
return
}
return
}
func newDigestFromReference(data []byte) (d digest, err error) {
if !bytes.HasPrefix(data, []byte(digestReferenceAlgorithm)) {
return digest{}, fmt.Errorf("digest reference should start with: %v, but was: %v", digestReferenceAlgorithm, data)
}
err = d.decode(data[len(digestReferenceAlgorithm):])
return
}
func (d *digest) decode(data []byte) error {
n, err := hex.Decode(d.hash[:], data)
if err != nil {
return err
}
if n != sha256.Size {
return fmt.Errorf("component should be valid %v, but was: %v", digestAlgorithm, data)
}
return nil
}
func (d *digest) hexHash() string {
return hex.EncodeToString(d.hash[:])
}
func (d *digest) path() string {
return filepath.Join(digestAlgorithm, d.hexHash())
}
func (d *digest) scopedPath() string {
hex := d.hexHash()
return filepath.Join(digestAlgorithm, hex[0:2], hex)
}
func (d *digest) reference() []byte {
return []byte(digestReferenceAlgorithm + d.hexHash())
}
func (d *digest) etag() string {
md5sum := md5.Sum(d.reference())
hex := hex.EncodeToString(md5sum[:])
return "\"" + hex + "\""
}
func (d digest) String() string {
return d.hexHash()
}
func (d *digest) valid() bool {
return !bytes.Equal(d.hash[:], digestEmpty[:])
}