Skip to content

Commit

Permalink
Further optimize RegionMap.MarshalJSON().
Browse files Browse the repository at this point in the history
- Stop calling the encoder for values as well.
- Use tests to ensure that regions always contain valid utf8.

Allocations are now down 80% in FormatHandler.
  • Loading branch information
bojanz committed Jan 29, 2023
1 parent 8ee9fb4 commit 1b71016
Show file tree
Hide file tree
Showing 2 changed files with 39 additions and 8 deletions.
14 changes: 6 additions & 8 deletions address.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ package address

import (
"bytes"
"encoding/json"
"fmt"
"regexp"
"sort"
Expand Down Expand Up @@ -172,17 +171,16 @@ func (r RegionMap) MarshalJSON() ([]byte, error) {
buf := &bytes.Buffer{}
buf.Grow(r.Len() * 30)
buf.WriteByte('{')
encoder := json.NewEncoder(buf)
for i, key := range r.keys {
if i > 0 {
buf.WriteByte(',')
}
// The key is assumed to be safe and need no escaping.
// Not calling encoder.Encode on the key saves many allocations.
buf.WriteString(`"` + key + `":`)
if err := encoder.Encode(r.values[key]); err != nil {
return nil, err
}
// A fully generic MarshalJSON would call encoder.Encode() to ensure both key and value
// are escaped and contain valid utf8. Since all regions are defined in the package, they
// are assumed to be valid, avoiding thousands of allocs when marshalling the format list.
buf.WriteString(`"` + key + `":"`)
buf.WriteString(r.values[key])
buf.WriteString(`"`)
}
buf.WriteByte('}')

Expand Down
33 changes: 33 additions & 0 deletions address_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"encoding/json"
"reflect"
"testing"
"unicode/utf8"

"github.com/bojanz/address"
)
Expand Down Expand Up @@ -339,6 +340,38 @@ func TestGetFormats(t *testing.T) {
}
}

func TestGetFormats_ValidRegionData(t *testing.T) {
// Confirm that all regions contain valid utf8.
// Avoids having the check at runtime, in RegionMap.MarshalJSON.
formats := address.GetFormats()
for countryCode, format := range formats {
if format.Regions.Len() > 0 {
keys := format.Regions.Keys()
for _, key := range keys {
value, _ := format.Regions.Get(key)
if !utf8.ValidString(key) {
t.Errorf("invalid key %v in %v regions", key, countryCode)
}
if !utf8.ValidString(value) {
t.Errorf("invalid value %v for key %v in %v regions", value, key, countryCode)
}
}
}
if format.LocalRegions.Len() > 0 {
keys := format.LocalRegions.Keys()
for _, key := range keys {
value, _ := format.LocalRegions.Get(key)
if !utf8.ValidString(key) {
t.Errorf("invalid key %v in %v local regions", key, countryCode)
}
if !utf8.ValidString(value) {
t.Errorf("invalid value %v for key %v in %v regions", value, key, countryCode)
}
}
}
}
}

func contains(a []string, x string) bool {
for _, v := range a {
if v == x {
Expand Down

0 comments on commit 1b71016

Please sign in to comment.