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/10 14:21:28 UTC

qpid-proton git commit: PROTON-827: go binding - unmarshal all basic types.

Repository: qpid-proton
Updated Branches:
  refs/heads/master 36e32d230 -> 0816badb2


PROTON-827: go binding - unmarshal  all basic types.

Switched to reflection, same performance as type switch but much more compact code.


Project: http://git-wip-us.apache.org/repos/asf/qpid-proton/repo
Commit: http://git-wip-us.apache.org/repos/asf/qpid-proton/commit/0816badb
Tree: http://git-wip-us.apache.org/repos/asf/qpid-proton/tree/0816badb
Diff: http://git-wip-us.apache.org/repos/asf/qpid-proton/diff/0816badb

Branch: refs/heads/master
Commit: 0816badb2361af12403c10768a38fd5794c5b84a
Parents: 36e32d2
Author: Alan Conway <ac...@redhat.com>
Authored: Tue Mar 10 09:21:09 2015 -0400
Committer: Alan Conway <ac...@redhat.com>
Committed: Tue Mar 10 09:21:09 2015 -0400

----------------------------------------------------------------------
 proton-c/bindings/go/README.md                  |   8 +-
 .../go/src/apache.org/proton/encoding.go        | 288 ++++++++++---------
 .../bindings/go/src/apache.org/proton/error.go  |  61 ++--
 .../go/src/apache.org/proton/interop_test.go    | 189 +++++++++---
 .../bindings/go/src/apache.org/proton/types.go  |  48 +++-
 5 files changed, 360 insertions(+), 234 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/0816badb/proton-c/bindings/go/README.md
----------------------------------------------------------------------
diff --git a/proton-c/bindings/go/README.md b/proton-c/bindings/go/README.md
index 31bdb5d..8ffa700 100644
--- a/proton-c/bindings/go/README.md
+++ b/proton-c/bindings/go/README.md
@@ -100,16 +100,17 @@ Working on API to marshal/unmarshal AMQP data into Go types.
 
 The API will follow the style of the standard libraries encoding/json and encoding/xml.
 
-To be done:
+## To be done:
 
 Easy unmarshaling into native Go types:
 
 - String-like AMQP types (symbol, binary, string) into Go string or []byte
 - Numeric AMQP types into any Go numeric (numeric conversion)
 - Any AMQP type into GO reflect.Value choosing the closest native Go type
-- AMQP map into go struct if keys match struct field names and values match field types
+- AMQP map into Go struct if keys match struct field names and values match field types
 - AMQP maps into map[K]T if all AMQP keys/values can convert to K and T (reflect.Value allowed)
 - AMQP list into []T if all list elements can convert to T (reflect.Value allowed)
+- AMQP list into Go struct if AMQP types match struct field types in order.
 
 Easy marshaling of native Go types:
 
@@ -126,8 +127,9 @@ Customization:
 
 Exact (strict) (un)marshaling:
 
-- Define special Go AMQP types that exactly reflect AMQP types & encodings.
+- Special Go AMQP types that exactly reflect AMQP types & encodings.
 - Unmarshal to special types only if exact match for wire
 - Marshal special types exactly
 - Define AMQPValue which can unmarshal from any AMQP type using strict unmarshaling types.
 
