You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

670 lines
17 KiB

//
// DISCLAIMER
//
// Copyright 2017 ArangoDB GmbH, Cologne, Germany
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Copyright holder is ArangoDB GmbH, Cologne, Germany
//
// Author Ewout Prangsma
//
// This code is heavily inspired by the Go sources.
// See https://golang.org/src/encoding/json/
package velocypack
import (
"bytes"
"encoding"
"encoding/json"
"io"
"reflect"
"runtime"
"sort"
"strconv"
"sync"
)
// An Encoder encodes Go structures into velocypack values written to an output stream.
type Encoder struct {
b Builder
w io.Writer
}
// Marshaler is implemented by types that can convert themselves into Velocypack.
type Marshaler interface {
MarshalVPack() (Slice, error)
}
// NewEncoder creates a new Encoder that writes output to the given writer.
func NewEncoder(w io.Writer) *Encoder {
return &Encoder{
w: w,
}
}
// Marshal writes the Velocypack encoding of v to a buffer and returns that buffer.
//
// Marshal traverses the value v recursively.
// If an encountered value implements the Marshaler interface
// and is not a nil pointer, Marshal calls its MarshalVPack method
// to produce Velocypack.
// If an encountered value implements the json.Marshaler interface
// and is not a nil pointer, Marshal calls its MarshalJSON method
// to produce JSON and converts the resulting JSON to VelocyPack.
// If no MarshalVPack or MarshalJSON method is present but the
// value implements encoding.TextMarshaler instead, Marshal calls
// its MarshalText method and encodes the result as a Velocypack string.
// The nil pointer exception is not strictly necessary
// but mimics a similar, necessary exception in the behavior of
// UnmarshalVPack.
//
// Otherwise, Marshal uses the following type-dependent default encodings:
//
// Boolean values encode as Velocypack booleans.
//
// Floating point, integer, and Number values encode as Velocypack Int's, UInt's and Double's.
//
// String values encode as Velocypack strings.
//
// Array and slice values encode as Velocypack arrays, except that
// []byte encodes as Velocypack Binary data, and a nil slice
// encodes as the Null Velocypack value.
//
// Struct values encode as Velocypack objects.
// The encoding follows the same rules as specified for json.Marshal.
// This means that all `json` tags are fully supported.
//
// Map values encode as Velocypack objects.
// The encoding follows the same rules as specified for json.Marshal.
//
// Pointer values encode as the value pointed to.
// A nil pointer encodes as the Null Velocypack value.
//
// Interface values encode as the value contained in the interface.
// A nil interface value encodes as the Null Velocypack value.
//
// Channel, complex, and function values cannot be encoded in Velocypack.
// Attempting to encode such a value causes Marshal to return
// an UnsupportedTypeError.
//
// Velocypack cannot represent cyclic data structures and Marshal does not
// handle them. Passing cyclic structures to Marshal will result in
// an infinite recursion.
//
func Marshal(v interface{}) (result Slice, err error) {
defer func() {
if r := recover(); r != nil {
if _, ok := r.(runtime.Error); ok {
panic(r)
}
if s, ok := r.(string); ok {
panic(s)
}
err = r.(error)
}
}()
var b Builder
reflectValue(&b, reflect.ValueOf(v), encoderOptions{})
return b.Slice()
}
// Encode writes the Velocypack encoding of v to the stream.
func (e *Encoder) Encode(v interface{}) (err error) {
defer func() {
if r := recover(); r != nil {
if _, ok := r.(runtime.Error); ok {
panic(r)
}
if s, ok := r.(string); ok {
panic(s)
}
err = r.(error)
}
}()
e.b.Clear()
reflectValue(&e.b, reflect.ValueOf(v), encoderOptions{})
if _, err := e.b.WriteTo(e.w); err != nil {
return WithStack(err)
}
return nil
}
// Builder returns a reference to the builder used in the given encoder.
func (e *Encoder) Builder() *Builder {
return &e.b
}
func isEmptyValue(v reflect.Value) bool {
switch v.Kind() {
case reflect.Array, reflect.Map, reflect.Slice, reflect.String:
return v.Len() == 0
case reflect.Bool:
return !v.Bool()
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return v.Int() == 0
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
return v.Uint() == 0
case reflect.Float32, reflect.Float64:
return v.Float() == 0
case reflect.Interface, reflect.Ptr:
return v.IsNil()
}
return false
}
func reflectValue(b *Builder, v reflect.Value, options encoderOptions) {
valueEncoder(v)(b, v, options)
}
type encoderOptions struct {
quoted bool
}
type encoderFunc func(b *Builder, v reflect.Value, options encoderOptions)
var encoderCache struct {
sync.RWMutex
m map[reflect.Type]encoderFunc
}
func valueEncoder(v reflect.Value) encoderFunc {
if !v.IsValid() {
return invalidValueEncoder
}
return typeEncoder(v.Type())
}
var (
marshalerType = reflect.TypeOf(new(Marshaler)).Elem()
jsonMarshalerType = reflect.TypeOf(new(json.Marshaler)).Elem()
textMarshalerType = reflect.TypeOf(new(encoding.TextMarshaler)).Elem()
nullValue = NewNullValue()
)
func typeEncoder(t reflect.Type) encoderFunc {
encoderCache.RLock()
f := encoderCache.m[t]
encoderCache.RUnlock()
if f != nil {
return f
}
// To deal with recursive types, populate the map with an
// indirect func before we build it. This type waits on the
// real func (f) to be ready and then calls it. This indirect
// func is only used for recursive types.
encoderCache.Lock()
if encoderCache.m == nil {
encoderCache.m = make(map[reflect.Type]encoderFunc)
}
var wg sync.WaitGroup
wg.Add(1)
encoderCache.m[t] = func(b *Builder, v reflect.Value, options encoderOptions) {
wg.Wait()
f(b, v, options)
}
encoderCache.Unlock()
// Compute fields without lock.
// Might duplicate effort but won't hold other computations back.
f = newTypeEncoder(t, true)
wg.Done()
encoderCache.Lock()
encoderCache.m[t] = f
encoderCache.Unlock()
return f
}
// newTypeEncoder constructs an encoderFunc for a type.
// The returned encoder only checks CanAddr when allowAddr is true.
func newTypeEncoder(t reflect.Type, allowAddr bool) encoderFunc {
if t.Implements(marshalerType) {
return marshalerEncoder
}
if t.Implements(jsonMarshalerType) {
return jsonMarshalerEncoder
}
if t.Kind() != reflect.Ptr && allowAddr {
if reflect.PtrTo(t).Implements(marshalerType) {
return newCondAddrEncoder(addrMarshalerEncoder, newTypeEncoder(t, false))
}
if reflect.PtrTo(t).Implements(jsonMarshalerType) {
return newCondAddrEncoder(addrJSONMarshalerEncoder, newTypeEncoder(t, false))
}
}
if t.Implements(textMarshalerType) {
return textMarshalerEncoder
}
if t.Kind() != reflect.Ptr && allowAddr {
if reflect.PtrTo(t).Implements(textMarshalerType) {
return newCondAddrEncoder(addrTextMarshalerEncoder, newTypeEncoder(t, false))
}
}
switch t.Kind() {
case reflect.Bool:
return boolEncoder
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return intEncoder
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
return uintEncoder
case reflect.Float32, reflect.Float64:
return doubleEncoder
case reflect.String:
return stringEncoder
case reflect.Interface:
return interfaceEncoder
case reflect.Struct:
return newStructEncoder(t)
case reflect.Map:
return newMapEncoder(t)
case reflect.Slice:
return newSliceEncoder(t)
case reflect.Array:
return newArrayEncoder(t)
case reflect.Ptr:
return newPtrEncoder(t)
default:
return unsupportedTypeEncoder
}
}
func invalidValueEncoder(b *Builder, v reflect.Value, options encoderOptions) {
b.addInternal(nullValue)
}
func marshalerEncoder(b *Builder, v reflect.Value, options encoderOptions) {
if v.Kind() == reflect.Ptr && v.IsNil() {
b.addInternal(nullValue)
return
}
m, ok := v.Interface().(Marshaler)
if !ok {
b.addInternal(nullValue)
return
}
if vpack, err := m.MarshalVPack(); err != nil {
panic(&MarshalerError{v.Type(), err})
} else {
b.addInternal(NewSliceValue(vpack))
}
}
func jsonMarshalerEncoder(b *Builder, v reflect.Value, options encoderOptions) {
if v.Kind() == reflect.Ptr && v.IsNil() {
b.addInternal(nullValue)
return
}
m, ok := v.Interface().(json.Marshaler)
if !ok {
b.addInternal(nullValue)
return
}
if json, err := m.MarshalJSON(); err != nil {
panic(&MarshalerError{v.Type(), err})
} else {
// Convert JSON to vpack
if slice, err := ParseJSON(bytes.NewReader(json)); err != nil {
panic(&MarshalerError{v.Type(), err})
} else {
b.addInternal(NewSliceValue(slice))
}
}
}
func addrMarshalerEncoder(b *Builder, v reflect.Value, options encoderOptions) {
va := v.Addr()
if va.IsNil() {
b.addInternal(nullValue)
return
}
m := va.Interface().(Marshaler)
if vpack, err := m.MarshalVPack(); err != nil {
panic(&MarshalerError{Type: v.Type(), Err: err})
} else {
if err = b.AddValue(NewSliceValue(vpack)); err != nil {
panic(&MarshalerError{Type: v.Type(), Err: err})
}
}
}
func addrJSONMarshalerEncoder(b *Builder, v reflect.Value, options encoderOptions) {
va := v.Addr()
if va.IsNil() {
b.addInternal(nullValue)
return
}
m := va.Interface().(json.Marshaler)
if json, err := m.MarshalJSON(); err != nil {
panic(&MarshalerError{Type: v.Type(), Err: err})
} else {
if slice, err := ParseJSON(bytes.NewReader(json)); err != nil {
panic(&MarshalerError{v.Type(), err})
} else {
// copy VPack into buffer, checking validity.
b.buf.Write(slice)
}
}
}
func textMarshalerEncoder(b *Builder, v reflect.Value, options encoderOptions) {
if v.Kind() == reflect.Ptr && v.IsNil() {
b.addInternal(nullValue)
return
}
m := v.Interface().(encoding.TextMarshaler)
text, err := m.MarshalText()
if err != nil {
panic(&MarshalerError{v.Type(), err})
}
b.addInternal(NewStringValue(string(text)))
}
func addrTextMarshalerEncoder(b *Builder, v reflect.Value, options encoderOptions) {
va := v.Addr()
if va.IsNil() {
b.addInternal(nullValue)
return
}
m := va.Interface().(encoding.TextMarshaler)
text, err := m.MarshalText()
if err != nil {
panic(&MarshalerError{v.Type(), err})
}
b.addInternal(NewStringValue(string(text)))
}
func boolEncoder(b *Builder, v reflect.Value, options encoderOptions) {
if options.quoted {
b.addInternal(NewStringValue(strconv.FormatBool(v.Bool())))
} else {
b.addInternal(NewBoolValue(v.Bool()))
}
}
func intEncoder(b *Builder, v reflect.Value, options encoderOptions) {
if options.quoted {
b.addInternal(NewStringValue(strconv.FormatInt(v.Int(), 10)))
} else {
b.addInternal(NewIntValue(v.Int()))
}
}
func uintEncoder(b *Builder, v reflect.Value, options encoderOptions) {
if options.quoted {
b.addInternal(NewStringValue(strconv.FormatUint(v.Uint(), 10)))
} else {
b.addInternal(NewUIntValue(v.Uint()))
}
}
func doubleEncoder(b *Builder, v reflect.Value, options encoderOptions) {
if options.quoted {
b.addInternal(NewStringValue(formatDouble(v.Float())))
} else {
b.addInternal(NewDoubleValue(v.Float()))
}
}
func stringEncoder(b *Builder, v reflect.Value, options encoderOptions) {
s := v.String()
if options.quoted {
raw, _ := json.Marshal(s)
s = string(raw)
}
b.addInternal(NewStringValue(s))
}
func interfaceEncoder(b *Builder, v reflect.Value, options encoderOptions) {
if v.IsNil() {
b.addInternal(nullValue)
return
}
vElem := v.Elem()
valueEncoder(vElem)(b, vElem, options)
}
func unsupportedTypeEncoder(b *Builder, v reflect.Value, options encoderOptions) {
panic(&UnsupportedTypeError{v.Type()})
}
type structEncoder struct {
fields []field
fieldEncs []encoderFunc
}
func (se *structEncoder) encode(b *Builder, v reflect.Value, options encoderOptions) {
if err := b.OpenObject(); err != nil {
panic(err)
}
for i, f := range se.fields {
fv := fieldByIndex(v, f.index)
if !fv.IsValid() || f.omitEmpty && isEmptyValue(fv) {
continue
}
// Key
_, err := b.addInternalKey(f.name)
if err != nil {
panic(err)
}
// Value
options.quoted = f.quoted
se.fieldEncs[i](b, fv, options)
}
if err := b.Close(); err != nil {
panic(err)
}
}
func newStructEncoder(t reflect.Type) encoderFunc {
fields := cachedTypeFields(t)
se := &structEncoder{
fields: fields,
fieldEncs: make([]encoderFunc, len(fields)),
}
for i, f := range fields {
se.fieldEncs[i] = typeEncoder(typeByIndex(t, f.index))
}
return se.encode
}
type mapEncoder struct {
elemEnc encoderFunc
}
func (e *mapEncoder) encode(b *Builder, v reflect.Value, options encoderOptions) {
if v.IsNil() {
b.addInternal(nullValue)
return
}
if err := b.OpenObject(); err != nil {
panic(err)
}
// Extract and sort the keys.
keys := v.MapKeys()
sv := make(reflectWithStringSlice, len(keys))
for i, v := range keys {
sv[i].v = v
if err := sv[i].resolve(); err != nil {
panic(&MarshalerError{v.Type(), err})
}
}
sort.Sort(sv)
for _, kv := range sv {
// Key
_, err := b.addInternalKey(kv.s)
if err != nil {
panic(err)
}
// Value
e.elemEnc(b, v.MapIndex(kv.v), options)
}
if err := b.Close(); err != nil {
panic(err)
}
}
func newMapEncoder(t reflect.Type) encoderFunc {
switch t.Key().Kind() {
case reflect.String,
reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
default:
if !t.Key().Implements(textMarshalerType) {
return unsupportedTypeEncoder
}
}
me := &mapEncoder{typeEncoder(t.Elem())}
return me.encode
}
func encodeByteSlice(b *Builder, v reflect.Value, options encoderOptions) {
if v.IsNil() {
b.addInternal(nullValue)
return
}
b.addInternal(NewBinaryValue(v.Bytes()))
}
// sliceEncoder just wraps an arrayEncoder, checking to make sure the value isn't nil.
type sliceEncoder struct {
arrayEnc encoderFunc
}
func (se *sliceEncoder) encode(b *Builder, v reflect.Value, options encoderOptions) {
if v.IsNil() {
b.addInternal(nullValue)
return
}
se.arrayEnc(b, v, options)
}
func newSliceEncoder(t reflect.Type) encoderFunc {
// Byte slices get special treatment; arrays don't.
if t.Elem().Kind() == reflect.Uint8 {
p := reflect.PtrTo(t.Elem())
if !p.Implements(marshalerType) && !p.Implements(jsonMarshalerType) && !p.Implements(textMarshalerType) {
return encodeByteSlice
}
}
enc := &sliceEncoder{newArrayEncoder(t)}
return enc.encode
}
type arrayEncoder struct {
elemEnc encoderFunc
}
func (ae *arrayEncoder) encode(b *Builder, v reflect.Value, options encoderOptions) {
if err := b.OpenArray(); err != nil {
panic(err)
}
n := v.Len()
for i := 0; i < n; i++ {
ae.elemEnc(b, v.Index(i), options)
}
if err := b.Close(); err != nil {
panic(err)
}
}
func newArrayEncoder(t reflect.Type) encoderFunc {
enc := &arrayEncoder{typeEncoder(t.Elem())}
return enc.encode
}
type ptrEncoder struct {
elemEnc encoderFunc
}
func (pe *ptrEncoder) encode(b *Builder, v reflect.Value, options encoderOptions) {
if v.IsNil() {
b.addInternal(nullValue)
return
}
pe.elemEnc(b, v.Elem(), options)
}
func newPtrEncoder(t reflect.Type) encoderFunc {
enc := &ptrEncoder{typeEncoder(t.Elem())}
return enc.encode
}
type condAddrEncoder struct {
canAddrEnc, elseEnc encoderFunc
}
func (ce *condAddrEncoder) encode(b *Builder, v reflect.Value, options encoderOptions) {
if v.CanAddr() {
ce.canAddrEnc(b, v, options)
} else {
ce.elseEnc(b, v, options)
}
}
// newCondAddrEncoder returns an encoder that checks whether its value
// CanAddr and delegates to canAddrEnc if so, else to elseEnc.
func newCondAddrEncoder(canAddrEnc, elseEnc encoderFunc) encoderFunc {
enc := &condAddrEncoder{canAddrEnc: canAddrEnc, elseEnc: elseEnc}
return enc.encode
}
type reflectWithString struct {
v reflect.Value
s string
}
func (w *reflectWithString) resolve() error {
if w.v.Kind() == reflect.String {
w.s = w.v.String()
return nil
}
if tm, ok := w.v.Interface().(encoding.TextMarshaler); ok {
buf, err := tm.MarshalText()
w.s = string(buf)
return err
}
switch w.v.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
w.s = strconv.FormatInt(w.v.Int(), 10)
return nil
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
w.s = strconv.FormatUint(w.v.Uint(), 10)
return nil
}
panic("unexpected map key type")
}
type reflectWithStringSlice []reflectWithString
// Len is the number of elements in the collection.
func (l reflectWithStringSlice) Len() int {
return len(l)
}
// Less reports whether the element with
// index i should sort before the element with index j.
func (l reflectWithStringSlice) Less(i, j int) bool {
return l[i].s < l[j].s
}
// Swap swaps the elements with indexes i and j.
func (l reflectWithStringSlice) Swap(i, j int) {
l[i], l[j] = l[j], l[i]
}