digests.go 2.2 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788
  1. package continuity
  2. import (
  3. "fmt"
  4. "io"
  5. "sort"
  6. "github.com/opencontainers/go-digest"
  7. )
  8. // Digester produces a digest for a given read stream
  9. type Digester interface {
  10. Digest(io.Reader) (digest.Digest, error)
  11. }
  12. // ContentProvider produces a read stream for a given digest
  13. type ContentProvider interface {
  14. Reader(digest.Digest) (io.ReadCloser, error)
  15. }
  16. type simpleDigester struct {
  17. algorithm digest.Algorithm
  18. }
  19. func (sd simpleDigester) Digest(r io.Reader) (digest.Digest, error) {
  20. digester := sd.algorithm.Digester()
  21. if _, err := io.Copy(digester.Hash(), r); err != nil {
  22. return "", err
  23. }
  24. return digester.Digest(), nil
  25. }
  26. // uniqifyDigests sorts and uniqifies the provided digest, ensuring that the
  27. // digests are not repeated and no two digests with the same algorithm have
  28. // different values. Because a stable sort is used, this has the effect of
  29. // "zipping" digest collections from multiple resources.
  30. func uniqifyDigests(digests ...digest.Digest) ([]digest.Digest, error) {
  31. sort.Stable(digestSlice(digests)) // stable sort is important for the behavior here.
  32. seen := map[digest.Digest]struct{}{}
  33. algs := map[digest.Algorithm][]digest.Digest{} // detect different digests.
  34. var out []digest.Digest
  35. // uniqify the digests
  36. for _, d := range digests {
  37. if _, ok := seen[d]; ok {
  38. continue
  39. }
  40. seen[d] = struct{}{}
  41. algs[d.Algorithm()] = append(algs[d.Algorithm()], d)
  42. if len(algs[d.Algorithm()]) > 1 {
  43. return nil, fmt.Errorf("conflicting digests for %v found", d.Algorithm())
  44. }
  45. out = append(out, d)
  46. }
  47. return out, nil
  48. }
  49. // digestsMatch compares the two sets of digests to see if they match.
  50. func digestsMatch(as, bs []digest.Digest) bool {
  51. all := append(as, bs...)
  52. uniqified, err := uniqifyDigests(all...)
  53. if err != nil {
  54. // the only error uniqifyDigests returns is when the digests disagree.
  55. return false
  56. }
  57. disjoint := len(as) + len(bs)
  58. if len(uniqified) == disjoint {
  59. // if these two sets have the same cardinality, we know both sides
  60. // didn't share any digests.
  61. return false
  62. }
  63. return true
  64. }
  65. type digestSlice []digest.Digest
  66. func (p digestSlice) Len() int { return len(p) }
  67. func (p digestSlice) Less(i, j int) bool { return p[i] < p[j] }
  68. func (p digestSlice) Swap(i, j int) { p[i], p[j] = p[j], p[i] }