working Encoder and Decoder with Marshaler and Unmarshaler interfaces.
This commit is contained in:
parent
3f6cde68d4
commit
4659ce9614
19
cache.go
19
cache.go
|
@ -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
|
||||
}
|
||||
|
||||
*/
|
||||
|
|
85
decode.go
85
decode.go
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
16
encode.go
16
encode.go
|
@ -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
|
||||
}
|
||||
|
||||
|
|
31
stream.go
31
stream.go
|
@ -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
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user