You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@qpid.apache.org by ac...@apache.org on 2017/11/10 20:37:06 UTC
qpid-proton git commit: PROTON-1685: [go] support for
marshalling/unmarshaling AMQP arrays
Repository: qpid-proton
Updated Branches:
refs/heads/master e2cacf7f5 -> 78a292683
PROTON-1685: [go] support for marshalling/unmarshaling AMQP arrays
Project: http://git-wip-us.apache.org/repos/asf/qpid-proton/repo
Commit: http://git-wip-us.apache.org/repos/asf/qpid-proton/commit/78a29268
Tree: http://git-wip-us.apache.org/repos/asf/qpid-proton/tree/78a29268
Diff: http://git-wip-us.apache.org/repos/asf/qpid-proton/diff/78a29268
Branch: refs/heads/master
Commit: 78a2926832d27ad422d896f1f2a34e08fa27065f
Parents: e2cacf7
Author: Alan Conway <ac...@redhat.com>
Authored: Fri Nov 10 15:20:32 2017 -0500
Committer: Alan Conway <ac...@redhat.com>
Committed: Fri Nov 10 15:36:47 2017 -0500
----------------------------------------------------------------------
.../go/src/qpid.apache.org/amqp/interop_test.go | 9 +-
.../go/src/qpid.apache.org/amqp/marshal.go | 219 ++++++++++----
.../go/src/qpid.apache.org/amqp/types.go | 18 +-
.../go/src/qpid.apache.org/amqp/types_test.go | 20 +-
.../go/src/qpid.apache.org/amqp/unmarshal.go | 302 ++++++++++++-------
5 files changed, 367 insertions(+), 201 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/78a29268/proton-c/bindings/go/src/qpid.apache.org/amqp/interop_test.go
----------------------------------------------------------------------
diff --git a/proton-c/bindings/go/src/qpid.apache.org/amqp/interop_test.go b/proton-c/bindings/go/src/qpid.apache.org/amqp/interop_test.go
index a5fb92e..77d935e 100644
--- a/proton-c/bindings/go/src/qpid.apache.org/amqp/interop_test.go
+++ b/proton-c/bindings/go/src/qpid.apache.org/amqp/interop_test.go
@@ -32,10 +32,17 @@ import (
"testing"
)
+var skipped = false
+
func getReader(t *testing.T, name string) (r io.Reader) {
dir := os.Getenv("PN_INTEROP_DIR")
if dir == "" {
- t.Skip("no PN_INTEROP_DIR in environment")
+ if !skipped {
+ skipped = true // Don't keep repeating
+ t.Skip("no PN_INTEROP_DIR in environment")
+ } else {
+ t.SkipNow()
+ }
}
r, err := os.Open(dir + "/" + name + ".amqp")
if err != nil {
http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/78a29268/proton-c/bindings/go/src/qpid.apache.org/amqp/marshal.go
----------------------------------------------------------------------
diff --git a/proton-c/bindings/go/src/qpid.apache.org/amqp/marshal.go b/proton-c/bindings/go/src/qpid.apache.org/amqp/marshal.go
index 0f1c78d..bbb2450 100644
--- a/proton-c/bindings/go/src/qpid.apache.org/amqp/marshal.go
+++ b/proton-c/bindings/go/src/qpid.apache.org/amqp/marshal.go
@@ -96,9 +96,9 @@ Go types are encoded as follows
+-------------------------------------+--------------------------------------------+
|Map |map, may have mixed types for keys, values |
+-------------------------------------+--------------------------------------------+
- |[]T |list with T converted as above |
+ |List, []interface{} |list, may have mixed-type values |
+-------------------------------------+--------------------------------------------+
- |List |list, may have mixed types values |
+ |[]T, [N]T |array, T is mapped as per this table |
+-------------------------------------+--------------------------------------------+
|Described |described type |
+-------------------------------------+--------------------------------------------+
@@ -107,10 +107,12 @@ Go types are encoded as follows
|UUID |uuid |
+-------------------------------------+--------------------------------------------+
-The following Go types cannot be marshaled: uintptr, function, channel, array (use slice), struct, complex64/128.
+The following Go types cannot be marshaled: uintptr, function, channel, struct, complex64/128
-AMQP types not yet supported: decimal32/64/128, array.
+AMQP types not yet supported:
+- decimal32/64/128,
*/
+
func Marshal(v interface{}, buffer []byte) (outbuf []byte, err error) {
defer recoverMarshal(&err)
data := C.pn_data(0)
@@ -160,88 +162,198 @@ func encodeGrow(buffer []byte, encode encodeFn) ([]byte, error) {
return buffer, err
}
-func marshal(v interface{}, data *C.pn_data_t) {
- switch v := v.(type) {
+const intIsLong bool = (unsafe.Sizeof(int(0)) == 8)
+
+// Marshal v to data if data != nil
+// Return the pn_type_t for v, even if data == nil
+func marshal(i interface{}, data *C.pn_data_t) C.pn_type_t {
+ if data != nil { // On exit, check for errors on the data object
+ defer func() {
+ if err := dataMarshalError(i, data); err != nil {
+ panic(err)
+ }
+ }()
+ }
+ switch v := i.(type) {
case nil:
- C.pn_data_put_null(data)
+ if data != nil {
+ C.pn_data_put_null(data)
+ }
+ return C.PN_NULL
case bool:
- C.pn_data_put_bool(data, C.bool(v))
+ if data != nil {
+ C.pn_data_put_bool(data, C.bool(v))
+ }
+ return C.PN_BOOL
case int8:
- C.pn_data_put_byte(data, C.int8_t(v))
+ if data != nil {
+ C.pn_data_put_byte(data, C.int8_t(v))
+ }
+ return C.PN_BYTE
case int16:
- C.pn_data_put_short(data, C.int16_t(v))
+ if data != nil {
+ C.pn_data_put_short(data, C.int16_t(v))
+ }
+ return C.PN_SHORT
case int32:
- C.pn_data_put_int(data, C.int32_t(v))
+ if data != nil {
+ C.pn_data_put_int(data, C.int32_t(v))
+ }
+ return C.PN_INT
case int64:
- C.pn_data_put_long(data, C.int64_t(v))
+ if data != nil {
+ C.pn_data_put_long(data, C.int64_t(v))
+ }
+ return C.PN_LONG
case int:
- if unsafe.Sizeof(int(0)) == 8 {
+ if intIsLong {
C.pn_data_put_long(data, C.int64_t(v))
+ return C.PN_LONG
} else {
C.pn_data_put_int(data, C.int32_t(v))
+ return C.PN_INT
}
case uint8:
- C.pn_data_put_ubyte(data, C.uint8_t(v))
+ if data != nil {
+ C.pn_data_put_ubyte(data, C.uint8_t(v))
+ }
+ return C.PN_UBYTE
case uint16:
- C.pn_data_put_ushort(data, C.uint16_t(v))
+ if data != nil {
+ C.pn_data_put_ushort(data, C.uint16_t(v))
+ }
+ return C.PN_USHORT
case uint32:
- C.pn_data_put_uint(data, C.uint32_t(v))
+ if data != nil {
+ C.pn_data_put_uint(data, C.uint32_t(v))
+ }
+ return C.PN_UINT
case uint64:
- C.pn_data_put_ulong(data, C.uint64_t(v))
+ if data != nil {
+ C.pn_data_put_ulong(data, C.uint64_t(v))
+ }
+ return C.PN_ULONG
case uint:
- if unsafe.Sizeof(int(0)) == 8 {
+ if intIsLong {
C.pn_data_put_ulong(data, C.uint64_t(v))
+ return C.PN_ULONG
} else {
C.pn_data_put_uint(data, C.uint32_t(v))
+ return C.PN_UINT
}
case float32:
- C.pn_data_put_float(data, C.float(v))
+ if data != nil {
+ C.pn_data_put_float(data, C.float(v))
+ }
+ return C.PN_FLOAT
case float64:
- C.pn_data_put_double(data, C.double(v))
+ if data != nil {
+ C.pn_data_put_double(data, C.double(v))
+ }
+ return C.PN_DOUBLE
case string:
- C.pn_data_put_string(data, pnBytes([]byte(v)))
+ if data != nil {
+ C.pn_data_put_string(data, pnBytes([]byte(v)))
+ }
+ return C.PN_STRING
+
case []byte:
- C.pn_data_put_binary(data, pnBytes(v))
+ if data != nil {
+ C.pn_data_put_binary(data, pnBytes(v))
+ }
+ return C.PN_BINARY
+
case Binary:
- C.pn_data_put_binary(data, pnBytes([]byte(v)))
+ if data != nil {
+ C.pn_data_put_binary(data, pnBytes([]byte(v)))
+ }
+ return C.PN_BINARY
+
case Symbol:
- C.pn_data_put_symbol(data, pnBytes([]byte(v)))
- case Map: // Special map type
- C.pn_data_put_map(data)
- C.pn_data_enter(data)
- for key, val := range v {
- marshal(key, data)
- marshal(val, data)
+ if data != nil {
+ C.pn_data_put_symbol(data, pnBytes([]byte(v)))
}
- C.pn_data_exit(data)
+ return C.PN_SYMBOL
+
case Described:
C.pn_data_put_described(data)
C.pn_data_enter(data)
marshal(v.Descriptor, data)
marshal(v.Value, data)
C.pn_data_exit(data)
+ return C.PN_DESCRIBED
+
case AnnotationKey:
- marshal(v.Get(), data)
+ return marshal(v.Get(), data)
+
case time.Time:
- C.pn_data_put_timestamp(data, C.pn_timestamp_t(v.UnixNano()/1000))
+ if data != nil {
+ C.pn_data_put_timestamp(data, C.pn_timestamp_t(v.UnixNano()/1000))
+ }
+ return C.PN_TIMESTAMP
+
case UUID:
- C.pn_data_put_uuid(data, *(*C.pn_uuid_t)(unsafe.Pointer(&v[0])))
+ if data != nil {
+ C.pn_data_put_uuid(data, *(*C.pn_uuid_t)(unsafe.Pointer(&v[0])))
+ }
+ return C.PN_UUID
+
case Char:
- C.pn_data_put_char(data, (C.pn_char_t)(v))
+ if data != nil {
+ C.pn_data_put_char(data, (C.pn_char_t)(v))
+ }
+ return C.PN_CHAR
+
default:
- switch reflect.TypeOf(v).Kind() {
+ // Look at more complex types by reflected structure
+
+ switch reflect.TypeOf(i).Kind() {
+
case reflect.Map:
- putMap(data, v)
- case reflect.Slice:
- putList(data, v)
+ if data != nil {
+ m := reflect.ValueOf(v)
+ C.pn_data_put_map(data)
+ C.pn_data_enter(data)
+ defer C.pn_data_exit(data)
+ for _, key := range m.MapKeys() {
+ marshal(key.Interface(), data)
+ marshal(m.MapIndex(key).Interface(), data)
+ }
+ }
+ return C.PN_MAP
+
+ case reflect.Slice, reflect.Array:
+ // Note: Go array and slice are mapped the same way:
+ // if element type is an interface, map to AMQP list (mixed type)
+ // if element type is a non-interface type map to AMQP array (single type)
+ s := reflect.ValueOf(v)
+ var ret C.pn_type_t
+ t := reflect.TypeOf(i).Elem()
+ if t.Kind() == reflect.Interface {
+ if data != nil {
+ C.pn_data_put_list(data)
+ }
+ ret = C.PN_LIST
+ } else {
+ if data != nil {
+ pnType := marshal(reflect.Zero(t).Interface(), nil)
+ C.pn_data_put_array(data, false, pnType)
+ }
+ ret = C.PN_ARRAY
+ }
+ if data != nil {
+ C.pn_data_enter(data)
+ defer C.pn_data_exit(data)
+ for j := 0; j < s.Len(); j++ {
+ marshal(s.Index(j).Interface(), data)
+ }
+ }
+ return ret
+
default:
panic(newMarshalError(v, "no conversion"))
}
}
- if err := dataMarshalError(v, data); err != nil {
- panic(err)
- }
- return
}
func clearMarshal(v interface{}, data *C.pn_data_t) {
@@ -249,27 +361,6 @@ func clearMarshal(v interface{}, data *C.pn_data_t) {
marshal(v, data)
}
-func putMap(data *C.pn_data_t, v interface{}) {
- mapValue := reflect.ValueOf(v)
- C.pn_data_put_map(data)
- C.pn_data_enter(data)
- for _, key := range mapValue.MapKeys() {
- marshal(key.Interface(), data)
- marshal(mapValue.MapIndex(key).Interface(), data)
- }
- C.pn_data_exit(data)
-}
-
-func putList(data *C.pn_data_t, v interface{}) {
- listValue := reflect.ValueOf(v)
- C.pn_data_put_list(data)
- C.pn_data_enter(data)
- for i := 0; i < listValue.Len(); i++ {
- marshal(listValue.Index(i).Interface(), data)
- }
- C.pn_data_exit(data)
-}
-
// Encoder encodes AMQP values to an io.Writer
type Encoder struct {
writer io.Writer
http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/78a29268/proton-c/bindings/go/src/qpid.apache.org/amqp/types.go
----------------------------------------------------------------------
diff --git a/proton-c/bindings/go/src/qpid.apache.org/amqp/types.go b/proton-c/bindings/go/src/qpid.apache.org/amqp/types.go
index 8943ff3..4b8d24f 100644
--- a/proton-c/bindings/go/src/qpid.apache.org/amqp/types.go
+++ b/proton-c/bindings/go/src/qpid.apache.org/amqp/types.go
@@ -25,7 +25,6 @@ import "C"
import (
"bytes"
"fmt"
- "reflect"
"time"
"unsafe"
)
@@ -87,21 +86,16 @@ func (t C.pn_type_t) String() string {
}
}
-// Go types
-var (
- bytesType = reflect.TypeOf([]byte{})
- valueType = reflect.TypeOf(reflect.Value{})
-)
-
-// TODO aconway 2015-04-08: can't handle AMQP maps with key types that are not valid Go map keys.
-
-// Map is a generic map that can have mixed key and value types and so can represent any AMQP map
+// The AMQP map type. A generic map that can have mixed-type keys and values.
type Map map[interface{}]interface{}
-// List is a generic list that can hold mixed values and can represent any AMQP list.
-//
+// The AMQP list type. A generic list that can hold mixed-type values.
type List []interface{}
+// The generic AMQP array type, used to unmarshal an array with nested array,
+// map or list elements. Arrays of simple type T unmarshal to []T
+type Array []interface{}
+
// Symbol is a string that is encoded as an AMQP symbol
type Symbol string
http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/78a29268/proton-c/bindings/go/src/qpid.apache.org/amqp/types_test.go
----------------------------------------------------------------------
diff --git a/proton-c/bindings/go/src/qpid.apache.org/amqp/types_test.go b/proton-c/bindings/go/src/qpid.apache.org/amqp/types_test.go
index b9d6a40..9d5a6b6 100644
--- a/proton-c/bindings/go/src/qpid.apache.org/amqp/types_test.go
+++ b/proton-c/bindings/go/src/qpid.apache.org/amqp/types_test.go
@@ -65,21 +65,23 @@ var rtValues = []interface{}{
float32(0.32), float64(0.64),
"string", Binary("Binary"), Symbol("symbol"),
nil,
- Map{"V": "X"},
- List{"V", int32(1)},
Described{"D", "V"},
timeValue,
UUID{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16},
Char('\u2318'),
+ Map{"V": "X"},
+ List{"V", int32(1)},
+ []string{"a", "b", "c"}, // to AMQP array
}
-// Go values that unmarshal as an equivalent value but a different type
-// if unmarshalled to interface{}.
+// Go values that unmarshal as an equivalent value but a different default type
+// if unmarshalled to an interface{}
var oddValues = []interface{}{
- int(-99), uint(99), // [u]int32|64
+ int(-99), // int32|64 depending on platform
+ uint(99), // int32|64 depending on platform
[]byte("byte"), // amqp.Binary
map[string]int{"str": 99}, // amqp.Map
- []string{"a", "b"}, // amqp.List
+ []Map{Map{}}, // amqp.Array - the generic array
}
var allValues = append(rtValues, oddValues...)
@@ -93,16 +95,18 @@ var vstrings = []string{
"0.32", "0.64",
"string", "Binary", "symbol",
"<nil>",
- "map[V:X]",
- "[V 1]",
"{D V}",
fmt.Sprintf("%v", timeValue),
"UUID(01020304-0506-0708-090a-0b0c0d0e0f10)",
"\u2318",
+ "map[V:X]",
+ "[V 1]",
+ "[a b c]",
// for oddValues
"-99", "99",
"[98 121 116 101]", /*"byte"*/
"map[str:99]",
+ "[map[]]",
"[a b]",
}
http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/78a29268/proton-c/bindings/go/src/qpid.apache.org/amqp/unmarshal.go
----------------------------------------------------------------------
diff --git a/proton-c/bindings/go/src/qpid.apache.org/amqp/unmarshal.go b/proton-c/bindings/go/src/qpid.apache.org/amqp/unmarshal.go
index 719b399..04917a5 100644
--- a/proton-c/bindings/go/src/qpid.apache.org/amqp/unmarshal.go
+++ b/proton-c/bindings/go/src/qpid.apache.org/amqp/unmarshal.go
@@ -139,82 +139,101 @@ func (d *Decoder) Decode(v interface{}) (err error) {
}
/*
-Unmarshal decodes AMQP-encoded bytes and stores the result in the Go value pointed to by v.
-Types are converted as follows:
-
- +------------------------+-------------------------------------------------+
- |To Go types |From AMQP types |
- +========================+=================================================+
- |bool |bool |
- +------------------------+-------------------------------------------------+
- |int, int8, int16, int32,|Equivalent or smaller signed integer type: byte, |
- |int64 |short, int, long or char. |
- +------------------------+-------------------------------------------------+
- |uint, uint8, uint16, |Equivalent or smaller unsigned integer type: |
- |uint32, uint64 |ubyte, ushort, uint, ulong |
- +------------------------+-------------------------------------------------+
- |float32, float64 |Equivalent or smaller float or double. |
- +------------------------+-------------------------------------------------+
- |string, []byte |string, symbol or binary. |
- +------------------------+-------------------------------------------------+
- |Symbol |symbol |
- +------------------------+-------------------------------------------------+
- |Char |char |
- +------------------------+-------------------------------------------------+
- |map[K]T |map, provided all keys and values can unmarshal |
- | |to types K,T |
- +------------------------+-------------------------------------------------+
- |Map |map, any AMQP map |
- +------------------------+-------------------------------------------------+
- |Described |described type |
- +------------------------+-------------------------------------------------+
- |Time |timestamp |
- +------------------------+-------------------------------------------------+
- |UUID |uuid |
- +--------------------------------------------------------------------------+
-
-An AMQP described type can unmarshal into the corresponding plain type, discarding the descriptor.
-For example an AMQP described string can unmarshal into a plain go string.
-Unmarshal into the Described type preserves the descriptor.
-
-Any AMQP type can unmarshal to an interface{}, the Go type used to unmarshal is chosen from the AMQP type as follows
-
- +------------------------+-------------------------------------------------+
- |AMQP Type |Go Type in interface{} |
- +========================+=================================================+
- |bool |bool |
- +------------------------+-------------------------------------------------+
- |byte,short,int,long |int8,int16,int32,int64 |
- +------------------------+-------------------------------------------------+
- |ubyte,ushort,uint,ulong |uint8,uint16,uint32,uint64 |
- +------------------------+-------------------------------------------------+
- |float, double |float32, float64 |
- +------------------------+-------------------------------------------------+
- |string |string |
- +------------------------+-------------------------------------------------+
- |symbol |Symbol |
- +------------------------+-------------------------------------------------+
- |char |Char |
- +------------------------+-------------------------------------------------+
- |binary |Binary |
- +------------------------+-------------------------------------------------+
- |null |nil |
- +------------------------+-------------------------------------------------+
- |map |Map |
- +------------------------+-------------------------------------------------+
- |list |List |
- +------------------------+-------------------------------------------------+
- |described type |Described |
- +--------------------------------------------------------------------------+
- |timestamp |time.Time |
- +--------------------------------------------------------------------------+
- |uuid |UUID |
- +--------------------------------------------------------------------------+
-
-The following Go types cannot be unmarshaled: uintptr, function, interface, channel, array (use slice), struct
-
-AMQP types not yet supported: decimal32/64/128, maps with key values that are not legal Go map keys.
+
+Unmarshal decodes AMQP-encoded bytes and stores the result in the Go value
+pointed to by v. Legal conversions from the source AMQP type to the target Go
+type as follows:
+
+ +----------------------------+-------------------------------------------------+
+ |Target Go type | Allowed AMQP types
+ +============================+==================================================+
+ |bool |bool |
+ +----------------------------+--------------------------------------------------+
+ |int, int8, int16, int32, |Equivalent or smaller signed integer type: |
+ |int64 |byte, short, int, long or char |
+ +----------------------------+--------------------------------------------------+
+ |uint, uint8, uint16, uint32,|Equivalent or smaller unsigned integer type: |
+ |uint64 |ubyte, ushort, uint, ulong |
+ +----------------------------+--------------------------------------------------+
+ |float32, float64 |Equivalent or smaller float or double |
+ +----------------------------+--------------------------------------------------+
+ |string, []byte |string, symbol or binary |
+ +----------------------------+--------------------------------------------------+
+ |Symbol |symbol |
+ +----------------------------+--------------------------------------------------+
+ |Char |char |
+ +----------------------------+--------------------------------------------------+
+ |Described |AMQP described type [1] |
+ +----------------------------+--------------------------------------------------+
+ |Time |timestamp |
+ +----------------------------+--------------------------------------------------+
+ |UUID |uuid |
+ +----------------------------+--------------------------------------------------+
+ |map[interface{}]interface{} |Any AMQP map |
+ +----------------------------+--------------------------------------------------+
+ |map[K]T |map, provided all keys and values can unmarshal |
+ | |to types K,T |
+ +----------------------------+--------------------------------------------------+
+ |[]interface{} |AMQP list or array |
+ +----------------------------+--------------------------------------------------+
+ |[]T |AMQP list or array if elements can unmarshal as T |
+ +----------------------------+------------------n-------------------------------+
+ |interface{} |any AMQP type[2] |
+ +----------------------------+--------------------------------------------------+
+
+[1] An AMQP described value can also convert as if it were a plain value,
+discarding the descriptor. Unmarshalling into the special amqp.Described type
+preserves the descriptor.
+
+[2] Any AMQP value can be unmarshalled to an interface{}. The Go type is
+chosen based on the AMQP type as follows:
+
+ +----------------------------+--------------------------------------------------+
+ |Source AMQP Type |Go Type in target interface{} |
+ +============================+==================================================+
+ |bool |bool |
+ +----------------------------+--------------------------------------------------+
+ |byte,short,int,long |int8,int16,int32,int64 |
+ +----------------------------+--------------------------------------------------+
+ |ubyte,ushort,uint,ulong |uint8,uint16,uint32,uint64 |
+ +----------------------------+--------------------------------------------------+
+ |float, double |float32, float64 |
+ +----------------------------+--------------------------------------------------+
+ |string |string |
+ +----------------------------+--------------------------------------------------+
+ |symbol |Symbol |
+ +----------------------------+--------------------------------------------------+
+ |char |Char |
+ +----------------------------+--------------------------------------------------+
+ |binary |Binary |
+ +----------------------------+--------------------------------------------------+
+ |null |nil |
+ +----------------------------+--------------------------------------------------+
+ |described type |Described |
+ +----------------------------+--------------------------------------------------+
+ |timestamp |time.Time |
+ +----------------------------+--------------------------------------------------+
+ |uuid |UUID |
+ +----------------------------+--------------------------------------------------+
+ |map |Map |
+ +----------------------------+--------------------------------------------------+
+ |list |List |
+ +----------------------------+--------------------------------------------------+
+ |array |[]T for simple types, T is chosen as above [3] |
+ +----------------------------+--------------------------------------------------+
+
+[3] An AMQP array of simple types unmarshalls as a slice of the corresponding Go type.
+An AMQP array containing complex types (lists, maps or nested arrays) unmarshals
+to the generic array type amqp.Array
+
+The following Go types cannot be unmarshaled: uintptr, function, interface,
+channel, array (use slice), struct
+
+AMQP types not yet supported:
+- decimal32/64/128
+- maps with key values that are not legal Go map keys.
*/
+
func Unmarshal(bytes []byte, v interface{}) (n int, err error) {
defer recoverUnmarshal(&err)
@@ -253,7 +272,8 @@ func (d *Decoder) more() error {
return err
}
-// Unmarshal from data into value pointed at by v.
+// Unmarshal from data into value pointed at by v. Returns v.
+// NOTE: If you update this you also need to update getInterface()
func unmarshal(v interface{}, data *C.pn_data_t) {
pnType := C.pn_data_type(data)
@@ -480,10 +500,6 @@ func unmarshal(v interface{}, data *C.pn_data_t) {
default:
panic(newUnmarshalError(pnType, v))
}
-
- case *interface{}:
- getInterface(data, v)
-
case *AnnotationKey:
if pnType == C.PN_ULONG || pnType == C.PN_SYMBOL || pnType == C.PN_STRING {
unmarshal(&v.value, data)
@@ -491,6 +507,9 @@ func unmarshal(v interface{}, data *C.pn_data_t) {
panic(newUnmarshalError(pnType, v))
}
+ case *interface{}:
+ getInterface(data, v)
+
default: // This is not one of the fixed well-known types, reflect for map and slice types
if reflect.TypeOf(v).Kind() != reflect.Ptr {
panic(newUnmarshalError(pnType, v))
@@ -499,7 +518,7 @@ func unmarshal(v interface{}, data *C.pn_data_t) {
case reflect.Map:
getMap(data, v)
case reflect.Slice:
- getList(data, v)
+ getSequence(data, v)
default:
panic(newUnmarshalError(pnType, v))
}
@@ -516,68 +535,114 @@ func rewindUnmarshal(v interface{}, data *C.pn_data_t) {
unmarshal(v, data)
}
-// Getting into an interface is driven completely by the AMQP type, since the interface{}
-// target is type-neutral.
-func getInterface(data *C.pn_data_t, v *interface{}) {
+// Unmarshalling into an interface{} the type is determined by the AMQP source type,
+// since the interface{} target can hold any Go type.
+func getInterface(data *C.pn_data_t, vp *interface{}) {
pnType := C.pn_data_type(data)
switch pnType {
case C.PN_BOOL:
- *v = bool(C.pn_data_get_bool(data))
+ *vp = bool(C.pn_data_get_bool(data))
case C.PN_UBYTE:
- *v = uint8(C.pn_data_get_ubyte(data))
+ *vp = uint8(C.pn_data_get_ubyte(data))
case C.PN_BYTE:
- *v = int8(C.pn_data_get_byte(data))
+ *vp = int8(C.pn_data_get_byte(data))
case C.PN_USHORT:
- *v = uint16(C.pn_data_get_ushort(data))
+ *vp = uint16(C.pn_data_get_ushort(data))
case C.PN_SHORT:
- *v = int16(C.pn_data_get_short(data))
+ *vp = int16(C.pn_data_get_short(data))
case C.PN_UINT:
- *v = uint32(C.pn_data_get_uint(data))
+ *vp = uint32(C.pn_data_get_uint(data))
case C.PN_INT:
- *v = int32(C.pn_data_get_int(data))
+ *vp = int32(C.pn_data_get_int(data))
case C.PN_CHAR:
- *v = Char(C.pn_data_get_char(data))
+ *vp = Char(C.pn_data_get_char(data))
case C.PN_ULONG:
- *v = uint64(C.pn_data_get_ulong(data))
+ *vp = uint64(C.pn_data_get_ulong(data))
case C.PN_LONG:
- *v = int64(C.pn_data_get_long(data))
+ *vp = int64(C.pn_data_get_long(data))
case C.PN_FLOAT:
- *v = float32(C.pn_data_get_float(data))
+ *vp = float32(C.pn_data_get_float(data))
case C.PN_DOUBLE:
- *v = float64(C.pn_data_get_double(data))
+ *vp = float64(C.pn_data_get_double(data))
case C.PN_BINARY:
- *v = Binary(goBytes(C.pn_data_get_binary(data)))
+ *vp = Binary(goBytes(C.pn_data_get_binary(data)))
case C.PN_STRING:
- *v = goString(C.pn_data_get_string(data))
+ *vp = goString(C.pn_data_get_string(data))
case C.PN_SYMBOL:
- *v = Symbol(goString(C.pn_data_get_symbol(data)))
+ *vp = Symbol(goString(C.pn_data_get_symbol(data)))
+ case C.PN_TIMESTAMP:
+ *vp = time.Unix(0, int64(C.pn_data_get_timestamp(data))*1000)
+ case C.PN_UUID:
+ var u UUID
+ unmarshal(&u, data)
+ *vp = u
case C.PN_MAP:
- m := make(Map)
+ m := Map{}
unmarshal(&m, data)
- *v = m
+ *vp = m
case C.PN_LIST:
- l := make(List, 0)
+ l := List{}
unmarshal(&l, data)
- *v = l
+ *vp = l
+ case C.PN_ARRAY:
+ sp := getArrayStore(data) // interface{} containing T* for suitable T
+ unmarshal(sp, data)
+ *vp = reflect.ValueOf(sp).Elem().Interface()
case C.PN_DESCRIBED:
d := Described{}
unmarshal(&d, data)
- *v = d
- case C.PN_TIMESTAMP:
- *v = time.Unix(0, int64(C.pn_data_get_timestamp(data))*1000)
- case C.PN_UUID:
- var u UUID
- unmarshal(&u, data)
- *v = u
+ *vp = d
case C.PN_NULL:
- *v = nil
+ *vp = nil
case C.PN_INVALID:
// Allow decoding from an empty data object to an interface, treat it like NULL.
// This happens when optional values or properties are omitted from a message.
- *v = nil
+ *vp = nil
default: // Don't know how to handle this
- panic(newUnmarshalError(pnType, v))
+ panic(newUnmarshalError(pnType, vp))
+ }
+}
+
+// Return an interface{} containing a pointer to an appropriate slice or Array
+func getArrayStore(data *C.pn_data_t) interface{} {
+ // TODO aconway 2017-11-10: described arrays.
+ switch C.pn_data_get_array_type(data) {
+ case C.PN_BOOL:
+ return new([]bool)
+ case C.PN_UBYTE:
+ return new([]uint8)
+ case C.PN_BYTE:
+ return new([]int8)
+ case C.PN_USHORT:
+ return new([]uint16)
+ case C.PN_SHORT:
+ return new([]int16)
+ case C.PN_UINT:
+ return new([]uint32)
+ case C.PN_INT:
+ return new([]int32)
+ case C.PN_CHAR:
+ return new([]Char)
+ case C.PN_ULONG:
+ return new([]uint64)
+ case C.PN_LONG:
+ return new([]int64)
+ case C.PN_FLOAT:
+ return new([]float32)
+ case C.PN_DOUBLE:
+ return new([]float64)
+ case C.PN_BINARY:
+ return new([]Binary)
+ case C.PN_STRING:
+ return new([]string)
+ case C.PN_SYMBOL:
+ return new([]Symbol)
+ case C.PN_TIMESTAMP:
+ return new([]time.Time)
+ case C.PN_UUID:
+ return new([]UUID)
}
+ return new(Array) // Not a simple type, use generic Array
}
// get into map pointed at by v
@@ -605,12 +670,17 @@ func getMap(data *C.pn_data_t, v interface{}) {
}
}
-func getList(data *C.pn_data_t, v interface{}) {
+func getSequence(data *C.pn_data_t, v interface{}) {
+ var count int
pnType := C.pn_data_type(data)
- if pnType != C.PN_LIST {
+ switch pnType {
+ case C.PN_LIST:
+ count = int(C.pn_data_get_list(data))
+ case C.PN_ARRAY:
+ count = int(C.pn_data_get_array(data))
+ default:
panic(newUnmarshalError(pnType, v))
}
- count := int(C.pn_data_get_list(data))
listValue := reflect.MakeSlice(reflect.TypeOf(v).Elem(), count, count)
if bool(C.pn_data_enter(data)) {
for i := 0; i < count; i++ {
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@qpid.apache.org
For additional commands, e-mail: commits-help@qpid.apache.org