Add RawMessage, Marshaler, and Unmarshaler
Add RawMessage type. RawMessage can be used to delay CBOR decoding or precompute CBOR encoding. Nil or empty RawMessage marshals to CBOR nil value. Add Marshaler and Unmarshaler interfaces to let user-defined types implement their own CBOR encoding and decoding.
This commit is contained in:
parent
9ff43a1d5d
commit
1a2918702e
53
decode.go
53
decode.go
@ -17,6 +17,7 @@ import (
|
||||
|
||||
var (
|
||||
typeTime = reflect.TypeOf(time.Time{})
|
||||
typeUnmarshaler = reflect.TypeOf((*Unmarshaler)(nil)).Elem()
|
||||
typeBinaryUnmarshaler = reflect.TypeOf((*encoding.BinaryUnmarshaler)(nil)).Elem()
|
||||
)
|
||||
|
||||
@ -93,6 +94,14 @@ func (e *UnmarshalTypeError) Error() string {
|
||||
return s
|
||||
}
|
||||
|
||||
// Unmarshaler is the interface implemented by types that can unmarshal a CBOR
|
||||
// representation of themselves. The input can be assumed to be a valid encoding
|
||||
// of a CBOR value. UnmarshalCBOR must copy the CBOR data if it wishes to retain
|
||||
// the data after returning.
|
||||
type Unmarshaler interface {
|
||||
UnmarshalCBOR([]byte) error
|
||||
}
|
||||
|
||||
type decodeState struct {
|
||||
data []byte
|
||||
offset int // next read offset in data
|
||||
@ -184,20 +193,30 @@ func (d *decodeState) parse(v reflect.Value) (err error) {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Process cbor nil/undefined.
|
||||
if d.data[d.offset] == 0xf6 || d.data[d.offset] == 0xf7 {
|
||||
d.offset++
|
||||
return fillNil(cborTypePrimitives, v)
|
||||
// Create new value for the pointer v to point to if CBOR value is not nil/undefined.
|
||||
if d.data[d.offset] != 0xf6 && d.data[d.offset] != 0xf7 {
|
||||
for v.Kind() == reflect.Ptr {
|
||||
if v.IsNil() {
|
||||
if !v.CanSet() {
|
||||
return errors.New("cbor: cannot set new value for " + v.Type().String())
|
||||
}
|
||||
v.Set(reflect.New(v.Type().Elem()))
|
||||
}
|
||||
v = v.Elem()
|
||||
}
|
||||
}
|
||||
|
||||
for v.Kind() == reflect.Ptr {
|
||||
if v.IsNil() {
|
||||
if !v.CanSet() {
|
||||
return errors.New("cbor: cannot set new value for " + v.Type().String())
|
||||
}
|
||||
v.Set(reflect.New(v.Type().Elem()))
|
||||
if reflect.PtrTo(v.Type()).Implements(typeUnmarshaler) {
|
||||
pv := reflect.New(v.Type())
|
||||
pv.Elem().Set(v)
|
||||
u := pv.Interface().(Unmarshaler)
|
||||
start := d.offset
|
||||
d.skip()
|
||||
if err := u.UnmarshalCBOR(d.data[start:d.offset]); err != nil {
|
||||
return err
|
||||
}
|
||||
v = v.Elem()
|
||||
v.Set(pv.Elem())
|
||||
return nil
|
||||
}
|
||||
|
||||
// Process byte/text string.
|
||||
@ -231,6 +250,8 @@ func (d *decodeState) parse(v reflect.Value) (err error) {
|
||||
switch ai {
|
||||
case 20, 21:
|
||||
return fillBool(t, ai == 21, v)
|
||||
case 22, 23:
|
||||
return fillNil(t, v)
|
||||
case 24:
|
||||
return fillPositiveInt(t, uint64(val), v)
|
||||
case 25:
|
||||
@ -281,11 +302,6 @@ func (d *decodeState) parse(v reflect.Value) (err error) {
|
||||
|
||||
// parseInterface assumes data is well-formed, and does not perform bounds checking.
|
||||
func (d *decodeState) parseInterface() (_ interface{}, err error) {
|
||||
if d.data[d.offset] == 0xf6 || d.data[d.offset] == 0xf7 {
|
||||
d.offset++
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Process byte/text string.
|
||||
t := cborType(d.data[d.offset] & 0xE0)
|
||||
if t == cborTypeByteString {
|
||||
@ -316,6 +332,8 @@ func (d *decodeState) parseInterface() (_ interface{}, err error) {
|
||||
switch ai {
|
||||
case 20, 21:
|
||||
return (ai == 21), nil
|
||||
case 22, 23:
|
||||
return nil, nil
|
||||
case 24:
|
||||
return uint64(val), nil
|
||||
case 25:
|
||||
@ -874,6 +892,9 @@ func isHashableKind(k reflect.Kind) bool {
|
||||
// time.Time value, Unmarshal creates an unix time with integer/float as seconds
|
||||
// and fractional seconds since January 1, 1970 UTC.
|
||||
//
|
||||
// To unmarshal CBOR into a value implementing the Unmarshaler interface,
|
||||
// Unmarshal calls that value's UnmarshalCBOR method.
|
||||
//
|
||||
// Unmarshal decodes a CBOR byte string into a value implementing
|
||||
// encoding.BinaryUnmarshaler.
|
||||
//
|
||||
|
||||
204
decode_test.go
204
decode_test.go
@ -616,6 +616,13 @@ func TestUnmarshal(t *testing.T) {
|
||||
} else if !reflect.DeepEqual(v, tc.emptyInterfaceValue) {
|
||||
t.Errorf("Unmarshal(0x%0x) = %v (%T), want %v (%T)", tc.cborData, v, v, tc.emptyInterfaceValue, tc.emptyInterfaceValue)
|
||||
}
|
||||
// Test unmarshalling CBOR into RawMessage.
|
||||
var r cbor.RawMessage
|
||||
if err := cbor.Unmarshal(tc.cborData, &r); err != nil {
|
||||
t.Errorf("Unmarshal(0x%0x) returns error %v", tc.cborData, err)
|
||||
} else if !bytes.Equal(r, tc.cborData) {
|
||||
t.Errorf("Unmarshal(0x%0x) returns RawMessage %v, want %v", tc.cborData, r, tc.cborData)
|
||||
}
|
||||
// Test unmarshalling CBOR into compatible data types.
|
||||
for _, value := range tc.values {
|
||||
v := reflect.New(reflect.TypeOf(value))
|
||||
@ -656,6 +663,13 @@ func TestUnmarshalFloat(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
// Test unmarshalling CBOR into RawMessage.
|
||||
var r cbor.RawMessage
|
||||
if err := cbor.Unmarshal(tc.cborData, &r); err != nil {
|
||||
t.Errorf("Unmarshal(0x%0x) returns error %v", tc.cborData, err)
|
||||
} else if !bytes.Equal(r, tc.cborData) {
|
||||
t.Errorf("Unmarshal(0x%0x) returns RawMessage %v, want %v", tc.cborData, r, tc.cborData)
|
||||
}
|
||||
// Test unmarshalling CBOR into compatible data types.
|
||||
for _, value := range tc.values {
|
||||
v := reflect.New(reflect.TypeOf(value))
|
||||
@ -701,6 +715,7 @@ func TestUnmarshalIntoPointer(t *testing.T) {
|
||||
|
||||
var p1 *int
|
||||
var p2 *string
|
||||
var p3 *cbor.RawMessage
|
||||
|
||||
var i int
|
||||
pi := &i
|
||||
@ -710,39 +725,67 @@ func TestUnmarshalIntoPointer(t *testing.T) {
|
||||
ps := &s
|
||||
pps := &ps
|
||||
|
||||
// Unmarshal CBOR nil into a pointer.
|
||||
var r cbor.RawMessage
|
||||
pr := &r
|
||||
ppr := &pr
|
||||
|
||||
// Unmarshal CBOR nil into a nil pointer.
|
||||
if err := cbor.Unmarshal(cborDataNil, &p1); err != nil {
|
||||
t.Errorf("Unmarshal(0x%0x) returns error %v", cborDataNil, err)
|
||||
} else if p1 != nil {
|
||||
t.Errorf("Unmarshal(0x%0x) = %v (%T), want nil", cborDataNil, p1, p1)
|
||||
}
|
||||
if err := cbor.Unmarshal(cborDataNil, &p2); err != nil {
|
||||
t.Errorf("Unmarshal(0x%0x) returns error %v", cborDataNil, err)
|
||||
} else if p2 != nil {
|
||||
t.Errorf("Unmarshal(0x%0x) = %v (%T), want nil", cborDataNil, p1, p1)
|
||||
}
|
||||
if err := cbor.Unmarshal(cborDataNil, &p3); err != nil {
|
||||
t.Errorf("Unmarshal(0x%0x) returns error %v", cborDataNil, err)
|
||||
} else if p3 != nil {
|
||||
t.Errorf("Unmarshal(0x%0x) = %v (%T), want nil", cborDataNil, p1, p1)
|
||||
}
|
||||
|
||||
// Unmarshal CBOR integer into a non-nil pointer.
|
||||
if err := cbor.Unmarshal(cborDataInt, &ppi); err != nil {
|
||||
t.Errorf("Unmarshal(0x%0x) returns error %v", cborDataNil, err)
|
||||
t.Errorf("Unmarshal(0x%0x) returns error %v", cborDataInt, err)
|
||||
} else if i != 24 {
|
||||
t.Errorf("Unmarshal(0x%0x) = %v (%T), want 24", cborDataNil, i, i)
|
||||
t.Errorf("Unmarshal(0x%0x) = %v (%T), want 24", cborDataInt, i, i)
|
||||
}
|
||||
|
||||
// Unmarshal CBOR integer into a nil pointer.
|
||||
if err := cbor.Unmarshal(cborDataInt, &p1); err != nil {
|
||||
t.Errorf("Unmarshal(0x%0x) returns error %v", cborDataNil, err)
|
||||
t.Errorf("Unmarshal(0x%0x) returns error %v", cborDataInt, err)
|
||||
} else if *p1 != 24 {
|
||||
t.Errorf("Unmarshal(0x%0x) = %v (%T), want 24", cborDataNil, *pi, pi)
|
||||
t.Errorf("Unmarshal(0x%0x) = %v (%T), want 24", cborDataInt, *pi, pi)
|
||||
}
|
||||
|
||||
// Unmarshal CBOR string into a non-nil pointer.
|
||||
if err := cbor.Unmarshal(cborDataString, &pps); err != nil {
|
||||
t.Errorf("Unmarshal(0x%0x) returns error %v", cborDataNil, err)
|
||||
t.Errorf("Unmarshal(0x%0x) returns error %v", cborDataString, err)
|
||||
} else if s != "streaming" {
|
||||
t.Errorf("Unmarshal(0x%0x) = %v (%T), want \"streaming\"", cborDataNil, s, s)
|
||||
t.Errorf("Unmarshal(0x%0x) = %v (%T), want \"streaming\"", cborDataString, s, s)
|
||||
}
|
||||
|
||||
// Unmarshal CBOR string into a nil pointer.
|
||||
if err := cbor.Unmarshal(cborDataString, &p2); err != nil {
|
||||
t.Errorf("Unmarshal(0x%0x) returns error %v", cborDataNil, err)
|
||||
t.Errorf("Unmarshal(0x%0x) returns error %v", cborDataString, err)
|
||||
} else if *p2 != "streaming" {
|
||||
t.Errorf("Unmarshal(0x%0x) = %v (%T), want \"streaming\"", cborDataNil, *p2, p2)
|
||||
t.Errorf("Unmarshal(0x%0x) = %v (%T), want \"streaming\"", cborDataString, *p2, p2)
|
||||
}
|
||||
|
||||
// Unmarshal CBOR string into a non-nil cbor.RawMessage.
|
||||
if err := cbor.Unmarshal(cborDataString, &ppr); err != nil {
|
||||
t.Errorf("Unmarshal(0x%0x) returns error %v", cborDataString, err)
|
||||
} else if !bytes.Equal(r, cborDataString) {
|
||||
t.Errorf("Unmarshal(0x%0x) = %v (%T), want %v", cborDataString, r, r, cborDataString)
|
||||
}
|
||||
|
||||
// Unmarshal CBOR string into a nil pointer to cbor.RawMessage.
|
||||
if err := cbor.Unmarshal(cborDataString, &p3); err != nil {
|
||||
t.Errorf("Unmarshal(0x%0x) returns error %v", cborDataString, err)
|
||||
} else if !bytes.Equal(*p3, cborDataString) {
|
||||
t.Errorf("Unmarshal(0x%0x) = %v (%T), want %v", cborDataString, *p3, p3, cborDataString)
|
||||
}
|
||||
}
|
||||
|
||||
@ -1461,6 +1504,12 @@ func (s *stru) UnmarshalBinary(data []byte) (err error) {
|
||||
return
|
||||
}
|
||||
|
||||
type marshalBinaryError string
|
||||
|
||||
func (n marshalBinaryError) MarshalBinary() (data []byte, err error) {
|
||||
return nil, errors.New(string(n))
|
||||
}
|
||||
|
||||
func TestBinaryUnmarshal(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
@ -1529,3 +1578,140 @@ func TestBinaryUnmarshalError(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestBinaryMarshalError(t *testing.T) {
|
||||
wantErrorMsg := "MarshalBinary: error"
|
||||
v := marshalBinaryError(wantErrorMsg)
|
||||
if _, err := cbor.Marshal(v, cbor.EncOptions{}); err == nil {
|
||||
t.Errorf("Unmarshal(0x%0x) doesn't return error, want error msg %s\n", v, wantErrorMsg)
|
||||
} else if err.Error() != wantErrorMsg {
|
||||
t.Errorf("Unmarshal(0x%0x) returns error %s, want %s", v, err, wantErrorMsg)
|
||||
}
|
||||
}
|
||||
|
||||
type number2 uint64
|
||||
|
||||
func (n number2) MarshalCBOR() (data []byte, err error) {
|
||||
m := map[string]uint64{"num": uint64(n)}
|
||||
return cbor.Marshal(m, cbor.EncOptions{})
|
||||
}
|
||||
|
||||
func (n *number2) UnmarshalCBOR(data []byte) (err error) {
|
||||
var v map[string]uint64
|
||||
if err := cbor.Unmarshal(data, &v); err != nil {
|
||||
return err
|
||||
}
|
||||
*n = number2(v["num"])
|
||||
return nil
|
||||
}
|
||||
|
||||
type stru2 struct {
|
||||
a, b, c string
|
||||
}
|
||||
|
||||
func (s *stru2) MarshalCBOR() ([]byte, error) {
|
||||
v := []string{s.a, s.b, s.c}
|
||||
return cbor.Marshal(v, cbor.EncOptions{})
|
||||
}
|
||||
|
||||
func (s *stru2) UnmarshalCBOR(data []byte) (err error) {
|
||||
var v []string
|
||||
if err := cbor.Unmarshal(data, &v); err != nil {
|
||||
return err
|
||||
}
|
||||
if len(v) > 0 {
|
||||
s.a = v[0]
|
||||
}
|
||||
if len(v) > 1 {
|
||||
s.b = v[1]
|
||||
}
|
||||
if len(v) > 2 {
|
||||
s.c = v[2]
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type marshalCBORError string
|
||||
|
||||
func (n marshalCBORError) MarshalCBOR() (data []byte, err error) {
|
||||
return nil, errors.New(string(n))
|
||||
}
|
||||
|
||||
func TestUnmarshalCBOR(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
obj interface{}
|
||||
wantCborData []byte
|
||||
}{
|
||||
{
|
||||
name: "primitive obj",
|
||||
obj: number2(1),
|
||||
wantCborData: hexDecode("a1636e756d01"),
|
||||
},
|
||||
{
|
||||
name: "struct obj",
|
||||
obj: stru2{a: "a", b: "b", c: "c"},
|
||||
wantCborData: hexDecode("83616161626163"),
|
||||
},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
b, err := cbor.Marshal(tc.obj, cbor.EncOptions{})
|
||||
if err != nil {
|
||||
t.Errorf("Marshal(%+v) returns error %v\n", tc.obj, err)
|
||||
}
|
||||
if !bytes.Equal(b, tc.wantCborData) {
|
||||
t.Errorf("Marshal(%+v) = 0x%0x, want 0x%0x", tc.obj, b, tc.wantCborData)
|
||||
}
|
||||
v := reflect.New(reflect.TypeOf(tc.obj))
|
||||
if err := cbor.Unmarshal(b, v.Interface()); err != nil {
|
||||
t.Errorf("Unmarshal() returns error %v\n", err)
|
||||
}
|
||||
if !reflect.DeepEqual(tc.obj, v.Elem().Interface()) {
|
||||
t.Errorf("Marshal-Unmarshal return different values: %v, %v\n", tc.obj, v.Elem().Interface())
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestUnmarshalCBORError(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
typ reflect.Type
|
||||
cborData []byte
|
||||
wantErrorMsg string
|
||||
}{
|
||||
{
|
||||
name: "primitive type",
|
||||
typ: reflect.TypeOf(number2(0)),
|
||||
cborData: hexDecode("44499602d2"),
|
||||
wantErrorMsg: "cbor: cannot unmarshal byte string into Go value of type map[string]uint64",
|
||||
},
|
||||
{
|
||||
name: "struct type",
|
||||
typ: reflect.TypeOf(stru2{}),
|
||||
cborData: hexDecode("47612C622C632C64"),
|
||||
wantErrorMsg: "cbor: cannot unmarshal byte string into Go value of type []string",
|
||||
},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
v := reflect.New(tc.typ)
|
||||
if err := cbor.Unmarshal(tc.cborData, v.Interface()); err == nil {
|
||||
t.Errorf("Unmarshal(0x%0x) doesn't return error, want error msg %s\n", tc.cborData, tc.wantErrorMsg)
|
||||
} else if err.Error() != tc.wantErrorMsg {
|
||||
t.Errorf("Unmarshal(0x%0x) returns error %s, want %s", tc.cborData, err, tc.wantErrorMsg)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestMarshalCBORError(t *testing.T) {
|
||||
wantErrorMsg := "MarshalCBOR: error"
|
||||
v := marshalCBORError(wantErrorMsg)
|
||||
if _, err := cbor.Marshal(v, cbor.EncOptions{}); err == nil {
|
||||
t.Errorf("Marshal(%+v) doesn't return error, want error msg %s\n", v, wantErrorMsg)
|
||||
} else if err.Error() != wantErrorMsg {
|
||||
t.Errorf("Marshal(%+v) returns error %s, want %s", v, err, wantErrorMsg)
|
||||
}
|
||||
}
|
||||
|
||||
32
encode.go
32
encode.go
@ -34,7 +34,16 @@ var (
|
||||
cborPositiveInfinity = []byte{0xf9, 0x7c, 0x00}
|
||||
cborNegativeInfinity = []byte{0xf9, 0xfc, 0x00}
|
||||
)
|
||||
var typeBinaryMarshaler = reflect.TypeOf((*encoding.BinaryMarshaler)(nil)).Elem()
|
||||
var (
|
||||
typeMarshaler = reflect.TypeOf((*Marshaler)(nil)).Elem()
|
||||
typeBinaryMarshaler = reflect.TypeOf((*encoding.BinaryMarshaler)(nil)).Elem()
|
||||
)
|
||||
|
||||
// Marshaler is the interface implemented by types that can marshal themselves
|
||||
// into valid CBOR.
|
||||
type Marshaler interface {
|
||||
MarshalCBOR() ([]byte, error)
|
||||
}
|
||||
|
||||
// EncOptions specifies encoding options.
|
||||
type EncOptions struct {
|
||||
@ -419,6 +428,20 @@ func encodeBinaryMarshalerType(e *encodeState, v reflect.Value, opts EncOptions)
|
||||
return n1 + n2, nil
|
||||
}
|
||||
|
||||
func encodeMarshalerType(e *encodeState, v reflect.Value, opts EncOptions) (int, error) {
|
||||
u, ok := v.Interface().(Marshaler)
|
||||
if !ok {
|
||||
pv := reflect.New(v.Type())
|
||||
pv.Elem().Set(v)
|
||||
u = pv.Interface().(Marshaler)
|
||||
}
|
||||
data, err := u.MarshalCBOR()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return e.Write(data)
|
||||
}
|
||||
|
||||
func getEncodeIndirectValueFunc(f encodeFunc) encodeFunc {
|
||||
return func(e *encodeState, v reflect.Value, opts EncOptions) (int, error) {
|
||||
for v.Kind() == reflect.Ptr && !v.IsNil() {
|
||||
@ -432,6 +455,9 @@ func getEncodeIndirectValueFunc(f encodeFunc) encodeFunc {
|
||||
}
|
||||
|
||||
func getEncodeFunc(t reflect.Type) encodeFunc {
|
||||
if reflect.PtrTo(t).Implements(typeMarshaler) {
|
||||
return encodeMarshalerType
|
||||
}
|
||||
if reflect.PtrTo(t).Implements(typeBinaryMarshaler) {
|
||||
if t == typeTime {
|
||||
return encodeTime
|
||||
@ -523,7 +549,9 @@ func isEmptyValue(v reflect.Value) bool {
|
||||
// EncOptions.TimeRFC3339 is true; otherwise, time.Time values encode as
|
||||
// numerical representation of seconds since January 1, 1970 UTC.
|
||||
//
|
||||
// Values implementing encoding.BinaryMarshaler encode as CBOR byte strings.
|
||||
// If value implements the Marshaler interface, Marshal calls its MarshalCBOR
|
||||
// method. If value implements encoding.BinaryMarshaler instead, Marhsal
|
||||
// calls its MarshalBinary method and encode it as CBOR byte string.
|
||||
//
|
||||
// Marshal supports format string stored under the "cbor" key in the struct
|
||||
// field's tag. CBOR format string can specify the name of the field, "omitempty"
|
||||
|
||||
@ -546,6 +546,12 @@ func testMarshal(t *testing.T, testCases []marshalTest) {
|
||||
t.Errorf("Marshal(%v, cbor.EncOptions{Canonical: true}) = 0x%0x, want 0x%0x", value, b, tc.cborData)
|
||||
}
|
||||
}
|
||||
r := cbor.RawMessage(tc.cborData)
|
||||
if b, err := cbor.Marshal(r, cbor.EncOptions{}); err != nil {
|
||||
t.Errorf("Marshal(%v, cbor.EncOptions{}) returns error %v", r, err)
|
||||
} else if !bytes.Equal(b, r) {
|
||||
t.Errorf("Marshal(%v, cbor.EncOptions{}) returns %v, want %v", r, b, r)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1229,3 +1235,95 @@ func TestMarshalStructLongFieldName(t *testing.T) {
|
||||
t.Errorf("Marshal(%+v) = %v, want %v", v, b, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMarshalRawMessageValue(t *testing.T) {
|
||||
type (
|
||||
T1 struct {
|
||||
M cbor.RawMessage `cbor:",omitempty"`
|
||||
}
|
||||
T2 struct {
|
||||
M *cbor.RawMessage `cbor:",omitempty"`
|
||||
}
|
||||
)
|
||||
|
||||
var (
|
||||
rawNil = cbor.RawMessage(nil)
|
||||
rawEmpty = cbor.RawMessage([]byte{})
|
||||
raw = cbor.RawMessage([]byte{0x01})
|
||||
)
|
||||
|
||||
tests := []struct {
|
||||
obj interface{}
|
||||
want []byte
|
||||
}{
|
||||
// Test with nil RawMessage.
|
||||
{rawNil, []byte{0xf6}},
|
||||
{&rawNil, []byte{0xf6}},
|
||||
{[]interface{}{rawNil}, []byte{0x81, 0xf6}},
|
||||
{&[]interface{}{rawNil}, []byte{0x81, 0xf6}},
|
||||
{[]interface{}{&rawNil}, []byte{0x81, 0xf6}},
|
||||
{&[]interface{}{&rawNil}, []byte{0x81, 0xf6}},
|
||||
{struct{ M cbor.RawMessage }{rawNil}, []byte{0xa1, 0x61, 0x4d, 0xf6}},
|
||||
{&struct{ M cbor.RawMessage }{rawNil}, []byte{0xa1, 0x61, 0x4d, 0xf6}},
|
||||
{struct{ M *cbor.RawMessage }{&rawNil}, []byte{0xa1, 0x61, 0x4d, 0xf6}},
|
||||
{&struct{ M *cbor.RawMessage }{&rawNil}, []byte{0xa1, 0x61, 0x4d, 0xf6}},
|
||||
{map[string]interface{}{"M": rawNil}, []byte{0xa1, 0x61, 0x4d, 0xf6}},
|
||||
{&map[string]interface{}{"M": rawNil}, []byte{0xa1, 0x61, 0x4d, 0xf6}},
|
||||
{map[string]interface{}{"M": &rawNil}, []byte{0xa1, 0x61, 0x4d, 0xf6}},
|
||||
{&map[string]interface{}{"M": &rawNil}, []byte{0xa1, 0x61, 0x4d, 0xf6}},
|
||||
{T1{rawNil}, []byte{0xa0}},
|
||||
{T2{&rawNil}, []byte{0xa1, 0x61, 0x4d, 0xf6}},
|
||||
{&T1{rawNil}, []byte{0xa0}},
|
||||
{&T2{&rawNil}, []byte{0xa1, 0x61, 0x4d, 0xf6}},
|
||||
|
||||
// Test with empty, but non-nil, RawMessage.
|
||||
{rawEmpty, []byte{0xf6}},
|
||||
{&rawEmpty, []byte{0xf6}},
|
||||
{[]interface{}{rawEmpty}, []byte{0x81, 0xf6}},
|
||||
{&[]interface{}{rawEmpty}, []byte{0x81, 0xf6}},
|
||||
{[]interface{}{&rawEmpty}, []byte{0x81, 0xf6}},
|
||||
{&[]interface{}{&rawEmpty}, []byte{0x81, 0xf6}},
|
||||
{struct{ M cbor.RawMessage }{rawEmpty}, []byte{0xa1, 0x61, 0x4d, 0xf6}},
|
||||
{&struct{ M cbor.RawMessage }{rawEmpty}, []byte{0xa1, 0x61, 0x4d, 0xf6}},
|
||||
{struct{ M *cbor.RawMessage }{&rawEmpty}, []byte{0xa1, 0x61, 0x4d, 0xf6}},
|
||||
{&struct{ M *cbor.RawMessage }{&rawEmpty}, []byte{0xa1, 0x61, 0x4d, 0xf6}},
|
||||
{map[string]interface{}{"M": rawEmpty}, []byte{0xa1, 0x61, 0x4d, 0xf6}},
|
||||
{&map[string]interface{}{"M": rawEmpty}, []byte{0xa1, 0x61, 0x4d, 0xf6}},
|
||||
{map[string]interface{}{"M": &rawEmpty}, []byte{0xa1, 0x61, 0x4d, 0xf6}},
|
||||
{&map[string]interface{}{"M": &rawEmpty}, []byte{0xa1, 0x61, 0x4d, 0xf6}},
|
||||
{T1{rawEmpty}, []byte{0xa0}},
|
||||
{T2{&rawEmpty}, []byte{0xa1, 0x61, 0x4d, 0xf6}},
|
||||
{&T1{rawEmpty}, []byte{0xa0}},
|
||||
{&T2{&rawEmpty}, []byte{0xa1, 0x61, 0x4d, 0xf6}},
|
||||
|
||||
// Test with RawMessage with some data.
|
||||
{raw, []byte{0x01}},
|
||||
{&raw, []byte{0x01}},
|
||||
{[]interface{}{raw}, []byte{0x81, 0x01}},
|
||||
{&[]interface{}{raw}, []byte{0x81, 0x01}},
|
||||
{[]interface{}{&raw}, []byte{0x81, 0x01}},
|
||||
{&[]interface{}{&raw}, []byte{0x81, 0x01}},
|
||||
{struct{ M cbor.RawMessage }{raw}, []byte{0xa1, 0x61, 0x4d, 0x01}},
|
||||
{&struct{ M cbor.RawMessage }{raw}, []byte{0xa1, 0x61, 0x4d, 0x01}},
|
||||
{struct{ M *cbor.RawMessage }{&raw}, []byte{0xa1, 0x61, 0x4d, 0x01}},
|
||||
{&struct{ M *cbor.RawMessage }{&raw}, []byte{0xa1, 0x61, 0x4d, 0x01}},
|
||||
{map[string]interface{}{"M": raw}, []byte{0xa1, 0x61, 0x4d, 0x01}},
|
||||
{&map[string]interface{}{"M": raw}, []byte{0xa1, 0x61, 0x4d, 0x01}},
|
||||
{map[string]interface{}{"M": &raw}, []byte{0xa1, 0x61, 0x4d, 0x01}},
|
||||
{&map[string]interface{}{"M": &raw}, []byte{0xa1, 0x61, 0x4d, 0x01}},
|
||||
{T1{raw}, []byte{0xa1, 0x61, 0x4d, 0x01}},
|
||||
{T2{&raw}, []byte{0xa1, 0x61, 0x4d, 0x01}},
|
||||
{&T1{raw}, []byte{0xa1, 0x61, 0x4d, 0x01}},
|
||||
{&T2{&raw}, []byte{0xa1, 0x61, 0x4d, 0x01}},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
b, err := cbor.Marshal(tc.obj, cbor.EncOptions{})
|
||||
if err != nil {
|
||||
t.Errorf("Marshal(%+v) returns error %v", tc.obj, err)
|
||||
}
|
||||
if !bytes.Equal(b, tc.want) {
|
||||
t.Errorf("Marshal(%+v) = 0x%0x, want 0x%0x", tc.obj, b, tc.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
22
stream.go
22
stream.go
@ -16,6 +16,28 @@ var cborIndefiniteHeader = map[cborType][]byte{
|
||||
cborTypeMap: {0xbf},
|
||||
}
|
||||
|
||||
// RawMessage is a raw encoded CBOR value. It implements Marshaler and
|
||||
// Unmarshaler interfaces and can be used to delay CBOR decoding or
|
||||
// precompute a CBOR encoding.
|
||||
type RawMessage []byte
|
||||
|
||||
// MarshalCBOR returns m as the CBOR encoding of m.
|
||||
func (m RawMessage) MarshalCBOR() ([]byte, error) {
|
||||
if len(m) == 0 {
|
||||
return cborNil, nil
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
// UnmarshalCBOR sets *m to a copy of data.
|
||||
func (m *RawMessage) UnmarshalCBOR(data []byte) error {
|
||||
if m == nil {
|
||||
return errors.New("cbor.RawMessage: UnmarshalCBOR on nil pointer")
|
||||
}
|
||||
*m = append((*m)[0:0], data...)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Decoder reads and decodes CBOR values from an input stream.
|
||||
type Decoder struct {
|
||||
r io.Reader
|
||||
|
||||
@ -345,3 +345,55 @@ func TestEncoderStructTag(t *testing.T) {
|
||||
t.Errorf("Encoding mismatch: got %v, want %v", w.Bytes(), want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRawMessage(t *testing.T) {
|
||||
type strc struct {
|
||||
A cbor.RawMessage `cbor:"a"`
|
||||
B *cbor.RawMessage `cbor:"b"`
|
||||
C *cbor.RawMessage `cbor:"c"`
|
||||
}
|
||||
cborData := hexDecode("a361610161628202036163f6") // {"a": 1, "b": [2, 3], "c": nil},
|
||||
r := cbor.RawMessage(hexDecode("820203"))
|
||||
want := strc{
|
||||
A: cbor.RawMessage([]byte{0x01}),
|
||||
B: &r,
|
||||
}
|
||||
var v strc
|
||||
if err := cbor.Unmarshal(cborData, &v); err != nil {
|
||||
t.Fatalf("Unmarshal(0x%0x) returns error %s", cborData, err)
|
||||
}
|
||||
if !reflect.DeepEqual(v, want) {
|
||||
t.Errorf("Unmarshal(0x%0x) returns v %v, want %v", cborData, v, want)
|
||||
}
|
||||
b, err := cbor.Marshal(v, cbor.EncOptions{Canonical: true})
|
||||
if err != nil {
|
||||
t.Fatalf("Marshal(%+v) returns error %s\n", v, err)
|
||||
}
|
||||
if !bytes.Equal(b, cborData) {
|
||||
t.Errorf("Marshal(%+v) = 0x%0x, want 0x%0x", v, b, cborData)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNullRawMessage(t *testing.T) {
|
||||
r := cbor.RawMessage(nil)
|
||||
wantCborData := []byte{0xf6}
|
||||
b, err := cbor.Marshal(r, cbor.EncOptions{})
|
||||
if err != nil {
|
||||
t.Errorf("Marshal(%+v) returns error %v\n", r, err)
|
||||
}
|
||||
if !bytes.Equal(b, wantCborData) {
|
||||
t.Errorf("Marshal(%+v) = 0x%0x, want 0x%0x", r, b, wantCborData)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEmptyRawMessage(t *testing.T) {
|
||||
var r cbor.RawMessage
|
||||
wantCborData := []byte{0xf6}
|
||||
b, err := cbor.Marshal(r, cbor.EncOptions{})
|
||||
if err != nil {
|
||||
t.Errorf("Marshal(%+v) returns error %v\n", r, err)
|
||||
}
|
||||
if !bytes.Equal(b, wantCborData) {
|
||||
t.Errorf("Marshal(%+v) = 0x%0x, want 0x%0x", r, b, wantCborData)
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user