Skip to content

Commit 1de3898

Browse files
authored
Fix omitempty on aliased types (#377)
Instead of doing return, insert an `if` block to skip emitting zero fields. This also allows the check to be inserted at any level. Fixes #376 Emitted code: ```Go // MarshalMsg implements msgp.Marshaler func (z TypeSamples) MarshalMsg(b []byte) (o []byte, err error) { o = msgp.Require(b, z.Msgsize()) o = msgp.AppendArrayHeader(o, uint32(len(z))) for zb0004 := range z { // check for omitted fields zb0001Len := uint32(2) var zb0001Mask uint8 /* 2 bits */ _ = zb0001Mask if z[zb0004].K == 0 { zb0001Len-- zb0001Mask |= 0x1 } if z[zb0004].V == 0 { zb0001Len-- zb0001Mask |= 0x2 } // variable map header, size zb0001Len o = append(o, 0x80|uint8(zb0001Len)) // skip if no fields are to be emitted if zb0001Len != 0 { if (zb0001Mask & 0x1) == 0 { // if not omitted // string "k" o = append(o, 0xa1, 0x6b) o = msgp.AppendUint32(o, z[zb0004].K) } if (zb0001Mask & 0x2) == 0 { // if not omitted // string "v" o = append(o, 0xa1, 0x76) o = msgp.AppendUint32(o, z[zb0004].V) } } } return } ``` If only 1 field, the check is omitted (and there is similar behavior on clearomitted).
1 parent 4558fbf commit 1de3898

File tree

6 files changed

+70
-14
lines changed

6 files changed

+70
-14
lines changed

_generated/omitempty.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,13 @@ type OmitEmpty0 struct {
5454
ATime time.Time `msg:"atime,omitempty"`
5555
}
5656

57+
type TypeSample struct {
58+
K uint32 `msg:"k,omitempty"`
59+
V uint32 `msg:"v,omitempty"`
60+
}
61+
62+
type TypeSamples []TypeSample
63+
5764
type (
5865
NamedBool bool
5966
NamedInt int

_generated/omitempty_test.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package _generated
33
import (
44
"bytes"
55
"io"
6+
"reflect"
67
"testing"
78

89
"github.com/tinylib/msgp/msgp"
@@ -285,3 +286,35 @@ func BenchmarkNotOmitEmpty10AllFull(b *testing.B) {
285286
}
286287
}
287288
}
289+
290+
func TestTypeAlias(t *testing.T) {
291+
value := TypeSamples{TypeSample{}, TypeSample{K: 1, V: 2}}
292+
encoded, err := value.MarshalMsg(nil)
293+
if err != nil {
294+
t.Fatal(err)
295+
}
296+
var got TypeSamples
297+
_, err = got.UnmarshalMsg(encoded)
298+
if err != nil {
299+
t.Fatal(err)
300+
}
301+
if !reflect.DeepEqual(value, got) {
302+
t.Errorf("UnmarshalMsg got %v want %v", value, got)
303+
}
304+
var buf bytes.Buffer
305+
w := msgp.NewWriter(&buf)
306+
err = value.EncodeMsg(w)
307+
if err != nil {
308+
t.Fatal(err)
309+
}
310+
w.Flush()
311+
got = TypeSamples{}
312+
r := msgp.NewReader(&buf)
313+
err = got.DecodeMsg(r)
314+
if err != nil {
315+
t.Fatal(err)
316+
}
317+
if !reflect.DeepEqual(value, got) {
318+
t.Errorf("UnmarshalMsg got %v want %v", value, got)
319+
}
320+
}

gen/decode.go

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,9 @@ func (d *decodeGen) structAsMap(s *Struct) {
159159

160160
if oeCount > 0 {
161161
d.p.printf("\n// Clear omitted fields.\n")
162-
d.p.printf("if %s {\n", bm.notAllSet())
162+
if bm.bitlen > 1 {
163+
d.p.printf("if %s {\n", bm.notAllSet())
164+
}
163165
for bitIdx, fieldIdx := range oeEmittedIdx {
164166
fieldElem := s.Fields[fieldIdx].FieldElem
165167

@@ -172,7 +174,9 @@ func (d *decodeGen) structAsMap(s *Struct) {
172174
}
173175
d.p.printf("}\n")
174176
}
175-
d.p.printf("}")
177+
if bm.bitlen > 1 {
178+
d.p.printf("}")
179+
}
176180
}
177181
}
178182

gen/encode.go

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ package gen
33
import (
44
"fmt"
55
"io"
6-
"strings"
76

87
"github.com/tinylib/msgp/msgp"
98
)
@@ -142,6 +141,7 @@ func (e *encodeGen) structmap(s *Struct) {
142141

143142
omitempty := s.AnyHasTagPart("omitempty")
144143
omitzero := s.AnyHasTagPart("omitzero")
144+
var closeZero bool
145145
var fieldNVar string
146146
if omitempty || omitzero {
147147

@@ -175,9 +175,11 @@ func (e *encodeGen) structmap(s *Struct) {
175175
return
176176
}
177177

178-
// quick return for the case where the entire thing is empty, but only at the top level
179-
if !strings.Contains(s.Varname(), ".") {
180-
e.p.printf("\nif %s == 0 { return }", fieldNVar)
178+
// Skip block, if no fields are set.
179+
if nfields > 1 {
180+
e.p.printf("\n\n// skip if no fields are to be emitted")
181+
e.p.printf("\nif %s != 0 {", fieldNVar)
182+
closeZero = true
181183
}
182184

183185
} else {
@@ -226,7 +228,9 @@ func (e *encodeGen) structmap(s *Struct) {
226228
if oeField || anField {
227229
e.p.print("\n}") // close if statement
228230
}
229-
231+
}
232+
if closeZero {
233+
e.p.printf("\n}") // close if statement
230234
}
231235
}
232236

gen/marshal.go

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ package gen
33
import (
44
"fmt"
55
"io"
6-
"strings"
76

87
"github.com/tinylib/msgp/msgp"
98
)
@@ -137,6 +136,7 @@ func (m *marshalGen) mapstruct(s *Struct) {
137136

138137
omitempty := s.AnyHasTagPart("omitempty")
139138
omitzero := s.AnyHasTagPart("omitzero")
139+
var closeZero bool
140140
var fieldNVar string
141141
if omitempty || omitzero {
142142

@@ -169,9 +169,11 @@ func (m *marshalGen) mapstruct(s *Struct) {
169169
return
170170
}
171171

172-
// quick return for the case where the entire thing is empty, but only at the top level
173-
if !strings.Contains(s.Varname(), ".") {
174-
m.p.printf("\nif %s == 0 { return }", fieldNVar)
172+
// Skip block, if no fields are set.
173+
if nfields > 1 {
174+
m.p.printf("\n\n// skip if no fields are to be emitted")
175+
m.p.printf("\nif %s != 0 {", fieldNVar)
176+
closeZero = true
175177
}
176178

177179
} else {
@@ -222,7 +224,9 @@ func (m *marshalGen) mapstruct(s *Struct) {
222224
if oeField || anField {
223225
m.p.printf("\n}") // close if statement
224226
}
225-
227+
}
228+
if closeZero {
229+
m.p.printf("\n}") // close if statement
226230
}
227231
}
228232

gen/unmarshal.go

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,9 @@ func (u *unmarshalGen) mapstruct(s *Struct) {
151151
u.p.print("\n}\n}") // close switch and for loop
152152
if oeCount > 0 {
153153
u.p.printf("\n// Clear omitted fields.\n")
154-
u.p.printf("if %s {\n", bm.notAllSet())
154+
if bm.bitlen > 1 {
155+
u.p.printf("if %s {\n", bm.notAllSet())
156+
}
155157
for bitIdx, fieldIdx := range oeEmittedIdx {
156158
fieldElem := s.Fields[fieldIdx].FieldElem
157159

@@ -164,7 +166,9 @@ func (u *unmarshalGen) mapstruct(s *Struct) {
164166
}
165167
u.p.printf("}\n")
166168
}
167-
u.p.printf("}")
169+
if bm.bitlen > 1 {
170+
u.p.printf("}")
171+
}
168172
}
169173
}
170174

0 commit comments

Comments
 (0)