+Use of tags to control AMQP marshalling/unmarshalling?

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/0816badb/proton-c/bindings/go/src/apache.org/proton/encoding.go
----------------------------------------------------------------------
diff --git a/proton-c/bindings/go/src/apache.org/proton/encoding.go b/proton-c/bindings/go/src/apache.org/proton/encoding.go
index eb42c8c..a781a68 100644
--- a/proton-c/bindings/go/src/apache.org/proton/encoding.go
+++ b/proton-c/bindings/go/src/apache.org/proton/encoding.go
@@ -1,6 +1,6 @@
 /*
 Licensed to the Apache Software Foundation (ASF) under one
-or more contributor license agreements.  See the NOTICE file
+oor more contributor license agreements.  See the NOTICE file
 distributed with this work for additional information
 regarding copyright ownership.  The ASF licenses this file
 to you under the Apache License, Version 2.0 (the
@@ -19,7 +19,7 @@ under the License.
 
 package proton
 
-//#include <proton/codec.h>
+// #include <proton/codec.h>
 import "C"
 
 import (
@@ -32,185 +32,189 @@ import (
 //
 // Decoding from a pn_data_t
 //
+// NOTE: we use panic() to signal a decoding error, simplifies decoding logic.
+// We recover() at the highest possible level - i.e. in the exported Unmarshal or Decode.
+//
 
-type pnDecoder struct{ data *C.pn_data_t }
+// Decoder decodes AMQP values from an io.Reader.
+//
+type Decoder struct {
+	reader io.Reader
+	buffer bytes.Buffer
+}
 
-func newPnDecoder() pnDecoder { return pnDecoder{C.pn_data(0)} }
-func (pd pnDecoder) free()    { C.pn_data_free((*C.pn_data_t)(pd.data)) }
+// NewDecoder returns a new decoder that reads from r.
+//
+// The decoder has it's own buffer and may read more data than required for the
+// AMQP values requested.  Use Buffered to see if there is data left in the
+// buffer.
+//
+func NewDecoder(r io.Reader) *Decoder {
+	return &Decoder{r, bytes.Buffer{}}
+}
 
-// decode from bytes. Return bytes decoded and errEOS if we run out of data.
-func (pd pnDecoder) decode(bytes []byte) (n int, err error) {
-	C.pn_data_clear(pd.data)
-	if len(bytes) == 0 {
-		return 0, errEOS
-	}
-	cBuf := (*C.char)(unsafe.Pointer(&bytes[0]))
-	result := int(C.pn_data_decode(pd.data, cBuf, C.size_t(len(bytes))))
-	if result < 0 {
-		return 0, errorCode(result)
-	} else {
-		return result, nil
-	}
+// Buffered returns a reader of the data remaining in the Decoder's buffer. The
+// reader is valid until the next call to Decode.
+//
+func (d *Decoder) Buffered() io.Reader {
+	return bytes.NewReader(d.buffer.Bytes())
 }
 
-// Unmarshal decodes bytes and converts into the value pointed to by v.
+// Decode reads the next AMQP value from the Reader and stores it in the value pointed to by v.
 //
-// Returns the number of bytes decoded and an errorCode on error.
-func (pd pnDecoder) unmarshal(bytes []byte, v interface{}) (n int, err error) {
-	n, err = pd.decode(bytes)
-	if err != nil {
-		return
-	}
-	switch v := v.(type) {
-	case *string:
-		err = pd.unmarshalString(v)
-	case *[]byte:
-		err = pd.unmarshalBytes(v)
-	case *Symbol:
-		err = pd.unmarshalSymbol(v)
-	default:
-		note := ""
-		if reflect.TypeOf(v).Kind() != reflect.Ptr {
-			note = "is not a pointer"
+// 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)
+		}
+	}()
+	data := C.pn_data(0)
+	defer C.pn_data_free(data)
+
+	var n int
+	for n == 0 {
+		n = unmarshal(data, d.buffer.Bytes(), v)
+		if n == 0 { // n == 0 means not enough data, read more
+			err = d.more()
 		}
-		return 0, errorf("Unmarshal bad type: %T %s", v, note)
-		// FIXME aconway 2015-03-02: not finished
-	}
-	if err != nil {
-		return 0, err
 	}
+	d.buffer.Next(n)
 	return
 }
 
-func (pd pnDecoder) unmarshalPnBytes(target string) (pnBytes C.pn_bytes_t, err error) {
-	switch amqpType := C.pn_data_type(pd.data); amqpType {
-	case C.PN_STRING:
-		pnBytes = C.pn_data_get_string(pd.data)
-	case C.PN_BINARY:
-		pnBytes = C.pn_data_get_binary(pd.data)
-	case C.PN_SYMBOL:
-		pnBytes = C.pn_data_get_symbol(pd.data)
-	default:
-		// FIXME aconway 2015-03-02: error message - json style UnmarsalTypeError?
-		return C.pn_bytes_t{}, errorf("Unmarshal cannot convert %#v to %s", amqpType, target)
-	}
-	return pnBytes, nil
-}
+/*
+Unmarshal decodes AMQP-encoded bytes and stores the result in the value pointed to by v.
 
-func (pd pnDecoder) unmarshalString(v *string) error {
-	pnBytes, err := pd.unmarshalPnBytes("string")
-	if err == nil {
-		*v = C.GoStringN(pnBytes.start, C.int(pnBytes.size))
-	}
-	return err
-}
+Go types that can be unmarshalled from AMQP types
 
-func (pd pnDecoder) unmarshalBytes(v *[]byte) error {
-	pnBytes, err := pd.unmarshalPnBytes("[]byte")
-	*v = C.GoBytes(unsafe.Pointer(pnBytes.start), C.int(pnBytes.size))
-	return err
-}
+bool from AMQP bool
 
-func (pd pnDecoder) unmarshalSymbol(v *Symbol) error {
-	pnBytes, err := pd.unmarshalPnBytes("symbol")
-	if err == nil {
-		*v = Symbol(C.GoStringN(pnBytes.start, C.int(pnBytes.size)))
-	}
-	return err
-}
+int8, int16, int32, int64 from equivalent or smaller AMQP signed integer type.
 
-/*
-Unmarshal decodes AMQP-encoded bytes and stores the result in the value pointed to by v.
+uint8, uint16, uint32, uint64 types from equivalent or smaller AMQP unsigned integer type.
 
-FIXME mapping details
-
- +-------------------------------+-----------------------------------------------+
- |AMQP type                      |Go type                                        |
- +-------------------------------+-----------------------------------------------+
- |string                         |string                                         |
- +-------------------------------+-----------------------------------------------+
- |symbol                         |proton.Symbol                                  |
- +-------------------------------+-----------------------------------------------+
- |binary                         |[]byte                                         |
- +-------------------------------+-----------------------------------------------+
-*/
-func Unmarshal(bytes []byte, v interface{}) error {
-	pd := newPnDecoder()
-	defer pd.free()
-	_, err := pd.unmarshal(bytes, v)
-	return err
-}
+float32, float64 from equivalent or smaller AMQP float type.
 
