commit f8719a06fabf4d2a6bd31d103b937b06f95cbdc1 Author: Pavel Merzlyakov Date: Sat Oct 1 21:05:46 2022 +0300 initial commit diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..bafc76b --- /dev/null +++ b/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2013 The msgpack for Go Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..f41a79e --- /dev/null +++ b/README.md @@ -0,0 +1,87 @@ +Msgpack implementation for Golang +================================= + +Supports: +- Primitives, arrays, maps, structs and interface{}. +- time.Time. +- Appengine *datastore.Key and datastore.Cursor. +- Extensions for user defined types. +- Tags. + +API docs: http://godoc.org/github.com/vmihailenco/msgpack + +Installation +------------ + +Install: + + go get github.com/vmihailenco/msgpack + +Usage +----- + +Examples: + + func ExampleEncode() { + b, err := msgpack.Marshal(true) + fmt.Printf("%v %#v\n", err, b) + // Output: []byte{0xc3} + } + + func ExampleDecode() { + var out bool + err := msgpack.Unmarshal([]byte{0xc3}, &out) + fmt.Println(err, out) + // Output: true + } + + func ExampleMapStringInterface() { + in := map[string]interface{}{"foo": 1, "hello": "world"} + b, err := msgpack.Marshal(in) + _ = err + + var out map[string]interface{} + err = msgpack.Unmarshal(b, &out) + fmt.Printf("%v %#v\n", err, out) + // Output: map[string]interface {}{"foo":1, "hello":"world"} + } + + func ExampleRecursiveMapStringInterface() { + buf := &bytes.Buffer{} + + enc := msgpack.NewEncoder(buf) + in := map[string]interface{}{"foo": map[string]interface{}{"hello": "world"}} + _ = enc.Encode(in) + + dec := msgpack.NewDecoder(buf) + dec.DecodeMapFunc = func(d *msgpack.Decoder) (interface{}, error) { + n, err := d.DecodeMapLen() + if err != nil { + return nil, err + } + + m := make(map[string]interface{}, n) + for i := 0; i < n; i++ { + mk, err := d.DecodeString() + if err != nil { + return nil, err + } + + mv, err := d.DecodeInterface() + if err != nil { + return nil, err + } + + m[mk] = mv + } + return m, nil + } + out, err := dec.DecodeInterface() + fmt.Printf("%v %#v\n", err, out) + // Output: map[string]interface {}{"foo":map[string]interface {}{"hello":"world"}} + } + +Extensions +---------- + +Look at [appengine.go](https://github.com/vmihailenco/msgpack/blob/master/appengine.go) for example. diff --git a/appengine.go b/appengine.go new file mode 100644 index 0000000..ef7788a --- /dev/null +++ b/appengine.go @@ -0,0 +1,69 @@ +// +build appengine + +package msgpack + +import ( + "reflect" + + ds "appengine/datastore" +) + +var ( + keyPtrType = reflect.TypeOf((*ds.Key)(nil)) + cursorType = reflect.TypeOf((*ds.Cursor)(nil)).Elem() +) + +func init() { + Register(keyPtrType, encodeDatastoreKeyValue, decodeDatastoreKeyValue) + Register(cursorType, encodeDatastoreCursorValue, decodeDatastoreCursorValue) +} + +func EncodeDatastoreKey(e *Encoder, key *ds.Key) error { + if key == nil { + return e.EncodeNil() + } + return e.EncodeString(key.Encode()) +} + +func encodeDatastoreKeyValue(e *Encoder, v reflect.Value) error { + key := v.Interface().(*ds.Key) + return EncodeDatastoreKey(e, key) +} + +func DecodeDatastoreKey(d *Decoder) (*ds.Key, error) { + v, err := d.DecodeString() + if err != nil { + return nil, err + } + if v == "" { + return nil, nil + } + return ds.DecodeKey(v) +} + +func decodeDatastoreKeyValue(d *Decoder, v reflect.Value) error { + key, err := DecodeDatastoreKey(d) + if err != nil { + return err + } + v.Set(reflect.ValueOf(key)) + return nil +} + +func encodeDatastoreCursorValue(e *Encoder, v reflect.Value) error { + cursor := v.Interface().(ds.Cursor) + return e.Encode(cursor.String()) +} + +func decodeDatastoreCursorValue(d *Decoder, v reflect.Value) error { + s, err := d.DecodeString() + if err != nil { + return err + } + cursor, err := ds.DecodeCursor(s) + if err != nil { + return err + } + v.Set(reflect.ValueOf(cursor)) + return nil +} diff --git a/codes.go b/codes.go new file mode 100644 index 0000000..497cfc5 --- /dev/null +++ b/codes.go @@ -0,0 +1,42 @@ +package msgpack + +const ( + posFixNumHighCode = 0x7f + negFixNumLowCode = 0xe0 + + nilCode = 0xc0 + + falseCode = 0xc2 + trueCode = 0xc3 + + floatCode = 0xca + doubleCode = 0xcb + + uint8Code = 0xcc + uint16Code = 0xcd + uint32Code = 0xce + uint64Code = 0xcf + + int8Code = 0xd0 + int16Code = 0xd1 + int32Code = 0xd2 + int64Code = 0xd3 + + fixRawLowCode = 0xa0 + fixRawHighCode = 0xbf + fixRawMask = 0x1f + raw16Code = 0xda + raw32Code = 0xdb + + fixArrayLowCode = 0x90 + fixArrayHighCode = 0x9f + fixArrayMask = 0xf + array16Code = 0xdc + array32Code = 0xdd + + fixMapLowCode = 0x80 + fixMapHighCode = 0x8f + fixMapMask = 0xf + map16Code = 0xde + map32Code = 0xdf +) diff --git a/custom.go b/custom.go new file mode 100644 index 0000000..69e9459 --- /dev/null +++ b/custom.go @@ -0,0 +1,19 @@ +package msgpack + +import ( + "reflect" +) + +var ( + typEncMap = make(map[reflect.Type]encoderFunc) + typDecMap = make(map[reflect.Type]decoderFunc) +) + +type encoderFunc func(*Encoder, reflect.Value) error + +type decoderFunc func(*Decoder, reflect.Value) error + +func Register(typ reflect.Type, enc encoderFunc, dec decoderFunc) { + typEncMap[typ] = enc + typDecMap[typ] = dec +} diff --git a/decode.go b/decode.go new file mode 100644 index 0000000..ceabd17 --- /dev/null +++ b/decode.go @@ -0,0 +1,807 @@ +package msgpack + +import ( + "errors" + "fmt" + "io" + "math" + "reflect" + "time" + + "github.com/vmihailenco/bufio" +) + +type bufReader interface { + Read([]byte) (int, error) + ReadByte() (byte, error) + UnreadByte() error + Peek(int) ([]byte, error) + ReadN(int) ([]byte, error) +} + +func Unmarshal(data []byte, v ...interface{}) error { + buf := bufio.NewBuffer(data) + return NewDecoder(buf).Decode(v...) +} + +type Decoder struct { + R bufReader + DecodeMapFunc func(*Decoder) (interface{}, error) +} + +func NewDecoder(rd io.Reader) *Decoder { + brd, ok := rd.(bufReader) + if !ok { + brd = bufio.NewReader(rd) + } + return &Decoder{ + R: brd, + DecodeMapFunc: decodeMap, + } +} + +func (d *Decoder) Decode(v ...interface{}) error { + for _, vv := range v { + if err := d.decode(vv); err != nil { + return err + } + } + return nil +} + +func (d *Decoder) decode(iv interface{}) error { + var err error + switch v := iv.(type) { + case *string: + if v != nil { + *v, err = d.DecodeString() + return err + } + case *[]byte: + if v != nil { + *v, err = d.DecodeBytes() + return err + } + case *int: + if v != nil { + *v, err = d.DecodeInt() + return err + } + case *int8: + if v != nil { + *v, err = d.DecodeInt8() + return err + } + case *int16: + if v != nil { + *v, err = d.DecodeInt16() + return err + } + case *int32: + if v != nil { + *v, err = d.DecodeInt32() + return err + } + case *int64: + if v != nil { + *v, err = d.DecodeInt64() + return err + } + case *uint: + if v != nil { + *v, err = d.DecodeUint() + return err + } + case *uint8: + if v != nil { + *v, err = d.DecodeUint8() + return err + } + case *uint16: + if v != nil { + *v, err = d.DecodeUint16() + return err + } + case *uint32: + if v != nil { + *v, err = d.DecodeUint32() + return err + } + case *uint64: + if v != nil { + *v, err = d.DecodeUint64() + return err + } + case *bool: + if v != nil { + *v, err = d.DecodeBool() + return err + } + case *float32: + if v != nil { + *v, err = d.DecodeFloat32() + return err + } + case *float64: + if v != nil { + *v, err = d.DecodeFloat64() + return err + } + case *[]string: + return d.decodeIntoStrings(v) + case *map[string]string: + return d.decodeIntoMapStringString(v) + case *time.Duration: + if v != nil { + vv, err := d.DecodeInt64() + *v = time.Duration(vv) + return err + } + case *time.Time: + if v != nil { + *v, err = d.DecodeTime() + return err + } + } + + v := reflect.ValueOf(iv) + if !v.IsValid() { + return errors.New("msgpack: Decode(" + v.String() + ")") + } + if v.Kind() != reflect.Ptr { + return errors.New("msgpack: pointer expected") + } + return d.DecodeValue(v) +} + +func (d *Decoder) DecodeValue(v reflect.Value) error { + c, err := d.R.ReadByte() + if err != nil { + return err + } + if c == nilCode { + return nil + } + if err := d.R.UnreadByte(); err != nil { + return err + } + + switch v.Kind() { + case reflect.Bool: + return d.boolValue(v) + case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint: + return d.uint64Value(v) + case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int: + return d.int64Value(v) + case reflect.Float32: + return d.float32Value(v) + case reflect.Float64: + return d.float64Value(v) + case reflect.String: + return d.stringValue(v) + case reflect.Array, reflect.Slice: + return d.sliceValue(v) + case reflect.Map: + return d.mapValue(v) + case reflect.Struct: + typ := v.Type() + if dec, ok := typDecMap[typ]; ok { + return dec(d, v) + } + if dec, ok := v.Interface().(decoder); ok { + return dec.DecodeMsgpack(d.R) + } + return d.structValue(v) + case reflect.Ptr: + typ := v.Type() + if v.IsNil() { + v.Set(reflect.New(typ.Elem())) + } + if dec, ok := typDecMap[typ]; ok { + return dec(d, v) + } + if dec, ok := v.Interface().(decoder); ok { + return dec.DecodeMsgpack(d.R) + } + return d.DecodeValue(v.Elem()) + case reflect.Interface: + if v.IsNil() { + return d.interfaceValue(v) + } else { + return d.DecodeValue(v.Elem()) + } + } + return fmt.Errorf("msgpack: unsupported type %v", v.Type().String()) +} + +func (d *Decoder) DecodeBool() (bool, error) { + c, err := d.R.ReadByte() + if err != nil { + return false, err + } + switch c { + case falseCode: + return false, nil + case trueCode: + return true, nil + } + return false, fmt.Errorf("msgpack: invalid code %x decoding bool", c) +} + +func (d *Decoder) boolValue(value reflect.Value) error { + v, err := d.DecodeBool() + if err != nil { + return err + } + value.SetBool(v) + return nil +} + +func (d *Decoder) uint16() (uint16, error) { + b, err := d.R.ReadN(2) + if err != nil { + return 0, err + } + return (uint16(b[0]) << 8) | uint16(b[1]), nil +} + +func (d *Decoder) uint32() (uint32, error) { + b, err := d.R.ReadN(4) + if err != nil { + return 0, err + } + n := (uint32(b[0]) << 24) | + (uint32(b[1]) << 16) | + (uint32(b[2]) << 8) | + uint32(b[3]) + return n, nil +} + +func (d *Decoder) uint64() (uint64, error) { + b, err := d.R.ReadN(8) + if err != nil { + return 0, err + } + n := (uint64(b[0]) << 56) | + (uint64(b[1]) << 48) | + (uint64(b[2]) << 40) | + (uint64(b[3]) << 32) | + (uint64(b[4]) << 24) | + (uint64(b[5]) << 16) | + (uint64(b[6]) << 8) | + uint64(b[7]) + return n, nil +} + +func (d *Decoder) DecodeUint64() (uint64, error) { + c, err := d.R.ReadByte() + if err != nil { + return 0, err + } + if c <= posFixNumHighCode { + return uint64(c), nil + } + switch c { + case uint8Code: + c, err := d.R.ReadByte() + if err != nil { + return 0, err + } + return uint64(c), nil + case uint16Code: + b, err := d.R.ReadN(2) + if err != nil { + return 0, err + } + return (uint64(b[0]) << 8) | uint64(b[1]), nil + case uint32Code: + b, err := d.R.ReadN(4) + if err != nil { + return 0, err + } + v := (uint64(b[0]) << 24) | + (uint64(b[1]) << 16) | + (uint64(b[2]) << 8) | + uint64(b[3]) + return v, nil + case uint64Code: + b, err := d.R.ReadN(8) + if err != nil { + return 0, err + } + v := (uint64(b[0]) << 56) | + (uint64(b[1]) << 48) | + (uint64(b[2]) << 40) | + (uint64(b[3]) << 32) | + (uint64(b[4]) << 24) | + (uint64(b[5]) << 16) | + (uint64(b[6]) << 8) | + uint64(b[7]) + return v, nil + } + return 0, fmt.Errorf("msgpack: invalid code %x decoding uint64", c) +} + +func (d *Decoder) uint64Value(value reflect.Value) error { + v, err := d.DecodeUint64() + if err != nil { + return err + } + value.SetUint(v) + return nil +} + +func (d *Decoder) DecodeInt64() (int64, error) { + c, err := d.R.ReadByte() + if err != nil { + return 0, err + } + if c <= posFixNumHighCode || c >= negFixNumLowCode { + return int64(int8(c)), nil + } + switch c { + case int8Code: + c, err := d.R.ReadByte() + if err != nil { + return 0, err + } + return int64(int8(c)), nil + case int16Code: + b, err := d.R.ReadN(2) + if err != nil { + return 0, err + } + return int64((int16(b[0]) << 8) | int16(b[1])), nil + case int32Code: + b, err := d.R.ReadN(4) + if err != nil { + return 0, err + } + v := int64((int32(b[0]) << 24) | + (int32(b[1]) << 16) | + (int32(b[2]) << 8) | + int32(b[3])) + return v, nil + case int64Code: + b, err := d.R.ReadN(8) + if err != nil { + return 0, err + } + v := (int64(b[0]) << 56) | + (int64(b[1]) << 48) | + (int64(b[2]) << 40) | + (int64(b[3]) << 32) | + (int64(b[4]) << 24) | + (int64(b[5]) << 16) | + (int64(b[6]) << 8) | + int64(b[7]) + return v, nil + } + return 0, fmt.Errorf("msgpack: invalid code %x decoding int64", c) +} + +func (d *Decoder) int64Value(value reflect.Value) error { + v, err := d.DecodeInt64() + if err != nil { + return err + } + value.SetInt(v) + return nil +} + +func (d *Decoder) DecodeFloat32() (float32, error) { + c, err := d.R.ReadByte() + if err != nil { + return 0, err + } + if c != floatCode { + return 0, fmt.Errorf("msgpack: invalid code %x decoding float32", c) + } + b, err := d.uint32() + if err != nil { + return 0, err + } + return math.Float32frombits(b), nil +} + +func (d *Decoder) float32Value(value reflect.Value) error { + v, err := d.DecodeFloat32() + if err != nil { + return err + } + value.SetFloat(float64(v)) + return nil +} + +func (d *Decoder) DecodeFloat64() (float64, error) { + c, err := d.R.ReadByte() + if err != nil { + return 0, err + } + if c != doubleCode { + return 0, fmt.Errorf("msgpack: invalid code %x decoding float64", c) + } + b, err := d.uint64() + if err != nil { + return 0, err + } + return math.Float64frombits(b), nil +} + +func (d *Decoder) float64Value(value reflect.Value) error { + v, err := d.DecodeFloat64() + if err != nil { + return err + } + value.SetFloat(v) + return nil +} + +func (d *Decoder) structLen() (int, error) { + c, err := d.R.ReadByte() + if err != nil { + return 0, err + } + if c >= fixMapLowCode && c <= fixMapHighCode { + return int(c & fixMapMask), nil + } + switch c { + case map16Code: + n, err := d.uint16() + return int(n), err + case map32Code: + n, err := d.uint32() + return int(n), err + } + return 0, fmt.Errorf("msgpack: invalid code %x decoding struct length", c) +} + +func (d *Decoder) structValue(v reflect.Value) error { + n, err := d.structLen() + if err != nil { + return err + } + + typ := v.Type() + for i := 0; i < n; i++ { + name, err := d.DecodeString() + if err != nil { + return err + } + + f := structs.Field(typ, name) + if f != nil { + if err := f.DecodeValue(d, v); err != nil { + return err + } + } else { + _, err := d.DecodeInterface() + if err != nil { + return err + } + } + } + return nil +} + +//------------------------------------------------------------------------------ + +func (d *Decoder) DecodeUint() (uint, error) { + c, err := d.R.ReadByte() + if err != nil { + return 0, err + } + if c <= posFixNumHighCode { + return uint(c), nil + } + switch c { + case uint8Code: + c, err := d.R.ReadByte() + if err != nil { + return 0, err + } + return uint(c), nil + case uint16Code: + b, err := d.R.ReadN(2) + if err != nil { + return 0, err + } + return (uint(b[0]) << 8) | uint(b[1]), nil + case uint32Code: + b, err := d.R.ReadN(4) + if err != nil { + return 0, err + } + v := (uint(b[0]) << 24) | + (uint(b[1]) << 16) | + (uint(b[2]) << 8) | + uint(b[3]) + return v, nil + case uint64Code: + b, err := d.R.ReadN(8) + if err != nil { + return 0, err + } + v := (uint(b[0]) << 56) | + (uint(b[1]) << 48) | + (uint(b[2]) << 40) | + (uint(b[3]) << 32) | + (uint(b[4]) << 24) | + (uint(b[5]) << 16) | + (uint(b[6]) << 8) | + uint(b[7]) + return v, nil + } + return 0, fmt.Errorf("msgpack: invalid code %x decoding uint", c) +} + +func (d *Decoder) DecodeUint8() (uint8, error) { + c, err := d.R.ReadByte() + if err != nil { + return 0, err + } + if c <= posFixNumHighCode { + return uint8(c), nil + } + switch c { + case uint8Code: + c, err := d.R.ReadByte() + if err != nil { + return 0, err + } + return uint8(c), nil + } + return 0, fmt.Errorf("msgpack: invalid code %x decoding uint8", c) +} + +func (d *Decoder) DecodeUint16() (uint16, error) { + c, err := d.R.ReadByte() + if err != nil { + return 0, err + } + if c <= posFixNumHighCode { + return uint16(c), nil + } + switch c { + case uint8Code: + c, err := d.R.ReadByte() + if err != nil { + return 0, err + } + return uint16(c), nil + case uint16Code: + b, err := d.R.ReadN(2) + if err != nil { + return 0, err + } + return (uint16(b[0]) << 8) | uint16(b[1]), nil + } + return 0, fmt.Errorf("msgpack: invalid code %x decoding uint16", c) +} + +func (d *Decoder) DecodeUint32() (uint32, error) { + c, err := d.R.ReadByte() + if err != nil { + return 0, err + } + if c <= posFixNumHighCode { + return uint32(c), nil + } + switch c { + case uint8Code: + c, err := d.R.ReadByte() + if err != nil { + return 0, err + } + return uint32(c), nil + case uint16Code: + b, err := d.R.ReadN(2) + if err != nil { + return 0, err + } + return (uint32(b[0]) << 8) | uint32(b[1]), nil + case uint32Code: + b, err := d.R.ReadN(4) + if err != nil { + return 0, err + } + v := (uint32(b[0]) << 24) | + (uint32(b[1]) << 16) | + (uint32(b[2]) << 8) | + uint32(b[3]) + return v, nil + } + return 0, fmt.Errorf("msgpack: invalid code %x decoding uint32", c) +} + +//------------------------------------------------------------------------------ + +func (d *Decoder) DecodeInt() (int, error) { + c, err := d.R.ReadByte() + if err != nil { + return 0, err + } + if c <= posFixNumHighCode || c >= negFixNumLowCode { + return int(int8(c)), nil + } + switch c { + case int8Code: + c, err := d.R.ReadByte() + if err != nil { + return 0, err + } + return int(int8(c)), nil + case int16Code: + b, err := d.R.ReadN(2) + if err != nil { + return 0, err + } + return int((int16(b[0]) << 8) | int16(b[1])), nil + case int32Code: + b, err := d.R.ReadN(4) + if err != nil { + return 0, err + } + v := int((int32(b[0]) << 24) | + (int32(b[1]) << 16) | + (int32(b[2]) << 8) | + int32(b[3])) + return v, nil + case int64Code: + b, err := d.R.ReadN(8) + if err != nil { + return 0, err + } + v := int((int64(b[0]) << 56) | + (int64(b[1]) << 48) | + (int64(b[2]) << 40) | + (int64(b[3]) << 32) | + (int64(b[4]) << 24) | + (int64(b[5]) << 16) | + (int64(b[6]) << 8) | + int64(b[7])) + return v, nil + } + return 0, fmt.Errorf("msgpack: invalid code %x decoding int64", c) +} + +func (d *Decoder) DecodeInt8() (int8, error) { + c, err := d.R.ReadByte() + if err != nil { + return 0, err + } + if c <= posFixNumHighCode || c >= negFixNumLowCode { + return int8(c), nil + } + switch c { + case int8Code: + c, err := d.R.ReadByte() + if err != nil { + return 0, err + } + return int8(c), nil + } + return 0, fmt.Errorf("msgpack: invalid code %x decoding int8", c) +} + +func (d *Decoder) DecodeInt16() (int16, error) { + c, err := d.R.ReadByte() + if err != nil { + return 0, err + } + if c <= posFixNumHighCode || c >= negFixNumLowCode { + return int16(int8(c)), nil + } + switch c { + case int8Code: + c, err := d.R.ReadByte() + if err != nil { + return 0, err + } + return int16(int8(c)), nil + case int16Code: + b, err := d.R.ReadN(2) + if err != nil { + return 0, err + } + return (int16(b[0]) << 8) | int16(b[1]), nil + } + return 0, fmt.Errorf("msgpack: invalid code %x decoding int16", c) +} + +func (d *Decoder) DecodeInt32() (int32, error) { + c, err := d.R.ReadByte() + if err != nil { + return 0, err + } + if c <= posFixNumHighCode || c >= negFixNumLowCode { + return int32(int8(c)), nil + } + switch c { + case int8Code: + c, err := d.R.ReadByte() + if err != nil { + return 0, err + } + return int32(int8(c)), nil + case int16Code: + b, err := d.R.ReadN(2) + if err != nil { + return 0, err + } + return int32((int16(b[0]) << 8) | int16(b[1])), nil + case int32Code: + b, err := d.R.ReadN(4) + if err != nil { + return 0, err + } + v := (int32(b[0]) << 24) | + (int32(b[1]) << 16) | + (int32(b[2]) << 8) | + int32(b[3]) + return v, nil + } + return 0, fmt.Errorf("msgpack: invalid code %x decoding int32", c) +} + +//------------------------------------------------------------------------------ + +func (d *Decoder) interfaceValue(v reflect.Value) error { + iface, err := d.DecodeInterface() + if err != nil { + return err + } + v.Set(reflect.ValueOf(iface)) + return nil +} + +// Decodes value into interface. Possible value types are: +// - nil, +// - int64, +// - uint64, +// - bool, +// - float32 and float64, +// - string, +// - slices of any of the above, +// - maps of any of the above. +func (d *Decoder) DecodeInterface() (interface{}, error) { + b, err := d.R.Peek(1) + if err != nil { + return nil, err + } + c := b[0] + + if c <= posFixNumHighCode || c >= negFixNumLowCode { + return d.DecodeInt64() + } else if c >= fixMapLowCode && c <= fixMapHighCode { + return d.DecodeMap() + } else if c >= fixArrayLowCode && c <= fixArrayHighCode { + return d.DecodeSlice() + } else if c >= fixRawLowCode && c <= fixRawHighCode { + return d.DecodeString() + } + + switch c { + case nilCode: + _, err := d.R.ReadByte() + return nil, err + case falseCode, trueCode: + return d.DecodeBool() + case floatCode: + return d.DecodeFloat32() + case doubleCode: + return d.DecodeFloat64() + case uint8Code, uint16Code, uint32Code, uint64Code: + return d.DecodeUint64() + case int8Code, int16Code, int32Code, int64Code: + return d.DecodeInt64() + case raw16Code, raw32Code: + return d.DecodeString() + case array16Code, array32Code: + return d.DecodeSlice() + case map16Code, map32Code: + return d.DecodeMap() + } + + return 0, fmt.Errorf("msgpack: invalid code %x decoding interface{}", c) +} diff --git a/encode.go b/encode.go new file mode 100644 index 0000000..f8a39eb --- /dev/null +++ b/encode.go @@ -0,0 +1,343 @@ +package msgpack + +import ( + "bytes" + "fmt" + "io" + "math" + "reflect" + "time" +) + +type writer interface { + io.Writer + WriteByte(byte) error + WriteString(string) (int, error) +} + +type writeByte struct { + io.Writer +} + +func (w *writeByte) WriteByte(b byte) error { + n, err := w.Write([]byte{b}) + if err != nil { + return err + } + if n != 1 { + return io.ErrShortWrite + } + return nil +} + +func (w *writeByte) WriteString(s string) (int, error) { + return w.Write([]byte(s)) +} + +func Marshal(v ...interface{}) ([]byte, error) { + buf := &bytes.Buffer{} + err := NewEncoder(buf).Encode(v...) + return buf.Bytes(), err +} + +type Encoder struct { + W writer +} + +func NewEncoder(w io.Writer) *Encoder { + ww, ok := w.(writer) + if !ok { + ww = &writeByte{Writer: w} + } + return &Encoder{ + W: ww, + } +} + +func (e *Encoder) Encode(v ...interface{}) error { + for _, vv := range v { + if err := e.encode(vv); err != nil { + return err + } + } + return nil +} + +func (e *Encoder) encode(iv interface{}) error { + if iv == nil { + return e.EncodeNil() + } + + switch v := iv.(type) { + case string: + return e.EncodeString(v) + case []byte: + return e.EncodeBytes(v) + case int: + return e.EncodeInt64(int64(v)) + case int64: + return e.EncodeInt64(v) + case uint: + return e.EncodeUint64(uint64(v)) + case uint64: + return e.EncodeUint64(v) + case bool: + return e.EncodeBool(v) + case float32: + return e.EncodeFloat32(v) + case float64: + return e.EncodeFloat64(v) + case []string: + return e.encodeStringSlice(v) + case map[string]string: + return e.encodeMapStringString(v) + case time.Duration: + return e.EncodeInt64(int64(v)) + case time.Time: + return e.EncodeTime(v) + case encoder: + return v.EncodeMsgpack(e.W) + } + return e.EncodeValue(reflect.ValueOf(iv)) +} + +func (e *Encoder) EncodeValue(v reflect.Value) error { + switch v.Kind() { + case reflect.String: + return e.EncodeString(v.String()) + case reflect.Bool: + return e.EncodeBool(v.Bool()) + case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint: + return e.EncodeUint64(v.Uint()) + case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int: + return e.EncodeInt64(v.Int()) + case reflect.Float32: + return e.EncodeFloat32(float32(v.Float())) + case reflect.Float64: + return e.EncodeFloat64(v.Float()) + case reflect.Array: + return e.encodeSlice(v) + case reflect.Slice: + if v.IsNil() { + return e.EncodeNil() + } + return e.encodeSlice(v) + case reflect.Map: + return e.encodeMap(v) + case reflect.Interface, reflect.Ptr: + if v.IsNil() { + return e.EncodeNil() + } + if enc, ok := typEncMap[v.Type()]; ok { + return enc(e, v) + } + if enc, ok := v.Interface().(encoder); ok { + return enc.EncodeMsgpack(e.W) + } + return e.EncodeValue(v.Elem()) + case reflect.Struct: + typ := v.Type() + if enc, ok := typEncMap[typ]; ok { + return enc(e, v) + } + if enc, ok := v.Interface().(encoder); ok { + return enc.EncodeMsgpack(e.W) + } + return e.encodeStruct(v) + default: + return fmt.Errorf("msgpack: unsupported type %v", v.Type().String()) + } + panic("not reached") +} + +func (e *Encoder) EncodeNil() error { + return e.W.WriteByte(nilCode) +} + +func (e *Encoder) EncodeUint(v uint) error { + return e.EncodeUint64(uint64(v)) +} + +func (e *Encoder) EncodeUint8(v uint8) error { + return e.EncodeUint64(uint64(v)) +} + +func (e *Encoder) EncodeUint16(v uint16) error { + return e.EncodeUint64(uint64(v)) +} + +func (e *Encoder) EncodeUint32(v uint32) error { + return e.EncodeUint64(uint64(v)) +} + +func (e *Encoder) EncodeUint64(v uint64) error { + switch { + case v < 128: + return e.W.WriteByte(byte(v)) + case v < 256: + return e.write([]byte{uint8Code, byte(v)}) + case v < 65536: + return e.write([]byte{uint16Code, byte(v >> 8), byte(v)}) + case v < 4294967296: + return e.write([]byte{ + uint32Code, + byte(v >> 24), + byte(v >> 16), + byte(v >> 8), + byte(v), + }) + default: + return e.write([]byte{ + uint64Code, + byte(v >> 56), + byte(v >> 48), + byte(v >> 40), + byte(v >> 32), + byte(v >> 24), + byte(v >> 16), + byte(v >> 8), + byte(v), + }) + } + panic("not reached") +} + +func (e *Encoder) EncodeInt(v int) error { + return e.EncodeInt64(int64(v)) +} + +func (e *Encoder) EncodeInt8(v int8) error { + return e.EncodeInt64(int64(v)) +} + +func (e *Encoder) EncodeInt16(v int16) error { + return e.EncodeInt64(int64(v)) +} + +func (e *Encoder) EncodeInt32(v int32) error { + return e.EncodeInt64(int64(v)) +} + +func (e *Encoder) EncodeInt64(v int64) error { + switch { + case v < -2147483648 || v >= 2147483648: + return e.write([]byte{ + int64Code, + byte(v >> 56), + byte(v >> 48), + byte(v >> 40), + byte(v >> 32), + byte(v >> 24), + byte(v >> 16), + byte(v >> 8), + byte(v), + }) + case v < -32768 || v >= 32768: + return e.write([]byte{ + int32Code, + byte(v >> 24), + byte(v >> 16), + byte(v >> 8), + byte(v), + }) + case v < -128 || v >= 128: + return e.write([]byte{int16Code, byte(v >> 8), byte(v)}) + case v < -32: + return e.write([]byte{int8Code, byte(v)}) + default: + return e.W.WriteByte(byte(v)) + } + panic("not reached") +} + +func (e *Encoder) EncodeBool(value bool) error { + if value { + return e.W.WriteByte(trueCode) + } + return e.W.WriteByte(falseCode) +} + +func (e *Encoder) EncodeFloat32(value float32) error { + v := math.Float32bits(value) + return e.write([]byte{ + floatCode, + byte(v >> 24), + byte(v >> 16), + byte(v >> 8), + byte(v), + }) +} + +func (e *Encoder) EncodeFloat64(value float64) error { + v := math.Float64bits(value) + return e.write([]byte{ + doubleCode, + byte(v >> 56), + byte(v >> 48), + byte(v >> 40), + byte(v >> 32), + byte(v >> 24), + byte(v >> 16), + byte(v >> 8), + byte(v), + }) +} + +func (e *Encoder) encodeStruct(v reflect.Value) error { + fields := structs.Fields(v.Type()) + switch l := len(fields); { + case l < 16: + if err := e.W.WriteByte(fixMapLowCode | byte(l)); err != nil { + return err + } + case l < 65536: + if err := e.write([]byte{ + map16Code, + byte(l >> 8), + byte(l), + }); err != nil { + return err + } + default: + if err := e.write([]byte{ + map32Code, + byte(l >> 24), + byte(l >> 16), + byte(l >> 8), + byte(l), + }); err != nil { + return err + } + } + for _, f := range fields { + if err := e.EncodeString(f.Name()); err != nil { + return err + } + if err := f.EncodeValue(e, v); err != nil { + return err + } + } + return nil +} + +func (e *Encoder) write(data []byte) error { + n, err := e.W.Write(data) + if err != nil { + return err + } + if n < len(data) { + return io.ErrShortWrite + } + return nil +} + +func (e *Encoder) writeString(s string) error { + n, err := e.W.WriteString(s) + if err != nil { + return err + } + if n < len(s) { + return io.ErrShortWrite + } + return nil +} diff --git a/example_test.go b/example_test.go new file mode 100644 index 0000000..c435cda --- /dev/null +++ b/example_test.go @@ -0,0 +1,67 @@ +package msgpack_test + +import ( + "bytes" + "fmt" + + "git.bit5.ru/backend/msgpack" +) + +func ExampleMarshal() { + b, err := msgpack.Marshal(true) + fmt.Printf("%v %#v\n", err, b) + // Output: []byte{0xc3} +} + +func ExampleUnmarshal() { + var out bool + err := msgpack.Unmarshal([]byte{0xc3}, &out) + fmt.Println(err, out) + // Output: true +} + +func Example_mapStringInterface() { + in := map[string]interface{}{"foo": 1, "hello": "world"} + b, err := msgpack.Marshal(in) + _ = err + + var out map[string]interface{} + err = msgpack.Unmarshal(b, &out) + fmt.Printf("%v %#v\n", err, out) + // Output: map[string]interface {}{"foo":1, "hello":"world"} +} + +func Example_recursiveMapStringInterface() { + buf := &bytes.Buffer{} + + enc := msgpack.NewEncoder(buf) + in := map[string]interface{}{"foo": map[string]interface{}{"hello": "world"}} + _ = enc.Encode(in) + + dec := msgpack.NewDecoder(buf) + dec.DecodeMapFunc = func(d *msgpack.Decoder) (interface{}, error) { + n, err := d.DecodeMapLen() + if err != nil { + return nil, err + } + + m := make(map[string]interface{}, n) + for i := 0; i < n; i++ { + mk, err := d.DecodeString() + if err != nil { + return nil, err + } + + mv, err := d.DecodeInterface() + if err != nil { + return nil, err + } + + m[mk] = mv + } + return m, nil + } + out, err := dec.DecodeInterface() + fmt.Printf("%v %#v\n", err, out) + // Output: map[string]interface {}{"foo":map[string]interface {}{"hello":"world"}} +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..c136082 --- /dev/null +++ b/go.mod @@ -0,0 +1,10 @@ +module git.bit5.ru/backend/msgpack + +go 1.13 + +require ( + github.com/ugorji/go/codec v1.2.7 + github.com/vmihailenco/bufio v0.0.0-20140618134113-fe7b595919de + gopkg.in/bufio.v1 v1.0.0-20140618132640-567b2bfa514e // indirect + launchpad.net/gocheck v0.0.0-20140225173054-000000000087 +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..bda8643 --- /dev/null +++ b/go.sum @@ -0,0 +1,10 @@ +github.com/ugorji/go v1.2.7 h1:qYhyWUUd6WbiM+C6JZAUkIJt/1WrjzNHY9+KCIjVqTo= +github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M= +github.com/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0= +github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY= +github.com/vmihailenco/bufio v0.0.0-20140618134113-fe7b595919de h1:U+I4zEVstMdfNES/2UO8iqkIf214SDMRhdaFTE3A5rA= +github.com/vmihailenco/bufio v0.0.0-20140618134113-fe7b595919de/go.mod h1:ghSGoeEoFFkXNguSget72dMA0+OLq3AGZiqRohVojxI= +gopkg.in/bufio.v1 v1.0.0-20140618132640-567b2bfa514e h1:wGA78yza6bu/mWcc4QfBuIEHEtc06xdiU0X8sY36yUU= +gopkg.in/bufio.v1 v1.0.0-20140618132640-567b2bfa514e/go.mod h1:xsQCaysVCudhrYTfzYWe577fCe7Ceci+6qjO2Rdc0Z4= +launchpad.net/gocheck v0.0.0-20140225173054-000000000087 h1:Izowp2XBH6Ya6rv+hqbceQyw/gSGoXfH/UPoTGduL54= +launchpad.net/gocheck v0.0.0-20140225173054-000000000087/go.mod h1:hj7XX3B/0A+80Vse0e+BUHsHMTEhd0O4cpUHr/e/BUM= diff --git a/map.go b/map.go new file mode 100644 index 0000000..e171e58 --- /dev/null +++ b/map.go @@ -0,0 +1,175 @@ +package msgpack + +import ( + "fmt" + "reflect" +) + +func (e *Encoder) encodeMapLen(l int) error { + switch { + case l < 16: + if err := e.W.WriteByte(fixMapLowCode | byte(l)); err != nil { + return err + } + case l < 65536: + if err := e.write([]byte{ + map16Code, + byte(l >> 8), + byte(l), + }); err != nil { + return err + } + default: + if err := e.write([]byte{ + map32Code, + byte(l >> 24), + byte(l >> 16), + byte(l >> 8), + byte(l), + }); err != nil { + return err + } + } + return nil +} + +func (e *Encoder) encodeMapStringString(m map[string]string) error { + if err := e.encodeMapLen(len(m)); err != nil { + return err + } + for mk, mv := range m { + if err := e.EncodeString(mk); err != nil { + return err + } + if err := e.EncodeString(mv); err != nil { + return err + } + } + return nil +} + +func (e *Encoder) encodeMap(value reflect.Value) error { + if err := e.encodeMapLen(value.Len()); err != nil { + return err + } + keys := value.MapKeys() + for _, k := range keys { + if err := e.EncodeValue(k); err != nil { + return err + } + if err := e.EncodeValue(value.MapIndex(k)); err != nil { + return err + } + } + return nil +} + +func decodeMap(d *Decoder) (interface{}, error) { + n, err := d.DecodeMapLen() + if err != nil { + return nil, err + } + + m := make(map[interface{}]interface{}, n) + for i := 0; i < n; i++ { + mk, err := d.DecodeInterface() + if err != nil { + return nil, err + } + mv, err := d.DecodeInterface() + if err != nil { + return nil, err + } + m[mk] = mv + } + return m, nil +} + +func (d *Decoder) DecodeMapLen() (int, error) { + c, err := d.R.ReadByte() + if err != nil { + return 0, err + } + if c == nilCode { + return -1, nil + } else if c >= fixMapLowCode && c <= fixMapHighCode { + return int(c & fixMapMask), nil + } + switch c { + case map16Code: + n, err := d.uint16() + return int(n), err + case map32Code: + n, err := d.uint32() + return int(n), err + } + return 0, fmt.Errorf("msgpack: invalid code %x decoding map length", c) +} + +func (d *Decoder) decodeIntoMapStringString(mp *map[string]string) error { + n, err := d.DecodeMapLen() + if err != nil { + return err + } + if n == -1 { + return nil + } + + // TODO(vmihailenco): simpler way? + m := *mp + if m == nil { + *mp = make(map[string]string, n) + m = *mp + } + + for i := 0; i < n; i++ { + mk, err := d.DecodeString() + if err != nil { + return err + } + mv, err := d.DecodeString() + if err != nil { + return err + } + m[mk] = mv + } + + return nil +} + +func (d *Decoder) DecodeMap() (interface{}, error) { + return d.DecodeMapFunc(d) +} + +func (d *Decoder) mapValue(v reflect.Value) error { + n, err := d.DecodeMapLen() + if err != nil { + return err + } + if n == -1 { + return nil + } + + typ := v.Type() + if v.IsNil() { + v.Set(reflect.MakeMap(typ)) + } + keyType := typ.Key() + valueType := typ.Elem() + + for i := 0; i < n; i++ { + mk := reflect.New(keyType).Elem() + if err := d.DecodeValue(mk); err != nil { + return err + } + + mv := reflect.New(valueType).Elem() + if err := d.DecodeValue(mv); err != nil { + return err + } + + v.SetMapIndex(mk, mv) + } + + return nil +} diff --git a/msgpack.go b/msgpack.go new file mode 100644 index 0000000..7056b85 --- /dev/null +++ b/msgpack.go @@ -0,0 +1,18 @@ +package msgpack + +import ( + "io" +) + +type encoder interface { + EncodeMsgpack(io.Writer) error +} + +type decoder interface { + DecodeMsgpack(io.Reader) error +} + +type Coder interface { + encoder + decoder +} diff --git a/msgpack_test.go b/msgpack_test.go new file mode 100644 index 0000000..df10cfb --- /dev/null +++ b/msgpack_test.go @@ -0,0 +1,1048 @@ +package msgpack_test + +import ( + "bufio" + "bytes" + "encoding/binary" + "encoding/csv" + "encoding/gob" + "encoding/json" + "io" + "math" + "strconv" + "testing" + "time" + + "git.bit5.ru/backend/msgpack" + + //msgpack2 "github.com/ugorji/go-msgpack" + "github.com/ugorji/go/codec" + . "launchpad.net/gocheck" +) + +type nameStruct struct { + Name string +} + +func Test(t *testing.T) { TestingT(t) } + +type MsgpackTest struct { + buf *bytes.Buffer + enc *msgpack.Encoder + dec *msgpack.Decoder +} + +var _ = Suite(&MsgpackTest{}) + +func (t *MsgpackTest) SetUpTest(c *C) { + t.buf = &bytes.Buffer{} + t.enc = msgpack.NewEncoder(t.buf) + t.dec = msgpack.NewDecoder(bufio.NewReader(t.buf)) +} + +func (t *MsgpackTest) TestUint(c *C) { + table := []struct { + v uint + b []byte + }{ + {0, []byte{0x00}}, + {1, []byte{0x01}}, + {2, []byte{0x02}}, + {125, []byte{0x7d}}, + {126, []byte{0x7e}}, + {127, []byte{0x7f}}, + {128, []byte{0xcc, 0x80}}, + {253, []byte{0xcc, 0xfd}}, + {254, []byte{0xcc, 0xfe}}, + {255, []byte{0xcc, 0xff}}, + {256, []byte{0xcd, 0x01, 0x00}}, + {65533, []byte{0xcd, 0xff, 0xfd}}, + {65534, []byte{0xcd, 0xff, 0xfe}}, + {65535, []byte{0xcd, 0xff, 0xff}}, + {65536, []byte{0xce, 0x00, 0x01, 0x00, 0x00}}, + {4294967293, []byte{0xce, 0xff, 0xff, 0xff, 0xfd}}, + {4294967294, []byte{0xce, 0xff, 0xff, 0xff, 0xfe}}, + {4294967295, []byte{0xce, 0xff, 0xff, 0xff, 0xff}}, + {4294967296, []byte{0xcf, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00}}, + {18446744073709551613, []byte{0xcf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfd}}, + {18446744073709551614, []byte{0xcf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe}}, + {18446744073709551615, []byte{0xcf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}}, + } + for _, r := range table { + c.Assert(t.enc.Encode(r.v), IsNil) + c.Assert(t.buf.Bytes(), DeepEquals, r.b, Commentf("err encoding %v", r.v)) + var v uint + c.Assert(t.dec.Decode(&v), IsNil) + c.Assert(v, Equals, r.v) + } +} + +func (t *MsgpackTest) TestInt(c *C) { + table := []struct { + v int + b []byte + }{ + {-9223372036854775808, []byte{0xd3, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {-9223372036854775807, []byte{0xd3, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01}}, + {-9223372036854775806, []byte{0xd3, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02}}, + {-2147483651, []byte{0xd3, 0xff, 0xff, 0xff, 0xff, 0x7f, 0xff, 0xff, 0xfd}}, + {-2147483650, []byte{0xd3, 0xff, 0xff, 0xff, 0xff, 0x7f, 0xff, 0xff, 0xfe}}, + {-2147483649, []byte{0xd3, 0xff, 0xff, 0xff, 0xff, 0x7f, 0xff, 0xff, 0xff}}, + {-2147483648, []byte{0xd2, 0x80, 0x00, 0x00, 0x00}}, + {-2147483647, []byte{0xd2, 0x80, 0x00, 0x00, 0x01}}, + {-2147483646, []byte{0xd2, 0x80, 0x00, 0x00, 0x002}}, + {-32771, []byte{0xd2, 0xff, 0xff, 0x7f, 0xfd}}, + {-32770, []byte{0xd2, 0xff, 0xff, 0x7f, 0xfe}}, + {-32769, []byte{0xd2, 0xff, 0xff, 0x7f, 0xff}}, + {-32768, []byte{0xd1, 0x80, 0x00}}, + {-32767, []byte{0xd1, 0x80, 0x01}}, + {-131, []byte{0xd1, 0xff, 0x7d}}, + {-130, []byte{0xd1, 0xff, 0x7e}}, + {-129, []byte{0xd1, 0xff, 0x7f}}, + {-128, []byte{0xd0, 0x80}}, + {-127, []byte{0xd0, 0x81}}, + {-34, []byte{0xd0, 0xde}}, + {-33, []byte{0xd0, 0xdf}}, + {-32, []byte{0xe0}}, + {-31, []byte{0xe1}}, + {0, []byte{0x00}}, + {1, []byte{0x01}}, + {126, []byte{0x7e}}, + {127, []byte{0x7f}}, + {128, []byte{0xd1, 0x00, 0x80}}, + {129, []byte{0xd1, 0x00, 0x81}}, + {130, []byte{0xd1, 0x00, 0x82}}, + {32765, []byte{0xd1, 0x7f, 0xfd}}, + {32766, []byte{0xd1, 0x7f, 0xfe}}, + {32767, []byte{0xd1, 0x7f, 0xff}}, + {32768, []byte{0xd2, 0x00, 0x00, 0x80, 0x00}}, + {32769, []byte{0xd2, 0x00, 0x00, 0x80, 0x01}}, + {32770, []byte{0xd2, 0x00, 0x00, 0x80, 0x02}}, + {2147483645, []byte{0xd2, 0x7f, 0xff, 0xff, 0xfd}}, + {2147483646, []byte{0xd2, 0x7f, 0xff, 0xff, 0xfe}}, + {2147483647, []byte{0xd2, 0x7f, 0xff, 0xff, 0xff}}, + {2147483648, []byte{0xd3, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00}}, + {2147483649, []byte{0xd3, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x01}}, + {2147483650, []byte{0xd3, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x02}}, + {4294967296, []byte{0xd3, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00}}, + {4294967297, []byte{0xd3, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01}}, + {4294967298, []byte{0xd3, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02}}, + } + for _, r := range table { + c.Assert(t.enc.Encode(r.v), IsNil) + c.Assert(t.buf.Bytes(), DeepEquals, r.b, Commentf("err encoding %v", r.v)) + var v int + c.Assert(t.dec.Decode(&v), IsNil) + c.Assert(v, Equals, r.v) + } +} + +func (t *MsgpackTest) TestFloat32(c *C) { + table := []struct { + v float32 + b []byte + }{ + {.1, []byte{0xca, 0x3d, 0xcc, 0xcc, 0xcd}}, + {.2, []byte{0xca, 0x3e, 0x4c, 0xcc, 0xcd}}, + {-.1, []byte{0xca, 0xbd, 0xcc, 0xcc, 0xcd}}, + {-.2, []byte{0xca, 0xbe, 0x4c, 0xcc, 0xcd}}, + {float32(math.Inf(1)), []byte{0xca, 0x7f, 0x80, 0x00, 0x00}}, + {float32(math.Inf(-1)), []byte{0xca, 0xff, 0x80, 0x00, 0x00}}, + {math.MaxFloat32, []byte{0xca, 0x7f, 0x7f, 0xff, 0xff}}, + {math.SmallestNonzeroFloat32, []byte{0xca, 0x0, 0x0, 0x0, 0x1}}, + } + for _, r := range table { + c.Assert(t.enc.Encode(r.v), IsNil) + c.Assert(t.buf.Bytes(), DeepEquals, r.b, Commentf("err encoding %v", r.v)) + var v float32 + c.Assert(t.dec.Decode(&v), IsNil) + c.Assert(v, Equals, r.v) + } + + in := float32(math.NaN()) + c.Assert(t.enc.Encode(in), IsNil) + var out float32 + c.Assert(t.dec.Decode(&out), IsNil) + c.Assert(math.IsNaN(float64(out)), Equals, true) +} + +func (t *MsgpackTest) TestFloat64(c *C) { + table := []struct { + v float64 + b []byte + }{ + {.1, []byte{0xcb, 0x3f, 0xb9, 0x99, 0x99, 0x99, 0x99, 0x99, 0x9a}}, + {.2, []byte{0xcb, 0x3f, 0xc9, 0x99, 0x99, 0x99, 0x99, 0x99, 0x9a}}, + {-.1, []byte{0xcb, 0xbf, 0xb9, 0x99, 0x99, 0x99, 0x99, 0x99, 0x9a}}, + {-.2, []byte{0xcb, 0xbf, 0xc9, 0x99, 0x99, 0x99, 0x99, 0x99, 0x9a}}, + {math.Inf(1), []byte{0xcb, 0x7f, 0xf0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}, + {math.Inf(-1), []byte{0xcb, 0xff, 0xf0, 0x00, 0x00, 0x0, 0x0, 0x0, 0x0}}, + {math.MaxFloat64, []byte{0xcb, 0x7f, 0xef, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}}, + {math.SmallestNonzeroFloat64, []byte{0xcb, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1}}, + } + for _, r := range table { + c.Assert(t.enc.Encode(r.v), IsNil) + c.Assert(t.buf.Bytes(), DeepEquals, r.b, Commentf("err encoding %v", r.v)) + var v float64 + c.Assert(t.dec.Decode(&v), IsNil) + c.Assert(v, Equals, r.v) + } + + in := math.NaN() + c.Assert(t.enc.Encode(in), IsNil) + var out float64 + c.Assert(t.dec.Decode(&out), IsNil) + c.Assert(math.IsNaN(out), Equals, true) +} + +func (t *MsgpackTest) TestBool(c *C) { + table := []struct { + v bool + b []byte + }{ + {false, []byte{0xc2}}, + {true, []byte{0xc3}}, + } + for _, r := range table { + c.Assert(t.enc.Encode(r.v), IsNil) + c.Assert(t.buf.Bytes(), DeepEquals, r.b, Commentf("err encoding %v", r.v)) + var v bool + c.Assert(t.dec.Decode(&v), IsNil) + c.Assert(v, Equals, r.v) + } +} + +func (t *MsgpackTest) TestNil(c *C) { + table := []interface{}{ + (*string)(nil), + (*[]byte)(nil), + (*int)(nil), + (*int8)(nil), + (*int16)(nil), + (*int32)(nil), + (*int64)(nil), + (*uint)(nil), + (*uint8)(nil), + (*uint16)(nil), + (*uint32)(nil), + (*uint64)(nil), + (*bool)(nil), + (*float32)(nil), + (*float64)(nil), + (*[]string)(nil), + (*map[string]string)(nil), + (*time.Duration)(nil), + (*time.Time)(nil), + (*struct{})(nil), + } + for _, dst := range table { + c.Assert(t.enc.Encode(nil), IsNil) + c.Assert(t.buf.Bytes(), DeepEquals, []byte{0xC0}) + c.Assert(t.dec.Decode(dst), IsNil) + c.Assert(dst, IsNil) + } +} + +func (t *MsgpackTest) TestDecodeNil(c *C) { + c.Assert(t.dec.Decode(nil), NotNil) +} + +func (t *MsgpackTest) TestTime(c *C) { + in := time.Now() + var out time.Time + c.Assert(t.enc.Encode(in), IsNil) + c.Assert(t.dec.Decode(&out), IsNil) + c.Assert(out.Equal(in), Equals, true) + + var zero time.Time + c.Assert(t.enc.Encode(zero), IsNil) + c.Assert(t.dec.Decode(&out), IsNil) + c.Assert(out.Equal(zero), Equals, true) + c.Assert(out.IsZero(), Equals, true) +} + +func (t *MsgpackTest) TestSliceOfInts(c *C) { + for _, i := range []struct { + src []int + b []byte + }{ + {nil, []byte{0xc0}}, + {[]int{}, []byte{0x90}}, + {[]int{0}, []byte{0x91, 0x0}}, + } { + c.Assert(t.enc.Encode(i.src), IsNil) + c.Assert(t.buf.Bytes(), DeepEquals, i.b) + var dst []int + c.Assert(t.dec.Decode(&dst), IsNil) + c.Assert(dst, DeepEquals, i.src) + } +} + +func (t *MsgpackTest) TestArrayOfInts(c *C) { + src := [3]int{1, 2, 3} + c.Assert(t.enc.Encode(src), IsNil) + var dst [3]int + c.Assert(t.dec.Decode(&dst), IsNil) + c.Assert(dst, DeepEquals, src) +} + +func (t *MsgpackTest) TestSliceOfStrings(c *C) { + for _, i := range []struct { + src []string + b []byte + }{ + {nil, []byte{0xc0}}, + {[]string{}, []byte{0x90}}, + {[]string{"foo", "bar"}, []byte{0x92, 0xa3, 'f', 'o', 'o', 0xa3, 'b', 'a', 'r'}}, + } { + c.Assert(t.enc.Encode(i.src), IsNil) + c.Assert(t.buf.Bytes(), DeepEquals, i.b) + var dst []string + c.Assert(t.dec.Decode(&dst), IsNil) + c.Assert(dst, DeepEquals, i.src) + } +} + +func (t *MsgpackTest) TestArrayOfStrings(c *C) { + src := [2]string{"hello", "world"} + c.Assert(t.enc.Encode(src), IsNil) + var dst [2]string + c.Assert(t.dec.Decode(&dst), IsNil) + c.Assert(dst, DeepEquals, src) +} + +func (t *MsgpackTest) TestString(c *C) { + for _, i := range []struct { + src string + b []byte + }{ + {"", []byte{0xa0}}, + {"a", []byte{0xa1, 'a'}}, + {"hello", append([]byte{0xa5}, "hello"...)}, + { + "world world world", + append([]byte{0xb1}, "world world world"...), + }, + { + "world world world world world world", + append([]byte{0xda, 0x0, 0x23}, "world world world world world world"...), + }, + } { + c.Assert(t.enc.Encode(i.src), IsNil) + c.Assert(t.buf.Bytes(), DeepEquals, i.b) + var dst string + c.Assert(t.dec.Decode(&dst), IsNil) + c.Assert(dst, Equals, i.src) + } +} + +func (t *MsgpackTest) TestBytes(c *C) { + for _, i := range []struct { + src []byte + b []byte + }{ + {nil, []byte{0xc0}}, + {[]byte{}, []byte{0xa0}}, + {[]byte("a"), []byte{0xa1, 'a'}}, + {[]byte("hello"), append([]byte{0xa5}, "hello"...)}, + { + []byte("world world world"), + append([]byte{0xb1}, "world world world"...), + }, + { + []byte("world world world world world world"), + append([]byte{0xda, 0x0, 0x23}, "world world world world world world"...), + }, + } { + c.Assert(t.enc.Encode(i.src), IsNil) + c.Assert(t.buf.Bytes(), DeepEquals, i.b) + var dst []byte + c.Assert(t.dec.Decode(&dst), IsNil) + c.Assert(dst, DeepEquals, i.src) + } +} + +func (t *MsgpackTest) TestLargeBytes(c *C) { + N := int(1e6) + + src := bytes.Repeat([]byte{'1'}, N) + c.Assert(t.enc.Encode(src), IsNil) + var dst []byte + c.Assert(t.dec.Decode(&dst), IsNil) + c.Assert(dst, DeepEquals, src) +} + +func (t *MsgpackTest) TestLargeString(c *C) { + N := int(1e6) + + src := string(bytes.Repeat([]byte{'1'}, N)) + c.Assert(t.enc.Encode(src), IsNil) + var dst string + c.Assert(t.dec.Decode(&dst), IsNil) + c.Assert(dst, Equals, src) +} + +func (t *MsgpackTest) TestSliceOfStructs(c *C) { + in := []*nameStruct{&nameStruct{"hello"}} + var out []*nameStruct + c.Assert(t.enc.Encode(in), IsNil) + c.Assert(t.dec.Decode(&out), IsNil) + c.Assert(out, DeepEquals, in) +} + +func (t *MsgpackTest) TestMap(c *C) { + for _, i := range []struct { + m map[string]string + b []byte + }{ + {map[string]string{}, []byte{0x80}}, + {map[string]string{"hello": "world"}, []byte{0x81, 0xa5, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0xa5, 0x77, 0x6f, 0x72, 0x6c, 0x64}}, + } { + c.Assert(t.enc.Encode(i.m), IsNil) + c.Assert(t.buf.Bytes(), DeepEquals, i.b, Commentf("err encoding %v", i.m)) + var m map[string]string + c.Assert(t.dec.Decode(&m), IsNil) + c.Assert(m, DeepEquals, i.m) + } +} + +func (t *MsgpackTest) TestStructNil(c *C) { + var dst *nameStruct + + c.Assert(t.enc.Encode(nameStruct{Name: "foo"}), IsNil) + c.Assert(t.dec.Decode(&dst), IsNil) + c.Assert(dst, Not(IsNil)) + c.Assert(dst.Name, Equals, "foo") +} + +type testStruct struct { + Name string + Tm time.Time + Data []byte + Colors []string +} + +func (t *MsgpackTest) TestStruct(c *C) { + in := &testStruct{ + Name: "hello world", + Tm: time.Now(), + Data: []byte{1, 2, 3}, + Colors: []string{"red", "orange", "yellow", "green", "blue", "violet"}, + } + var out testStruct + c.Assert(t.enc.Encode(in), IsNil) + c.Assert(t.dec.Decode(&out), IsNil) + c.Assert(out.Name, Equals, "hello world") + c.Assert(out.Tm.Equal(in.Tm), Equals, true) + c.Assert(out.Data, DeepEquals, []byte{1, 2, 3}) + c.Assert( + out.Colors, + DeepEquals, + []string{"red", "orange", "yellow", "green", "blue", "violet"}, + ) +} + +func (t *MsgpackTest) TestStructUnknownField(c *C) { + in := struct { + Field1 string + Field2 string + Field3 string + }{ + Field1: "value1", + Field2: "value2", + Field3: "value3", + } + c.Assert(t.enc.Encode(in), IsNil) + + out := struct { + Field2 string + }{} + c.Assert(t.dec.Decode(&out), IsNil) + c.Assert(out.Field2, Equals, "value2") +} + +//------------------------------------------------------------------------------ + +type coderStruct struct { + name string +} + +func (s *coderStruct) Name() string { + return s.name +} + +func (s *coderStruct) EncodeMsgpack(w io.Writer) error { + return msgpack.NewEncoder(w).Encode(s.name) +} + +func (s *coderStruct) DecodeMsgpack(r io.Reader) error { + return msgpack.NewDecoder(r).Decode(&s.name) +} + +var _ msgpack.Coder = &coderStruct{} + +func (t *MsgpackTest) TestCoder(c *C) { + in := &coderStruct{name: "hello"} + var out coderStruct + c.Assert(t.enc.Encode(in), IsNil) + c.Assert(t.dec.Decode(&out), IsNil) + c.Assert(out.Name(), Equals, "hello") +} + +func (t *MsgpackTest) TestNilCoder(c *C) { + in := &coderStruct{name: "hello"} + var out *coderStruct + c.Assert(t.enc.Encode(in), IsNil) + c.Assert(t.dec.Decode(&out), IsNil) + c.Assert(out.Name(), Equals, "hello") +} + +// func (t *MsgpackTest) TestNilCoderValue(c *C) { +// in := &coderStruct{name: "hello"} +// var out *coderStruct +// v := reflect.ValueOf(out) +// c.Assert(t.enc.Encode(in), IsNil) +// c.Assert(t.dec.DecodeValue(v), IsNil) +// c.Assert(out.Name(), Equals, "hello") +// } + +func (t *MsgpackTest) TestPtrToCoder(c *C) { + in := &coderStruct{name: "hello"} + var out coderStruct + out2 := &out + c.Assert(t.enc.Encode(in), IsNil) + c.Assert(t.dec.Decode(&out2), IsNil) + c.Assert(out.Name(), Equals, "hello") +} + +//------------------------------------------------------------------------------ + +type struct2 struct { + Name string +} + +type struct1 struct { + Name string + Struct2 struct2 +} + +func (t *MsgpackTest) TestNestedStructs(c *C) { + in := &struct1{Name: "hello", Struct2: struct2{Name: "world"}} + var out struct1 + c.Assert(t.enc.Encode(in), IsNil) + c.Assert(t.dec.Decode(&out), IsNil) + c.Assert(out.Name, Equals, in.Name) + c.Assert(out.Struct2.Name, Equals, in.Struct2.Name) +} + +type Struct4 struct { + Name2 string +} + +type Struct3 struct { + Struct4 + Name1 string +} + +func (t *MsgpackTest) TestEmbedding(c *C) { + in := &Struct3{ + Name1: "hello", + Struct4: Struct4{ + Name2: "world", + }, + } + var out Struct3 + c.Assert(t.enc.Encode(in), IsNil) + c.Assert(t.dec.Decode(&out), IsNil) + c.Assert(out.Name1, Equals, in.Name1) + c.Assert(out.Name2, Equals, in.Name2) +} + +func (t *MsgpackTest) TestSliceInterface(c *C) { + in := []interface{}{1, "hello"} + var out []interface{} + + c.Assert(t.enc.Encode(in), IsNil) + c.Assert(t.dec.Decode(&out), IsNil) + + c.Assert(out, HasLen, 2) + c.Assert(out[0], Equals, int64(1)) + c.Assert(out[1], Equals, "hello") +} + +func (t *MsgpackTest) TestSliceNil(c *C) { + in := [][]*int{nil} + var out [][]*int + + c.Assert(t.enc.Encode(in), IsNil) + c.Assert(t.dec.Decode(&out), IsNil) + c.Assert(out, DeepEquals, in) +} + +//------------------------------------------------------------------------------ + +func (t *MsgpackTest) TestMapStringInterface(c *C) { + in := map[string]interface{}{ + "foo": "bar", + "hello": map[string]interface{}{ + "foo": "bar", + }, + } + var out map[string]interface{} + + c.Assert(t.enc.Encode(in), IsNil) + c.Assert(t.dec.Decode(&out), IsNil) + + c.Assert(out["foo"], Equals, "bar") + mm := out["hello"].(map[interface{}]interface{}) + c.Assert(mm["foo"], Equals, "bar") +} + +func (t *MsgpackTest) TestMapStringInterface2(c *C) { + buf := &bytes.Buffer{} + enc := msgpack.NewEncoder(buf) + dec := msgpack.NewDecoder(buf) + dec.DecodeMapFunc = func(d *msgpack.Decoder) (interface{}, error) { + n, err := d.DecodeMapLen() + if err != nil { + return nil, err + } + + m := make(map[string]interface{}, n) + for i := 0; i < n; i++ { + mk, err := d.DecodeString() + if err != nil { + return nil, err + } + + mv, err := d.DecodeInterface() + if err != nil { + return nil, err + } + + m[mk] = mv + } + return m, nil + } + + in := map[string]interface{}{ + "foo": "bar", + "hello": map[string]interface{}{ + "foo": "bar", + }, + } + var out map[string]interface{} + + c.Assert(enc.Encode(in), IsNil) + c.Assert(dec.Decode(&out), IsNil) + + c.Assert(out["foo"], Equals, "bar") + mm := out["hello"].(map[string]interface{}) + c.Assert(mm["foo"], Equals, "bar") +} + +//------------------------------------------------------------------------------ + +func (t *MsgpackTest) BenchmarkBool(c *C) { + var v bool + for i := 0; i < c.N; i++ { + t.enc.Encode(true) + t.dec.Decode(&v) + } + c.Assert(t.buf.Len(), Equals, 0) +} + +func (t *MsgpackTest) BenchmarkInt(c *C) { + var v int + for i := 0; i < c.N; i++ { + if err := t.enc.Encode(1); err != nil { + panic(err) + } + if err := t.dec.Decode(&v); err != nil { + panic(err) + } + } + c.Assert(t.buf.Len(), Equals, 0) +} + +func (t *MsgpackTest) BenchmarkIntBinary(c *C) { + buf := &bytes.Buffer{} + + var out int32 + for i := 0; i < c.N; i++ { + if err := binary.Write(buf, binary.BigEndian, int32(1)); err != nil { + panic(err) + } + if err := binary.Read(buf, binary.BigEndian, &out); err != nil { + panic(err) + } + } + c.Assert(buf.Len(), Equals, 0) +} + +/* +func (t *MsgpackTest) BenchmarkIntMsgpack2(c *C) { + buf := &bytes.Buffer{} + dec := msgpack2.NewDecoder(buf, nil) + enc := msgpack2.NewEncoder(buf) + + var out int + for i := 0; i < c.N; i++ { + if err := enc.Encode(1); err != nil { + panic(err) + } + if err := dec.Decode(&out); err != nil { + panic(err) + } + } + c.Assert(t.buf.Len(), Equals, 0) +} +*/ + +func (t *MsgpackTest) BenchmarkIntMsgpack3(c *C) { + buf := &bytes.Buffer{} + enc := codec.NewEncoder(buf, &codec.MsgpackHandle{}) + dec := codec.NewDecoder(buf, &codec.MsgpackHandle{}) + + var out int + for i := 0; i < c.N; i++ { + if err := enc.Encode(1); err != nil { + panic(err) + } + if err := dec.Decode(&out); err != nil { + panic(err) + } + } + c.Assert(t.buf.Len(), Equals, 0) +} + +func (t *MsgpackTest) BenchmarkTime(c *C) { + in := time.Now() + var out time.Time + for i := 0; i < c.N; i++ { + if err := t.enc.Encode(in); err != nil { + panic(err) + } + if err := t.dec.Decode(&out); err != nil { + panic(err) + } + } + c.Assert(t.buf.Len(), Equals, 0) +} + +func (t *MsgpackTest) BenchmarkDuration(c *C) { + in := time.Hour + var out time.Duration + for i := 0; i < c.N; i++ { + if err := t.enc.Encode(in); err != nil { + panic(err) + } + if err := t.dec.Decode(&out); err != nil { + panic(err) + } + } + c.Assert(t.buf.Len(), Equals, 0) +} + +func (t *MsgpackTest) BenchmarkBytes(c *C) { + in := make([]byte, 1024) + var out []byte + for i := 0; i < c.N; i++ { + if err := t.enc.Encode(in); err != nil { + panic(err) + } + if err := t.dec.Decode(&out); err != nil { + panic(err) + } + } + c.Assert(t.buf.Len(), Equals, 0) +} + +func (t *MsgpackTest) BenchmarkMapStringString(c *C) { + in := map[string]string{ + "hello": "world", + "foo": "bar", + } + var out map[string]string + + for i := 0; i < c.N; i++ { + if err := t.enc.Encode(in); err != nil { + panic(err) + } + if err := t.dec.Decode(&out); err != nil { + panic(err) + } + } + + c.Assert(t.buf.Len(), Equals, 0) +} + +func (t *MsgpackTest) BenchmarkMapStringStringPtr(c *C) { + in := map[string]string{ + "hello": "world", + "foo": "bar", + } + var out map[string]string + out2 := &out + + for i := 0; i < c.N; i++ { + if err := t.enc.Encode(&in); err != nil { + panic(err) + } + if err := t.dec.Decode(&out2); err != nil { + panic(err) + } + } + + c.Assert(t.buf.Len(), Equals, 0) +} + +func (t *MsgpackTest) BenchmarkMapIntInt(c *C) { + in := map[int]int{ + 1: 10, + 2: 20, + } + var out map[int]int + + for i := 0; i < c.N; i++ { + if err := t.enc.Encode(in); err != nil { + panic(err) + } + if err := t.dec.Decode(&out); err != nil { + panic(err) + } + } + + c.Assert(t.buf.Len(), Equals, 0) +} + +func (t *MsgpackTest) BenchmarkStringSlice(c *C) { + in := []string{"hello", "world"} + var out []string + + for i := 0; i < c.N; i++ { + if err := t.enc.Encode(in); err != nil { + panic(err) + } + if err := t.dec.Decode(&out); err != nil { + panic(err) + } + } + + c.Assert(t.buf.Len(), Equals, 0) +} + +func (t *MsgpackTest) BenchmarkStringSlicePtr(c *C) { + in := []string{"hello", "world"} + var out []string + out2 := &out + + for i := 0; i < c.N; i++ { + if err := t.enc.Encode(&in); err != nil { + panic(err) + } + if err := t.dec.Decode(&out2); err != nil { + panic(err) + } + } + + c.Assert(t.buf.Len(), Equals, 0) +} + +type benchmarkStruct struct { + Name string + Colors []string + Age int + Data []byte + CreatedAt time.Time + UpdatedAt time.Time +} + +type benchmarkStruct2 struct { + Name string + Colors []string + Age int + Data []byte + CreatedAt time.Time + UpdatedAt time.Time +} + +func (s *benchmarkStruct2) EncodeMsgpack(w io.Writer) error { + enc := msgpack.NewEncoder(w) + return enc.Encode( + s.Name, + s.Colors, + s.Age, + s.Data, + s.CreatedAt, + s.UpdatedAt, + ) +} + +func (s *benchmarkStruct2) DecodeMsgpack(r io.Reader) error { + dec := msgpack.NewDecoder(r) + return dec.Decode( + &s.Name, + &s.Colors, + &s.Age, + &s.Data, + &s.CreatedAt, + &s.UpdatedAt, + ) +} + +var _ msgpack.Coder = &benchmarkStruct2{} + +func (t *MsgpackTest) structForBenchmark() *benchmarkStruct { + return &benchmarkStruct{ + Name: "Hello World", + Colors: []string{"red", "orange", "yellow", "green", "blue", "violet"}, + Age: math.MaxInt32, + Data: make([]byte, 1024), + CreatedAt: time.Now(), + UpdatedAt: time.Now(), + } +} + +func (t *MsgpackTest) structForBenchmark2() *benchmarkStruct2 { + return &benchmarkStruct2{ + Name: "Hello World", + Colors: []string{"red", "orange", "yellow", "green", "blue", "violet"}, + Age: math.MaxInt32, + Data: make([]byte, 1024), + CreatedAt: time.Now(), + UpdatedAt: time.Now(), + } +} + +func (t *MsgpackTest) BenchmarkStruct(c *C) { + in := t.structForBenchmark() + out := &benchmarkStruct{} + for i := 0; i < c.N; i++ { + b, err := msgpack.Marshal(in) + if err != nil { + panic(err) + } + err = msgpack.Unmarshal(b, out) + if err != nil { + panic(err) + } + } +} + +func (t *MsgpackTest) BenchmarkStructManual(c *C) { + in := t.structForBenchmark2() + out := &benchmarkStruct2{} + for i := 0; i < c.N; i++ { + b, err := msgpack.Marshal(in) + if err != nil { + panic(err) + } + err = msgpack.Unmarshal(b, out) + if err != nil { + panic(err) + } + } +} + +/* +func (t *MsgpackTest) BenchmarkStructMsgpack2(c *C) { + in := t.structForBenchmark() + out := &benchmarkStruct{} + for i := 0; i < c.N; i++ { + b, err := msgpack2.Marshal(in) + if err != nil { + panic(err) + } + err = msgpack2.Unmarshal(b, out, nil) + if err != nil { + panic(err) + } + } +} +*/ + +func (t *MsgpackTest) BenchmarkStructMsgpack3(c *C) { + in := t.structForBenchmark() + out := &benchmarkStruct{} + for i := 0; i < c.N; i++ { + buf := &bytes.Buffer{} + enc := codec.NewEncoder(buf, &codec.MsgpackHandle{}) + dec := codec.NewDecoder(buf, &codec.MsgpackHandle{}) + + if err := enc.Encode(in); err != nil { + panic(err) + } + if err := dec.Decode(out); err != nil { + panic(err) + } + } +} + +func (t *MsgpackTest) BenchmarkStructJSON(c *C) { + in := t.structForBenchmark() + out := &benchmarkStruct{} + for i := 0; i < c.N; i++ { + b, err := json.Marshal(in) + if err != nil { + panic(err) + } + err = json.Unmarshal(b, out) + if err != nil { + panic(err) + } + } +} + +func (t *MsgpackTest) BenchmarkStructGOB(c *C) { + in := t.structForBenchmark() + out := &benchmarkStruct{} + for i := 0; i < c.N; i++ { + buf := &bytes.Buffer{} + enc := gob.NewEncoder(buf) + dec := gob.NewDecoder(buf) + + if err := enc.Encode(in); err != nil { + panic(err) + } + if err := dec.Decode(out); err != nil { + panic(err) + } + } +} + +func (t *MsgpackTest) BenchmarkCSV(c *C) { + for i := 0; i < c.N; i++ { + record := []string{strconv.FormatInt(int64(1), 10), "hello", "world"} + + buf := &bytes.Buffer{} + r := csv.NewReader(buf) + w := csv.NewWriter(buf) + + if err := w.Write(record); err != nil { + panic(err) + } + w.Flush() + if _, err := r.Read(); err != nil { + panic(err) + } + } +} + +func (t *MsgpackTest) BenchmarkCSVMsgpack(c *C) { + for i := 0; i < c.N; i++ { + var num int + var hello, world string + + buf := &bytes.Buffer{} + enc := msgpack.NewEncoder(buf) + dec := msgpack.NewDecoder(buf) + + if err := enc.Encode(1, "hello", "world"); err != nil { + panic(err) + } + if err := dec.Decode(&num, &hello, &world); err != nil { + panic(err) + } + } +} diff --git a/slice.go b/slice.go new file mode 100644 index 0000000..6fa483c --- /dev/null +++ b/slice.go @@ -0,0 +1,285 @@ +package msgpack + +import ( + "fmt" + "io" + "reflect" + + "github.com/vmihailenco/bufio" +) + +func (e *Encoder) encodeBytesLen(l int) error { + switch { + case l < 32: + if err := e.W.WriteByte(fixRawLowCode | uint8(l)); err != nil { + return err + } + case l < 65536: + if err := e.write([]byte{ + raw16Code, + byte(l >> 8), + byte(l), + }); err != nil { + return err + } + default: + if err := e.write([]byte{ + raw32Code, + byte(l >> 24), + byte(l >> 16), + byte(l >> 8), + byte(l), + }); err != nil { + return err + } + } + return nil +} + +func (e *Encoder) EncodeString(v string) error { + if err := e.encodeBytesLen(len(v)); err != nil { + return err + } + return e.writeString(v) +} + +func (e *Encoder) EncodeBytes(v []byte) error { + if v == nil { + return e.EncodeNil() + } + if err := e.encodeBytesLen(len(v)); err != nil { + return err + } + return e.write(v) +} + +func (e *Encoder) encodeSliceLen(l int) error { + switch { + case l < 16: + if err := e.W.WriteByte(fixArrayLowCode | byte(l)); err != nil { + return err + } + case l < 65536: + if err := e.write([]byte{array16Code, byte(l >> 8), byte(l)}); err != nil { + return err + } + default: + if err := e.write([]byte{ + array32Code, + byte(l >> 24), + byte(l >> 16), + byte(l >> 8), + byte(l), + }); err != nil { + return err + } + } + return nil +} + +func (e *Encoder) encodeStringSlice(s []string) error { + if s == nil { + return e.EncodeNil() + } + if err := e.encodeSliceLen(len(s)); err != nil { + return err + } + for _, v := range s { + if err := e.EncodeString(v); err != nil { + return err + } + } + return nil +} + +func (e *Encoder) encodeSlice(v reflect.Value) error { + switch v.Type().Elem().Kind() { + case reflect.Uint8: + return e.EncodeBytes(v.Bytes()) + } + + l := v.Len() + if err := e.encodeSliceLen(l); err != nil { + return err + } + for i := 0; i < l; i++ { + if err := e.EncodeValue(v.Index(i)); err != nil { + return err + } + } + + return nil +} + +func (d *Decoder) DecodeBytesLen() (int, error) { + c, err := d.R.ReadByte() + if err != nil { + return 0, err + } + if c == nilCode { + return -1, nil + } else if c >= fixRawLowCode && c <= fixRawHighCode { + return int(c & fixRawMask), nil + } + switch c { + case raw16Code: + n, err := d.uint16() + return int(n), err + case raw32Code: + n, err := d.uint32() + return int(n), err + } + return 0, fmt.Errorf("msgpack: invalid code %x decoding bytes length", c) +} + +func (d *Decoder) DecodeBytes() ([]byte, error) { + n, err := d.DecodeBytesLen() + if err != nil { + return nil, err + } + if n == -1 { + return nil, nil + } + b := make([]byte, n) + _, err = io.ReadFull(d.R, b) + if err != nil { + return nil, err + } + return b, nil +} + +func (d *Decoder) bytesValue(value reflect.Value) error { + v, err := d.DecodeBytes() + if err != nil { + return err + } + value.SetBytes(v) + return nil +} + +func (d *Decoder) DecodeString() (string, error) { + n, err := d.DecodeBytesLen() + if err != nil { + return "", err + } + if n == -1 { + return "", nil + } + b, err := d.R.ReadN(n) + if err == bufio.ErrBufferFull { + newB := make([]byte, n) + n := copy(newB, b) + b = newB + + _, err := io.ReadFull(d.R, newB[n:]) + if err != nil { + return "", err + } + } else if err != nil { + return "", err + } + return string(b), nil +} + +func (d *Decoder) stringValue(value reflect.Value) error { + v, err := d.DecodeString() + if err != nil { + return err + } + value.SetString(v) + return nil +} + +func (d *Decoder) DecodeSliceLen() (int, error) { + c, err := d.R.ReadByte() + if err != nil { + return 0, err + } + if c == nilCode { + return -1, nil + } else if c >= fixArrayLowCode && c <= fixArrayHighCode { + return int(c & fixArrayMask), nil + } + switch c { + case array16Code: + n, err := d.uint16() + return int(n), err + case array32Code: + n, err := d.uint32() + return int(n), err + } + return 0, fmt.Errorf("msgpack: invalid code %x decoding array length", c) +} + +func (d *Decoder) decodeIntoStrings(sp *[]string) error { + n, err := d.DecodeSliceLen() + if err != nil { + return err + } + if n == -1 { + return nil + } + s := *sp + if s == nil || len(s) < n { + s = make([]string, n) + } + for i := 0; i < n; i++ { + v, err := d.DecodeString() + if err != nil { + return err + } + s[i] = v + } + *sp = s + return nil +} + +func (d *Decoder) DecodeSlice() ([]interface{}, error) { + n, err := d.DecodeSliceLen() + if err != nil { + return nil, err + } + if n == -1 { + return nil, nil + } + s := make([]interface{}, n) + for i := 0; i < n; i++ { + v, err := d.DecodeInterface() + if err != nil { + return nil, err + } + s[i] = v + } + return s, nil +} + +func (d *Decoder) sliceValue(v reflect.Value) error { + elemType := v.Type().Elem() + switch elemType.Kind() { + case reflect.Uint8: + b, err := d.DecodeBytes() + if err != nil { + return err + } + v.SetBytes(b) + return nil + } + + n, err := d.DecodeSliceLen() + if err != nil { + return err + } + + if v.Len() < n || (v.Kind() == reflect.Slice && v.IsNil()) { + v.Set(reflect.MakeSlice(v.Type(), n, n)) + } + + for i := 0; i < n; i++ { + sv := v.Index(i) + if err := d.DecodeValue(sv); err != nil { + return err + } + } + + return nil +} diff --git a/time.go b/time.go new file mode 100644 index 0000000..b7a9167 --- /dev/null +++ b/time.go @@ -0,0 +1,59 @@ +package msgpack + +import ( + "fmt" + "reflect" + "time" +) + +var ( + timeType = reflect.TypeOf((*time.Time)(nil)).Elem() +) + +func init() { + Register(timeType, encodeTime, decodeTime) +} + +func (e *Encoder) EncodeTime(tm time.Time) error { + if err := e.W.WriteByte(0x92); err != nil { + return err + } + if err := e.EncodeInt64(tm.Unix()); err != nil { + return err + } + return e.EncodeInt(tm.Nanosecond()) +} + +func (d *Decoder) DecodeTime() (time.Time, error) { + b, err := d.R.ReadByte() + if err != nil { + return time.Time{}, err + } + if b != 0x92 { + return time.Time{}, fmt.Errorf("msgpack: invalid code %x decoding time", b) + } + + sec, err := d.DecodeInt64() + if err != nil { + return time.Time{}, err + } + nsec, err := d.DecodeInt64() + if err != nil { + return time.Time{}, err + } + return time.Unix(sec, nsec), nil +} + +func encodeTime(e *Encoder, v reflect.Value) error { + tm := v.Interface().(time.Time) + return e.EncodeTime(tm) +} + +func decodeTime(d *Decoder, v reflect.Value) error { + tm, err := d.DecodeTime() + if err != nil { + return err + } + v.Set(reflect.ValueOf(tm)) + return nil +} diff --git a/typeinfo.go b/typeinfo.go new file mode 100644 index 0000000..4ede442 --- /dev/null +++ b/typeinfo.go @@ -0,0 +1,277 @@ +package msgpack + +import ( + "reflect" + "strings" + "sync" +) + +var structs = newStructCache() + +//------------------------------------------------------------------------------ + +type field interface { + Name() string + + EncodeValue(*Encoder, reflect.Value) error + DecodeValue(*Decoder, reflect.Value) error +} + +type baseField struct { + idx []int + name string +} + +func (f *baseField) Name() string { + return f.name +} + +func (f *baseField) EncodeValue(e *Encoder, v reflect.Value) error { + fv := v.FieldByIndex(f.idx) + return e.EncodeValue(fv) +} + +func (f *baseField) DecodeValue(d *Decoder, v reflect.Value) error { + fv := v.FieldByIndex(f.idx) + return d.DecodeValue(fv) +} + +//------------------------------------------------------------------------------ + +type boolField struct { + *baseField +} + +func (f *boolField) EncodeValue(e *Encoder, v reflect.Value) error { + fv := v.FieldByIndex(f.idx) + return e.EncodeBool(fv.Bool()) +} + +func (f *boolField) DecodeValue(d *Decoder, v reflect.Value) error { + fv := v.FieldByIndex(f.idx) + return d.boolValue(fv) +} + +//------------------------------------------------------------------------------ + +type float32Field struct { + *baseField +} + +func (f *float32Field) EncodeValue(e *Encoder, v reflect.Value) error { + fv := v.FieldByIndex(f.idx) + return e.EncodeFloat32(float32(fv.Float())) +} + +func (f *float32Field) DecodeValue(d *Decoder, v reflect.Value) error { + fv := v.FieldByIndex(f.idx) + return d.float32Value(fv) +} + +//------------------------------------------------------------------------------ + +type float64Field struct { + *baseField +} + +func (f *float64Field) EncodeValue(e *Encoder, v reflect.Value) error { + fv := v.FieldByIndex(f.idx) + return e.EncodeFloat64(fv.Float()) +} + +func (f *float64Field) DecodeValue(d *Decoder, v reflect.Value) error { + fv := v.FieldByIndex(f.idx) + return d.float64Value(fv) +} + +//------------------------------------------------------------------------------ + +type stringField struct { + *baseField +} + +func (f *stringField) EncodeValue(e *Encoder, v reflect.Value) error { + fv := v.FieldByIndex(f.idx) + return e.EncodeString(fv.String()) +} + +func (f *stringField) DecodeValue(d *Decoder, v reflect.Value) error { + fv := v.FieldByIndex(f.idx) + return d.stringValue(fv) +} + +//------------------------------------------------------------------------------ + +type bytesField struct { + *baseField +} + +func (f *bytesField) EncodeValue(e *Encoder, v reflect.Value) error { + fv := v.FieldByIndex(f.idx) + return e.EncodeBytes(fv.Bytes()) +} + +func (f *bytesField) DecodeValue(d *Decoder, v reflect.Value) error { + fv := v.FieldByIndex(f.idx) + return d.bytesValue(fv) +} + +//------------------------------------------------------------------------------ + +type intField struct { + *baseField +} + +func (f *intField) EncodeValue(e *Encoder, v reflect.Value) error { + fv := v.FieldByIndex(f.idx) + return e.EncodeInt64(fv.Int()) +} + +func (f *intField) DecodeValue(d *Decoder, v reflect.Value) error { + fv := v.FieldByIndex(f.idx) + return d.int64Value(fv) +} + +//------------------------------------------------------------------------------ + +type uintField struct { + *baseField +} + +func (f *uintField) EncodeValue(e *Encoder, v reflect.Value) error { + fv := v.FieldByIndex(f.idx) + return e.EncodeUint64(fv.Uint()) +} + +func (f *uintField) DecodeValue(d *Decoder, v reflect.Value) error { + fv := v.FieldByIndex(f.idx) + return d.uint64Value(fv) +} + +//------------------------------------------------------------------------------ + +type customField struct { + *baseField + encode encoderFunc + decode decoderFunc +} + +func (f *customField) EncodeValue(e *Encoder, v reflect.Value) error { + fv := v.FieldByIndex(f.idx) + return f.encode(e, fv) +} + +func (f *customField) DecodeValue(d *Decoder, v reflect.Value) error { + fv := v.FieldByIndex(f.idx) + return f.decode(d, fv) +} + +//------------------------------------------------------------------------------ + +type structCache struct { + l sync.RWMutex + m map[reflect.Type][]field +} + +func newStructCache() *structCache { + return &structCache{ + m: make(map[reflect.Type][]field), + } +} + +func (m *structCache) Fields(typ reflect.Type) []field { + m.l.RLock() + fields, ok := m.m[typ] + m.l.RUnlock() + if ok { + return fields + } + + numField := typ.NumField() + fields = make([]field, 0, numField) + for i := 0; i < numField; i++ { + f := typ.Field(i) + if f.PkgPath != "" { + continue + } + finfo := m.newStructField(typ, &f) + if finfo != nil { + fields = append(fields, finfo) + } + } + + m.l.Lock() + m.m[typ] = fields + m.l.Unlock() + + return fields +} + +func (m *structCache) newStructField(typ reflect.Type, f *reflect.StructField) field { + tokens := strings.Split(f.Tag.Get("msgpack"), ",") + name := tokens[0] + if name == "-" { + return nil + } else if name == "" { + name = f.Name + } + + baseField := &baseField{ + idx: f.Index, + name: name, + } + + ft := typ.FieldByIndex(f.Index).Type + if encodeFunc, ok := typEncMap[ft]; ok { + decodeFunc := typDecMap[ft] + return &customField{ + encode: encodeFunc, + decode: decodeFunc, + baseField: baseField, + } + } + + switch ft.Kind() { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return &intField{ + baseField: baseField, + } + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + return &uintField{ + baseField: baseField, + } + case reflect.Bool: + return &boolField{ + baseField: baseField, + } + case reflect.Float32: + return &float32Field{ + baseField: baseField, + } + case reflect.Float64: + return &float64Field{ + baseField: baseField, + } + case reflect.Array, reflect.Slice: + if ft.Elem().Kind() == reflect.Uint8 { + return &bytesField{ + baseField: baseField, + } + } + case reflect.String: + return &stringField{ + baseField: baseField, + } + } + return baseField +} + +func (m *structCache) Field(typ reflect.Type, name string) field { + // TODO(vmihailenco): binary search? + for _, f := range m.Fields(typ) { + if f.Name() == name { + return f + } + } + return nil +}