From 82dd9f540be12c4c93679d2a60c4d1950e57eec8 Mon Sep 17 00:00:00 2001 From: Laria Carolin Chabowski Date: Sat, 24 Jun 2017 21:37:21 +0200 Subject: Add basic idea of objects --- objects/object.go | 105 +++++++++++++++++++++++++++++++++++++++++++++++++ objects/object_test.go | 88 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 193 insertions(+) create mode 100644 objects/object.go create mode 100644 objects/object_test.go diff --git a/objects/object.go b/objects/object.go new file mode 100644 index 0000000..2bc3a53 --- /dev/null +++ b/objects/object.go @@ -0,0 +1,105 @@ +package objects + +import ( + "fmt" + "io" + "strconv" + "strings" +) + +type ObjectId interface { + fmt.Stringer +} + +type ObjectType string + +const ( + OTBlob ObjectType = "blob" + OTFile ObjectType = "file" + OTTree ObjectType = "tree" + OTSnapshot ObjectType = "snapshot" +) + +type Object struct { + Type ObjectType + Payload []byte +} + +func (o Object) Serialize(w io.Writer) error { + if _, err := fmt.Fprintf(w, "%s %d\n", o.Type, len(o.Payload)); err != nil { + return err + } + + _, err := w.Write(o.Payload) + return err +} + +type UnserializeError struct { + Reason error +} + +func (err UnserializeError) Error() string { + if err.Reason == nil { + return "Invalid object" + } else { + return fmt.Sprintf("Invalid object: %s", err.Reason) + } +} + +type bytewiseReader struct { + r io.Reader + buf []byte +} + +func newBytewiseReader(r io.Reader) io.ByteReader { + return &bytewiseReader{ + r: r, + buf: make([]byte, 1), + } +} + +func (br *bytewiseReader) ReadByte() (b byte, err error) { + _, err = br.r.Read(br.buf) + b = br.buf[0] + + return +} + +func UnserializeObject(r io.Reader) (Object, error) { + br := newBytewiseReader(r) + + line := []byte{} + + for { + b, err := br.ReadByte() + if err != nil { + return Object{}, UnserializeError{err} + } + + if b == '\n' { + break + } + line = append(line, b) + } + + parts := strings.SplitN(string(line), " ", 2) + if len(parts) != 2 { + return Object{}, UnserializeError{} + } + + size, err := strconv.ParseUint(parts[1], 10, 64) + if err != nil { + return Object{}, UnserializeError{err} + } + + o := Object{ + Type: ObjectType(parts[0]), + Payload: make([]byte, size), + } + + if _, err := io.ReadFull(r, o.Payload); err != nil { + return Object{}, UnserializeError{err} + } + + return o, nil +} diff --git a/objects/object_test.go b/objects/object_test.go new file mode 100644 index 0000000..c596b39 --- /dev/null +++ b/objects/object_test.go @@ -0,0 +1,88 @@ +package objects + +import ( + "bufio" + "bytes" + "testing" +) + +func TestUnserializeSuccess(t *testing.T) { + r := bufio.NewReader(bytes.NewBuffer([]byte("blob 16\n0123456789abcdef"))) + + o, err := UnserializeObject(r) + if err != nil { + t.Fatalf("Unserialize failed: %s", err) + } + + if o.Type != OTBlob { + t.Errorf("expected type %s, got %s", OTBlob, o.Type) + } + + if !bytes.Equal([]byte("0123456789abcdef"), o.Payload) { + t.Errorf("Unexpected payload: (size %d) %v", len(o.Payload), o.Payload) + } +} + +func TestUnserializeSuccess0Payload(t *testing.T) { + r := bufio.NewReader(bytes.NewBuffer([]byte("blob 0\n"))) + + o, err := UnserializeObject(r) + if err != nil { + t.Fatalf("Unserialize failed: %s", err) + } + + if o.Type != OTBlob { + t.Errorf("expected type %s, got %s", OTBlob, o.Type) + } + + if !bytes.Equal([]byte{}, o.Payload) { + t.Errorf("Unexpected payload: (size %d) %v", len(o.Payload), o.Payload) + } +} + +func unserializeMustFail(b []byte, t *testing.T) { + r := bufio.NewReader(bytes.NewBuffer(b)) + + o, err := UnserializeObject(r) + if err == nil { + t.Fatalf("Expected an error, but object was successfully read: %#v", o) + } + + _, ok := err.(UnserializeError) + if !ok { + t.Fatalf("Unknown error, expected an UnserializeError: %#v", err) + } +} + +func TestUnserializeInvalidEmpty(t *testing.T) { + unserializeMustFail([]byte{}, t) +} + +func TestUnserializeIncompleteHeader(t *testing.T) { + unserializeMustFail([]byte("foo\nbar"), t) +} + +func TestUnserializeInvalidNumber(t *testing.T) { + unserializeMustFail([]byte("blob abc\nbar"), t) +} + +func TestUnserializePayloadTooSmall(t *testing.T) { + unserializeMustFail([]byte("blob 10\nbar"), t) +} + +func TestSerialize(t *testing.T) { + o := Object{ + Type: OTBlob, + Payload: []byte("foo bar\nbaz"), + } + + buf := new(bytes.Buffer) + if err := o.Serialize(buf); err != nil { + t.Fatalf("Serialization failed:%s", err) + } + + b := buf.Bytes() + if !bytes.Equal(b, []byte("blob 11\nfoo bar\nbaz")) { + t.Errorf("Unexpected serialization result: %v", b) + } +} -- cgit v1.2.3-54-g00ecf