-// Decoder decodes AMQP values from an io.Reader.
-//
-type Decoder struct {
-	reader  io.Reader
-	buffer  bytes.Buffer
-	readErr error // Outstanding error on our reader
+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: uint, int, 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)
+		}
+	}()
+	data := C.pn_data(0)
+	defer C.pn_data_free(data)
+	n = unmarshal(data, bytes, v)
+	if n == 0 {
+		err = errorf("not enough data")
+	}
+	return
 }
 
-// NewDecoder returns a new decoder that reads from r.
-//
-// The decoder has it's own buffer and may read more data than required for the
-// AMQP values requested.  Use Buffered to see if there is data left in the
-// buffer.
-//
-func NewDecoder(r io.Reader) *Decoder {
-	return &Decoder{r, bytes.Buffer{}, nil}
+func pnDataError(data *C.pn_data_t) (code int, msg string) {
+	pnError := C.pn_data_error(data)
+	return int(C.pn_error_code(pnError)), C.GoString(C.pn_error_text(pnError))
 }
 
-// Buffered returns a reader of the data remaining in the Decoder's buffer. The
-// reader is valid until the next call to Decode.
+// decode from bytes.
+// Return bytes decoded or 0 if we could not decode a complete object.
 //
-func (d *Decoder) Buffered() io.Reader {
-	return bytes.NewReader(d.buffer.Bytes())
+func decode(data *C.pn_data_t, bytes []byte) int {
+	if len(bytes) == 0 {
+		return 0
+	}
+	cBuf := (*C.char)(unsafe.Pointer(&bytes[0]))
+	n := int(C.pn_data_decode(data, cBuf, C.size_t(len(bytes))))
+	if n == int(C.PN_EOS) {
+		return 0
+	} else if n <= 0 {
+		panic(errorf("unmarshal %s", pnErrorName(n)))
+	}
+	return n
 }
 
 // more reads more data when we can't parse a complete AMQP type
 func (d *Decoder) more() error {
-	if d.readErr != nil { // Reader already broken, give up
-		return d.readErr
-	}
 	var readSize int64 = 256
 	if int64(d.buffer.Len()) > readSize { // Grow by doubling
 		readSize = int64(d.buffer.Len())
 	}
 	var n int64
-	n, d.readErr = d.buffer.ReadFrom(&io.LimitedReader{d.reader, readSize})
+	n, err := d.buffer.ReadFrom(&io.LimitedReader{d.reader, readSize})
 	if n == 0 { // ReadFrom won't report io.EOF, just returns 0
-		if d.readErr != nil {
-			return d.readErr
+		if err != nil {
+			panic(err)
 		} else {
-			return errorf("no data")
+			panic("not enough data")
 		}
 	}
 	return nil
 }
 
-// Decode reads the next AMQP value from the Reader and stores it in the value pointed to by v.
+// unmarshal decodes from bytes and converts into the value pointed to by v.
+// Used by Unmarshal and Decode
 //
-// See the documentation for Unmarshal for details about the conversion of AMQP into a Go value.
+// Returns the number of bytes decoded or 0 if not enough data.
 //
-func (d *Decoder) Decode(v interface{}) (err error) {
-	pd := newPnDecoder()
-	defer pd.free()
-
-	// On errEOS, read more data and try again till we have a complete pn_data.
-	for {
-		var n int
-		n, err = pd.unmarshal(d.buffer.Bytes(), v)
-		switch err {
-		case nil:
-			d.buffer.Next(n)
-			return
-		case errEOS:
-			err = d.more()
-			if err != nil {
-				return err
-			}
-		default:
-			return err
-		}
+func unmarshal(data *C.pn_data_t, bytes []byte, v interface{}) (n int) {
+	n = decode(data, bytes)
+	if n == 0 {
+		return 0
+	}
+	ptrValue := reflect.ValueOf(v)
+	if ptrValue.Type().Kind() != reflect.Ptr {
+		panic(errorf("cannot unmarshal to %T, not a pointer", v))
+	}
+	value := ptrValue.Elem()
+	pnType, ok := pnTypes[C.pn_data_type(data)]
+	if !ok {
+		panic(errorf("unknown AMQP type code %v", C.pn_data_type(data)))
+	}
+	if pnType.getter == nil {
+		panic(errorf("cannot unmarshal AMQP type %s", pnType.name))
+	}
+	decoded := pnType.getter(data)
+	if !decoded.Type().ConvertibleTo(value.Type()) {
+		panic(errorf("cannot unmarshal AMQP %s to %s", pnType.name, value.Type()))
 	}
-	return err
+	converted := decoded.Convert(value.Type())
+	value.Set(converted)
+	return
 }
+
+func bytesValue(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)))
+}
+
+// Get functions to convert PN data to reflect.Value
+func getPnString(data *C.pn_data_t) reflect.Value { return bytesValue(C.pn_data_get_string(data)) }
+func getPnSymbol(data *C.pn_data_t) reflect.Value { return bytesValue(C.pn_data_get_symbol(data)) }
+func getPnBinary(data *C.pn_data_t) reflect.Value { return bytesValue(C.pn_data_get_binary(data)) }
+func getPnBool(data *C.pn_data_t) reflect.Value   { return reflect.ValueOf(C.pn_data_get_bool(data)) }
+func getPnByte(data *C.pn_data_t) reflect.Value   { return reflect.ValueOf(C.pn_data_get_byte(data)) }
+func getPnChar(data *C.pn_data_t) reflect.Value   { return reflect.ValueOf(C.pn_data_get_char(data)) }
+func getPnShort(data *C.pn_data_t) reflect.Value  { return reflect.ValueOf(C.pn_data_get_short(data)) }
+func getPnInt(data *C.pn_data_t) reflect.Value    { return reflect.ValueOf(C.pn_data_get_int(data)) }
+func getPnLong(data *C.pn_data_t) reflect.Value   { return reflect.ValueOf(C.pn_data_get_long(data)) }
+func getPnUbyte(data *C.pn_data_t) reflect.Value  { return reflect.ValueOf(C.pn_data_get_ubyte(data)) }
+func getPnUshort(data *C.pn_data_t) reflect.Value { return reflect.ValueOf(C.pn_data_get_ushort(data)) }
+func getPnUint(data *C.pn_data_t) reflect.Value   { return reflect.ValueOf(C.pn_data_get_uint(data)) }
+func getPnUlong(data *C.pn_data_t) reflect.Value  { return reflect.ValueOf(C.pn_data_get_ulong(data)) }
+func getPnFloat(data *C.pn_data_t) reflect.Value  { return reflect.ValueOf(C.pn_data_get_float(data)) }
+func getPnDouble(data *C.pn_data_t) reflect.Value { return reflect.ValueOf(C.pn_data_get_double(data)) }

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/0816badb/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 51cde28..6875042 100644
--- a/proton-c/bindings/go/src/apache.org/proton/error.go
+++ b/proton-c/bindings/go/src/apache.org/proton/error.go
@@ -26,54 +26,31 @@ import (
 	"fmt"
 )
 
-// errorCode is an error code returned by proton C.
-type errorCode int
-
-const (
-	errEOS         errorCode = C.PN_EOS
-	errError                 = C.PN_ERR
-	errOverflow              = C.PN_OVERFLOW
-	errUnderflow             = C.PN_UNDERFLOW
-	errState                 = C.PN_STATE_ERR
-	errArgument              = C.PN_ARG_ERR
-	errTimeout               = C.PN_TIMEOUT
-	errInterrupted           = C.PN_INTR
-	errInProgress            = C.PN_INPROGRESS
-)
+var pnErrorNames = map[int]string{
+	C.PN_EOS:        "end of data",
+	C.PN_ERR:        "error",
+	C.PN_OVERFLOW:   "overflow",
+	C.PN_UNDERFLOW:  "underflow",
+	C.PN_STATE_ERR:  "bad state",
+	C.PN_ARG_ERR:    "invalid argument",
+	C.PN_TIMEOUT:    "timeout",
+	C.PN_INTR:       "interrupted",
+	C.PN_INPROGRESS: "in progress",
+}
 
-// String gives a brief description of an errorCode.
-func (code errorCode) String() string {
-	switch code {
-	case errEOS:
-		return "end of data"
-	case errError:
-		return "error"
-	case errOverflow:
-		return "overflow"
-	case errUnderflow:
-		return "underflow"
-	case errState:
-		return "bad state"
-	case errArgument:
-		return "invalid argument"
-	case errTimeout:
-		return "timeout"
-	case errInterrupted:
-		return "interrupted"
-	case errInProgress:
-		return "in progress"
+func nonBlank(a, b string) string {
+	if a == "" {
+		return b
 	}
-	return fmt.Sprintf("invalid error code %d", code)
+	return a
 }
 
-// An errorCode can be used as an error
-func (code errorCode) Error() string {
-	return fmt.Sprintf("proton: %v", code)
+func pnErrorName(code int) string {
+	return nonBlank(pnErrorNames[code], "unknown error code")
 }
 
-// pnError is a simple error string.
-//
-// NOTE: All error types used in proton have both String() and Error() methods.
+///
+// 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:"

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/0816badb/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 db988d2..35e39f0 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
@@ -26,18 +26,19 @@ under the License.
 package proton
 
 import (
-	"fmt"
+	"bytes"
 	"io"
 	"io/ioutil"
 	"os"
 	"reflect"
+	"strings"
 	"testing"
 )
 
-func getReader(t *testing.T, name string) (r io.Reader) {
+func getReader(name string) (r io.Reader) {
 	r, err := os.Open("../../../../../../tests/interop/" + name + ".amqp")
 	if err != nil {
-		t.Fatalf("Can't open %#v: %v", name, err)
+		panic(errorf("Can't open %#v: %v", name, err))
 	}
 	return
 }
@@ -47,50 +48,89 @@ func remaining(d *Decoder) string {
 	return string(remainder)
 }
 
-// Expectation of a test, want is the expected value, got is a pointer to a
-// instance of the same type, which will be replaced by Decode.
-type expect struct {
-	want, got interface{}
-}
-
-// checkDecode: want is the expected value, gotPtr is a pointer to a
+// assertDecode: want is the expected value, gotPtr is a pointer to a
 // instance of the same type for Decode.
-func checkDecode(d *Decoder, want interface{}, gotPtr interface{}) error {
+func assertDecode(d *Decoder, want interface{}, gotPtr interface{}) {
 	err := d.Decode(gotPtr)
 	if err != nil {
-		return err
+		panic(err)
 	}
 	got := reflect.ValueOf(gotPtr).Elem().Interface()
 	if !reflect.DeepEqual(want, got) {
-		return fmt.Errorf("%#v != %#v", want, got)
+		panic(errorf("%T(%#v) != %T(%#v)", want, want, got, got))
 	}
-	return nil
 }
 
 func TestUnmarshal(t *testing.T) {
-	bytes, err := ioutil.ReadAll(getReader(t, "strings"))
-	if err != nil {
-		t.Error(err)
-	}
-	var got string
-	err = Unmarshal(bytes, &got)
+	bytes, err := ioutil.ReadAll(getReader("strings"))
 	if err != nil {
 		t.Error(err)
 	}
-	want := "abc\000defg"
-	if want != got {
-		t.Errorf("%#v != %#v", want, got)
+	for _, want := range []string{"abc\000defg", "abcdefg", "abcdefg", "", "", ""} {
+		var got string
+		n, err := Unmarshal(bytes, &got)
+		if err != nil {
+			t.Error(err)
+		}
+		if want != got {
+			t.Errorf("%#v != %#v", want, got)
+		}
+		bytes = bytes[n:]
 	}
 }
 
+func TestPrimitivesExact(t *testing.T) {
+	d := NewDecoder(getReader("primitives"))
+	// Decoding into exact types
+	var b bool
+	assertDecode(d, true, &b)
+	assertDecode(d, false, &b)
+	var u8 uint8
+	assertDecode(d, uint8(42), &u8)
+	var u16 uint16
+	assertDecode(d, uint16(42), &u16)
+	var i16 int16
+	assertDecode(d, int16(-42), &i16)
+	var u32 uint32
+	assertDecode(d, uint32(12345), &u32)
+	var i32 int32
+	assertDecode(d, int32(-12345), &i32)
+	var u64 uint64
+	assertDecode(d, uint64(12345), &u64)
+	var i64 int64
+	assertDecode(d, int64(-12345), &i64)
+	var f32 float32
+	assertDecode(d, float32(0.125), &f32)
+	var f64 float64
+	assertDecode(d, float64(0.125), &f64)
+}
+
+func TestPrimitivesCompatible(t *testing.T) {
+	d := NewDecoder(getReader("primitives"))
+	// Decoding into compatible types
+	var b bool
+	var i int
+	var u uint
+	var f float64
+	assertDecode(d, true, &b)
+	assertDecode(d, false, &b)
+	assertDecode(d, uint(42), &u)
+	assertDecode(d, uint(42), &u)
+	assertDecode(d, -42, &i)
+	assertDecode(d, uint(12345), &u)
+	assertDecode(d, -12345, &i)
+	assertDecode(d, uint(12345), &u)
+	assertDecode(d, -12345, &i)
+	assertDecode(d, 0.125, &f)
+	assertDecode(d, 0.125, &f)
+}
+
 func TestStrings(t *testing.T) {
-	d := NewDecoder(getReader(t, "strings"))
+	d := NewDecoder(getReader("strings"))
 	// Test decoding as plain Go strings
-	for i, want := range []string{"abc\000defg", "abcdefg", "abcdefg", "", "", ""} {
+	for _, want := range []string{"abc\000defg", "abcdefg", "abcdefg", "", "", ""} {
 		var got string
-		if err := checkDecode(d, want, &got); err != nil {
-			t.Errorf("%d: %v", i, err)
-		}
+		assertDecode(d, want, &got)
 	}
 	remains := remaining(d)
 	if remains != "" {
@@ -98,24 +138,85 @@ func TestStrings(t *testing.T) {
 	}
 
 	// Test decoding as specific string types
-	d = NewDecoder(getReader(t, "strings"))
+	d = NewDecoder(getReader("strings"))
 	var bytes []byte
-	var str string
-	var sym Symbol
-	for i, expect := range []expect{
-		{[]byte("abc\000defg"), &bytes},
-		{"abcdefg", &str},
-		{Symbol("abcdefg"), &sym},
-		{make([]byte, 0), &bytes},
-		{"", &str},
-		{Symbol(""), &sym},
-	} {
-		if err := checkDecode(d, expect.want, expect.got); err != nil {
-			t.Errorf("%d: %v", i, err)
-		}
-	}
+	var str, sym string
+	assertDecode(d, []byte("abc\000defg"), &bytes)
+	assertDecode(d, "abcdefg", &str)
+	assertDecode(d, "abcdefg", &sym)
+	assertDecode(d, make([]byte, 0), &bytes)
+	assertDecode(d, "", &str)
+	assertDecode(d, "", &sym)
 	remains = remaining(d)
 	if remains != "" {
-		t.Errorf("leftover: %s", remains)
+		panic(errorf("leftover: %s", remains))
+	}
+
+	// Test some error handling
+	d = NewDecoder(getReader("strings"))
+	var s string
+	err := d.Decode(s)
+	if !strings.Contains(err.Error(), "not a pointer") {
+		t.Error(err)
+	}
+	var i int
+	err = d.Decode(&i)
+	if !strings.Contains(err.Error(), "cannot unmarshal") {
+		t.Error(err)
+	}
+}
+
+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)
+		}
+	}
+
+	d := NewDecoder(bytes.NewReader(buf.Bytes()))
+
+	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()))
 	}
 }

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/0816badb/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 bde1ddd..042d9eb 100644
--- a/proton-c/bindings/go/src/apache.org/proton/types.go
+++ b/proton-c/bindings/go/src/apache.org/proton/types.go
@@ -19,7 +19,49 @@ under the License.
 
 package proton
 
-// Types to exactly represent specific AMQP encodings
+// #include <proton/codec.h>
+import "C"
 
-// Symbol is the AMQP symbol data type, it can be converted to a Go string or []byte
-type Symbol string
+import (
+	"reflect"
+)
+
+type pnGetter func(data *C.pn_data_t) reflect.Value
+
+type pnType struct {
+	code   C.pn_type_t
+	name   string
+	getter pnGetter
+}
+
+var pnTypes = map[C.pn_type_t]pnType{
+	C.PN_NULL:       {C.PN_NULL, "null", nil},
+	C.PN_BOOL:       {C.PN_BOOL, "bool", getPnBool},
+	C.PN_UBYTE:      {C.PN_UBYTE, "ubyte", getPnUbyte},
+	C.PN_BYTE:       {C.PN_BYTE, "byte", getPnByte},
+	C.PN_USHORT:     {C.PN_USHORT, "ushort", getPnUshort},
+	C.PN_SHORT:      {C.PN_SHORT, "short", getPnShort},
+	C.PN_UINT:       {C.PN_UINT, "uint", getPnUint},
+	C.PN_INT:        {C.PN_INT, "int", getPnInt},
+	C.PN_CHAR:       {C.PN_CHAR, "char", getPnChar},
+	C.PN_ULONG:      {C.PN_ULONG, "ulong", getPnUlong},
+	C.PN_LONG:       {C.PN_LONG, "long", getPnLong},
+	C.PN_TIMESTAMP:  {C.PN_TIMESTAMP, "timestamp", nil},
+	C.PN_FLOAT:      {C.PN_FLOAT, "float", getPnFloat},
+	C.PN_DOUBLE:     {C.PN_DOUBLE, "double", getPnDouble},
+	C.PN_DECIMAL32:  {C.PN_DECIMAL32, "decimal32", nil},
+	C.PN_DECIMAL64:  {C.PN_DECIMAL64, "decimal64", nil},
+	C.PN_DECIMAL128: {C.PN_DECIMAL128, "decimal128", nil},
+	C.PN_UUID:       {C.PN_UUID, "uuid", nil},
+	C.PN_BINARY:     {C.PN_BINARY, "binary", getPnBinary},
+	C.PN_STRING:     {C.PN_STRING, "string", getPnString},
+	C.PN_SYMBOL:     {C.PN_SYMBOL, "symbol", getPnSymbol},
+	C.PN_DESCRIBED:  {C.PN_DESCRIBED, "described", nil},
+	C.PN_ARRAY:      {C.PN_ARRAY, "array", nil},
+	C.PN_LIST:       {C.PN_LIST, "list", nil},
+}
+
+func pnTypeName(t C.pn_type_t) string {
+	name := pnTypes[t].name
+	return nonBlank(name, "unknown type")
+}


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@qpid.apache.org
For additional commands, e-mail: commits-help@qpid.apache.org