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

Reply via email to