Skip to content

Commit 3019618

Browse files
authored
Merge pull request #50 from Basekick-Labs/perf/isempty-cache-iszeroer
perf(encode): cache isZeroer check to skip interface boxing
2 parents 54f8276 + 19c85e5 commit 3019618

1 file changed

Lines changed: 22 additions & 11 deletions

File tree

types.go

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -90,11 +90,12 @@ func (m *structCache) Fields(typ reflect.Type, tag string) *fields {
9090
//------------------------------------------------------------------------------
9191

9292
type field struct {
93-
encoder encoderFunc
94-
decoder decoderFunc
95-
name string
96-
index []int
97-
omitEmpty bool
93+
encoder encoderFunc
94+
decoder decoderFunc
95+
name string
96+
index []int
97+
omitEmpty bool
98+
maybeZeroer bool // true when the field type implements isZeroer
9899
}
99100

100101
func (f *field) Omit(e *Encoder, strct reflect.Value) bool {
@@ -103,7 +104,7 @@ func (f *field) Omit(e *Encoder, strct reflect.Value) bool {
103104
return true
104105
}
105106
forced := e.flags&omitEmptyFlag != 0
106-
return (f.omitEmpty || forced) && e.isEmptyValue(v)
107+
return (f.omitEmpty || forced) && e.isEmptyValueHint(v, f.maybeZeroer)
107108
}
108109

109110
func (f *field) EncodeValue(e *Encoder, strct reflect.Value) error {
@@ -211,9 +212,10 @@ func getFields(typ reflect.Type, fallbackTag string) *fields {
211212
}
212213

213214
field := &field{
214-
name: tag.Name,
215-
index: f.Index,
216-
omitEmpty: omitEmpty || tag.HasOption("omitempty"),
215+
name: tag.Name,
216+
index: f.Index,
217+
omitEmpty: omitEmpty || tag.HasOption("omitempty"),
218+
maybeZeroer: f.Type.Implements(isZeroerType) || reflect.PtrTo(f.Type).Implements(isZeroerType),
217219
}
218220

219221
if tag.HasOption("intern") {
@@ -331,7 +333,13 @@ type isZeroer interface {
331333
IsZero() bool
332334
}
333335

336+
var isZeroerType = reflect.TypeOf((*isZeroer)(nil)).Elem()
337+
334338
func (e *Encoder) isEmptyValue(v reflect.Value) bool {
339+
return e.isEmptyValueHint(v, true)
340+
}
341+
342+
func (e *Encoder) isEmptyValueHint(v reflect.Value, maybeZeroer bool) bool {
335343
kind := v.Kind()
336344

337345
for kind == reflect.Interface {
@@ -340,10 +348,13 @@ func (e *Encoder) isEmptyValue(v reflect.Value) bool {
340348
}
341349
v = v.Elem()
342350
kind = v.Kind()
351+
maybeZeroer = true // dynamic type — must check at runtime
343352
}
344353

345-
if z, ok := v.Interface().(isZeroer); ok {
346-
return nilable(kind) && v.IsNil() || z.IsZero()
354+
if maybeZeroer {
355+
if z, ok := v.Interface().(isZeroer); ok {
356+
return nilable(kind) && v.IsNil() || z.IsZero()
357+
}
347358
}
348359

349360
switch kind {

0 commit comments

Comments
 (0)