Skip to content

Commit

Permalink
Optimizing marshaling (#1038)
Browse files Browse the repository at this point in the history
  • Loading branch information
xibz committed Jan 16, 2017
1 parent 5e1afe1 commit 28294d4
Showing 1 changed file with 35 additions and 19 deletions.
54 changes: 35 additions & 19 deletions private/protocol/json/jsonutil/build.go
Expand Up @@ -25,6 +25,7 @@ func BuildJSON(v interface{}) ([]byte, error) {
}

func buildAny(value reflect.Value, buf *bytes.Buffer, tag reflect.StructTag) error {
origVal := value
value = reflect.Indirect(value)
if !value.IsValid() {
return nil
Expand Down Expand Up @@ -61,7 +62,7 @@ func buildAny(value reflect.Value, buf *bytes.Buffer, tag reflect.StructTag) err
case "map":
return buildMap(value, buf, tag)
default:
return buildScalar(value, buf, tag)
return buildScalar(origVal, buf, tag)
}
}

Expand All @@ -87,6 +88,10 @@ func buildStruct(value reflect.Value, buf *bytes.Buffer, tag reflect.StructTag)
first := true
for i := 0; i < t.NumField(); i++ {
member := value.Field(i)

// This allocates the most memory.
// Additionally, we cannot skip nil fields due to
// idempotency auto filling.
field := t.Field(i)

if field.PkgPath != "" {
Expand Down Expand Up @@ -182,21 +187,28 @@ func buildMap(value reflect.Value, buf *bytes.Buffer, tag reflect.StructTag) err
return nil
}

func buildScalar(value reflect.Value, buf *bytes.Buffer, tag reflect.StructTag) error {
switch value.Kind() {
func buildScalar(v reflect.Value, buf *bytes.Buffer, tag reflect.StructTag) error {
// prevents allocation on the heap.
scratch := [64]byte{}
switch value := reflect.Indirect(v); value.Kind() {
case reflect.String:
writeString(value.String(), buf)
case reflect.Bool:
buf.WriteString(strconv.FormatBool(value.Bool()))
if value.Bool() {
buf.WriteString("true")
} else {
buf.WriteString("false")
}
case reflect.Int64:
buf.WriteString(strconv.FormatInt(value.Int(), 10))
buf.Write(strconv.AppendInt(scratch[:0], value.Int(), 10))
case reflect.Float64:
buf.WriteString(strconv.FormatFloat(value.Float(), 'f', -1, 64))
buf.Write(strconv.AppendFloat(scratch[:0], value.Float(), 'f', -1, 64))
default:
switch value.Type() {
case timeType:
converted := value.Interface().(time.Time)
buf.WriteString(strconv.FormatInt(converted.UTC().Unix(), 10))
converted := v.Interface().(*time.Time)

buf.Write(strconv.AppendInt(scratch[:0], converted.UTC().Unix(), 10))
case byteSliceType:
if !value.IsNil() {
converted := value.Interface().([]byte)
Expand All @@ -222,27 +234,31 @@ func buildScalar(value reflect.Value, buf *bytes.Buffer, tag reflect.StructTag)
return nil
}

var hex = "0123456789abcdef"

func writeString(s string, buf *bytes.Buffer) {
buf.WriteByte('"')
for _, r := range s {
if r == '"' {
for i := 0; i < len(s); i++ {
if s[i] == '"' {
buf.WriteString(`\"`)
} else if r == '\\' {
} else if s[i] == '\\' {
buf.WriteString(`\\`)
} else if r == '\b' {
} else if s[i] == '\b' {
buf.WriteString(`\b`)
} else if r == '\f' {
} else if s[i] == '\f' {
buf.WriteString(`\f`)
} else if r == '\r' {
} else if s[i] == '\r' {
buf.WriteString(`\r`)
} else if r == '\t' {
} else if s[i] == '\t' {
buf.WriteString(`\t`)
} else if r == '\n' {
} else if s[i] == '\n' {
buf.WriteString(`\n`)
} else if r < 32 {
fmt.Fprintf(buf, "\\u%0.4x", r)
} else if s[i] < 32 {
buf.WriteString("\\u00")
buf.WriteByte(hex[s[i]>>4])
buf.WriteByte(hex[s[i]&0xF])
} else {
buf.WriteRune(r)
buf.WriteByte(s[i])
}
}
buf.WriteByte('"')
Expand Down

0 comments on commit 28294d4

Please sign in to comment.