traefik/vendor/github.com/containerd/continuity/digests.go

89 lines
2.2 KiB
Go
Raw Normal View History

2018-01-22 11:16:03 +00:00
package continuity
import (
"fmt"
"io"
"sort"
"github.com/opencontainers/go-digest"
)
// Digester produces a digest for a given read stream
type Digester interface {
Digest(io.Reader) (digest.Digest, error)
}
// ContentProvider produces a read stream for a given digest
type ContentProvider interface {
Reader(digest.Digest) (io.ReadCloser, error)
}
type simpleDigester struct {
algorithm digest.Algorithm
}
func (sd simpleDigester) Digest(r io.Reader) (digest.Digest, error) {
digester := sd.algorithm.Digester()
if _, err := io.Copy(digester.Hash(), r); err != nil {
return "", err
}
return digester.Digest(), nil
}
// uniqifyDigests sorts and uniqifies the provided digest, ensuring that the
// digests are not repeated and no two digests with the same algorithm have
// different values. Because a stable sort is used, this has the effect of
// "zipping" digest collections from multiple resources.
func uniqifyDigests(digests ...digest.Digest) ([]digest.Digest, error) {
sort.Stable(digestSlice(digests)) // stable sort is important for the behavior here.
seen := map[digest.Digest]struct{}{}
algs := map[digest.Algorithm][]digest.Digest{} // detect different digests.
var out []digest.Digest
// uniqify the digests
for _, d := range digests {
if _, ok := seen[d]; ok {
continue
}
seen[d] = struct{}{}
algs[d.Algorithm()] = append(algs[d.Algorithm()], d)
if len(algs[d.Algorithm()]) > 1 {
return nil, fmt.Errorf("conflicting digests for %v found", d.Algorithm())
}
out = append(out, d)
}
return out, nil
}
// digestsMatch compares the two sets of digests to see if they match.
func digestsMatch(as, bs []digest.Digest) bool {
all := append(as, bs...)
uniqified, err := uniqifyDigests(all...)
if err != nil {
// the only error uniqifyDigests returns is when the digests disagree.
return false
}
disjoint := len(as) + len(bs)
if len(uniqified) == disjoint {
// if these two sets have the same cardinality, we know both sides
// didn't share any digests.
return false
}
return true
}
type digestSlice []digest.Digest
func (p digestSlice) Len() int { return len(p) }
func (p digestSlice) Less(i, j int) bool { return p[i] < p[j] }
func (p digestSlice) Swap(i, j int) { p[i], p[j] = p[j], p[i] }