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 <acon...@redhat.com> Authored: Fri Nov 10 15:20:32 2017 -0500 Committer: Alan Conway <acon...@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-unsubscr...@qpid.apache.org For additional commands, e-mail: commits-h...@qpid.apache.org