working Encoder and Decoder with Marshaler and Unmarshaler interfaces.

This commit is contained in:
Emery Hemingway 2013-09-16 15:21:57 -04:00
parent 3f6cde68d4
commit 4659ce9614
4 changed files with 110 additions and 41 deletions

View File

@ -4,16 +4,15 @@
package ebml
import (
"fmt"
"reflect"
"sync"
)
var (
fieldIdMap = make(map[reflect.Type]map[Id]int)
fieldIdMutex sync.RWMutex
fieldDecoderMap = make(map[reflect.Type][]decoderFunc)
fieldDecoderMutex sync.RWMutex
fieldIdMap = make(map[reflect.Type]map[Id]int)
fieldIdMutex sync.RWMutex
//fieldDecoderMap = make(map[reflect.Type][]decoderFunc)
//fieldDecoderMutex sync.RWMutex
)
// cachedFieldIdMap returns a map that contains Id to field number
@ -55,6 +54,8 @@ func cachedFieldIdMap(typ reflect.Type) map[Id]int {
return m
}
/*
// cachedFieldIpDecoderTable returns a slice that contains decoder functions
// indexed by field number
func cachedFieldDecoderTable(typ reflect.Type) []decoderFunc {
@ -100,15 +101,19 @@ func cachedFieldDecoderTable(typ reflect.Type) []decoderFunc {
s[i] = decodeUint
case reflect.Uint64:
s[i] = decodeUint
case reflect.Slice:
s[i] = decodeSlice
case reflect.String:
s[i] = decodeString
case reflect.Struct:
s[i] = decodeStruct
default:
decError(fmt.Sprintf("cannot decode to %s field %s, %s decoding is unsupported ",
typ, f.Name, f.Type))
decError(fmt.Sprintf("cannot decode to %s.%s, %s decoding is unsupported ",
typ, f.Name, f.Type.Kind()))
}
}
fieldDecoderMap[typ] = s
return s
}
*/

View File

