From d70adbd6ce50c7f3124e0e9f5aa09a1f2b536ce5 Mon Sep 17 00:00:00 2001 From: Emery Hemingway Date: Wed, 22 May 2013 21:40:00 -0500 Subject: [PATCH] Container and Element interfaces, new encoder is untested --- element.go | 58 +++++++++++++++++-- encoder.go | 165 +++++++++++++++++++++++++++++++++++++++++++++++++++++ id.go | 44 -------------- size.go | 85 --------------------------- value.go | 102 +++++++++++++++++++++++++++++++++ 5 files changed, 321 insertions(+), 133 deletions(-) delete mode 100644 id.go delete mode 100644 size.go create mode 100644 value.go diff --git a/element.go b/element.go index ac7ba95..a254a20 100644 --- a/element.go +++ b/element.go @@ -2,8 +2,58 @@ package ebml import "io" -type Element struct { - ID uint32 - Size uint64 - Data io.Reader +const ( + HeaderID = 0xa45dfa3 + DocTypeID = 0x4282 + DocTypeVersionID = 0x4287 + DocTypeReadVersionID = 0x4285 +) + +type Element interface { + ID() uint32 + Size() uint64 + io.Reader +} + +type Container interface { + ID() uint32 + Size() uint64 + Next() Element +} + +type header struct { + docType *String + docTypeVersion *Uint + docTypeReadVersion *Uint +} + +func newHeader(docType string, docTypeVersion, docTypeReadVersion uint64) *header { + return &header{ + NewString(DocTypeID, docType), + NewUint(DocTypeVersionID, docTypeVersion), + NewUint(DocTypeReadVersionID, docTypeReadVersion), + } +} + +func (h *header) ID() uint32 { return HeaderID } + +func (h *header) Size() (n uint64) { + return h.docType.Size() + h.docTypeVersion.Size() + h.docTypeReadVersion.Size() +} + +func (h *header) Next() (e Element) { + switch { + case h.docType != nil: + e = h.docType + h.docType = nil + + case h.docTypeVersion != nil: + e = h.docTypeVersion + h.docTypeVersion = nil + + case h.docTypeReadVersion != nil: + e = h.docTypeReadVersion + h.docTypeReadVersion = nil + } + return } diff --git a/encoder.go b/encoder.go index 27e1412..bf59be7 100644 --- a/encoder.go +++ b/encoder.go @@ -15,3 +15,168 @@ func NewEncoder(w io.Writer) *Encoder { return &Encoder{Writer: bufio.NewWriter(w)} } +// Encode writes a value that conforms to the Container +// or Element interface. +func (e *Encoder) Encode(v interface{}) (err error) { + if V, ok := v.(Container); ok { + e.EncodeID(V.ID()) + e.EncodeSize(V.Size()) + for element := V.Next(); element != nil; element = V.Next() { + err = e.Encode(element) + if err != nil { + return + } + } + return + } + if V, ok := v.(Element); ok { + e.EncodeID(V.ID()) + e.EncodeSize(V.Size()) + _, err = io.Copy(e, V) + return + } + panic("Could not encode value") +} + +// PutHeader writes a EBML header to the encoder stream +// docType is an ASCII string that identifies the type of the document. +// docTypeVersion is the version of document type to which the +// document conforms to. +// docTypeReadVersion is the minimum DocType version an interpreter +// has to support in order to read the document. +func (e *Encoder) PutHeader(docType string, docTypeVersion, docTypeReadVersion uint64) error { + return e.Encode(newHeader(docType, docTypeVersion, docTypeReadVersion)) +} + +// EncodeID writes an element ID to the encoder stream. +// +// See the Encode convenience function. +func (e *Encoder) EncodeID(x uint32) (err error) { + var buf []byte + switch { + case x == 0: + _, err = e.Write([]byte{byte(0)}) + return err + + case x < 127: + b := byte(x) + buf = []byte{b | 0x80} + + case x < 16383: + buf = make([]byte, 2) + buf[1] = byte(x) + x >>= 8 + buf[0] = byte(x) | 0x40 + + case x < 2097151: + buf = make([]byte, 3) + buf[2] = byte(x) + x >>= 8 + buf[1] = byte(x) + x >>= 8 + buf[0] = byte(x) | 0x20 + + case x < 268435455: + buf = make([]byte, 4) + buf[3] = byte(x) + x >>= 8 + buf[2] = byte(x) + x >>= 8 + buf[1] = byte(x) + x >>= 8 + buf[0] = byte(x) | 0x10 + + default: + panic("element ID overflow") + } + + _, err = e.Write(buf) + return +} + +const ( + o1 = 1<<7 - 2 + o2 = 1<<14 - 2 + o3 = 1<<21 - 2 + o4 = 1<<28 - 2 + o5 = 1<<35 - 2 + o6 = 1<<42 - 2 + o7 = 1<<49 - 2 + o8 = 1<<56 - 2 +) + +// EncodeID writes an element size to the encoder stream. +// +// See the Encode convenience function. +func (e *Encoder) EncodeSize(x uint64) (err error) { + var buf []byte + switch { + case x == 0: + _, err = e.Write([]byte{byte(0)}) + return err + + case x < o1: + b := byte(x) + buf = []byte{b | 0x80} + + case x < o2: + buf = make([]byte, 2) + buf[1] = byte(x) + x >>= 8 + buf[0] = byte(x) | 0x40 + + case x < o3: + buf = make([]byte, 3) + buf[2] = byte(x) + x >>= 8 + buf[1] = byte(x) + x >>= 8 + buf[0] = byte(x) | 0x20 + + case x < o4: + buf = make([]byte, 4) + for i := 3; i > 0; i-- { + buf[i] = byte(x) + x >>= 8 + } + buf[0] = byte(x) | 0x10 + + case x < o5: + buf = make([]byte, 5) + for i := 4; i > 0; i-- { + buf[i] = byte(x) + x >>= 8 + } + buf[0] = byte(x) | 0x08 + + case x < o6: + buf = make([]byte, 6) + for i := 5; i > 0; i-- { + buf[i] = byte(x) + x >>= 8 + } + buf[0] = byte(x) | 0x04 + + case x < o7: + buf = make([]byte, 7) + for i := 6; i > 0; i-- { + buf[i] = byte(x) + x >>= 8 + } + buf[0] = byte(x) | 0x02 + + case x < o8: + buf = make([]byte, 8) + for i := 7; i > 0; i-- { + buf[i] = byte(x) + x >>= 8 + } + buf[0] = 0x01 + + default: + panic("element size overflow") + } + + _, err = e.Write(buf) + return +} diff --git a/id.go b/id.go deleted file mode 100644 index 8260e54..0000000 --- a/id.go +++ /dev/null @@ -1,44 +0,0 @@ -package ebml - -func (e *Encoder) EncodeID(x uint32) (err error) { - var buf []byte - switch { - case x == 0: - _, err = e.Write([]byte{byte(0)}) - return err - - case x < 127: - b := byte(x) - buf = []byte{b | 0x80} - - case x < 16383: - buf = make([]byte, 2) - buf[1] = byte(x) - x >>= 8 - buf[0] = byte(x) | 0x40 - - case x < 2097151: - buf = make([]byte, 3) - buf[2] = byte(x) - x >>= 8 - buf[1] = byte(x) - x >>= 8 - buf[0] = byte(x) | 0x20 - - case x < 268435455: - buf = make([]byte, 4) - buf[3] = byte(x) - x >>= 8 - buf[2] = byte(x) - x >>= 8 - buf[1] = byte(x) - x >>= 8 - buf[0] = byte(x) | 0x10 - - default: - panic("element ID overflow") - } - - _, err = e.Write(buf) - return -} diff --git a/size.go b/size.go deleted file mode 100644 index 6e2a629..0000000 --- a/size.go +++ /dev/null @@ -1,85 +0,0 @@ -package ebml - -const ( - o1 = 1<<7 - 2 - o2 = 1<<14 - 2 - o3 = 1<<21 - 2 - o4 = 1<<28 - 2 - o5 = 1<<35 - 2 - o6 = 1<<42 - 2 - o7 = 1<<49 - 2 - o8 = 1<<56 - 2 -) - -func (e *Encoder) EncodeSize(x uint64) (err error) { - var buf []byte - switch { - case x == 0: - _, err = e.Write([]byte{byte(0)}) - return err - - case x < o1: - b := byte(x) - buf = []byte{b | 0x80} - - case x < o2: - buf = make([]byte, 2) - buf[1] = byte(x) - x >>= 8 - buf[0] = byte(x) | 0x40 - - case x < o3: - buf = make([]byte, 3) - buf[2] = byte(x) - x >>= 8 - buf[1] = byte(x) - x >>= 8 - buf[0] = byte(x) | 0x20 - - case x < o4: - buf = make([]byte, 4) - for i := 3; i > 0; i-- { - buf[i] = byte(x) - x >>= 8 - } - buf[0] = byte(x) | 0x10 - - case x < o5: - buf = make([]byte, 5) - for i := 4; i > 0; i-- { - buf[i] = byte(x) - x >>= 8 - } - buf[0] = byte(x) | 0x08 - - case x < o6: - buf = make([]byte, 6) - for i := 5; i > 0; i-- { - buf[i] = byte(x) - x >>= 8 - } - buf[0] = byte(x) | 0x04 - - case x < o7: - buf = make([]byte, 7) - for i := 6; i > 0; i-- { - buf[i] = byte(x) - x >>= 8 - } - buf[0] = byte(x) | 0x02 - - case x < o8: - buf = make([]byte, 8) - for i := 7; i > 0; i-- { - buf[i] = byte(x) - x >>= 8 - } - buf[0] = 0x01 - - default: - panic("element size overflow") - } - - _, err = e.Write(buf) - return -} diff --git a/value.go b/value.go new file mode 100644 index 0000000..1282b33 --- /dev/null +++ b/value.go @@ -0,0 +1,102 @@ +package ebml + +import ( + "bytes" +) + +type Int struct { + id uint32 + *bytes.Buffer +} + +func (i *Int) ID() uint32 { return i.id } + +func (i *Int) Size() uint64 { return uint64(i.Len()) } + +func NewInt(id uint32, v int64) (i *Int) { + var s int + switch { + case v < 0x8F, v > -0x8F: + s = 1 + case v < 0x8FFF, v > -0x8FFF: + s = 2 + case v < 0x8FFFFF, v > -0x8FFFFF: + s = 3 + case v < 0x8FFFFFFF, v > -0x8FFFFFFF: + s = 4 + case v < 0x8FFFFFFFFF, v > -0x8FFFFFFFFF: + s = 5 + case v < 0x8FFFFFFFFFFF, v > -0x8FFFFFFFFFFF: + s = 6 + case v < 0x8FFFFFFFFFFFFF, v > -0x8FFFFFFFFFFFFF: + s = 7 + default: + s = 8 + } + b := make([]byte, s) + for s > 1 { + s-- + b[s] = byte(v) + v = v >> 8 + } + b[s] = byte(v) + + return &Int{id, bytes.NewBuffer(b)} +} + +type Uint struct { + id uint32 + *bytes.Buffer +} + +func (u *Uint) ID() uint32 { return u.id } + +func (u *Uint) Size() uint64 { return uint64(u.Len()) } + +func NewUint(id uint32, v uint64) *Uint { + var s int + switch { + case v < 0xFF: + s = 1 + case v < 0xFFFF: + s = 2 + case v < 0xFFFFFF: + s = 3 + case v < 0xFFFFFFFF: + s = 4 + case v < 0xFFFFFFFFFF: + s = 5 + case v < 0xFFFFFFFFFFFF: + s = 6 + case v < 0xFFFFFFFFFFFFFF: + s = 7 + default: + s = 8 + } + b := make([]byte, s) + for s > 1 { + s-- + b[s] = byte(v) + v = v >> 8 + } + + return &Uint{id, bytes.NewBuffer(b)} +} + +type Float struct { + s int + b []byte +} + +type String struct { + id uint32 + *bytes.Buffer +} + +func (s *String) ID() uint32 { return s.id } + +func (s *String) Size() uint64 { return uint64(s.Len()) } + +func NewString(id uint32, s string) *String { + return &String{id, bytes.NewBufferString(s)} +}