670 lines
21 KiB
Go
670 lines
21 KiB
Go
/*
|
|
* Copyright (c) 2012-2014 Dave Collins <dave@davec.name>
|
|
*
|
|
* Permission to use, copy, modify, and distribute this software for any
|
|
* purpose with or without fee is hereby granted, provided that the above
|
|
* copyright notice and this permission notice appear in all copies.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
*/
|
|
|
|
package xdr
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"math"
|
|
"reflect"
|
|
"time"
|
|
)
|
|
|
|
var errIOEncode = "%s while encoding %d bytes"
|
|
|
|
/*
|
|
Marshal writes the XDR encoding of v to writer w and returns the number of bytes
|
|
written. It traverses v recursively and automatically indirects pointers
|
|
through arbitrary depth to encode the actual value pointed to.
|
|
|
|
Marshal uses reflection to determine the type of the concrete value contained by
|
|
v and performs a mapping of Go types to the underlying XDR types as follows:
|
|
|
|
Go Type -> XDR Type
|
|
--------------------
|
|
int8, int16, int32, int -> XDR Integer
|
|
uint8, uint16, uint32, uint -> XDR Unsigned Integer
|
|
int64 -> XDR Hyper Integer
|
|
uint64 -> XDR Unsigned Hyper Integer
|
|
bool -> XDR Boolean
|
|
float32 -> XDR Floating-Point
|
|
float64 -> XDR Double-Precision Floating-Point
|
|
string -> XDR String
|
|
byte -> XDR Integer
|
|
[]byte -> XDR Variable-Length Opaque Data
|
|
[#]byte -> XDR Fixed-Length Opaque Data
|
|
[]<type> -> XDR Variable-Length Array
|
|
[#]<type> -> XDR Fixed-Length Array
|
|
struct -> XDR Structure
|
|
map -> XDR Variable-Length Array of two-element XDR Structures
|
|
time.Time -> XDR String encoded with RFC3339 nanosecond precision
|
|
|
|
Notes and Limitations:
|
|
|
|
* Automatic marshalling of variable and fixed-length arrays of uint8s
|
|
requires a special struct tag `xdropaque:"false"` since byte slices and
|
|
byte arrays are assumed to be opaque data and byte is a Go alias for uint8
|
|
thus indistinguishable under reflection
|
|
* Channel, complex, and function types cannot be encoded
|
|
* Interfaces without a concrete value cannot be encoded
|
|
* Cyclic data structures are not supported and will result in infinite loops
|
|
* Strings are marshalled with UTF-8 character encoding which differs from
|
|
the XDR specification of ASCII, however UTF-8 is backwards compatible with
|
|
ASCII so this should rarely cause issues
|
|
|
|
If any issues are encountered during the marshalling process, a MarshalError is
|
|
returned with a human readable description as well as an ErrorCode value for
|
|
further inspection from sophisticated callers. Some potential issues are
|
|
unsupported Go types, attempting to encode more opaque data than can be
|
|
represented by a single opaque XDR entry, and exceeding max slice limitations.
|
|
*/
|
|
func Marshal(w io.Writer, v interface{}) (int, error) {
|
|
enc := Encoder{w: w}
|
|
return enc.Encode(v)
|
|
}
|
|
|
|
// An Encoder wraps an io.Writer that will receive the XDR encoded byte stream.
|
|
// See NewEncoder.
|
|
type Encoder struct {
|
|
w io.Writer
|
|
}
|
|
|
|
// EncodeInt writes the XDR encoded representation of the passed 32-bit signed
|
|
// integer to the encapsulated writer and returns the number of bytes written.
|
|
//
|
|
// A MarshalError with an error code of ErrIO is returned if writing the data
|
|
// fails.
|
|
//
|
|
// Reference:
|
|
// RFC Section 4.1 - Integer
|
|
// 32-bit big-endian signed integer in range [-2147483648, 2147483647]
|
|
func (enc *Encoder) EncodeInt(v int32) (int, error) {
|
|
var b [4]byte
|
|
b[0] = byte(v >> 24)
|
|
b[1] = byte(v >> 16)
|
|
b[2] = byte(v >> 8)
|
|
b[3] = byte(v)
|
|
|
|
n, err := enc.w.Write(b[:])
|
|
if err != nil {
|
|
msg := fmt.Sprintf(errIOEncode, err.Error(), 4)
|
|
err := marshalError("EncodeInt", ErrIO, msg, b[:n], err)
|
|
return n, err
|
|
}
|
|
|
|
return n, nil
|
|
}
|
|
|
|
// EncodeUint writes the XDR encoded representation of the passed 32-bit
|
|
// unsigned integer to the encapsulated writer and returns the number of bytes
|
|
// written.
|
|
//
|
|
// A MarshalError with an error code of ErrIO is returned if writing the data
|
|
// fails.
|
|
//
|
|
// Reference:
|
|
// RFC Section 4.2 - Unsigned Integer
|
|
// 32-bit big-endian unsigned integer in range [0, 4294967295]
|
|
func (enc *Encoder) EncodeUint(v uint32) (int, error) {
|
|
var b [4]byte
|
|
b[0] = byte(v >> 24)
|
|
b[1] = byte(v >> 16)
|
|
b[2] = byte(v >> 8)
|
|
b[3] = byte(v)
|
|
|
|
n, err := enc.w.Write(b[:])
|
|
if err != nil {
|
|
msg := fmt.Sprintf(errIOEncode, err.Error(), 4)
|
|
err := marshalError("EncodeUint", ErrIO, msg, b[:n], err)
|
|
return n, err
|
|
}
|
|
|
|
return n, nil
|
|
}
|
|
|
|
// EncodeEnum treats the passed 32-bit signed integer as an enumeration value
|
|
// and, if it is in the list of passed valid enumeration values, writes the XDR
|
|
// encoded representation of it to the encapsulated writer. It returns the
|
|
// number of bytes written.
|
|
//
|
|
// A MarshalError is returned if the enumeration value is not one of the
|
|
// provided valid values or if writing the data fails.
|
|
//
|
|
// Reference:
|
|
// RFC Section 4.3 - Enumeration
|
|
// Represented as an XDR encoded signed integer
|
|
func (enc *Encoder) EncodeEnum(v int32, validEnums map[int32]bool) (int, error) {
|
|
if !validEnums[v] {
|
|
err := marshalError("EncodeEnum", ErrBadEnumValue,
|
|
"invalid enum", v, nil)
|
|
return 0, err
|
|
}
|
|
return enc.EncodeInt(v)
|
|
}
|
|
|
|
// EncodeBool writes the XDR encoded representation of the passed boolean to the
|
|
// encapsulated writer and returns the number of bytes written.
|
|
//
|
|
// A MarshalError with an error code of ErrIO is returned if writing the data
|
|
// fails.
|
|
//
|
|
// Reference:
|
|
// RFC Section 4.4 - Boolean
|
|
// Represented as an XDR encoded enumeration where 0 is false and 1 is true
|
|
func (enc *Encoder) EncodeBool(v bool) (int, error) {
|
|
i := int32(0)
|
|
if v == true {
|
|
i = 1
|
|
}
|
|
return enc.EncodeInt(i)
|
|
}
|
|
|
|
// EncodeHyper writes the XDR encoded representation of the passed 64-bit
|
|
// signed integer to the encapsulated writer and returns the number of bytes
|
|
// written.
|
|
//
|
|
// A MarshalError with an error code of ErrIO is returned if writing the data
|
|
// fails.
|
|
//
|
|
// Reference:
|
|
// RFC Section 4.5 - Hyper Integer
|
|
// 64-bit big-endian signed integer in range [-9223372036854775808, 9223372036854775807]
|
|
func (enc *Encoder) EncodeHyper(v int64) (int, error) {
|
|
var b [8]byte
|
|
b[0] = byte(v >> 56)
|
|
b[1] = byte(v >> 48)
|
|
b[2] = byte(v >> 40)
|
|
b[3] = byte(v >> 32)
|
|
b[4] = byte(v >> 24)
|
|
b[5] = byte(v >> 16)
|
|
b[6] = byte(v >> 8)
|
|
b[7] = byte(v)
|
|
|
|
n, err := enc.w.Write(b[:])
|
|
if err != nil {
|
|
msg := fmt.Sprintf(errIOEncode, err.Error(), 8)
|
|
err := marshalError("EncodeHyper", ErrIO, msg, b[:n], err)
|
|
return n, err
|
|
}
|
|
|
|
return n, nil
|
|
}
|
|
|
|
// EncodeUhyper writes the XDR encoded representation of the passed 64-bit
|
|
// unsigned integer to the encapsulated writer and returns the number of bytes
|
|
// written.
|
|
//
|
|
// A MarshalError with an error code of ErrIO is returned if writing the data
|
|
// fails.
|
|
//
|
|
// Reference:
|
|
// RFC Section 4.5 - Unsigned Hyper Integer
|
|
// 64-bit big-endian unsigned integer in range [0, 18446744073709551615]
|
|
func (enc *Encoder) EncodeUhyper(v uint64) (int, error) {
|
|
var b [8]byte
|
|
b[0] = byte(v >> 56)
|
|
b[1] = byte(v >> 48)
|
|
b[2] = byte(v >> 40)
|
|
b[3] = byte(v >> 32)
|
|
b[4] = byte(v >> 24)
|
|
b[5] = byte(v >> 16)
|
|
b[6] = byte(v >> 8)
|
|
b[7] = byte(v)
|
|
|
|
n, err := enc.w.Write(b[:])
|
|
if err != nil {
|
|
msg := fmt.Sprintf(errIOEncode, err.Error(), 8)
|
|
err := marshalError("EncodeUhyper", ErrIO, msg, b[:n], err)
|
|
return n, err
|
|
}
|
|
|
|
return n, nil
|
|
}
|
|
|
|
// EncodeFloat writes the XDR encoded representation of the passed 32-bit
|
|
// (single-precision) floating point to the encapsulated writer and returns the
|
|
// number of bytes written.
|
|
//
|
|
// A MarshalError with an error code of ErrIO is returned if writing the data
|
|
// fails.
|
|
//
|
|
// Reference:
|
|
// RFC Section 4.6 - Floating Point
|
|
// 32-bit single-precision IEEE 754 floating point
|
|
func (enc *Encoder) EncodeFloat(v float32) (int, error) {
|
|
ui := math.Float32bits(v)
|
|
return enc.EncodeUint(ui)
|
|
}
|
|
|
|
// EncodeDouble writes the XDR encoded representation of the passed 64-bit
|
|
// (double-precision) floating point to the encapsulated writer and returns the
|
|
// number of bytes written.
|
|
//
|
|
// A MarshalError with an error code of ErrIO is returned if writing the data
|
|
// fails.
|
|
//
|
|
// Reference:
|
|
// RFC Section 4.7 - Double-Precision Floating Point
|
|
// 64-bit double-precision IEEE 754 floating point
|
|
func (enc *Encoder) EncodeDouble(v float64) (int, error) {
|
|
ui := math.Float64bits(v)
|
|
return enc.EncodeUhyper(ui)
|
|
}
|
|
|
|
// RFC Section 4.8 - Quadruple-Precision Floating Point
|
|
// 128-bit quadruple-precision floating point
|
|
// Not Implemented
|
|
|
|
// EncodeFixedOpaque treats the passed byte slice as opaque data of a fixed
|
|
// size and writes the XDR encoded representation of it to the encapsulated
|
|
// writer. It returns the number of bytes written.
|
|
//
|
|
// A MarshalError with an error code of ErrIO is returned if writing the data
|
|
// fails.
|
|
//
|
|
// Reference:
|
|
// RFC Section 4.9 - Fixed-Length Opaque Data
|
|
// Fixed-length uninterpreted data zero-padded to a multiple of four
|
|
func (enc *Encoder) EncodeFixedOpaque(v []byte) (int, error) {
|
|
l := len(v)
|
|
pad := (4 - (l % 4)) % 4
|
|
|
|
// Write the actual bytes.
|
|
n, err := enc.w.Write(v)
|
|
if err != nil {
|
|
msg := fmt.Sprintf(errIOEncode, err.Error(), len(v))
|
|
err := marshalError("EncodeFixedOpaque", ErrIO, msg, v[:n], err)
|
|
return n, err
|
|
}
|
|
|
|
// Write any padding if needed.
|
|
if pad > 0 {
|
|
b := make([]byte, pad)
|
|
n2, err := enc.w.Write(b)
|
|
n += n2
|
|
if err != nil {
|
|
written := make([]byte, l+n2)
|
|
copy(written, v)
|
|
copy(written[l:], b[:n2])
|
|
msg := fmt.Sprintf(errIOEncode, err.Error(), l+pad)
|
|
err := marshalError("EncodeFixedOpaque", ErrIO, msg,
|
|
written, err)
|
|
return n, err
|
|
}
|
|
}
|
|
|
|
return n, nil
|
|
}
|
|
|
|
// EncodeOpaque treats the passed byte slice as opaque data of a variable
|
|
// size and writes the XDR encoded representation of it to the encapsulated
|
|
// writer. It returns the number of bytes written.
|
|
//
|
|
// A MarshalError with an error code of ErrIO is returned if writing the data
|
|
// fails.
|
|
//
|
|
// Reference:
|
|
// RFC Section 4.10 - Variable-Length Opaque Data
|
|
// Unsigned integer length followed by fixed opaque data of that length
|
|
func (enc *Encoder) EncodeOpaque(v []byte) (int, error) {
|
|
// Length of opaque data.
|
|
n, err := enc.EncodeUint(uint32(len(v)))
|
|
if err != nil {
|
|
return n, err
|
|
}
|
|
|
|
n2, err := enc.EncodeFixedOpaque(v)
|
|
n += n2
|
|
return n, err
|
|
}
|
|
|
|
// EncodeString writes the XDR encoded representation of the passed string
|
|
// to the encapsulated writer and returns the number of bytes written.
|
|
// Character encoding is assumed to be UTF-8 and therefore ASCII compatible. If
|
|
// the underlying character encoding is not compatible with this assumption, the
|
|
// data can instead be written as variable-length opaque data (EncodeOpaque) and
|
|
// manually converted as needed.
|
|
//
|
|
// A MarshalError with an error code of ErrIO is returned if writing the data
|
|
// fails.
|
|
//
|
|
// Reference:
|
|
// RFC Section 4.11 - String
|
|
// Unsigned integer length followed by bytes zero-padded to a multiple of four
|
|
func (enc *Encoder) EncodeString(v string) (int, error) {
|
|
// Length of string.
|
|
n, err := enc.EncodeUint(uint32(len(v)))
|
|
if err != nil {
|
|
return n, err
|
|
}
|
|
|
|
n2, err := enc.EncodeFixedOpaque([]byte(v))
|
|
n += n2
|
|
return n, err
|
|
}
|
|
|
|
// encodeFixedArray writes the XDR encoded representation of each element
|
|
// in the passed array represented by the reflection value to the encapsulated
|
|
// writer and returns the number of bytes written. The ignoreOpaque flag
|
|
// controls whether or not uint8 (byte) elements should be encoded individually
|
|
// or as a fixed sequence of opaque data.
|
|
//
|
|
// A MarshalError is returned if any issues are encountered while encoding
|
|
// the array elements.
|
|
//
|
|
// Reference:
|
|
// RFC Section 4.12 - Fixed-Length Array
|
|
// Individually XDR encoded array elements
|
|
func (enc *Encoder) encodeFixedArray(v reflect.Value, ignoreOpaque bool) (int, error) {
|
|
// Treat [#]byte (byte is alias for uint8) as opaque data unless ignored.
|
|
if !ignoreOpaque && v.Type().Elem().Kind() == reflect.Uint8 {
|
|
// Create a slice of the underlying array for better efficiency
|
|
// when possible. Can't create a slice of an unaddressable
|
|
// value.
|
|
if v.CanAddr() {
|
|
return enc.EncodeFixedOpaque(v.Slice(0, v.Len()).Bytes())
|
|
}
|
|
|
|
// When the underlying array isn't addressable fall back to
|
|
// copying the array into a new slice. This is rather ugly, but
|
|
// the inability to create a constant slice from an
|
|
// unaddressable array is a limitation of Go.
|
|
slice := make([]byte, v.Len(), v.Len())
|
|
reflect.Copy(reflect.ValueOf(slice), v)
|
|
return enc.EncodeFixedOpaque(slice)
|
|
}
|
|
|
|
// Encode each array element.
|
|
var n int
|
|
for i := 0; i < v.Len(); i++ {
|
|
n2, err := enc.encode(v.Index(i))
|
|
n += n2
|
|
if err != nil {
|
|
return n, err
|
|
}
|
|
}
|
|
|
|
return n, nil
|
|
}
|
|
|
|
// encodeArray writes an XDR encoded integer representing the number of
|
|
// elements in the passed slice represented by the reflection value followed by
|
|
// the XDR encoded representation of each element in slice to the encapsulated
|
|
// writer and returns the number of bytes written. The ignoreOpaque flag
|
|
// controls whether or not uint8 (byte) elements should be encoded individually
|
|
// or as a variable sequence of opaque data.
|
|
//
|
|
// A MarshalError is returned if any issues are encountered while encoding
|
|
// the array elements.
|
|
//
|
|
// Reference:
|
|
// RFC Section 4.13 - Variable-Length Array
|
|
// Unsigned integer length followed by individually XDR encoded array elements
|
|
func (enc *Encoder) encodeArray(v reflect.Value, ignoreOpaque bool) (int, error) {
|
|
numItems := uint32(v.Len())
|
|
n, err := enc.EncodeUint(numItems)
|
|
if err != nil {
|
|
return n, err
|
|
}
|
|
|
|
n2, err := enc.encodeFixedArray(v, ignoreOpaque)
|
|
n += n2
|
|
return n, err
|
|
}
|
|
|
|
// encodeStruct writes an XDR encoded representation of each value in the
|
|
// exported fields of the struct represented by the passed reflection value to
|
|
// the encapsulated writer and returns the number of bytes written. Pointers
|
|
// are automatically indirected through arbitrary depth to encode the actual
|
|
// value pointed to.
|
|
//
|
|
// A MarshalError is returned if any issues are encountered while encoding
|
|
// the elements.
|
|
//
|
|
// Reference:
|
|
// RFC Section 4.14 - Structure
|
|
// XDR encoded elements in the order of their declaration in the struct
|
|
func (enc *Encoder) encodeStruct(v reflect.Value) (int, error) {
|
|
var n int
|
|
vt := v.Type()
|
|
for i := 0; i < v.NumField(); i++ {
|
|
// Skip unexported fields and indirect through pointers.
|
|
vtf := vt.Field(i)
|
|
if vtf.PkgPath != "" {
|
|
continue
|
|
}
|
|
vf := v.Field(i)
|
|
vf = enc.indirect(vf)
|
|
|
|
// Handle non-opaque data to []uint8 and [#]uint8 based on struct tag.
|
|
tag := vtf.Tag.Get("xdropaque")
|
|
if tag == "false" {
|
|
switch vf.Kind() {
|
|
case reflect.Slice:
|
|
n2, err := enc.encodeArray(vf, true)
|
|
n += n2
|
|
if err != nil {
|
|
return n, err
|
|
}
|
|
continue
|
|
|
|
case reflect.Array:
|
|
n2, err := enc.encodeFixedArray(vf, true)
|
|
n += n2
|
|
if err != nil {
|
|
return n, err
|
|
}
|
|
continue
|
|
}
|
|
}
|
|
|
|
// Encode each struct field.
|
|
n2, err := enc.encode(vf)
|
|
n += n2
|
|
if err != nil {
|
|
return n, err
|
|
}
|
|
}
|
|
|
|
return n, nil
|
|
}
|
|
|
|
// RFC Section 4.15 - Discriminated Union
|
|
// RFC Section 4.16 - Void
|
|
// RFC Section 4.17 - Constant
|
|
// RFC Section 4.18 - Typedef
|
|
// RFC Section 4.19 - Optional data
|
|
// RFC Sections 4.15 though 4.19 only apply to the data specification language
|
|
// which is not implemented by this package. In the case of discriminated
|
|
// unions, struct tags are used to perform a similar function.
|
|
|
|
// encodeMap treats the map represented by the passed reflection value as a
|
|
// variable-length array of 2-element structures whose fields are of the same
|
|
// type as the map keys and elements and writes its XDR encoded representation
|
|
// to the encapsulated writer. It returns the number of bytes written.
|
|
//
|
|
// A MarshalError is returned if any issues are encountered while encoding
|
|
// the elements.
|
|
func (enc *Encoder) encodeMap(v reflect.Value) (int, error) {
|
|
// Number of elements.
|
|
n, err := enc.EncodeUint(uint32(v.Len()))
|
|
if err != nil {
|
|
return n, err
|
|
}
|
|
|
|
// Encode each key and value according to their type.
|
|
for _, key := range v.MapKeys() {
|
|
n2, err := enc.encode(key)
|
|
n += n2
|
|
if err != nil {
|
|
return n, err
|
|
}
|
|
|
|
n2, err = enc.encode(v.MapIndex(key))
|
|
n += n2
|
|
if err != nil {
|
|
return n, err
|
|
}
|
|
}
|
|
|
|
return n, nil
|
|
}
|
|
|
|
// encodeInterface examines the interface represented by the passed reflection
|
|
// value to detect whether it is an interface that can be encoded if it is,
|
|
// extracts the underlying value to pass back into the encode function for
|
|
// encoding according to its type.
|
|
//
|
|
// A MarshalError is returned if any issues are encountered while encoding
|
|
// the interface.
|
|
func (enc *Encoder) encodeInterface(v reflect.Value) (int, error) {
|
|
if v.IsNil() || !v.CanInterface() {
|
|
msg := fmt.Sprintf("can't encode nil interface")
|
|
err := marshalError("encodeInterface", ErrNilInterface, msg,
|
|
nil, nil)
|
|
return 0, err
|
|
}
|
|
|
|
// Extract underlying value from the interface and indirect through pointers.
|
|
ve := reflect.ValueOf(v.Interface())
|
|
ve = enc.indirect(ve)
|
|
return enc.encode(ve)
|
|
}
|
|
|
|
// encode is the main workhorse for marshalling via reflection. It uses
|
|
// the passed reflection value to choose the XDR primitives to encode into
|
|
// the encapsulated writer and returns the number of bytes written. It is a
|
|
// recursive function, so cyclic data structures are not supported and will
|
|
// result in an infinite loop.
|
|
func (enc *Encoder) encode(v reflect.Value) (int, error) {
|
|
if !v.IsValid() {
|
|
msg := fmt.Sprintf("type '%s' is not valid", v.Kind().String())
|
|
err := marshalError("encode", ErrUnsupportedType, msg, nil, nil)
|
|
return 0, err
|
|
}
|
|
|
|
// Indirect through pointers to get at the concrete value.
|
|
ve := enc.indirect(v)
|
|
|
|
// Handle time.Time values by encoding them as an RFC3339 formatted
|
|
// string with nanosecond precision. Check the type string before
|
|
// doing a full blown conversion to interface and type assertion since
|
|
// checking a string is much quicker.
|
|
if ve.Type().String() == "time.Time" && ve.CanInterface() {
|
|
viface := ve.Interface()
|
|
if tv, ok := viface.(time.Time); ok {
|
|
return enc.EncodeString(tv.Format(time.RFC3339Nano))
|
|
}
|
|
}
|
|
|
|
// Handle native Go types.
|
|
switch ve.Kind() {
|
|
case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int:
|
|
return enc.EncodeInt(int32(ve.Int()))
|
|
|
|
case reflect.Int64:
|
|
return enc.EncodeHyper(ve.Int())
|
|
|
|
case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint:
|
|
return enc.EncodeUint(uint32(ve.Uint()))
|
|
|
|
case reflect.Uint64:
|
|
return enc.EncodeUhyper(ve.Uint())
|
|
|
|
case reflect.Bool:
|
|
return enc.EncodeBool(ve.Bool())
|
|
|
|
case reflect.Float32:
|
|
return enc.EncodeFloat(float32(ve.Float()))
|
|
|
|
case reflect.Float64:
|
|
return enc.EncodeDouble(ve.Float())
|
|
|
|
case reflect.String:
|
|
return enc.EncodeString(ve.String())
|
|
|
|
case reflect.Array:
|
|
return enc.encodeFixedArray(ve, false)
|
|
|
|
case reflect.Slice:
|
|
return enc.encodeArray(ve, false)
|
|
|
|
case reflect.Struct:
|
|
return enc.encodeStruct(ve)
|
|
|
|
case reflect.Map:
|
|
return enc.encodeMap(ve)
|
|
|
|
case reflect.Interface:
|
|
return enc.encodeInterface(ve)
|
|
}
|
|
|
|
// The only unhandled types left are unsupported. At the time of this
|
|
// writing the only remaining unsupported types that exist are
|
|
// reflect.Uintptr and reflect.UnsafePointer.
|
|
msg := fmt.Sprintf("unsupported Go type '%s'", ve.Kind().String())
|
|
err := marshalError("encode", ErrUnsupportedType, msg, nil, nil)
|
|
return 0, err
|
|
}
|
|
|
|
// indirect dereferences pointers until it reaches a non-pointer. This allows
|
|
// transparent encoding through arbitrary levels of indirection.
|
|
func (enc *Encoder) indirect(v reflect.Value) reflect.Value {
|
|
rv := v
|
|
for rv.Kind() == reflect.Ptr {
|
|
rv = rv.Elem()
|
|
}
|
|
return rv
|
|
}
|
|
|
|
// Encode operates identically to the Marshal function with the exception of
|
|
// using the writer associated with the Encoder for the destination of the
|
|
// XDR-encoded data instead of a user-supplied writer. See the Marshal
|
|
// documentation for specifics.
|
|
func (enc *Encoder) Encode(v interface{}) (int, error) {
|
|
if v == nil {
|
|
msg := "can't marshal nil interface"
|
|
err := marshalError("Marshal", ErrNilInterface, msg, nil, nil)
|
|
return 0, err
|
|
}
|
|
|
|
vv := reflect.ValueOf(v)
|
|
vve := vv
|
|
for vve.Kind() == reflect.Ptr {
|
|
if vve.IsNil() {
|
|
msg := fmt.Sprintf("can't marshal nil pointer '%v'",
|
|
vv.Type().String())
|
|
err := marshalError("Marshal", ErrBadArguments, msg,
|
|
nil, nil)
|
|
return 0, err
|
|
}
|
|
vve = vve.Elem()
|
|
}
|
|
|
|
return enc.encode(vve)
|
|
}
|
|
|
|
// NewEncoder returns an object that can be used to manually choose fields to
|
|
// XDR encode to the passed writer w. Typically, Marshal should be used instead
|
|
// of manually creating an Encoder. An Encoder, along with several of its
|
|
// methods to encode XDR primitives, is exposed so it is possible to perform
|
|
// manual encoding of data without relying on reflection should it be necessary
|
|
// in complex scenarios where automatic reflection-based encoding won't work.
|
|
func NewEncoder(w io.Writer) *Encoder {
|
|
return &Encoder{w: w}
|
|
}
|