Skip to content

Extension not invoked for MsgpackHandle #426

@jcloyd-fubo

Description

@jcloyd-fubo

Hello, I'm attempting to register an extension for encoding and decoding a struct that has a polymorphic interface{} field. I am not sure if this is a bug, or if I am misusing the library but would appreciate feedback either way.

If I set the value of check in extHandle.getExt(...) to be true through a debugger or local clone then this test passes as I expected. If I do not overwrite this value it is always passed as false via *encoderMsgpackBytes.fn(...) and *decoderMsgpackBytes.fn(...) and the resulting type for Polymorphic.Data is always a map[string]any.

package msgp_test

import (
	"fmt"
	"reflect"
	"testing"

	mp "github.com/ugorji/go/codec"
)

type Polymorphic struct {
	Type string
	Data any
}

type One struct {
	Value string
}

type Two struct {
	Value int
}

type PolymorphicExt struct{}

func (m PolymorphicExt) WriteExt(v interface{}) []byte {
	dto, ok := v.(*Polymorphic)
	if !ok {
		return nil
	}

	switch dto.Type {
	case "one":
		return []byte(dto.Data.(*One).Value)
	case "two":
		return []byte(fmt.Sprintf("two:%d", dto.Data.(*Two).Value))
	default:
		return nil
	}
}

func (m PolymorphicExt) ReadExt(dst interface{}, src []byte) {
	dto, ok := dst.(*Polymorphic)
	if !ok {
		return
	}

	// hack this to simplify reproduction
	dto.Type = "two"

	switch dto.Type {
	case "one":
		dto.Data = &One{Value: string(src)}
	case "two":
		var value int
		if _, err := fmt.Sscanf(string(src), "two:%d", &value); err != nil {
			panic(err)
		}

		dto.Data = &Two{Value: value}
	default:
		panic(fmt.Sprintf("unknown type: %s", dto.Type))
	}
}

func TestEncodingDecoding(t *testing.T) {
	handle := &mp.MsgpackHandle{
		WriteExt: true,
	}
	if err := handle.SetBytesExt(reflect.TypeOf(Polymorphic{}), 0xaa, PolymorphicExt{}); err != nil {
		t.Fatalf("SetBytesExt() failed: %v", err)
	}

	input := &Polymorphic{
		Type: "two",
		Data: &Two{Value: 420},
	}

	byteSlice := make([]byte, 0, reflect.TypeOf(input).Size())
	if err := mp.NewEncoderBytes(&byteSlice, handle).Encode(input); err != nil {
		t.Fatalf("failed to encode polymorphic data: %v", err)
	}

	var decoded Polymorphic
	if err := mp.NewDecoderBytes(byteSlice, handle).Decode(&decoded); err != nil {
		t.Fatalf("failed to decode polymorphic data: %v", err)
	}

	t.Logf("%T", decoded.Data)

	data, ok := decoded.Data.(*Two)
	if !ok {
		t.Fatalf("failed to type case to *One, got %T", decoded.Data)
	}

	if actual := input.Data.(*Two).Value; data.Value != actual {
		t.Fatalf("expected %v, got %v", actual, data.Value)
	}
}

Here is my local diff that gets this test to pass

➜  go git:(master) ✗ git diff
diff --git a/codec/msgpack.mono.generated.go b/codec/msgpack.mono.generated.go
index ff2fe4a..7bab9c8 100644
--- a/codec/msgpack.mono.generated.go
+++ b/codec/msgpack.mono.generated.go
@@ -1,4 +1,4 @@
-//go:build !notmono && !codec.notmono
+//go:build !notmono && !codec.notmono

 // Copyright (c) 2012-2020 Ugorji Nwoke. All rights reserved.
 // Use of this source code is governed by a MIT license found in the LICENSE file.
@@ -1138,7 +1138,7 @@ func (e *encoderMsgpackBytes) rawBytes(vv Raw) {
 }

 func (e *encoderMsgpackBytes) fn(t reflect.Type) *encFnMsgpackBytes {
-	return e.dh.encFnViaBH(t, e.rtidFn, e.h, e.fp, false)
+	return e.dh.encFnViaBH(t, e.rtidFn, e.h, e.fp, true)
 }

 func (e *encoderMsgpackBytes) fnNoExt(t reflect.Type) *encFnMsgpackBytes {
@@ -2847,7 +2847,7 @@ func (d *decoderMsgpackBytes) interfaceExtConvertAndDecode(v interface{}, ext In
 }

 func (d *decoderMsgpackBytes) fn(t reflect.Type) *decFnMsgpackBytes {
-	return d.dh.decFnViaBH(t, d.rtidFn, d.h, d.fp, false)
+	return d.dh.decFnViaBH(t, d.rtidFn, d.h, d.fp, true)
 }

 func (d *decoderMsgpackBytes) fnNoExt(t reflect.Type) *decFnMsgpackBytes {
➜  go git:(master) ✗

Test results before changes

--- FAIL: TestEncodingDecoding (0.00s)
    msgpack_test.go:88: map[interface {}]interface {}
    msgpack_test.go:92: failed to type case to *One, got map[interface {}]interface {}
FAIL
FAIL	msgp-sam-testing	0.243s
FAIL

Thanks for your time.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions