Skip to content

Commit d5e2e42

Browse files
authored
Merge pull request #42 from Basekick-Labs/fix/non-addressable-pointer-encode
Fix/non addressable pointer encode
2 parents 725a4d2 + 3a1dedd commit d5e2e42

3 files changed

Lines changed: 45 additions & 20 deletions

File tree

encode_value.go

Lines changed: 17 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -132,11 +132,21 @@ func ptrEncoderFunc(typ reflect.Type) encoderFunc {
132132
}
133133
}
134134

135-
func encodeCustomValuePtr(e *Encoder, v reflect.Value) error {
136-
if !v.CanAddr() {
137-
return fmt.Errorf("msgpack: Encode(non-addressable %T)", v.Interface())
135+
// ensureAddr returns v.Addr() if v is addressable, otherwise it allocates
136+
// a new value, copies v into it, and returns the pointer. This allows
137+
// types with pointer receivers to encode even when the source is not
138+
// addressable (e.g. map values, bare literals).
139+
func ensureAddr(v reflect.Value) reflect.Value {
140+
if v.CanAddr() {
141+
return v.Addr()
138142
}
139-
encoder := v.Addr().Interface().(CustomEncoder)
143+
ptr := reflect.New(v.Type())
144+
ptr.Elem().Set(v)
145+
return ptr
146+
}
147+
148+
func encodeCustomValuePtr(e *Encoder, v reflect.Value) error {
149+
encoder := ensureAddr(v).Interface().(CustomEncoder)
140150
return encoder.EncodeMsgpack(e)
141151
}
142152

@@ -150,10 +160,7 @@ func encodeCustomValue(e *Encoder, v reflect.Value) error {
150160
}
151161

152162
func marshalValuePtr(e *Encoder, v reflect.Value) error {
153-
if !v.CanAddr() {
154-
return fmt.Errorf("msgpack: Encode(non-addressable %T)", v.Interface())
155-
}
156-
return marshalValue(e, v.Addr())
163+
return marshalValue(e, ensureAddr(v))
157164
}
158165

159166
func marshalValue(e *Encoder, v reflect.Value) error {
@@ -210,10 +217,7 @@ func nilableType(t reflect.Type) bool {
210217
//------------------------------------------------------------------------------
211218

212219
func marshalBinaryValueAddr(e *Encoder, v reflect.Value) error {
213-
if !v.CanAddr() {
214-
return fmt.Errorf("msgpack: Encode(non-addressable %T)", v.Interface())
215-
}
216-
return marshalBinaryValue(e, v.Addr())
220+
return marshalBinaryValue(e, ensureAddr(v))
217221
}
218222

219223
func marshalBinaryValue(e *Encoder, v reflect.Value) error {
@@ -233,10 +237,7 @@ func marshalBinaryValue(e *Encoder, v reflect.Value) error {
233237
//------------------------------------------------------------------------------
234238

235239
func marshalTextValueAddr(e *Encoder, v reflect.Value) error {
236-
if !v.CanAddr() {
237-
return fmt.Errorf("msgpack: Encode(non-addressable %T)", v.Interface())
238-
}
239-
return marshalTextValue(e, v.Addr())
240+
return marshalTextValue(e, ensureAddr(v))
240241
}
241242

242243
func marshalTextValue(e *Encoder, v reflect.Value) error {

ext.go

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -95,10 +95,7 @@ func makeExtEncoder(
9595

9696
func makeExtEncoderAddr(extEncoder encoderFunc) encoderFunc {
9797
return func(e *Encoder, v reflect.Value) error {
98-
if !v.CanAddr() {
99-
return fmt.Errorf("msgpack: EncodeExt(nonaddressable %T)", v.Interface())
100-
}
101-
return extEncoder(e, v.Addr())
98+
return extEncoder(e, ensureAddr(v))
10299
}
103100
}
104101

types_test.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -289,6 +289,33 @@ var encoderTests = []encoderTest{
289289
{&Intern{A: "foo", B: "foo", C: "foo"}, "83a141a3666f6fa142d48000a143d48000"},
290290
}
291291

292+
// Issue #3: types with pointer receivers should encode even when non-addressable.
293+
type PtrRecvMarshaler uint16
294+
295+
func (p *PtrRecvMarshaler) MarshalMsgpack() ([]byte, error) {
296+
return msgpack.Marshal(uint16(*p))
297+
}
298+
299+
func (p *PtrRecvMarshaler) UnmarshalMsgpack(b []byte) error {
300+
var n uint16
301+
if err := msgpack.Unmarshal(b, &n); err != nil {
302+
return err
303+
}
304+
*p = PtrRecvMarshaler(n)
305+
return nil
306+
}
307+
308+
func TestEncodeNonAddressablePtrReceiver(t *testing.T) {
309+
// Encoding a non-pointer value whose pointer type implements Marshaler.
310+
v := PtrRecvMarshaler(42)
311+
b, err := msgpack.Marshal(v)
312+
require.NoError(t, err)
313+
314+
var out PtrRecvMarshaler
315+
require.NoError(t, msgpack.Unmarshal(b, &out))
316+
require.Equal(t, v, out)
317+
}
318+
292319
func TestEncoder(t *testing.T) {
293320
var buf bytes.Buffer
294321
enc := msgpack.NewEncoder(&buf)

0 commit comments

Comments
 (0)