Add function to generate short refs for paths (#845)
Currently using 12 hex characters of a sha256 sum. This is expected to start giving collisions after about 16.7M paths hashed. Short refs are stable in the sense that calling ShortRef multiple times on the same path will yield the same result. They can be used to identify unique paths if desired, but extra lookup functionality will be needed because short refs cannot be directly transformed back into a path.
This commit is contained in:
parent
f2b287498b
commit
8b83a728ef
@ -36,6 +36,9 @@
|
||||
package path
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/sha256"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
@ -46,6 +49,8 @@ const templateErrPathParsing = "parsing resource path from %s"
|
||||
const (
|
||||
escapeCharacter = '\\'
|
||||
pathSeparator = '/'
|
||||
|
||||
shortRefCharacters = 12
|
||||
)
|
||||
|
||||
var charactersToEscape = map[rune]struct{}{
|
||||
@ -82,6 +87,10 @@ type Path interface {
|
||||
// the old Path if possible. If the old Path is an item Path then Append
|
||||
// returns an error.
|
||||
Append(element string, isItem bool) (Path, error)
|
||||
// ShortRef returns a short reference representing this path. The short
|
||||
// reference is guaranteed to be unique. No guarantees are made about whether
|
||||
// a short reference can be converted back into the Path that generated it.
|
||||
ShortRef() string
|
||||
}
|
||||
|
||||
// Builder is a simple path representation that only tracks path elements. It
|
||||
@ -188,6 +197,25 @@ func (pb Builder) String() string {
|
||||
return join(escaped)
|
||||
}
|
||||
|
||||
func (pb Builder) ShortRef() string {
|
||||
data := bytes.Buffer{}
|
||||
|
||||
for _, element := range pb.elements {
|
||||
data.WriteString(element)
|
||||
}
|
||||
|
||||
sum := sha256.Sum256(data.Bytes())
|
||||
|
||||
// Some conversions to get the right number of characters in the output. This
|
||||
// outputs hex, so we need to take the target number of characters and do the
|
||||
// equivalent of (shortRefCharacters * 4) / 8. This is
|
||||
// <number of bits represented> / <bits per byte> which gets us how many bytes
|
||||
// to give to our format command.
|
||||
numBytes := shortRefCharacters / 2
|
||||
|
||||
return fmt.Sprintf("%x", sum[:numBytes])
|
||||
}
|
||||
|
||||
// Elements returns all the elements in the path. This is a temporary function
|
||||
// and will likely be updated to handle encoded elements instead of clear-text
|
||||
// elements in the future.
|
||||
|
||||
@ -365,6 +365,35 @@ func (suite *PathUnitSuite) TestPopFront() {
|
||||
}
|
||||
}
|
||||
|
||||
func (suite *PathUnitSuite) TestShortRef() {
|
||||
pb := Builder{}.Append("this", "is", "a", "path")
|
||||
ref := pb.ShortRef()
|
||||
assert.Len(suite.T(), ref, shortRefCharacters)
|
||||
}
|
||||
|
||||
func (suite *PathUnitSuite) TestShortRefIsStable() {
|
||||
t := suite.T()
|
||||
pb := Builder{}.Append("this", "is", "a", "path")
|
||||
prevRef := pb.ShortRef()
|
||||
assert.Len(t, prevRef, shortRefCharacters)
|
||||
|
||||
for i := 0; i < 5; i++ {
|
||||
ref := pb.ShortRef()
|
||||
assert.Len(t, ref, shortRefCharacters)
|
||||
assert.Equal(t, prevRef, ref, "ShortRef changed between calls")
|
||||
|
||||
prevRef = ref
|
||||
}
|
||||
}
|
||||
|
||||
func (suite *PathUnitSuite) TestShortRefIsUnique() {
|
||||
pb1 := Builder{}.Append("this", "is", "a", "path")
|
||||
pb2 := pb1.Append("also")
|
||||
|
||||
require.NotEqual(suite.T(), pb1, pb2)
|
||||
assert.NotEqual(suite.T(), pb1.ShortRef(), pb2.ShortRef())
|
||||
}
|
||||
|
||||
func (suite *PathUnitSuite) TestFromStringErrors() {
|
||||
table := []struct {
|
||||
name string
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user