@ -4,16 +4,13 @@
package ebml
import (
//"bytes"
//"errors"
"fmt"
"io"
"reflect"
//"unsafe"
)
// readIdFrom reads an Id from a Reader and returns the number of bytes read and the Id
func readIdFrom(r io.Reader) (int, Id) {
func readIdFrom(r io.ReadSeeker) (int, Id) {
buf := make([]byte, 8)
n, err := r.Read(buf[:1])
if err != nil {
@ -31,7 +28,11 @@ func readIdFrom(r io.Reader) (int, Id) {
case id >= 0x10:
buf = buf[:3]
default:
encError("positioned at an invalid Id or EBMLMaxIDLength > 4")
p, err := r.Seek(-1, 1)
if err != nil {
encError(err.Error())
}
encError(fmt.Sprintf("invalid Id at reader position %x or EBMLMaxIDLength > 4, read byte %x", p, buf[0]))
}
var nn int
nn, err = r.Read(buf)
@ -96,20 +97,45 @@ func readSizeFrom(r io.Reader) (int, int64) {
type decoderFunc func(d *Decoder, id Id, size int64, v reflect.Value)
/* Sadly this using this table results in 'initialization loops' during building
var decoderFuncTable = [...]decoderFunc{
reflect.Uint: decodeUint,
reflect.Uint8: decodeUint,
reflect.Uint16: decodeUint,
reflect.Uint32: decodeUint,
reflect.Uint64: decodeUint,
reflect.Slice: decodeSlice,
reflect.String: decodeString,
reflect.Struct: decodeStruct,
}
*/
func decodeValue(d *Decoder, id Id, size int64, v reflect.Value) {
if um, ok := v.Interface().(Unmarshaler); ok {
if v.Kind() == reflect.Ptr && v.IsNil() {
v.Set(reflect.New(v.Type().Elem()))
um = v.Interface().(Unmarshaler)
}
fmt.Println("made an unmashaler out of", id, v)
rf := um.UnmarshalEBML(size)
rf.ReadFrom(d.r)
r := io.LimitReader(d.r, size)
rf.ReadFrom(r)
return
}
// If we got an interface or a pointer, dereference it.
for v.Kind() == reflect.Interface || v.Kind() == reflect.Ptr {
for v.Kind() == reflect.Ptr || v.Kind() == reflect.Interface {
if v.IsNil() {
v.Set(reflect.New(v.Type().Elem()))
}
v = v.Elem()
}
// I wanted to use an array of functions indexed by reflect.Kind,
// but kept getting initialization loop build errors
// look up the function to decode to value v
var fn decoderFunc
// I wanted to use an array of functions indexed by reflect.Kind,
// but kept getting initialization loop build errors
switch v.Kind() {
case reflect.Uint:
fn = decodeUint
@ -121,6 +147,8 @@ func decodeValue(d *Decoder, id Id, size int64, v reflect.Value) {
fn = decodeUint
case reflect.Uint64:
fn = decodeUint
case reflect.Slice:
fn = decodeSlice
case reflect.String:
fn = decodeString
case reflect.Struct:
@ -137,6 +165,9 @@ func decodeUint(d *Decoder, id Id, size int64, v reflect.Value) {
for _, c := range d.buf[1:] {
x += uint64(c)
}
if x == 0 {
return
}
if v.OverflowUint(x) {
decError(fmt.Sprintf("element %s value %d overflows %s", id, x, v.Type()))
}
@ -146,6 +177,23 @@ func decodeUint(d *Decoder, id Id, size int64, v reflect.Value) {
}
}
func decodeSlice(d *Decoder, id Id, size int64, v reflect.Value) {
// TODO(Emery): would be nice to use reflect.Append()
n := v.Len()
if n >= v.Cap() {
newcap := v.Cap() + v.Cap()/2
if newcap < 4 {
newcap = 4
}
newv := reflect.MakeSlice(v.Type(), n+1, newcap)
reflect.Copy(newv, v)
v.Set(newv)
} else {
v.SetLen(n + 1)
}
decodeValue(d, id, size, v.Index(n))
}
func decodeString(d *Decoder, id Id, size int64, v reflect.Value) {
buf := make([]byte, size)
_, err := d.r.Read(buf)
@ -157,9 +205,10 @@ func decodeString(d *Decoder, id Id, size int64, v reflect.Value) {
func decodeStruct(d *Decoder, id Id, size int64, v reflect.Value) {
t := v.Type()
// get Id -> field mappings
// get Id to field mappings
idField := cachedFieldIdMap(t)
fieldFunc := cachedFieldDecoderTable(t)
// BUG(Emery): not caching decoder funtions for struct fields is suboptimal
//fieldFunc := cachedFieldDecoderTable(t)
var n int
var subId Id
@ -176,8 +225,18 @@ func decodeStruct(d *Decoder, id Id, size int64, v reflect.Value) {
if n, ok = idField[subId]; !ok {
continue
}
// use the cached decoder funtion for field
fieldFunc[n](d, subId, subSize, v.Field(n))
decodeValue(d, subId, subSize, v.Field(n))
/*
subV = v.Field(n)
// Derefence pointer
for subV.Kind() == reflect.Ptr {
subV = subV.Elem()
}
// use the cached decoder funtion for field
fieldFunc[n]
*/
size -= subSize
}
}

View File

@ -307,3 +307,19 @@ func marshalString(id Id, s string) encoder {
copy(b[n:], sb)
return b
}
func newStructEncoder(id Id, v reflect.Value) encoder {
e := &containerElement{id: id}
for fid, i := range cachedFieldIdMap(v.Type()) {
fv := v.Field(i)
if !fv.IsValid() || isEmptyValue(fv) {
continue
}
fe := newEncoder(fid, fv)
if e != nil {
e.Append(fe)
}
}
return e
}

View File

@ -56,6 +56,8 @@ type Decoder struct {
// NewDecoder returns a new decoder that decodes from r.
func NewDecoder(r io.ReadSeeker) *Decoder {
// TODO(Emery): just ask for a io.Reader and try and make it
// an io.ReadSeeker
return &Decoder{r: r, buf: make([]byte, 8)}
}
@ -123,12 +125,6 @@ func Unmarshal(data []byte, element interface{}) error {
// shall be written and is used to build the element header
// and compute the size of the parent element before it is
// writen to an EBML stream.
//
// If a struct both implements Marshaler and contains ebml
// tagged fields, the fields will be ignored. This implies
// if a Marshaler is an embedded field, the parent struct
// will inherit it's interface, and the marshaler will take
// the place of the parent in the encoder.
type Marshaler interface {
// BUG(Emery): an embedded Marshaler will trample on a struct
MarshalEBML() (wt io.WriterTo, size int64)
@ -137,23 +133,16 @@ type Marshaler interface {
// Unmarshaler is the interface implemented by objects that
// can unmarshal themselves from an EBML stream. The data
// read into ReaderFrom will contain the data for the element
// being unmarshaled, and not an id or size header. n shall be
// the size of the element data, and it is the resposibility of
// the unmarshaler to not read beyond n.
// being unmarshaled, and not an id or size header.
//
// If a struct both implements Unmarshaler and contains ebml
// tagged fields, the fields will be ignored. This implies
// that if an Unmarshaler is an embedded field, the parent
// struct will inherit it's interface, and the marshaler will
// take the place of the parent in the decoder.
// n shall be the size of the element data, and it is not the
// resposibility of an Unmarshaler to limit reading to n.
//
// An Unmarshaler is usually sent to the decoding engine as a nil pointer
// in a struct and created when a tagged element is encountered, for this
// reason the UnmarshalEBML method should behave as if the Unmarshaler is
// at a zero value state.
type Unmarshaler interface {
// BUG(Emery): an embedded Unmarshaler will trample on a struct
UnmarshalEBML(n int64) io.ReaderFrom
}
// MarshalUnmarshaler is an interface that
// combines both the Marshaler and Unmarshaler.
type MarshalUnmarshaler interface {
Marshaler
Unmarshaler
}