From 496fca7357463f6ca05acab688018e8a1dc11855 Mon Sep 17 00:00:00 2001 From: Luke Shumaker Date: Mon, 27 Jan 2020 19:14:44 -0500 Subject: rrdbinary: Switch to a interface-based incremental decoder --- rrdformat/rrdbinary/types.go | 10 ++++ rrdformat/rrdbinary/unmarshal.go | 122 ++++++++++++++++----------------------- 2 files changed, 59 insertions(+), 73 deletions(-) diff --git a/rrdformat/rrdbinary/types.go b/rrdformat/rrdbinary/types.go index 006af3c..0bf4913 100644 --- a/rrdformat/rrdbinary/types.go +++ b/rrdformat/rrdbinary/types.go @@ -31,3 +31,13 @@ type EOF struct{} // 0 bytes func (u Unival) AsUint64() uint64 { return uint64(u) } func (u Unival) AsFloat64() float64 { return math.Float64frombits(uint64(u)) } + +// Statically assert that each of the above types implements the +// 'unmarshaler' interface. +var _ unmarshaler = func() *String { return nil }() +var _ unmarshaler = func() *Float { return nil }() +var _ unmarshaler = func() *Uint { return nil }() +var _ unmarshaler = func() *Int { return nil }() +var _ unmarshaler = func() *Unival { return nil }() +var _ unmarshaler = func() *Time { return nil }() +var _ unmarshaler = func() *EOF { return nil }() diff --git a/rrdformat/rrdbinary/unmarshal.go b/rrdformat/rrdbinary/unmarshal.go index 142ee9f..00f1222 100644 --- a/rrdformat/rrdbinary/unmarshal.go +++ b/rrdformat/rrdbinary/unmarshal.go @@ -9,62 +9,54 @@ import ( "strings" ) +type Decoder struct { + arch Architecture + pos int + data []byte +} + +type unmarshaler interface { + unmarshalRRD(d *Decoder, tag string) error +} + func Unmarshal(arch Architecture, data []byte, ptr interface{}) error { ptrValue := reflect.ValueOf(ptr) if ptrValue.Kind() != reflect.Ptr { return typeErrorf("ptr is a %v, not a pointer", ptrValue.Kind()) } - decoder := &unmarshaler{ + decoder := &Decoder{ arch: arch, pos: 0, data: data, } - return decoder.unmarshal(ptrValue, "") + return decoder.Decode(ptrValue, "") } -type unmarshaler struct { - arch Architecture - pos int - data []byte -} - -func (d *unmarshaler) binError(ctxLen int, msg string) error { +func (d *Decoder) binError(ctxLen int, msg string) error { return NewBinError(msg, d.data, d.pos, ctxLen) } -func (d *unmarshaler) binErrorf(ctxLen int, format string, a ...interface{}) error { +func (d *Decoder) binErrorf(ctxLen int, format string, a ...interface{}) error { return d.binError(ctxLen, fmt.Sprintf(format, a...)) } -func (d *unmarshaler) unmarshal(v reflect.Value, tag string) error { - switch v.Type() { - case reflect.TypeOf(String("")): - return d.unmarshalString(v, tag) - case reflect.TypeOf(Float(0)): - return d.unmarshalFloat(v, tag) - case reflect.TypeOf(Uint(0)): - return d.unmarshalUint(v, tag) - case reflect.TypeOf(Int(0)): - return d.unmarshalInt(v, tag) - case reflect.TypeOf(Unival(0)): - return d.unmarshalUnival(v, tag) - case reflect.TypeOf(Time(0)): - return d.unmarshalTime(v, tag) - case reflect.TypeOf(EOF{}): - return d.unmarshalEOF(v, tag) - default: - switch v.Type().Kind() { - case reflect.Struct: - return d.unmarshalStruct(v, tag) - case reflect.Array, reflect.Slice: - return d.unmarshalList(v, tag) - default: - return typeErrorf("invalid type for rrdbinary.Unmarshal: %v", v.Type()) +func (d *Decoder) Decode(v reflect.Value, tag string) error { + if v.CanInterface() { + if u, ok := v.Interface().(unmarshaler); ok { + return u.unmarshalRRD(d, tag) } } + switch v.Type().Kind() { + case reflect.Struct: + return d.decodeStruct(v, tag) + case reflect.Array, reflect.Slice: + return d.decodeList(v, tag) + default: + return typeErrorf("invalid type for rrdbinary.Decoder.Decode: %v", v.Type()) + } } -func (d *unmarshaler) unmarshalStruct(v reflect.Value, tag string) error { +func (d *Decoder) decodeStruct(v reflect.Value, tag string) error { panicUnless(v.Kind() == reflect.Struct) panicUnless(v.CanSet()) @@ -78,29 +70,26 @@ func (d *unmarshaler) unmarshalStruct(v reflect.Value, tag string) error { if tag == "-" { continue } - if err := d.unmarshal(v.Field(i), tag); err != nil { + if err := d.Decode(v.Field(i), tag); err != nil { return fmt.Errorf("field %s: %w", fieldInfo.Name, err) } } return nil } -func (d *unmarshaler) unmarshalList(v reflect.Value, tag string) error { +func (d *Decoder) decodeList(v reflect.Value, tag string) error { panicUnless(v.Kind() == reflect.Array || v.Kind() == reflect.Slice) panicUnless(v.CanSet()) for i := 0; i < v.Len(); i++ { - if err := d.unmarshal(v.Index(i), tag); err != nil { + if err := d.Decode(v.Index(i), tag); err != nil { return fmt.Errorf("index %d: %w", i, err) } } return nil } -func (d *unmarshaler) unmarshalString(v reflect.Value, tag string) error { - panicUnless(v.Type() == reflect.TypeOf(String(""))) - panicUnless(v.CanSet()) - +func (obj *String) unmarshalRRD(d *Decoder, tag string) error { size := 0 switch { case tag == "": @@ -142,15 +131,12 @@ func (d *unmarshaler) unmarshalString(v reflect.Value, tag string) error { size = nul + 1 } - v.SetString(string(data[:nul])) + *obj = String(data[:nul]) d.pos += size return nil } -func (d *unmarshaler) unmarshalFloat(v reflect.Value, tag string) error { - panicUnless(v.Type() == reflect.TypeOf(Float(0))) - panicUnless(v.CanSet()) - +func (obj *Float) unmarshalRRD(d *Decoder, tag string) error { if d.arch.FloatWidth != 8 { return archErrorf("rrdbinary does not support FloatWidth=%d; only supports 8", d.arch.FloatWidth) } @@ -173,15 +159,12 @@ func (d *unmarshaler) unmarshalFloat(v reflect.Value, tag string) error { return d.binErrorf(d.arch.FloatWidth, "unexpected end-of-file in %d-byte float", d.arch.FloatWidth) } - v.SetFloat(math.Float64frombits(d.arch.ByteOrder.Uint64(data))) + *obj = Float(math.Float64frombits(d.arch.ByteOrder.Uint64(data))) d.pos += padding + d.arch.FloatWidth return nil } -func (d *unmarshaler) unmarshalUint(v reflect.Value, tag string) error { - panicUnless(v.Type() == reflect.TypeOf(Uint(0))) - panicUnless(v.CanSet()) - +func (obj *Uint) unmarshalRRD(d *Decoder, tag string) error { if d.arch.IntWidth != 4 && d.arch.IntWidth != 8 { return archErrorf("rrdbinary does not support IntWidth=%d; only supports 4 or 8", d.arch.IntWidth) } @@ -206,18 +189,15 @@ func (d *unmarshaler) unmarshalUint(v reflect.Value, tag string) error { switch d.arch.IntWidth { case 4: - v.SetUint(uint64(d.arch.ByteOrder.Uint32(data))) + *obj = Uint(d.arch.ByteOrder.Uint32(data)) case 8: - v.SetUint(d.arch.ByteOrder.Uint64(data)) + *obj = Uint(d.arch.ByteOrder.Uint64(data)) } d.pos += padding + d.arch.IntWidth return nil } -func (d *unmarshaler) unmarshalInt(v reflect.Value, tag string) error { - panicUnless(v.Type() == reflect.TypeOf(Int(0))) - panicUnless(v.CanSet()) - +func (obj *Int) unmarshalRRD(d *Decoder, tag string) error { if d.arch.IntWidth != 4 && d.arch.IntWidth != 8 { return archErrorf("rrdbinary does not support IntWidth=%d; only supports 4 or 8", d.arch.IntWidth) } @@ -242,18 +222,15 @@ func (d *unmarshaler) unmarshalInt(v reflect.Value, tag string) error { switch d.arch.IntWidth { case 4: - v.SetInt(int64(int32(d.arch.ByteOrder.Uint32(data)))) + *obj = Int(int32(d.arch.ByteOrder.Uint32(data))) case 8: - v.SetInt(int64(d.arch.ByteOrder.Uint64(data))) + *obj = Int(d.arch.ByteOrder.Uint64(data)) } d.pos += padding + d.arch.IntWidth return nil } -func (d *unmarshaler) unmarshalUnival(v reflect.Value, tag string) error { - panicUnless(v.Type() == reflect.TypeOf(Unival(0))) - panicUnless(v.CanSet()) - +func (obj *Unival) unmarshalRRD(d *Decoder, tag string) error { if d.arch.UnivalWidth != 8 { return archErrorf("rrdbinary does not support UnivalWidth=%d; only supports 8", d.arch.UnivalWidth) } @@ -276,15 +253,12 @@ func (d *unmarshaler) unmarshalUnival(v reflect.Value, tag string) error { return d.binErrorf(d.arch.UnivalWidth, "unexpected end-of-file in %d-byte unival", d.arch.UnivalWidth) } - v.SetUint(d.arch.ByteOrder.Uint64(data)) + *obj = Unival(d.arch.ByteOrder.Uint64(data)) d.pos += padding + d.arch.UnivalWidth return nil } -func (d *unmarshaler) unmarshalTime(v reflect.Value, tag string) error { - panicUnless(v.Type() == reflect.TypeOf(Time(0))) - panicUnless(v.CanSet()) - +func (obj *Time) unmarshalRRD(d *Decoder, tag string) error { if d.arch.TimeWidth != 4 && d.arch.TimeWidth != 8 { return archErrorf("rrdbinary does not support TimeWidth=%d; only supports 4 or 8", d.arch.TimeWidth) } @@ -309,16 +283,18 @@ func (d *unmarshaler) unmarshalTime(v reflect.Value, tag string) error { switch d.arch.TimeWidth { case 4: - v.SetInt(int64(int32(d.arch.ByteOrder.Uint32(data)))) + *obj = Time(int32(d.arch.ByteOrder.Uint32(data))) case 8: - v.SetInt(int64(d.arch.ByteOrder.Uint64(data))) + *obj = Time(d.arch.ByteOrder.Uint64(data)) } d.pos += padding + d.arch.TimeWidth return nil } -func (d *unmarshaler) unmarshalEOF(v reflect.Value, tag string) error { - panicUnless(v.Type() == reflect.TypeOf(EOF{})) +func (_ *EOF) unmarshalRRD(d *Decoder, tag string) error { + if tag != "" { + return typeErrorf("invalid rrdbinary struct tag for eof: %q", tag) + } data := d.data[d.pos:] if len(data) > 0 { -- cgit v1.2.3