mirror of https://github.com/perkeep/perkeep.git
Merge "pkg/blob: add ref.HasPrefix method"
This commit is contained in:
commit
7bbda1509c
|
@ -66,6 +66,7 @@ type digestType interface {
|
||||||
digestName() string
|
digestName() string
|
||||||
newHash() hash.Hash
|
newHash() hash.Hash
|
||||||
equalString(string) bool
|
equalString(string) bool
|
||||||
|
hasPrefix(string) bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r Ref) String() string {
|
func (r Ref) String() string {
|
||||||
|
@ -97,6 +98,12 @@ func (r Ref) StringMinusOne() string {
|
||||||
// It does not allocate.
|
// It does not allocate.
|
||||||
func (r Ref) EqualString(s string) bool { return r.digest.equalString(s) }
|
func (r Ref) EqualString(s string) bool { return r.digest.equalString(s) }
|
||||||
|
|
||||||
|
// HasPrefix reports whether s is a prefix of r.String(). It returns false if s
|
||||||
|
// does not contain at least the digest name prefix (e.g. "sha1-") and one byte of
|
||||||
|
// digest.
|
||||||
|
// It does not allocate.
|
||||||
|
func (r Ref) HasPrefix(s string) bool { return r.digest.hasPrefix(s) }
|
||||||
|
|
||||||
func (r Ref) appendString(buf []byte) []byte {
|
func (r Ref) appendString(buf []byte) []byte {
|
||||||
dname := r.digest.digestName()
|
dname := r.digest.digestName()
|
||||||
bs := r.digest.bytes()
|
bs := r.digest.bytes()
|
||||||
|
@ -425,6 +432,40 @@ func (d sha1Digest) equalString(s string) bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (d sha1Digest) hasPrefix(s string) bool {
|
||||||
|
if len(s) > 45 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if len(s) == 45 {
|
||||||
|
return d.equalString(s)
|
||||||
|
}
|
||||||
|
if !strings.HasPrefix(s, "sha1-") {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
s = s[len("sha1-"):]
|
||||||
|
if len(s) == 0 {
|
||||||
|
// we want at least one digest char to match on
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for i, b := range d[:] {
|
||||||
|
even := i * 2
|
||||||
|
if even == len(s) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if s[even] != hexDigit[b>>4] {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
odd := i*2 + 1
|
||||||
|
if odd == len(s) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if s[odd] != hexDigit[b&0xf] {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
const maxOtherDigestLen = 128
|
const maxOtherDigestLen = 128
|
||||||
|
|
||||||
type otherDigest struct {
|
type otherDigest struct {
|
||||||
|
@ -460,6 +501,44 @@ func (d otherDigest) equalString(s string) bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (d otherDigest) hasPrefix(s string) bool {
|
||||||
|
maxLen := len(d.name) + len("-") + 2*d.sumLen
|
||||||
|
if d.odd {
|
||||||
|
maxLen--
|
||||||
|
}
|
||||||
|
if len(s) > maxLen || !strings.HasPrefix(s, d.name) || s[len(d.name)] != '-' {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if len(s) == maxLen {
|
||||||
|
return d.equalString(s)
|
||||||
|
}
|
||||||
|
s = s[len(d.name)+1:]
|
||||||
|
if len(s) == 0 {
|
||||||
|
// we want at least one digest char to match on
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for i, b := range d.sum[:d.sumLen] {
|
||||||
|
even := i * 2
|
||||||
|
if even == len(s) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if s[even] != hexDigit[b>>4] {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
odd := i*2 + 1
|
||||||
|
if odd == len(s) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if i == d.sumLen-1 && d.odd {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if s[odd] != hexDigit[b&0xf] {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
var sha1Meta = &digestMeta{
|
var sha1Meta = &digestMeta{
|
||||||
ctor: sha1FromBinary,
|
ctor: sha1FromBinary,
|
||||||
ctors: sha1FromHexString,
|
ctors: sha1FromHexString,
|
||||||
|
|
|
@ -334,3 +334,63 @@ func BenchmarkEqualString(b *testing.B) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var hasPrefixTests = []struct {
|
||||||
|
ref Ref
|
||||||
|
str string
|
||||||
|
want bool
|
||||||
|
}{
|
||||||
|
{MustParse("sha1-ce284c167558a9ef22df04390c87a6d0c9ed9659"), "sha1-ce284c167558a9ef22df04390c87a6d0c9ed9659", true},
|
||||||
|
{MustParse("sha1-ce284c167558a9ef22df04390c87a6d0c9ed9659"), "sha1-ce284c167558a9ef22df04390c87a6d0c9ed", true},
|
||||||
|
{MustParse("sha1-ce284c167558a9ef22df04390c87a6d0c9ed9659"), "sha1-ce284c167558a9ef22df04390c87a6d0c9e", true},
|
||||||
|
// last digit wrong:
|
||||||
|
{MustParse("sha1-ce284c167558a9ef22df04390c87a6d0c9ed9659"), "sha1-ce284c167558a9ef22df04390c87a6d0c9ee", false},
|
||||||
|
// second to last digit wrong:
|
||||||
|
{MustParse("sha1-ce284c167558a9ef22df04390c87a6d0c9ed9659"), "sha1-ce284c167558a9ef22df04390c87a6d0c9f", false},
|
||||||
|
// hyphen wrong:
|
||||||
|
{MustParse("sha1-ce284c167558a9ef22df04390c87a6d0c9ed9659"), "sha1xce284c167558a9ef22df04390c87a6d0c9ed", false},
|
||||||
|
// truncated:
|
||||||
|
{MustParse("sha1-ce284c167558a9ef22df04390c87a6d0c9ed9659"), "sha1-c", true},
|
||||||
|
{MustParse("sha1-ce284c167558a9ef22df04390c87a6d0c9ed9659"), "sha1-", false},
|
||||||
|
{MustParse("sha1-ce284c167558a9ef22df04390c87a6d0c9ed9659"), "sha1", false},
|
||||||
|
{MustParse("sha1-ce284c167558a9ef22df04390c87a6d0c9ed9659"), "", false},
|
||||||
|
// wrong hash:
|
||||||
|
{MustParse("sha1-ce284c167558a9ef22df04390c87a6d0c9ed9659"), "sha2-ce284c167558a9ef22df04390c87a6d0c9ed96", false},
|
||||||
|
|
||||||
|
// Other hashes:
|
||||||
|
{MustParse("foo-cafe"), "foo-cafe", true},
|
||||||
|
{MustParse("foo-cafe"), "foo-caf", true},
|
||||||
|
{MustParse("foo-cafe"), "foo-ca", true},
|
||||||
|
{MustParse("foo-cafe"), "foo-c", true},
|
||||||
|
|
||||||
|
{MustParse("foo-cafe"), "foo-", false},
|
||||||
|
{MustParse("foo-cafe"), "", false},
|
||||||
|
{MustParse("foo-cafe"), "foo-beef", false},
|
||||||
|
{MustParse("foo-cafe"), "foo-bee", false},
|
||||||
|
{MustParse("foo-cafe"), "bar-cafe", false},
|
||||||
|
{MustParse("foo-cafe"), "fooxbe", false},
|
||||||
|
{MustParse("foo-cafe"), "foo-c", true},
|
||||||
|
{MustParse("foo-caf"), "foo-cae", false},
|
||||||
|
{MustParse("foo-caf"), "foo-cb", false},
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHasPrefix(t *testing.T) {
|
||||||
|
for _, tt := range hasPrefixTests {
|
||||||
|
got := tt.ref.HasPrefix(tt.str)
|
||||||
|
if got != tt.want {
|
||||||
|
t.Errorf("ref %q HasPrefix(%q) = %v; want %v", tt.ref, tt.str, got, tt.want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkHasPrefix(b *testing.B) {
|
||||||
|
b.ReportAllocs()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
for _, tt := range hasPrefixTests {
|
||||||
|
got := tt.ref.HasPrefix(tt.str)
|
||||||
|
if got != tt.want {
|
||||||
|
b.Fatalf("ref %q HasPrefix(%q) = %v; want %v", tt.ref, tt.str, got, tt.want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue