383 lines
7.2 KiB
Go
383 lines
7.2 KiB
Go
package encoding
|
|
|
|
import (
|
|
"fmt"
|
|
"math"
|
|
"reflect"
|
|
|
|
"github.com/shamaton/msgpack/v2/def"
|
|
"github.com/shamaton/msgpack/v2/internal/common"
|
|
)
|
|
|
|
type encoder struct {
|
|
d []byte
|
|
asArray bool
|
|
common.Common
|
|
mk map[uintptr][]reflect.Value
|
|
mv map[uintptr][]reflect.Value
|
|
}
|
|
|
|
// Encode returns the MessagePack-encoded byte array of v.
|
|
func Encode(v interface{}, asArray bool) (b []byte, err error) {
|
|
e := encoder{asArray: asArray}
|
|
/*
|
|
defer func() {
|
|
e := recover()
|
|
if e != nil {
|
|
b = nil
|
|
err = fmt.Errorf("unexpected error!! \n%s", stackTrace())
|
|
}
|
|
}()
|
|
*/
|
|
|
|
rv := reflect.ValueOf(v)
|
|
if rv.Kind() == reflect.Ptr {
|
|
rv = rv.Elem()
|
|
if rv.Kind() == reflect.Ptr {
|
|
rv = rv.Elem()
|
|
}
|
|
}
|
|
size, err := e.calcSize(rv)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
e.d = make([]byte, size)
|
|
last := e.create(rv, 0)
|
|
if size != last {
|
|
return nil, fmt.Errorf("%w size=%d, lastIdx=%d", def.ErrNotMatchLastIndex, size, last)
|
|
}
|
|
return e.d, err
|
|
}
|
|
|
|
//func stackTrace() string {
|
|
// msg := ""
|
|
// for depth := 0; ; depth++ {
|
|
// _, file, line, ok := runtime.Caller(depth)
|
|
// if !ok {
|
|
// break
|
|
// }
|
|
// msg += fmt.Sprintln(depth, ": ", file, ":", line)
|
|
// }
|
|
// return msg
|
|
//}
|
|
|
|
func (e *encoder) calcSize(rv reflect.Value) (int, error) {
|
|
switch rv.Kind() {
|
|
case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint:
|
|
v := rv.Uint()
|
|
return e.calcUint(v), nil
|
|
|
|
case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int:
|
|
v := rv.Int()
|
|
return e.calcInt(int64(v)), nil
|
|
|
|
case reflect.Float32:
|
|
return e.calcFloat32(0), nil
|
|
|
|
case reflect.Float64:
|
|
return e.calcFloat64(0), nil
|
|
|
|
case reflect.String:
|
|
return e.calcString(rv.String()), nil
|
|
|
|
case reflect.Bool:
|
|
return def.Byte1, nil
|
|
|
|
case reflect.Complex64:
|
|
return e.calcComplex64(), nil
|
|
|
|
case reflect.Complex128:
|
|
return e.calcComplex128(), nil
|
|
|
|
case reflect.Slice:
|
|
if rv.IsNil() {
|
|
return def.Byte1, nil
|
|
}
|
|
// bin format
|
|
if e.isByteSlice(rv) {
|
|
size, err := e.calcByteSlice(rv.Len())
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
return size, nil
|
|
}
|
|
|
|
if size, find := e.calcFixedSlice(rv); find {
|
|
return size, nil
|
|
}
|
|
|
|
// func
|
|
elem := rv.Type().Elem()
|
|
var f structCalcFunc
|
|
if elem.Kind() == reflect.Struct {
|
|
f = e.getStructCalc(elem)
|
|
} else {
|
|
f = e.calcSize
|
|
}
|
|
|
|
l := rv.Len()
|
|
size, err := e.calcLength(l)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
// objects size
|
|
for i := 0; i < l; i++ {
|
|
s, err := f(rv.Index(i))
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
size += s
|
|
}
|
|
return size, nil
|
|
|
|
case reflect.Array:
|
|
// bin format
|
|
if e.isByteSlice(rv) {
|
|
size, err := e.calcByteSlice(rv.Len())
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
return size, nil
|
|
}
|
|
|
|
// func
|
|
elem := rv.Type().Elem()
|
|
var f structCalcFunc
|
|
if elem.Kind() == reflect.Struct {
|
|
f = e.getStructCalc(elem)
|
|
} else {
|
|
f = e.calcSize
|
|
}
|
|
|
|
l := rv.Len()
|
|
size, err := e.calcLength(l)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
// objects size
|
|
for i := 0; i < l; i++ {
|
|
s, err := f(rv.Index(i))
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
size += s
|
|
}
|
|
return size, nil
|
|
|
|
case reflect.Map:
|
|
if rv.IsNil() {
|
|
return def.Byte1, nil
|
|
}
|
|
|
|
if size, find := e.calcFixedMap(rv); find {
|
|
return size, nil
|
|
}
|
|
|
|
if e.mk == nil {
|
|
e.mk = map[uintptr][]reflect.Value{}
|
|
e.mv = map[uintptr][]reflect.Value{}
|
|
}
|
|
|
|
keys := rv.MapKeys()
|
|
size, err := e.calcLength(len(keys))
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
// key-value
|
|
mv := make([]reflect.Value, len(keys))
|
|
i := 0
|
|
for _, k := range keys {
|
|
keySize, err := e.calcSize(k)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
value := rv.MapIndex(k)
|
|
valueSize, err := e.calcSize(value)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
size += keySize + valueSize
|
|
mv[i] = value
|
|
i++
|
|
}
|
|
e.mk[rv.Pointer()], e.mv[rv.Pointer()] = keys, mv
|
|
return size, nil
|
|
|
|
case reflect.Struct:
|
|
size, err := e.calcStruct(rv)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
return size, nil
|
|
|
|
case reflect.Ptr:
|
|
if rv.IsNil() {
|
|
return def.Byte1, nil
|
|
}
|
|
size, err := e.calcSize(rv.Elem())
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
return size, nil
|
|
|
|
case reflect.Interface:
|
|
size, err := e.calcSize(rv.Elem())
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
return size, nil
|
|
|
|
case reflect.Invalid:
|
|
// do nothing (return nil)
|
|
return def.Byte1, nil
|
|
|
|
default:
|
|
return 0, fmt.Errorf("%v is %w type", rv.Kind(), def.ErrUnsupportedType)
|
|
}
|
|
}
|
|
|
|
func (e *encoder) calcLength(l int) (int, error) {
|
|
if l <= 0x0f {
|
|
return def.Byte1, nil
|
|
} else if l <= math.MaxUint16 {
|
|
return def.Byte1 + def.Byte2, nil
|
|
} else if uint(l) <= math.MaxUint32 {
|
|
return def.Byte1 + def.Byte4, nil
|
|
}
|
|
// not supported error
|
|
return 0, fmt.Errorf("array length %d is %w", l, def.ErrUnsupportedLength)
|
|
}
|
|
|
|
func (e *encoder) create(rv reflect.Value, offset int) int {
|
|
|
|
switch rv.Kind() {
|
|
case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint:
|
|
v := rv.Uint()
|
|
offset = e.writeUint(v, offset)
|
|
|
|
case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int:
|
|
v := rv.Int()
|
|
offset = e.writeInt(v, offset)
|
|
|
|
case reflect.Float32:
|
|
offset = e.writeFloat32(rv.Float(), offset)
|
|
|
|
case reflect.Float64:
|
|
offset = e.writeFloat64(rv.Float(), offset)
|
|
|
|
case reflect.Bool:
|
|
offset = e.writeBool(rv.Bool(), offset)
|
|
|
|
case reflect.String:
|
|
offset = e.writeString(rv.String(), offset)
|
|
|
|
case reflect.Complex64:
|
|
offset = e.writeComplex64(complex64(rv.Complex()), offset)
|
|
|
|
case reflect.Complex128:
|
|
offset = e.writeComplex128(rv.Complex(), offset)
|
|
|
|
case reflect.Slice:
|
|
if rv.IsNil() {
|
|
return e.writeNil(offset)
|
|
}
|
|
|
|
// bin format
|
|
if e.isByteSlice(rv) {
|
|
offset = e.writeByteSliceLength(rv.Len(), offset)
|
|
offset = e.setBytes(rv.Bytes(), offset)
|
|
return offset
|
|
}
|
|
|
|
if offset, find := e.writeFixedSlice(rv, offset); find {
|
|
return offset
|
|
}
|
|
|
|
// func
|
|
elem := rv.Type().Elem()
|
|
var f structWriteFunc
|
|
if elem.Kind() == reflect.Struct {
|
|
f = e.getStructWriter(elem)
|
|
} else {
|
|
f = e.create
|
|
}
|
|
|
|
// objects
|
|
l := rv.Len()
|
|
offset = e.writeSliceLength(l, offset)
|
|
for i := 0; i < l; i++ {
|
|
offset = f(rv.Index(i), offset)
|
|
}
|
|
|
|
case reflect.Array:
|
|
l := rv.Len()
|
|
// bin format
|
|
if e.isByteSlice(rv) {
|
|
offset = e.writeByteSliceLength(l, offset)
|
|
// objects
|
|
for i := 0; i < l; i++ {
|
|
offset = e.setByte1Uint64(rv.Index(i).Uint(), offset)
|
|
}
|
|
return offset
|
|
}
|
|
|
|
// format
|
|
offset = e.writeSliceLength(l, offset)
|
|
|
|
// func
|
|
elem := rv.Type().Elem()
|
|
var f structWriteFunc
|
|
if elem.Kind() == reflect.Struct {
|
|
f = e.getStructWriter(elem)
|
|
} else {
|
|
f = e.create
|
|
}
|
|
|
|
// objects
|
|
for i := 0; i < l; i++ {
|
|
offset = f(rv.Index(i), offset)
|
|
}
|
|
|
|
case reflect.Map:
|
|
if rv.IsNil() {
|
|
return e.writeNil(offset)
|
|
}
|
|
|
|
l := rv.Len()
|
|
offset = e.writeMapLength(l, offset)
|
|
|
|
if offset, find := e.writeFixedMap(rv, offset); find {
|
|
return offset
|
|
}
|
|
|
|
// key-value
|
|
p := rv.Pointer()
|
|
for i := range e.mk[p] {
|
|
offset = e.create(e.mk[p][i], offset)
|
|
offset = e.create(e.mv[p][i], offset)
|
|
}
|
|
|
|
case reflect.Struct:
|
|
offset = e.writeStruct(rv, offset)
|
|
|
|
case reflect.Ptr:
|
|
if rv.IsNil() {
|
|
return e.writeNil(offset)
|
|
}
|
|
|
|
offset = e.create(rv.Elem(), offset)
|
|
|
|
case reflect.Interface:
|
|
offset = e.create(rv.Elem(), offset)
|
|
|
|
case reflect.Invalid:
|
|
return e.writeNil(offset)
|
|
|
|
}
|
|
return offset
|
|
}
|