mirror of https://github.com/perkeep/perkeep.git
pkg/blob: add ref.HasPrefix method
Continuation of 504604e22d
Needed by #972
Change-Id: Ia4873aba5d1b1be7774ce38eeff15761961b41d7
This commit is contained in:
parent
bf4b74b5d6
commit
b71aa74f31
|
@ -66,6 +66,7 @@ type digestType interface {
|
|||
digestName() string
|
||||
newHash() hash.Hash
|
||||
equalString(string) bool
|
||||
hasPrefix(string) bool
|
||||
}
|
||||
|
||||
func (r Ref) String() string {
|
||||
|
@ -97,6 +98,12 @@ func (r Ref) StringMinusOne() string {
|
|||
// It does not allocate.
|
||||
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 {
|
||||
dname := r.digest.digestName()
|
||||
bs := r.digest.bytes()
|
||||
|
@ -425,6 +432,40 @@ func (d sha1Digest) equalString(s string) bool {
|
|||
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
|
||||
|
||||
type otherDigest struct {
|
||||
|
@ -460,6 +501,44 @@ func (d otherDigest) equalString(s string) bool {
|
|||
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{
|
||||
ctor: sha1FromBinary,
|
||||
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