Merge branch 'master' into git-odb-pack-idx-v2
This commit is contained in:
commit
9dde72f2fb
3
.gitignore
vendored
3
.gitignore
vendored
@ -14,6 +14,9 @@ debian/git-lfs/
|
||||
debian/*.log
|
||||
debian/files
|
||||
debian/*.substvars
|
||||
debian/debhelper-build-stamp
|
||||
debian/.debhelper
|
||||
/.pc
|
||||
obj-*
|
||||
|
||||
rpm/BUILD*
|
||||
|
@ -15,6 +15,8 @@ const (
|
||||
VersionWidth = 4
|
||||
// V2Width is the total width of the header in V2.
|
||||
V2Width = MagicWidth + VersionWidth
|
||||
// V1Width is the total width of the header in V1.
|
||||
V1Width = 0
|
||||
|
||||
// FanoutEntries is the number of entries in the fanout table.
|
||||
FanoutEntries = 256
|
||||
@ -23,6 +25,9 @@ const (
|
||||
// FanoutWidth is the width of the entire fanout table.
|
||||
FanoutWidth = FanoutEntries * FanoutEntryWidth
|
||||
|
||||
// OffsetV1Start is the location of the first object outside of the V1
|
||||
// header.
|
||||
OffsetV1Start = V1Width + FanoutWidth
|
||||
// OffsetV2Start is the location of the first object outside of the V2
|
||||
// header.
|
||||
OffsetV2Start = V2Width + FanoutWidth
|
||||
@ -39,6 +44,8 @@ const (
|
||||
// encoded into the small offset.
|
||||
ObjectLargeOffsetWidth = 8
|
||||
|
||||
// ObjectEntryV1Width is the width of one contiguous object entry in V1.
|
||||
ObjectEntryV1Width = ObjectNameWidth + ObjectSmallOffsetWidth
|
||||
// ObjectEntryV2Width is the width of one non-contiguous object entry in
|
||||
// V2.
|
||||
ObjectEntryV2Width = ObjectNameWidth + ObjectCRCWidth + ObjectSmallOffsetWidth
|
||||
@ -94,13 +101,13 @@ func decodeIndexHeader(r io.ReaderAt) (IndexVersion, error) {
|
||||
|
||||
version := IndexVersion(binary.BigEndian.Uint32(vb))
|
||||
switch version {
|
||||
case V2:
|
||||
case V1, V2:
|
||||
return version, nil
|
||||
}
|
||||
|
||||
return version, &UnsupportedVersionErr{uint32(version)}
|
||||
}
|
||||
return IndexVersion(0), nil
|
||||
return V1, nil
|
||||
}
|
||||
|
||||
// decodeIndexFanout decodes the fanout table given by "r" and beginning at the
|
||||
|
@ -9,6 +9,13 @@ import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestDecodeIndexV1InvalidFanout(t *testing.T) {
|
||||
idx, err := DecodeIndex(bytes.NewReader(make([]byte, FanoutWidth-1)))
|
||||
|
||||
assert.Equal(t, ErrShortFanout, err)
|
||||
assert.Nil(t, idx)
|
||||
}
|
||||
|
||||
func TestDecodeIndexV2(t *testing.T) {
|
||||
buf := make([]byte, 0, V2Width+FanoutWidth)
|
||||
buf = append(buf, 0xff, 0x74, 0x4f, 0x63)
|
||||
@ -36,8 +43,12 @@ func TestDecodeIndexV2InvalidFanout(t *testing.T) {
|
||||
|
||||
idx, err := DecodeIndex(bytes.NewReader(buf))
|
||||
|
||||
assert.Equal(t, ErrShortFanout, err)
|
||||
assert.Nil(t, idx)
|
||||
func TestDecodeIndexV1(t *testing.T) {
|
||||
idx, err := DecodeIndex(bytes.NewReader(make([]byte, FanoutWidth)))
|
||||
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, V1, idx.version)
|
||||
assert.EqualValues(t, 0, idx.Count())
|
||||
}
|
||||
|
||||
func TestDecodeIndexUnsupportedVersion(t *testing.T) {
|
||||
@ -56,4 +67,4 @@ func TestDecodeIndexEmptyContents(t *testing.T) {
|
||||
|
||||
assert.Equal(t, io.EOF, err)
|
||||
assert.Nil(t, idx)
|
||||
}
|
||||
}
|
52
git/odb/pack/index_v1.go
Normal file
52
git/odb/pack/index_v1.go
Normal file
@ -0,0 +1,52 @@
|
||||
package pack
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
)
|
||||
|
||||
const (
|
||||
// V1 is an instance of IndexVersion corresponding to the V1 index file
|
||||
// format.
|
||||
V1 IndexVersion = 1
|
||||
)
|
||||
|
||||
// v1Search implements the IndexVersion.Search method for V1 packfiles.
|
||||
func v1Search(idx *Index, name []byte, at int64) (*IndexEntry, int, error) {
|
||||
var sha [20]byte
|
||||
if _, err := idx.readAt(sha[:], v1ShaOffset(at)); err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
cmp := bytes.Compare(name, sha[:])
|
||||
if cmp != 0 {
|
||||
return nil, cmp, nil
|
||||
}
|
||||
|
||||
var offs [4]byte
|
||||
if _, err := idx.readAt(offs[:], v1EntryOffset(at)); err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
return &IndexEntry{
|
||||
PackOffset: uint64(binary.BigEndian.Uint32(offs[:])),
|
||||
}, 0, nil
|
||||
}
|
||||
|
||||
// v1ShaOffset returns the location of the SHA1 of an object given at "at".
|
||||
func v1ShaOffset(at int64) int64 {
|
||||
// Skip forward until the desired entry.
|
||||
return v1EntryOffset(at) +
|
||||
// Skip past the 4-byte object offset in the desired entry to
|
||||
// the SHA1.
|
||||
ObjectSmallOffsetWidth
|
||||
}
|
||||
|
||||
// v1EntryOffset returns the location of the packfile offset for the object
|
||||
// given at "at".
|
||||
func v1EntryOffset(at int64) int64 {
|
||||
// Skip the L1 fanout table
|
||||
return OffsetV1Start +
|
||||
// Skip the object entries before the one located at "at"
|
||||
(ObjectEntryV1Width * at)
|
||||
}
|
98
git/odb/pack/index_v1_test.go
Normal file
98
git/odb/pack/index_v1_test.go
Normal file
@ -0,0 +1,98 @@
|
||||
package pack
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
var (
|
||||
V1IndexFanout = make([]uint32, FanoutEntries)
|
||||
|
||||
V1IndexSmallEntry = []byte{
|
||||
0x0, 0x0, 0x0, 0x1,
|
||||
|
||||
0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1,
|
||||
0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1,
|
||||
}
|
||||
V1IndexSmallSha = V1IndexSmallEntry[4:]
|
||||
|
||||
V1IndexMediumEntry = []byte{
|
||||
0x0, 0x0, 0x0, 0x2,
|
||||
|
||||
0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2,
|
||||
0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2, 0x2,
|
||||
}
|
||||
V1IndexMediumSha = V1IndexMediumEntry[4:]
|
||||
|
||||
V1IndexLargeEntry = []byte{
|
||||
0x0, 0x0, 0x0, 0x3,
|
||||
|
||||
0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3,
|
||||
0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3,
|
||||
}
|
||||
V1IndexLargeSha = V1IndexLargeEntry[4:]
|
||||
|
||||
V1Index = &Index{
|
||||
fanout: V1IndexFanout,
|
||||
version: V1,
|
||||
}
|
||||
)
|
||||
|
||||
func TestIndexV1SearchExact(t *testing.T) {
|
||||
e, cmp, err := V1.Search(V1Index, V1IndexMediumSha, 1)
|
||||
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 0, cmp)
|
||||
assert.EqualValues(t, 2, e.PackOffset)
|
||||
}
|
||||
|
||||
func TestIndexV1SearchSmall(t *testing.T) {
|
||||
e, cmp, err := V1.Search(V1Index, V1IndexMediumSha, 0)
|
||||
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 1, cmp)
|
||||
assert.Nil(t, e)
|
||||
}
|
||||
|
||||
func TestIndexV1SearchBig(t *testing.T) {
|
||||
e, cmp, err := V1.Search(V1Index, V1IndexMediumSha, 2)
|
||||
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, -1, cmp)
|
||||
assert.Nil(t, e)
|
||||
}
|
||||
|
||||
func TestIndexV1SearchOutOfBounds(t *testing.T) {
|
||||
e, cmp, err := V1.Search(V1Index, V1IndexMediumSha, 10)
|
||||
|
||||
assert.Nil(t, e)
|
||||
assert.Equal(t, 0, cmp)
|
||||
assert.Equal(t, ErrIndexOutOfBounds, err)
|
||||
}
|
||||
|
||||
func init() {
|
||||
V1IndexFanout[1] = 1
|
||||
V1IndexFanout[2] = 2
|
||||
V1IndexFanout[3] = 3
|
||||
|
||||
for i := 3; i < len(V1IndexFanout); i++ {
|
||||
V1IndexFanout[i] = 3
|
||||
}
|
||||
|
||||
fanout := make([]byte, FanoutWidth)
|
||||
for i, n := range V1IndexFanout {
|
||||
binary.BigEndian.PutUint32(fanout[i*FanoutEntryWidth:], n)
|
||||
}
|
||||
|
||||
buf := make([]byte, 0, OffsetV1Start+(3*ObjectEntryV1Width))
|
||||
|
||||
buf = append(buf, fanout...)
|
||||
buf = append(buf, V1IndexSmallEntry...)
|
||||
buf = append(buf, V1IndexMediumEntry...)
|
||||
buf = append(buf, V1IndexLargeEntry...)
|
||||
|
||||
V1Index.f = bytes.NewReader(buf)
|
||||
}
|
@ -20,6 +20,8 @@ func (v IndexVersion) Width() int64 {
|
||||
switch v {
|
||||
case V2:
|
||||
return V2Width
|
||||
case V1:
|
||||
return V1Width
|
||||
}
|
||||
panic(fmt.Sprintf("git/odb/pack: width unknown for pack version %d", v))
|
||||
}
|
||||
@ -44,6 +46,8 @@ func (v IndexVersion) Search(idx *Index, name []byte, at int64) (*IndexEntry, in
|
||||
switch v {
|
||||
case V2:
|
||||
return v2Search(idx, name, at)
|
||||
case V1:
|
||||
return v1Search(idx, name, at)
|
||||
}
|
||||
return nil, 0, &UnsupportedVersionErr{Got: uint32(v)}
|
||||
}
|
||||
|
@ -6,6 +6,10 @@ import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestIndexVersionWidthV1(t *testing.T) {
|
||||
assert.EqualValues(t, 0, V1.Width())
|
||||
}
|
||||
|
||||
func TestIndexVersionWidthV2(t *testing.T) {
|
||||
assert.EqualValues(t, 8, V2.Width())
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user