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 2015/03/13 20:27:56 UTC
qpid-proton git commit: PROTON-827: Marshal/unmarshal maps and lists.
Repository: qpid-proton
Updated Branches:
refs/heads/master 7e42628ed -> faf925c4a
PROTON-827: Marshal/unmarshal maps and lists.
Project: http://git-wip-us.apache.org/repos/asf/qpid-proton/repo
Commit: http://git-wip-us.apache.org/repos/asf/qpid-proton/commit/faf925c4
Tree: http://git-wip-us.apache.org/repos/asf/qpid-proton/tree/faf925c4
Diff: http://git-wip-us.apache.org/repos/asf/qpid-proton/diff/faf925c4
Branch: refs/heads/master
Commit: faf925c4afe02da2dced7a6592586b575ceea2ec
Parents: 7e42628
Author: Alan Conway <ac...@redhat.com>
Authored: Thu Mar 12 17:12:44 2015 -0400
Committer: Alan Conway <ac...@redhat.com>
Committed: Fri Mar 13 15:25:57 2015 -0400
----------------------------------------------------------------------
.../bindings/go/src/apache.org/proton/error.go | 55 ++--
.../go/src/apache.org/proton/interop_test.go | 175 ++++--------
.../go/src/apache.org/proton/marshal.go | 115 +++++---
.../bindings/go/src/apache.org/proton/types.go | 33 ++-
.../go/src/apache.org/proton/unmarshal.go | 286 ++++++++++++-------
5 files changed, 384 insertions(+), 280 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/faf925c4/proton-c/bindings/go/src/apache.org/proton/error.go
----------------------------------------------------------------------
diff --git a/proton-c/bindings/go/src/apache.org/proton/error.go b/proton-c/bindings/go/src/apache.org/proton/error.go
index 63426de..f9cc948 100644
--- a/proton-c/bindings/go/src/apache.org/proton/error.go
+++ b/proton-c/bindings/go/src/apache.org/proton/error.go
@@ -25,6 +25,8 @@ import "C"
import (
"fmt"
+ "reflect"
+ "runtime"
)
var pnErrorNames = map[int]string{
@@ -39,33 +41,35 @@ var pnErrorNames = map[int]string{
C.PN_INPROGRESS: "in progress",
}
-func nonBlank(a, b string) string {
- if a == "" {
- return b
+func pnErrorName(code int) string {
+ name := pnErrorNames[code]
+ if name != "" {
+ return name
+ } else {
+ return "unknown error code"
}
- return a
}
-func pnErrorName(code int) string {
- return nonBlank(pnErrorNames[code], "unknown error code")
+type BadUnmarshal struct {
+ AMQPType C.pn_type_t
+ GoType reflect.Type
}
-///
-// NOTE: pnError has String() and Error() methods.
-// The String() method prints the plain error message, the Error() method
-// prints the error message with a "proton:" prefix.
-// Thus you can format nested error messages with "%s" without getting nested "proton:"
-// prefixes but the prefix will be added when the end user uses Error()
-// or "%v" on the error value.
-//
-type pnError string
+func newBadUnmarshal(pnType C.pn_type_t, v interface{}) *BadUnmarshal {
+ return &BadUnmarshal{pnType, reflect.TypeOf(v)}
+}
-func (err pnError) String() string { return string(err) }
-func (err pnError) Error() string { return fmt.Sprintf("proton: %s", string(err)) }
+func (e BadUnmarshal) Error() string {
+ if e.GoType.Kind() != reflect.Ptr {
+ return fmt.Sprintf("proton: cannot unmarshal to type %s, not a pointer", e.GoType)
+ } else {
+ return fmt.Sprintf("proton: cannot unmarshal AMQP %s to %s", getPnType(e.AMQPType), e.GoType)
+ }
+}
// errorf creates an error with a formatted message
func errorf(format string, a ...interface{}) error {
- return pnError(fmt.Sprintf(format, a...))
+ return fmt.Errorf("proton: %s", fmt.Sprintf(format, a...))
}
func pnDataError(data *C.pn_data_t) string {
@@ -75,3 +79,18 @@ func pnDataError(data *C.pn_data_t) string {
}
return ""
}
+
+// doRecover is called to recover from internal panics
+func doRecover(err *error) {
+ r := recover()
+ switch r := r.(type) {
+ case nil:
+ return
+ case runtime.Error:
+ panic(r)
+ case error:
+ *err = r
+ default:
+ panic(r)
+ }
+}
http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/faf925c4/proton-c/bindings/go/src/apache.org/proton/interop_test.go
----------------------------------------------------------------------
diff --git a/proton-c/bindings/go/src/apache.org/proton/interop_test.go b/proton-c/bindings/go/src/apache.org/proton/interop_test.go
index 5333055..c51d74a 100644
--- a/proton-c/bindings/go/src/apache.org/proton/interop_test.go
+++ b/proton-c/bindings/go/src/apache.org/proton/interop_test.go
@@ -20,9 +20,6 @@ under the License.
// Test that conversion of Go type to/from AMQP is compatible with other
// bindings.
//
-// FIXME aconway 2015-03-01: this should move to proton/tests/go when we integrate
-// better with the proton build system.
-//
package proton
import (
@@ -37,7 +34,7 @@ import (
func assertEqual(want interface{}, got interface{}) {
if !reflect.DeepEqual(want, got) {
- panic(errorf("%T(%v) != %T(%v)", want, want, got, got))
+ panic(errorf("%#v != %#v", want, got))
}
}
@@ -144,38 +141,35 @@ func TestPrimitivesCompatible(t *testing.T) {
}
// assertDecodeValue: want is the expected value, decode into a reflect.Value
-func assertDecodeValue(d *Decoder, want interface{}) {
+func assertDecodeInterface(d *Decoder, want interface{}) {
- var v reflect.Value
- assertNil(d.Decode(&v))
+ var got, got2 interface{}
+ assertNil(d.Decode(&got))
- got := v.Interface()
assertEqual(want, got)
// Try round trip encoding
- bytes, err := Marshal(v)
+ bytes, err := Marshal(got)
assertNil(err)
- n, err := Unmarshal(bytes, &v)
+ n, err := Unmarshal(bytes, &got2)
assertNil(err)
assertEqual(n, len(bytes))
- got = v.Interface()
- assertEqual(want, got)
+ assertEqual(want, got2)
}
-func TestPrimitivesValue(t *testing.T) {
+func TestPrimitivesInterface(t *testing.T) {
d := NewDecoder(getReader("primitives"))
- // Decoding into reflect.Value
- assertDecodeValue(d, true)
- assertDecodeValue(d, false)
- assertDecodeValue(d, uint8(42))
- assertDecodeValue(d, uint16(42))
- assertDecodeValue(d, int16(-42))
- assertDecodeValue(d, uint32(12345))
- assertDecodeValue(d, int32(-12345))
- assertDecodeValue(d, uint64(12345))
- assertDecodeValue(d, int64(-12345))
- assertDecodeValue(d, float32(0.125))
- assertDecodeValue(d, float64(0.125))
+ assertDecodeInterface(d, true)
+ assertDecodeInterface(d, false)
+ assertDecodeInterface(d, uint8(42))
+ assertDecodeInterface(d, uint16(42))
+ assertDecodeInterface(d, int16(-42))
+ assertDecodeInterface(d, uint32(12345))
+ assertDecodeInterface(d, int32(-12345))
+ assertDecodeInterface(d, uint64(12345))
+ assertDecodeInterface(d, int64(-12345))
+ assertDecodeInterface(d, float32(0.125))
+ assertDecodeInterface(d, float64(0.125))
}
func TestStrings(t *testing.T) {
@@ -209,6 +203,9 @@ func TestStrings(t *testing.T) {
d = NewDecoder(getReader("strings"))
var s string
err := d.Decode(s)
+ if err == nil {
+ t.Fatal("Expected error")
+ }
if !strings.Contains(err.Error(), "not a pointer") {
t.Error(err)
}
@@ -217,6 +214,14 @@ func TestStrings(t *testing.T) {
if !strings.Contains(err.Error(), "cannot unmarshal") {
t.Error(err)
}
+ _, err = Unmarshal([]byte{}, nil)
+ if !strings.Contains(err.Error(), "not enough data") {
+ t.Error(err)
+ }
+ _, err = Unmarshal([]byte("foobar"), nil)
+ if !strings.Contains(err.Error(), "invalid argument") {
+ t.Error(err)
+ }
}
func TestEncodeDecode(t *testing.T) {
@@ -226,9 +231,10 @@ func TestEncodeDecode(t *testing.T) {
u8 uint8
b bool
f float32
+ v interface{}
}
- in := data{"foo", 42, 9, true, 1.234}
+ in := data{"foo", 42, 9, true, 1.234, "thing"}
buf := bytes.Buffer{}
e := NewEncoder(&buf)
@@ -237,6 +243,7 @@ func TestEncodeDecode(t *testing.T) {
e.Encode(in.u8)
e.Encode(in.b)
e.Encode(in.f)
+ e.Encode(in.v)
var out data
d := NewDecoder(&buf)
@@ -245,103 +252,39 @@ func TestEncodeDecode(t *testing.T) {
d.Decode(&out.u8)
d.Decode(&out.b)
d.Decode(&out.f)
+ d.Decode(&out.v)
assertEqual(in, out)
-
- vIn := reflect.ValueOf("thing")
- e.Encode(vIn)
- var vOut reflect.Value
- d.Decode(&vOut)
- assertEqual("thing", vOut.Interface())
}
-func BenchmarkDecode(b *testing.B) {
- var buf bytes.Buffer
- for _, f := range []string{"strings", "primitives"} {
- _, err := buf.ReadFrom(getReader(f))
- if err != nil {
- panic(err)
- }
- }
+func TestMap(t *testing.T) {
+ d := NewDecoder(getReader("maps"))
- d := NewDecoder(bytes.NewReader(buf.Bytes()))
+ // Generic map
+ var m Map
+ assertDecode(d, Map{"one": int32(1), "two": int32(2), "three": int32(3)}, &m)
- decode := func(v interface{}) {
- err := d.Decode(v)
- if err != nil {
- panic(err)
- }
- }
-
- for i := 0; i < b.N; i++ {
- var by []byte
- // strings
- decode(&by)
- var s string
- decode(&s)
- decode(&s)
- decode(&by)
- decode(&s)
- decode(&s)
- // primitives
- var b bool
- decode(&b)
- decode(&b)
- var u8 uint8
- decode(&u8)
- var u16 uint16
- decode(&u16)
- var i16 int16
- decode(&i16)
- var u32 uint32
- decode(&u32)
- var i32 int32
- decode(&i32)
- var u64 uint64
- decode(&u64)
- var i64 int64
- decode(&i64)
- var f32 float32
- decode(&f32)
- var f64 float64
- decode(&f64)
-
- d = NewDecoder(bytes.NewReader(buf.Bytes()))
- }
-}
+ // Interface as map
+ var i interface{}
+ assertDecode(d, Map{int32(1): "one", int32(2): "two", int32(3): "three"}, &i)
-func BenchmarkEncode(b *testing.B) {
+ d = NewDecoder(getReader("maps"))
+ // Specific typed map
+ var m2 map[string]int
+ assertDecode(d, map[string]int{"one": 1, "two": 2, "three": 3}, &m2)
- var buf bytes.Buffer
- buf.Grow(10000) // Avoid buffer reallocation during benchmark
- e := NewEncoder(&buf)
- encode := func(v interface{}) {
- err := e.Encode(v)
- if err != nil {
- panic(err)
- }
- }
+ // Round trip a nested map
+ m = Map{int64(1): "one", "two": int32(2), true: Map{uint8(1): true, uint8(2): false}}
+ bytes, err := Marshal(m)
+ assertNil(err)
+ _, err = Unmarshal(bytes, &i)
+ assertNil(err)
+ assertEqual(m, i)
+}
- for i := 0; i < b.N; i++ {
- // strings
- encode([]byte("foo"))
- encode("foo")
- encode("bar")
- encode([]byte(""))
- encode("")
- encode("")
- // primitives
- encode(true)
- encode(false)
- encode(uint8(42))
- encode(int8(-42))
- encode(uint16(12345))
- encode(int16(-12345))
- encode(uint32(123453245))
- encode(int32(-123453245))
- encode(uint64(123456445))
- encode(int64(-123456445))
- encode(float32(1.2345))
- encode(float64(1.23456))
- }
+func TestList(t *testing.T) {
+ d := NewDecoder(getReader("lists"))
+ var l List
+ assertDecode(d, List{int32(32), "foo", true}, &l)
+ assertDecode(d, List{}, &l)
}
http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/faf925c4/proton-c/bindings/go/src/apache.org/proton/marshal.go
----------------------------------------------------------------------
diff --git a/proton-c/bindings/go/src/apache.org/proton/marshal.go b/proton-c/bindings/go/src/apache.org/proton/marshal.go
index 957e0cf..674a25f 100644
--- a/proton-c/bindings/go/src/apache.org/proton/marshal.go
+++ b/proton-c/bindings/go/src/apache.org/proton/marshal.go
@@ -35,21 +35,31 @@ Marshal encodes a value as AMQP.
Go types are encoded as follows
-bool to AMQP bool.
-
-int, int8, int16, int32, int64 to equivalent AMQP signed integer type.
-
-uint, uint8, uint16, uint32, uint64 to equivalent or smaller AMQP unsigned integer type.
-
-float32, float64 to AMQP float or double.
-
-string to AMQP string.
-
-[]byte to AMQP binary.
+ +-------------------------------------+--------------------------------------------+
+ |Go type |AMQP type |
+ +-------------------------------------+--------------------------------------------+
+ |bool |bool |
+ +-------------------------------------+--------------------------------------------+
+ |int8, int16, int32, int64 (int) |byte, short, int, long (int or long) |
+ +-------------------------------------+--------------------------------------------+
+ |uint8, uint16, uint32, uint64 (uint) |ubyte, ushort, uint, ulong (uint or ulong) |
+ +-------------------------------------+--------------------------------------------+
+ |float32, float64 |float, double. |
+ +-------------------------------------+--------------------------------------------+
+ |string |string |
+ +-------------------------------------+--------------------------------------------+
+ |[]byte |binary |
+ +-------------------------------------+--------------------------------------------+
+ |interface{} |as to the contained type |
+ +-------------------------------------+--------------------------------------------+
+ |map[K]T |map with K and T converted as above |
+ +-------------------------------------+--------------------------------------------+
+ |Map |map may have mixed types for keys and values|
+ +-------------------------------------+--------------------------------------------+
TODO types
-Go: array, slice, struct, map, reflect/Value
+Go: array, slice, struct
Go types that cannot be marshaled
@@ -57,17 +67,32 @@ complex64/128, uintptr, function, interface, channel
*/
func Marshal(v interface{}) (bytes []byte, err error) {
- defer func() {
- if x := recover(); x != nil {
- err = errorf("%v", x)
- }
- }()
return marshal(make([]byte, minEncode), v)
}
func marshal(bytesIn []byte, v interface{}) (bytes []byte, err error) {
+ defer doRecover(&err)
data := C.pn_data(0)
defer C.pn_data_free(data)
+ put(data, v)
+ // FIXME aconway 2015-03-11: get size from proton.
+ bytes = bytesIn
+ for {
+ n := int(C.pn_data_encode(data, (*C.char)(unsafe.Pointer(&bytes[0])), C.size_t(cap(bytes))))
+ if n != int(C.PN_EOS) {
+ if n < 0 {
+ err = errorf(pnErrorName(n))
+ } else {
+ bytes = bytes[0:n]
+ }
+ return
+ }
+ bytes = make([]byte, cap(bytes)*2)
+ }
+ return
+}
+
+func put(data *C.pn_data_t, v interface{}) {
switch v := v.(type) {
case bool:
C.pn_data_put_bool(data, C.bool(v))
@@ -107,28 +132,52 @@ func marshal(bytesIn []byte, v interface{}) (bytes []byte, err error) {
C.pn_data_put_string(data, toPnBytes([]byte(v)))
case []byte:
C.pn_data_put_binary(data, toPnBytes(v))
- case reflect.Value:
- return marshal(bytesIn, v.Interface())
+ case Map: // Special map type
+ C.pn_data_put_map(data)
+ C.pn_data_enter(data)
+ for key, val := range v {
+ put(data, key)
+ put(data, val)
+ }
+ C.pn_data_exit(data)
default:
- panic(errorf("cannot marshal %s to AMQP", reflect.TypeOf(v)))
- }
- // FIXME aconway 2015-03-11: get size from proton.
- bytes = bytesIn
- for {
- n := int(C.pn_data_encode(data, (*C.char)(unsafe.Pointer(&bytes[0])), C.size_t(cap(bytes))))
- if n != int(C.PN_EOS) {
- if n < 0 {
- err = errorf(pnErrorName(n))
- } else {
- bytes = bytes[0:n]
- }
- return
+ switch reflect.TypeOf(v).Kind() {
+ case reflect.Map:
+ putMap(data, v)
+ case reflect.Slice:
+ putList(data, v)
+ default:
+ panic(errorf("cannot marshal %s to AMQP", reflect.TypeOf(v)))
}
- bytes = make([]byte, cap(bytes)*2)
+ }
+ err := pnDataError(data)
+ if err != "" {
+ panic(errorf("marshal %s", err))
}
return
}
+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() {
+ put(data, key.Interface())
+ put(data, mapValue.MapIndex(key).Interface())
+ }
+ 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++ {
+ put(data, listValue.Index(i).Interface())
+ }
+ 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/faf925c4/proton-c/bindings/go/src/apache.org/proton/types.go
----------------------------------------------------------------------
diff --git a/proton-c/bindings/go/src/apache.org/proton/types.go b/proton-c/bindings/go/src/apache.org/proton/types.go
index 84d0d90..f8c5560 100644
--- a/proton-c/bindings/go/src/apache.org/proton/types.go
+++ b/proton-c/bindings/go/src/apache.org/proton/types.go
@@ -23,6 +23,8 @@ package proton
import "C"
import (
+ "bytes"
+ "fmt"
"reflect"
)
@@ -33,13 +35,6 @@ type pnPut func(data *C.pn_data_t, v interface{})
type pnType struct {
code C.pn_type_t // AMQP type code
name string // AMQP type name
-
- // unmarshal data
- get pnGet // pn_data_*_get function wrapper returning a reflect.Value
- getType reflect.Type // go type for unmarshaling into a Value
-
- // marshal data
- put pnPut // pn_data_*_put function wrapper taking an interface{}
}
func (pt *pnType) String() string { return pt.name }
@@ -116,3 +111,27 @@ var (
bytesType = reflect.TypeOf([]byte{})
valueType = reflect.TypeOf(reflect.Value{})
)
+
+// Map is a generic map that can have mixed key and value types and so can represent any AMQP map
+//
+type Map map[interface{}]interface{}
+
+// List is a generic list that can hold mixed values and can represent any AMQP list.
+//
+type List []interface{}
+
+// GoString for Map prints values with their types, useful for debugging.
+func (m Map) GoString() string {
+ out := &bytes.Buffer{}
+ fmt.Fprintf(out, "%s{", reflect.TypeOf(m))
+ i := len(m)
+ for k, v := range m {
+ fmt.Fprintf(out, "%T(%#v): %T(%#v)", k, k, v, v)
+ i--
+ if i > 0 {
+ fmt.Fprint(out, ", ")
+ }
+ }
+ fmt.Fprint(out, "}")
+ return out.String()
+}
http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/faf925c4/proton-c/bindings/go/src/apache.org/proton/unmarshal.go
----------------------------------------------------------------------
diff --git a/proton-c/bindings/go/src/apache.org/proton/unmarshal.go b/proton-c/bindings/go/src/apache.org/proton/unmarshal.go
index 4255b28..a003019 100644
--- a/proton-c/bindings/go/src/apache.org/proton/unmarshal.go
+++ b/proton-c/bindings/go/src/apache.org/proton/unmarshal.go
@@ -67,11 +67,7 @@ func (d *Decoder) Buffered() io.Reader {
// See the documentation for Unmarshal for details about the conversion of AMQP into a Go value.
//
func (d *Decoder) Decode(v interface{}) (err error) {
- defer func() {
- if x := recover(); x != nil {
- err = errorf("%v", x)
- }
- }()
+ defer doRecover(&err)
data := C.pn_data(0)
defer C.pn_data_free(data)
var n int
@@ -90,35 +86,59 @@ func (d *Decoder) Decode(v interface{}) (err error) {
/*
Unmarshal decodes AMQP-encoded bytes and stores the result in the value pointed to by v.
+Types are converted as follows:
+
+ +---------------------------+----------------------------------------------------------------------+
+ |To Go types |From AMQP types |
+ +===========================+======================================================================+
+ |bool |bool |
+ +---------------------------+----------------------------------------------------------------------+
+ |int, int8, int16, |Equivalent or smaller signed integer type: char, byte, short, int, |
+ |int32, int64 |long. |
+ +---------------------------+----------------------------------------------------------------------+
+ |uint, uint8, uint16, |Equivalent or smaller unsigned integer type: char, ubyte, ushort, |
+ |uint32, uint64 types |uint, ulong |
+ +---------------------------+----------------------------------------------------------------------+
+ |float32, float64 |Equivalent or smaller float or double. |
+ +---------------------------+----------------------------------------------------------------------+
+ |string, []byte |string, symbol or binary. |
+ +---------------------------+----------------------------------------------------------------------+
+ |map[K]T |map, provided all keys and values can unmarshal to types K, T |
+ +---------------------------+----------------------------------------------------------------------+
+ |Map|map, any AMQP map |
+ +---------------------------+----------------------------------------------------------------------+
+ |interface{} |Any AMQP value can be unmarshaled to an interface{} as follows: |
+ | +------------------------+---------------------------------------------+
+ | |AMQP Type |Go Type in interface{} |
+ | +========================+=============================================+
+ | |bool |bool |
+ | +------------------------+---------------------------------------------+
+ | |char |unint8 |
+ | +------------------------+---------------------------------------------+
+ | |byte,short,int,long |int8,int16,int32,int64 |
+ | +------------------------+---------------------------------------------+
+ | |ubyte,ushort,uint,ulong |uint8,uint16,uint32,uint64 |
+ | +------------------------+---------------------------------------------+
+ | |float, double |float32, float64 |
+ | +------------------------+---------------------------------------------+
+ | |string, symbol |string |
+ | +------------------------+---------------------------------------------+
+ | |binary |byte[] |
+ | +------------------------+---------------------------------------------+
+ | |map |Map |
+ +---------------------------+------------------------+---------------------------------------------+
+
+The following Go types cannot be unmarshaled: complex64/128, uintptr, function, interface, channel.
+
+ODO types
+
+AMQP: null, timestamp, decimal32/64/128, uuid, described, array, list.
+
+Go: array, slice, struct.
-Go types that can be unmarshalled from AMQP types
-
-bool from AMQP bool
-
-int, int8, int16, int32, int64 from equivalent or smaller AMQP signed integer type or char
-
-uint, uint8, uint16, uint32, uint64 types from equivalent or smaller AMQP unsigned integer type or char
-
-float32, float64 from equivalent or smaller AMQP float type.
-
-string, []byte from AMQP string, symbol or binary.
-
-TODO types
-
-AMQP from AMQP null, char, timestamp, decimal32/64/128, uuid, described, array, list, map
-
-Go: array, slice, struct, map, reflect/Value
-
-Go types that cannot be unmarshalled
-
-complex64/128, uintptr, function, interface, channel
*/
func Unmarshal(bytes []byte, v interface{}) (n int, err error) {
- defer func() {
- if x := recover(); x != nil {
- err = errorf("%v", x)
- }
- }()
+ defer doRecover(&err)
data := C.pn_data(0)
defer C.pn_data_free(data)
n = unmarshal(data, bytes, v)
@@ -152,15 +172,14 @@ func unmarshal(data *C.pn_data_t, bytes []byte, v interface{}) (n int) {
if n == 0 {
return 0
}
- pnType := C.pn_data_type(data)
+ get(data, v)
+ return
+}
- badUnmarshal := func() error {
- notPtr := ""
- if reflect.TypeOf(v).Kind() != reflect.Ptr {
- notPtr = ", target is not a pointer"
- }
- return errorf("cannot unmarshal AMQP %s to %s%s", getPnType(pnType), reflect.TypeOf(v), notPtr)
- }
+// Get value v from data
+func get(data *C.pn_data_t, v interface{}) {
+
+ pnType := C.pn_data_type(data)
switch v := v.(type) {
case *bool:
@@ -168,7 +187,7 @@ func unmarshal(data *C.pn_data_t, bytes []byte, v interface{}) (n int) {
case C.PN_BOOL:
*v = bool(C.pn_data_get_bool(data))
default:
- panic(badUnmarshal())
+ panic(newBadUnmarshal(pnType, v))
}
case *int8:
switch pnType {
@@ -177,7 +196,7 @@ func unmarshal(data *C.pn_data_t, bytes []byte, v interface{}) (n int) {
case C.PN_BYTE:
*v = int8(C.pn_data_get_byte(data))
default:
- panic(badUnmarshal())
+ panic(newBadUnmarshal(pnType, v))
}
case *uint8:
switch pnType {
@@ -186,7 +205,7 @@ func unmarshal(data *C.pn_data_t, bytes []byte, v interface{}) (n int) {
case C.PN_UBYTE:
*v = uint8(C.pn_data_get_ubyte(data))
default:
- panic(badUnmarshal())
+ panic(newBadUnmarshal(pnType, v))
}
case *int16:
switch pnType {
@@ -197,7 +216,7 @@ func unmarshal(data *C.pn_data_t, bytes []byte, v interface{}) (n int) {
case C.PN_SHORT:
*v = int16(C.pn_data_get_short(data))
default:
- panic(badUnmarshal())
+ panic(newBadUnmarshal(pnType, v))
}
case *uint16:
switch pnType {
@@ -208,7 +227,7 @@ func unmarshal(data *C.pn_data_t, bytes []byte, v interface{}) (n int) {
case C.PN_USHORT:
*v = uint16(C.pn_data_get_ushort(data))
default:
- panic(badUnmarshal())
+ panic(newBadUnmarshal(pnType, v))
}
case *int32:
switch pnType {
@@ -221,7 +240,7 @@ func unmarshal(data *C.pn_data_t, bytes []byte, v interface{}) (n int) {
case C.PN_INT:
*v = int32(C.pn_data_get_int(data))
default:
- panic(badUnmarshal())
+ panic(newBadUnmarshal(pnType, v))
}
case *uint32:
switch pnType {
@@ -234,7 +253,7 @@ func unmarshal(data *C.pn_data_t, bytes []byte, v interface{}) (n int) {
case C.PN_UINT:
*v = uint32(C.pn_data_get_uint(data))
default:
- panic(badUnmarshal())
+ panic(newBadUnmarshal(pnType, v))
}
case *int64:
@@ -250,7 +269,7 @@ func unmarshal(data *C.pn_data_t, bytes []byte, v interface{}) (n int) {
case C.PN_LONG:
*v = int64(C.pn_data_get_long(data))
default:
- panic(badUnmarshal())
+ panic(newBadUnmarshal(pnType, v))
}
case *uint64:
@@ -264,7 +283,7 @@ func unmarshal(data *C.pn_data_t, bytes []byte, v interface{}) (n int) {
case C.PN_ULONG:
*v = uint64(C.pn_data_get_ulong(data))
default:
- panic(badUnmarshal())
+ panic(newBadUnmarshal(pnType, v))
}
case *int:
@@ -281,10 +300,10 @@ func unmarshal(data *C.pn_data_t, bytes []byte, v interface{}) (n int) {
if unsafe.Sizeof(0) == 8 {
*v = int(C.pn_data_get_long(data))
} else {
- panic(badUnmarshal())
+ panic(newBadUnmarshal(pnType, v))
}
default:
- panic(badUnmarshal())
+ panic(newBadUnmarshal(pnType, v))
}
case *uint:
@@ -301,10 +320,10 @@ func unmarshal(data *C.pn_data_t, bytes []byte, v interface{}) (n int) {
if unsafe.Sizeof(0) == 8 {
*v = uint(C.pn_data_get_ulong(data))
} else {
- panic(badUnmarshal())
+ panic(newBadUnmarshal(pnType, v))
}
default:
- panic(badUnmarshal())
+ panic(newBadUnmarshal(pnType, v))
}
case *float32:
@@ -312,7 +331,7 @@ func unmarshal(data *C.pn_data_t, bytes []byte, v interface{}) (n int) {
case C.PN_FLOAT:
*v = float32(C.pn_data_get_float(data))
default:
- panic(badUnmarshal())
+ panic(newBadUnmarshal(pnType, v))
}
case *float64:
@@ -322,7 +341,7 @@ func unmarshal(data *C.pn_data_t, bytes []byte, v interface{}) (n int) {
case C.PN_DOUBLE:
*v = float64(C.pn_data_get_double(data))
default:
- panic(badUnmarshal())
+ panic(newBadUnmarshal(pnType, v))
}
case *string:
@@ -334,7 +353,7 @@ func unmarshal(data *C.pn_data_t, bytes []byte, v interface{}) (n int) {
case C.PN_BINARY:
*v = goString(C.pn_data_get_binary(data))
default:
- panic(badUnmarshal())
+ panic(newBadUnmarshal(pnType, v))
}
case *[]byte:
@@ -346,47 +365,24 @@ func unmarshal(data *C.pn_data_t, bytes []byte, v interface{}) (n int) {
case C.PN_BINARY:
*v = goBytes(C.pn_data_get_binary(data))
default:
- panic(badUnmarshal())
+ panic(newBadUnmarshal(pnType, v))
}
- case *reflect.Value:
- switch pnType {
- case C.PN_BOOL:
- *v = reflect.ValueOf(bool(C.pn_data_get_bool(data)))
- case C.PN_UBYTE:
- *v = reflect.ValueOf(uint8(C.pn_data_get_ubyte(data)))
- case C.PN_BYTE:
- *v = reflect.ValueOf(int8(C.pn_data_get_byte(data)))
- case C.PN_USHORT:
- *v = reflect.ValueOf(uint16(C.pn_data_get_ushort(data)))
- case C.PN_SHORT:
- *v = reflect.ValueOf(int16(C.pn_data_get_short(data)))
- case C.PN_UINT:
- *v = reflect.ValueOf(uint32(C.pn_data_get_uint(data)))
- case C.PN_INT:
- *v = reflect.ValueOf(int32(C.pn_data_get_int(data)))
- case C.PN_CHAR:
- *v = reflect.ValueOf(uint8(C.pn_data_get_char(data)))
- case C.PN_ULONG:
- *v = reflect.ValueOf(uint64(C.pn_data_get_ulong(data)))
- case C.PN_LONG:
- *v = reflect.ValueOf(int64(C.pn_data_get_long(data)))
- case C.PN_FLOAT:
- *v = reflect.ValueOf(float32(C.pn_data_get_float(data)))
- case C.PN_DOUBLE:
- *v = reflect.ValueOf(float64(C.pn_data_get_double(data)))
- case C.PN_BINARY:
- *v = valueOfBytes(C.pn_data_get_binary(data))
- case C.PN_STRING:
- *v = valueOfString(C.pn_data_get_string(data))
- case C.PN_SYMBOL:
- *v = valueOfString(C.pn_data_get_symbol(data))
- default:
- panic(badUnmarshal())
- }
+ case *interface{}:
+ getInterface(data, v)
default:
- panic(badUnmarshal())
+ if reflect.TypeOf(v).Kind() != reflect.Ptr {
+ panic(newBadUnmarshal(pnType, v))
+ }
+ switch reflect.TypeOf(v).Elem().Kind() {
+ case reflect.Map:
+ getMap(data, v)
+ case reflect.Slice:
+ getList(data, v)
+ default:
+ panic(newBadUnmarshal(pnType, v))
+ }
}
err := pnDataError(data)
if err != "" {
@@ -395,6 +391,98 @@ func unmarshal(data *C.pn_data_t, bytes []byte, v interface{}) (n int) {
return
}
+// 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{}) {
+ pnType := C.pn_data_type(data)
+ switch pnType {
+ case C.PN_BOOL:
+ *v = bool(C.pn_data_get_bool(data))
+ case C.PN_UBYTE:
+ *v = uint8(C.pn_data_get_ubyte(data))
+ case C.PN_BYTE:
+ *v = int8(C.pn_data_get_byte(data))
+ case C.PN_USHORT:
+ *v = uint16(C.pn_data_get_ushort(data))
+ case C.PN_SHORT:
+ *v = int16(C.pn_data_get_short(data))
+ case C.PN_UINT:
+ *v = uint32(C.pn_data_get_uint(data))
+ case C.PN_INT:
+ *v = int32(C.pn_data_get_int(data))
+ case C.PN_CHAR:
+ *v = uint8(C.pn_data_get_char(data))
+ case C.PN_ULONG:
+ *v = uint64(C.pn_data_get_ulong(data))
+ case C.PN_LONG:
+ *v = int64(C.pn_data_get_long(data))
+ case C.PN_FLOAT:
+ *v = float32(C.pn_data_get_float(data))
+ case C.PN_DOUBLE:
+ *v = float64(C.pn_data_get_double(data))
+ case C.PN_BINARY:
+ *v = goBytes(C.pn_data_get_binary(data))
+ case C.PN_STRING:
+ *v = goString(C.pn_data_get_string(data))
+ case C.PN_SYMBOL:
+ *v = goString(C.pn_data_get_symbol(data))
+ case C.PN_MAP:
+ m := make(Map)
+ get(data, &m)
+ *v = m // FIXME aconway 2015-03-13: avoid the copy?
+ case C.PN_LIST:
+ l := make(List, 0)
+ get(data, &l)
+ *v = l
+ default:
+ panic(newBadUnmarshal(pnType, v))
+ }
+}
+
+func getMap(data *C.pn_data_t, v interface{}) {
+ pnType := C.pn_data_type(data)
+ if pnType != C.PN_MAP {
+ panic(newBadUnmarshal(pnType, v))
+ }
+ mapValue := reflect.ValueOf(v).Elem()
+ mapValue.Set(reflect.MakeMap(mapValue.Type())) // Clear the map
+ count := int(C.pn_data_get_map(data))
+ if bool(C.pn_data_enter(data)) {
+ for i := 0; i < count/2; i++ {
+ if bool(C.pn_data_next(data)) {
+ key := reflect.New(mapValue.Type().Key())
+ get(data, key.Interface())
+ if bool(C.pn_data_next(data)) {
+ val := reflect.New(mapValue.Type().Elem())
+ get(data, val.Interface())
+ mapValue.SetMapIndex(key.Elem(), val.Elem())
+ }
+ }
+ }
+ C.pn_data_exit(data)
+ }
+}
+
+func getList(data *C.pn_data_t, v interface{}) {
+ pnType := C.pn_data_type(data)
+ if pnType != C.PN_LIST {
+ panic(newBadUnmarshal(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++ {
+ if bool(C.pn_data_next(data)) {
+ val := reflect.New(listValue.Type().Elem())
+ get(data, val.Interface())
+ listValue.Index(i).Set(val.Elem())
+ }
+ }
+ C.pn_data_exit(data)
+ }
+ reflect.ValueOf(v).Elem().Set(listValue)
+}
+
// decode from bytes.
// Return bytes decoded or 0 if we could not decode a complete object.
//
@@ -412,20 +500,6 @@ func decode(data *C.pn_data_t, bytes []byte) int {
return n
}
-func valueOfBytes(bytes C.pn_bytes_t) reflect.Value {
- if bytes.start == nil || bytes.size == 0 {
- return reflect.ValueOf([]byte{})
- }
- return reflect.ValueOf(C.GoBytes(unsafe.Pointer(bytes.start), C.int(bytes.size)))
-}
-
-func valueOfString(bytes C.pn_bytes_t) reflect.Value {
- if bytes.start == nil || bytes.size == 0 {
- return reflect.ValueOf("")
- }
- return reflect.ValueOf(C.GoStringN(bytes.start, C.int(bytes.size)))
-}
-
func goBytes(cBytes C.pn_bytes_t) (bytes []byte) {
if cBytes.start != nil {
bytes = C.GoBytes(unsafe.Pointer(cBytes.start), C.int(cBytes.size))
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@qpid.apache.org
For additional commands, e-mail: commits-help@qpid.apache.org