Skip to content

Commit

Permalink
object: add Prefix type
Browse files Browse the repository at this point in the history
  • Loading branch information
zombiezen committed Jan 16, 2021
1 parent 936c61c commit b250490
Show file tree
Hide file tree
Showing 3 changed files with 110 additions and 0 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
interoperable with Git packfile index files.
([#12](https://github.com/gg-scm/gg-git/issues/12))
- `packfile.ReadHeader` enables random access to a packfile.
- `object.Prefix` allows marshaling and unmarshaling the `"blob 42\x00"` prefix
used as part of the Git object hash.

### Changed

Expand Down
52 changes: 52 additions & 0 deletions object/object.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ https://git-scm.com/book/en/v2/Git-Internals-Git-Objects
package object

import (
"bytes"
"crypto/sha1"
"fmt"
"io"
Expand Down Expand Up @@ -64,6 +65,57 @@ func BlobSum(r io.Reader, size int64) (githash.SHA1, error) {
return sum, nil
}

// Prefix is a parsed Git object prefix like "blob 42\x00".
type Prefix struct {
Type Type
Size int64
}

// MarshalBinary returns the result of AppendPrefix.
func (p Prefix) MarshalBinary() ([]byte, error) {
if !p.Type.IsValid() {
return nil, fmt.Errorf("marshal git object prefix: unknown type %q", p.Type)
}
if p.Size < 0 {
return nil, fmt.Errorf("marshal git object prefix: negative size")
}
return AppendPrefix(nil, p.Type, p.Size), nil
}

// UnmarshalBinary parses an object prefix.
func (p *Prefix) UnmarshalBinary(data []byte) error {
if len(data) == 0 || data[len(data)-1] != 0 {
return fmt.Errorf("unmarshal git object prefix: does not end with NUL")
}
typeEnd := bytes.IndexByte(data, ' ')
if typeEnd == -1 {
return fmt.Errorf("unmarshal git object prefix: missing space")
}
typ := Type(data[:typeEnd])
if !typ.IsValid() {
return fmt.Errorf("unmarshal git object prefix: unknown type %q", typ)
}
sizeStart := typeEnd + 1
sizeEnd := len(data) - 1
size, err := strconv.ParseInt(string(data[sizeStart:sizeEnd]), 10, 64)
if err != nil {
return fmt.Errorf("unmarshal git object prefix: size: %v", err)
}
if size < 0 {
return fmt.Errorf("unmarshal git object prefix: negative size")
}
p.Type = typ
p.Size = size
return nil
}

// String returns the prefix without the trailing NUL byte.
func (p Prefix) String() string {
buf := AppendPrefix(nil, p.Type, p.Size)
buf = buf[:len(buf)-1]
return string(buf)
}

// AppendPrefix appends a Git object prefix (e.g. "blob 42\x00")
// to a byte slice.
func AppendPrefix(dst []byte, typ Type, n int64) []byte {
Expand Down
56 changes: 56 additions & 0 deletions object/object_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,18 @@
package object

import (
"encoding"
"strings"
"testing"

"gg-scm.io/pkg/git/githash"
)

var (
_ encoding.BinaryMarshaler = Prefix{}
_ encoding.BinaryUnmarshaler = new(Prefix)
)

func TestBlobSum(t *testing.T) {
tests := []struct {
data string
Expand Down Expand Up @@ -56,6 +62,56 @@ func TestBlobSum(t *testing.T) {
})
}

func TestPrefixUnmarshalBinary(t *testing.T) {
tests := []struct {
data string
want Prefix
wantError bool
}{
{
data: "blob 0\x00",
want: Prefix{Type: TypeBlob, Size: 0},
},
{
data: "tree 42\x00",
want: Prefix{Type: TypeTree, Size: 42},
},
{
data: "tree abc\x00",
wantError: true,
},
{
data: "tree -42\x00",
wantError: true,
},
{
data: "foo 42\x00",
wantError: true,
},
{
data: "blob 0",
wantError: true,
},
}
for _, test := range tests {
var got Prefix
err := got.UnmarshalBinary([]byte(test.data))
if err != nil {
if !test.wantError {
t.Errorf("new(Prefix).UnmarshalBinary([]byte(%q)) = %v; want <nil>", test.data, err)
}
continue
}
if test.wantError {
t.Errorf("new(Prefix).UnmarshalBinary([]byte(%q)) = <nil>; want error", test.data)
continue
}
if got != test.want {
t.Errorf("new(Prefix).UnarshalBinary([]byte(%q)) yields %+v; want %+v", test.data, got, test.want)
}
}
}

func hashLiteral(s string) githash.SHA1 {
var h githash.SHA1
if err := h.UnmarshalText([]byte(s)); err != nil {
Expand Down

0 comments on commit b250490

Please sign in to comment.