diff options
Diffstat (limited to 'core')
-rw-r--r-- | core/errors.go | 35 | ||||
-rw-r--r-- | core/hash.go | 40 | ||||
-rw-r--r-- | core/hash_test.go | 35 | ||||
-rw-r--r-- | core/object.go | 113 |
4 files changed, 223 insertions, 0 deletions
diff --git a/core/errors.go b/core/errors.go new file mode 100644 index 0000000..fd1133e --- /dev/null +++ b/core/errors.go @@ -0,0 +1,35 @@ +package core + +import "fmt" + +type PermanentError struct { + err error +} + +func NewPermanentError(err error) *PermanentError { + if err == nil { + return nil + } + + return &PermanentError{err: err} +} + +func (e *PermanentError) Error() string { + return fmt.Sprintf("permanent client error: %s", e.err.Error()) +} + +type UnexpectedError struct { + err error +} + +func NewUnexpectedError(err error) *UnexpectedError { + if err == nil { + return nil + } + + return &UnexpectedError{err: err} +} + +func (e *UnexpectedError) Error() string { + return fmt.Sprintf("unexpected client error: %s", e.err.Error()) +} diff --git a/core/hash.go b/core/hash.go new file mode 100644 index 0000000..0b6f274 --- /dev/null +++ b/core/hash.go @@ -0,0 +1,40 @@ +package core + +import ( + "crypto/sha1" + "encoding/hex" + "strconv" +) + +// Hash SHA1 hased content +type Hash [20]byte + +// ComputeHash compute the hash for a given ObjectType and content +func ComputeHash(t ObjectType, content []byte) Hash { + h := t.Bytes() + h = append(h, ' ') + h = strconv.AppendInt(h, int64(len(content)), 10) + h = append(h, 0) + h = append(h, content...) + + return Hash(sha1.Sum(h)) +} + +// NewHash return a new Hash from a hexadecimal hash representation +func NewHash(s string) Hash { + b, _ := hex.DecodeString(s) + + var h Hash + copy(h[:], b) + + return h +} + +func (h Hash) IsZero() bool { + var empty Hash + return h == empty +} + +func (h Hash) String() string { + return hex.EncodeToString(h[:]) +} diff --git a/core/hash_test.go b/core/hash_test.go new file mode 100644 index 0000000..8c4ed67 --- /dev/null +++ b/core/hash_test.go @@ -0,0 +1,35 @@ +package core + +import ( + "testing" + + . "gopkg.in/check.v1" +) + +func Test(t *testing.T) { TestingT(t) } + +type HashSuite struct{} + +var _ = Suite(&HashSuite{}) + +func (s *HashSuite) TestComputeHash(c *C) { + hash := ComputeHash(BlobObject, []byte("")) + c.Assert(hash.String(), Equals, "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391") + + hash = ComputeHash(BlobObject, []byte("Hello, World!\n")) + c.Assert(hash.String(), Equals, "8ab686eafeb1f44702738c8b0f24f2567c36da6d") +} + +func (s *HashSuite) TestNewHash(c *C) { + hash := ComputeHash(BlobObject, []byte("Hello, World!\n")) + + c.Assert(hash, Equals, NewHash(hash.String())) +} + +func (s *HashSuite) TestIsZero(c *C) { + hash := NewHash("foo") + c.Assert(hash.IsZero(), Equals, true) + + hash = NewHash("8ab686eafeb1f44702738c8b0f24f2567c36da6d") + c.Assert(hash.IsZero(), Equals, false) +} diff --git a/core/object.go b/core/object.go new file mode 100644 index 0000000..4e8a587 --- /dev/null +++ b/core/object.go @@ -0,0 +1,113 @@ +package core + +import ( + "bytes" + "io" +) + +// Object is a generic representation of any git object +type Object interface { + Type() ObjectType + SetType(ObjectType) + Size() int64 + SetSize(int64) + Hash() Hash + Reader() io.Reader + Writer() io.Writer +} + +// ObjectStorage generic storage of objects +type ObjectStorage interface { + New() Object + Set(Object) Hash + Get(Hash) (Object, bool) +} + +// ObjectType internal object type's +type ObjectType int8 + +const ( + CommitObject ObjectType = 1 + TreeObject ObjectType = 2 + BlobObject ObjectType = 3 + TagObject ObjectType = 4 + OFSDeltaObject ObjectType = 6 + REFDeltaObject ObjectType = 7 +) + +func (t ObjectType) String() string { + switch t { + case CommitObject: + return "commit" + case TreeObject: + return "tree" + case BlobObject: + return "blob" + default: + return "-" + } +} + +func (t ObjectType) Bytes() []byte { + return []byte(t.String()) +} + +type RAWObject struct { + b []byte + t ObjectType + s int64 +} + +func (o *RAWObject) Type() ObjectType { return o.t } +func (o *RAWObject) SetType(t ObjectType) { o.t = t } +func (o *RAWObject) Size() int64 { return o.s } +func (o *RAWObject) SetSize(s int64) { o.s = s } +func (o *RAWObject) Reader() io.Reader { return bytes.NewBuffer(o.b) } +func (o *RAWObject) Hash() Hash { return ComputeHash(o.t, o.b) } +func (o *RAWObject) Writer() io.Writer { return o } +func (o *RAWObject) Write(p []byte) (n int, err error) { + o.b = append(o.b, p...) + return len(p), nil +} + +type RAWObjectStorage struct { + Objects map[Hash]Object + Commits map[Hash]Object + Trees map[Hash]Object + Blobs map[Hash]Object +} + +func NewRAWObjectStorage() *RAWObjectStorage { + return &RAWObjectStorage{ + Objects: make(map[Hash]Object, 0), + Commits: make(map[Hash]Object, 0), + Trees: make(map[Hash]Object, 0), + Blobs: make(map[Hash]Object, 0), + } +} + +func (o *RAWObjectStorage) New() Object { + return &RAWObject{} +} + +func (o *RAWObjectStorage) Set(obj Object) Hash { + h := obj.Hash() + o.Objects[h] = obj + + switch obj.Type() { + case CommitObject: + o.Commits[h] = o.Objects[h] + case TreeObject: + o.Trees[h] = o.Objects[h] + case BlobObject: + o.Blobs[h] = o.Objects[h] + } + + return h +} + +func (o *RAWObjectStorage) Get(h Hash) (Object, bool) { + obj, ok := o.Objects[h] + + return obj, ok +} |