Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ You can get the fastest performance with [msgpackgen](https://github.com/shamato
* Supported types : primitive / array / slice / struct / map / interface{} and time.Time
* Renaming fields via `msgpack:"field_name"`
* Omitting fields via `msgpack:"-"`
* Omitting empty fields via msgpack:"field_name,omitempty"
* Supports extend encoder / decoder
* Can also Encoding / Decoding struct as array

Expand Down
37 changes: 28 additions & 9 deletions internal/common/common.go
Original file line number Diff line number Diff line change
@@ -1,23 +1,42 @@
package common

import "reflect"
import (
"reflect"
"strings"
)

// Common is used encoding/decoding
type Common struct {
}

// CheckField returns flag whether should encode/decode or not and field name
func (c *Common) CheckField(field reflect.StructField) (bool, string) {
func (c *Common) CheckField(field reflect.StructField) (public, omit bool, name string) {
// A to Z
if c.isPublic(field.Name) {
if tag := field.Tag.Get("msgpack"); tag == "-" {
return false, ""
} else if len(tag) > 0 {
return true, tag
if !c.isPublic(field.Name) {
return false, false, ""
}
tag := field.Tag.Get("msgpack")
if tag == "" {
return true, false, field.Name
}

parts := strings.Split(tag, ",")
// check ignore
if parts[0] == "-" {
return false, false, ""
}
// check omitempty
for _, part := range parts[1:] {
if part == "omitempty" {
omit = true
}
return true, field.Name
}
return false, ""
// check name
name = field.Name
if parts[0] != "" {
name = parts[0]
}
return true, omit, name
}

func (c *Common) isPublic(name string) bool {
Expand Down
50 changes: 42 additions & 8 deletions internal/common/common_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,34 +15,68 @@ func TestCommon_CheckField(t *testing.T) {
Name: "A",
Tag: `msgpack:"-"`,
}
b, v := common.CheckField(field)
tu.Equal(t, b, false)
public, omit, v := common.CheckField(field)
tu.Equal(t, public, false)
tu.Equal(t, omit, false)
tu.Equal(t, v, "")
})
t.Run("tag:,omitempty", func(t *testing.T) {
field := reflect.StructField{
Name: "A",
Tag: `msgpack:",omitempty"`,
}
public, omit, v := common.CheckField(field)
tu.Equal(t, public, true)
tu.Equal(t, omit, true)
tu.Equal(t, v, "A")
})
t.Run("tag:-,omitempty", func(t *testing.T) {
field := reflect.StructField{
Name: "A",
Tag: `msgpack:"-,omitempty"`,
}
public, omit, v := common.CheckField(field)
tu.Equal(t, public, false)
tu.Equal(t, omit, false)
tu.Equal(t, v, "")
})
t.Run("tag:B", func(t *testing.T) {
field := reflect.StructField{
Name: "A",
Tag: `msgpack:"B"`,
}
b, v := common.CheckField(field)
tu.Equal(t, b, true)
public, omit, v := common.CheckField(field)
tu.Equal(t, public, true)
tu.Equal(t, omit, false)
tu.Equal(t, v, "B")
})
t.Run("tag:B,omitempty", func(t *testing.T) {
field := reflect.StructField{
Name: "A",
Tag: `msgpack:"B,omitempty"`,
}
public, omit, v := common.CheckField(field)
tu.Equal(t, public, true)
tu.Equal(t, omit, true)
tu.Equal(t, v, "B")
})
t.Run("name:A", func(t *testing.T) {
field := reflect.StructField{
Name: "A",
Tag: `msgpack:""`,
}
b, v := common.CheckField(field)
tu.Equal(t, b, true)
public, omit, v := common.CheckField(field)
tu.Equal(t, public, true)
tu.Equal(t, omit, false)
tu.Equal(t, v, "A")
})
t.Run("private", func(t *testing.T) {
field := reflect.StructField{
Name: "a",
}
b, v := common.CheckField(field)
tu.Equal(t, b, false)
public, omit, v := common.CheckField(field)
tu.Equal(t, public, false)
tu.Equal(t, omit, false)
tu.Equal(t, v, "")
})
}
4 changes: 2 additions & 2 deletions internal/decoding/struct.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ func (d *decoder) setStructFromArray(rv reflect.Value, offset int, k reflect.Kin
if !findCache {
scta = &structCacheTypeArray{}
for i := 0; i < rv.NumField(); i++ {
if ok, _ := d.CheckField(rv.Type().Field(i)); ok {
if ok, _, _ := d.CheckField(rv.Type().Field(i)); ok {
scta.m = append(scta.m, i)
}
}
Expand Down Expand Up @@ -112,7 +112,7 @@ func (d *decoder) setStructFromMap(rv reflect.Value, offset int, k reflect.Kind)
if !cacheFind {
sctm = &structCacheTypeMap{}
for i := 0; i < rv.NumField(); i++ {
if ok, name := d.CheckField(rv.Type().Field(i)); ok {
if ok, _, name := d.CheckField(rv.Type().Field(i)); ok {
sctm.keys = append(sctm.keys, []byte(name))
sctm.indexes = append(sctm.indexes, i)
}
Expand Down
99 changes: 78 additions & 21 deletions internal/encoding/struct.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import (
type structCache struct {
indexes []int
names []string
omits []bool
noOmit bool
common.Common
}

Expand Down Expand Up @@ -60,19 +62,30 @@ func (e *encoder) calcStructArray(rv reflect.Value) (int, error) {
cache, find := cachemap.Load(t)
var c *structCache
if !find {
c = &structCache{}
for i := 0; i < rv.NumField(); i++ {
num := rv.NumField()
c = &structCache{
indexes: make([]int, 0, num),
names: make([]string, 0, num),
omits: make([]bool, 0, num),
}
omitCount := 0
for i := 0; i < num; i++ {
field := t.Field(i)
if ok, name := e.CheckField(field); ok {
if ok, omit, name := e.CheckField(field); ok {
size, err := e.calcSize(rv.Field(i))
if err != nil {
return 0, err
}
ret += size
c.indexes = append(c.indexes, i)
c.names = append(c.names, name)
c.omits = append(c.omits, omit)
if omit {
omitCount++
}
}
}
c.noOmit = omitCount == 0
cachemap.Store(t, c)
} else {
c = cache.(*structCache)
Expand Down Expand Up @@ -105,35 +118,50 @@ func (e *encoder) calcStructMap(rv reflect.Value) (int, error) {
t := rv.Type()
cache, find := cachemap.Load(t)
var c *structCache
var l int
if !find {
c = &structCache{}
for i := 0; i < rv.NumField(); i++ {
if ok, name := e.CheckField(rv.Type().Field(i)); ok {
keySize := def.Byte1 + e.calcString(name)
valueSize, err := e.calcSize(rv.Field(i))
num := rv.NumField()
c = &structCache{
indexes: make([]int, 0, num),
names: make([]string, 0, num),
omits: make([]bool, 0, num),
}
omitCount := 0
for i := 0; i < num; i++ {
if ok, omit, name := e.CheckField(rv.Type().Field(i)); ok {
size, err := e.calcSizeWithOmitEmpty(rv.Field(i), name, omit)
if err != nil {
return 0, err
}
ret += keySize + valueSize
ret += size
c.indexes = append(c.indexes, i)
c.names = append(c.names, name)
c.omits = append(c.omits, omit)
if omit {
omitCount++
}
if size > 0 {
l++
}
}
}
c.noOmit = omitCount == 0
cachemap.Store(t, c)
} else {
c = cache.(*structCache)
for i := 0; i < len(c.indexes); i++ {
keySize := def.Byte1 + e.calcString(c.names[i])
valueSize, err := e.calcSize(rv.Field(c.indexes[i]))
size, err := e.calcSizeWithOmitEmpty(rv.Field(c.indexes[i]), c.names[i], c.omits[i])
if err != nil {
return 0, err
}
ret += keySize + valueSize
ret += size
if size > 0 {
l++
}
}
}

// format size
l := len(c.indexes)
if l <= 0x0f {
// format code only
} else if l <= math.MaxUint16 {
Expand All @@ -147,6 +175,20 @@ func (e *encoder) calcStructMap(rv reflect.Value) (int, error) {
return ret, nil
}

func (e *encoder) calcSizeWithOmitEmpty(rv reflect.Value, name string, omit bool) (int, error) {
keySize := 0
valueSize := 0
if !omit || !rv.IsZero() {
keySize = def.Byte1 + e.calcString(name)
vSize, err := e.calcSize(rv)
if err != nil {
return 0, err
}
valueSize = vSize
}
return keySize + valueSize, nil
}

func (e *encoder) getStructWriter(typ reflect.Type) structWriteFunc {

for i := range extCoders {
Expand Down Expand Up @@ -212,19 +254,34 @@ func (e *encoder) writeStructMap(rv reflect.Value, offset int) int {

// format size
num := len(c.indexes)
if num <= 0x0f {
offset = e.setByte1Int(def.FixMap+num, offset)
} else if num <= math.MaxUint16 {
l := 0
if c.noOmit {
l = num
} else {
for i := 0; i < num; i++ {
irv := rv.Field(c.indexes[i])
if !c.omits[i] || !irv.IsZero() {
l++
}
}
}

if l <= 0x0f {
offset = e.setByte1Int(def.FixMap+l, offset)
} else if l <= math.MaxUint16 {
offset = e.setByte1Int(def.Map16, offset)
offset = e.setByte2Int(num, offset)
} else if uint(num) <= math.MaxUint32 {
offset = e.setByte2Int(l, offset)
} else if uint(l) <= math.MaxUint32 {
offset = e.setByte1Int(def.Map32, offset)
offset = e.setByte4Int(num, offset)
offset = e.setByte4Int(l, offset)
}

for i := 0; i < num; i++ {
offset = e.writeString(c.names[i], offset)
offset = e.create(rv.Field(c.indexes[i]), offset)
irv := rv.Field(c.indexes[i])
if !c.omits[i] || !irv.IsZero() {
offset = e.writeString(c.names[i], offset)
offset = e.create(irv, offset)
}
}
return offset
}
Loading
Loading