diff --git a/pkg/storage/donut/object/objectv1/objectv1.go b/pkg/storage/donut/object/objectv1/objectv1.go new file mode 100644 index 000000000..6b0d0d14a --- /dev/null +++ b/pkg/storage/donut/object/objectv1/objectv1.go @@ -0,0 +1,65 @@ +package objectv1 + +import ( + "bytes" + "encoding/binary" + "encoding/gob" + "errors" + "io" + "strconv" + "time" +) + +// Package Version +const Version = uint32(1) + +// ObjectType is the type of object stored. It is either an Object or Multipart Object. +type ObjectType uint8 + +const ( + // Object is a full object + Object ObjectType = iota + // MultipartObject is a collection of Objects uploaded separately that represent a large object. + MultipartObject +) + +// Object Metadata +type ObjectMetadata struct { + Bucket string + Key string + ErasurePart uint16 + EncodedPart uint8 + + ContentType string + Created time.Time + Length uint64 + Md5 []byte + ObjectType ObjectType +} + +func Write(target io.Writer, metadata ObjectMetadata, reader io.Reader) error { + buffer := new(bytes.Buffer) + binary.Write(buffer, binary.LittleEndian, uint32(Version)) + encoder := gob.NewEncoder(buffer) + if err := encoder.Encode(metadata); err != nil { + return err + } + reader = io.MultiReader(buffer, reader) + _, err := io.Copy(target, reader) + return err +} + +func ReadMetadata(reader io.Reader) (metadata ObjectMetadata, err error) { + versionBytes := make([]byte, 4) + if err := binary.Read(reader, binary.LittleEndian, versionBytes); err != nil { + return metadata, err + } + var version uint32 + version = binary.LittleEndian.Uint32(versionBytes) + if version != 1 { + return metadata, errors.New("Unknown Version: " + strconv.FormatUint(uint64(version), 10)) + } + decoder := gob.NewDecoder(reader) + err = decoder.Decode(&metadata) + return metadata, err +} diff --git a/pkg/storage/donut/object/objectv1/objectv1_test.go b/pkg/storage/donut/object/objectv1/objectv1_test.go new file mode 100644 index 000000000..416f007f5 --- /dev/null +++ b/pkg/storage/donut/object/objectv1/objectv1_test.go @@ -0,0 +1,61 @@ +package objectv1 + +import ( + "testing" + + "bytes" + "crypto/md5" + "encoding/binary" + "encoding/gob" + . "gopkg.in/check.v1" + "io" + "time" +) + +func Test(t *testing.T) { TestingT(t) } + +type MySuite struct{} + +var _ = Suite(&MySuite{}) + +func (s *MySuite) TestObjectV1ReadWrite(c *C) { + var buffer bytes.Buffer + + data := "Hello, World" + + hash := md5.New() + hash.Sum([]byte(data)) + sum := hash.Sum(nil) + + objectMetadata := ObjectMetadata{ + Bucket: "bucket", + Key: "key", + ErasurePart: 1, + EncodedPart: 2, + + ObjectType: Object, + Created: time.Now(), + ContentType: "application/text", + Md5: sum, + Length: uint64(len(sum)), + } + + err := Write(&buffer, objectMetadata, bytes.NewBufferString(data)) + c.Assert(err, IsNil) + + versionBuffer := make([]byte, 4) + buffer.Read(versionBuffer) + c.Assert(binary.LittleEndian.Uint32(versionBuffer), Equals, uint32(1)) + + actualMetadata := ObjectMetadata{} + decoder := gob.NewDecoder(&buffer) + decoder.Decode(&actualMetadata) + + c.Assert(actualMetadata, DeepEquals, objectMetadata) + + var actualData bytes.Buffer + + _, err = io.Copy(&actualData, &buffer) + c.Assert(err, IsNil) + c.Assert(actualData.Bytes(), DeepEquals, []byte(data)) +}