From 853307006799c798798a2f569ef779522d66a60b Mon Sep 17 00:00:00 2001 From: Vladimir Avtsenov Date: Wed, 4 Feb 2026 14:32:23 +0300 Subject: [PATCH] "ignore struct length" flag --- decode.go | 10 ++++++++++ decode_map.go | 9 ++++++++- types_test.go | 25 +++++++++++++++++++++++++ 3 files changed, 43 insertions(+), 1 deletion(-) diff --git a/decode.go b/decode.go index 6e40351..01d4ab2 100644 --- a/decode.go +++ b/decode.go @@ -23,6 +23,7 @@ const ( disallowUnknownFieldsFlag usePreallocateValues disableAllocLimitFlag + ignoreStructLength ) type bufReader interface { @@ -261,6 +262,15 @@ func (d *Decoder) DisableAllocLimit(on bool) { } } +// IgnoreStructLength disables field count verification during parsing +func (d *Decoder) IgnoreStructLength(on bool) { + if on { + d.flags |= ignoreStructLength + } else { + d.flags &= ^ignoreStructLength + } +} + // 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 { diff --git a/decode_map.go b/decode_map.go index 7ba8e10..cd12d88 100644 --- a/decode_map.go +++ b/decode_map.go @@ -368,15 +368,22 @@ func decodeStructValue(d *Decoder, v reflect.Value) error { } fields := structs.Fields(v.Type(), d.structTag) - if n != len(fields.List) { + if d.flags&ignoreStructLength == 0 && n != len(fields.List) { return errArrayStruct } for _, f := range fields.List { + n-- + if n < 0 { + break + } if err := f.DecodeValue(d, v); err != nil { return err } } + if n > 0 { + d.skipNext(n) + } return nil } diff --git a/types_test.go b/types_test.go index 91faff6..ae6a03d 100644 --- a/types_test.go +++ b/types_test.go @@ -33,6 +33,12 @@ func (o *Object) UnmarshalMsgpack(b []byte) error { //------------------------------------------------------------------------------ +const ( + ignoreStructLength uint32 = 1 << iota +) + +//------------------------------------------------------------------------------ + type CustomTime time.Time func (t CustomTime) EncodeMsgpack(enc *msgpack.Encoder) error { @@ -451,6 +457,7 @@ type typeTest struct { out interface{} encErr string decErr string + decFlags uint32 wantnil bool wantzero bool wanted interface{} @@ -631,6 +638,18 @@ var ( out: new(unexported), decErr: "msgpack: number of fields in array-encoded struct has changed", }, + { + in: OmitEmptyTest{"foo", "bar"}, + out: new(unexported), + wanted: unexported{Foo: "foo"}, + decFlags: ignoreStructLength, + }, + { + in: unexported{Foo: "foo"}, + out: new(OmitEmptyTest), + wanted: OmitEmptyTest{"foo", ""}, + decFlags: ignoreStructLength, + }, {in: (*EventTime)(nil), out: new(*EventTime)}, {in: &EventTime{time.Unix(0, 0)}, out: new(*EventTime)}, @@ -690,6 +709,9 @@ func TestTypes(t *testing.T) { } dec := msgpack.NewDecoder(&buf) + if test.decFlags&ignoreStructLength != 0 { + dec.IgnoreStructLength(true) + } err = dec.Decode(test.out) if test.decErr != "" { test.requireErr(err, test.decErr) @@ -737,6 +759,9 @@ func TestTypes(t *testing.T) { var dst interface{} dec := msgpack.NewDecoder(bytes.NewReader(b)) + if test.decFlags&ignoreStructLength != 0 { + dec.IgnoreStructLength(true) + } dec.SetMapDecoder(func(dec *msgpack.Decoder) (interface{}, error) { return dec.DecodeUntypedMap() })