Repository: ogen-go/ogen
Pull request #98 (2022-01-02 23:39:50)

feat(json): optimize UUID encode/decode
```
name          old time/op    new time/op    delta
DecodeUUID-4     173ns ± 3%     148ns ±34%   -14.15%  (p=0.001 n=13+15)
EncodeUUID-4     182ns ± 8%      29ns ±18%   -84.11%  (p=0.000 n=13+15)

name          old speed      new speed      delta
DecodeUUID-4   220MB/s ± 3%   261MB/s ±26%   +18.39%  (p=0.001 n=13+15)
EncodeUUID-4   209MB/s ± 8%  1323MB/s ±16%  +532.72%  (p=0.000 n=13+15)

name          old alloc/op   new alloc/op   delta
DecodeUUID-4     48.0B ± 0%      0.0B       -100.00%  (p=0.000 n=15+15)
EncodeUUID-4     48.0B ± 0%      0.0B       -100.00%  (p=0.000 n=15+15)

name          old allocs/op  new allocs/op  delta
DecodeUUID-4      1.00 ± 0%      0.00       -100.00%  (p=0.000 n=15+15)
EncodeUUID-4      1.00 ± 0%      0.00       -100.00%  (p=0.000 n=15+15)
```


Patch:
diff --git a/json/uuid.go b/json/uuid.go
index 9690a4c2..9db56906 100644
--- a/json/uuid.go
+++ b/json/uuid.go
@@ -7,14 +7,59 @@ import (
 
 // DecodeUUID decodes UUID from json.
 func DecodeUUID(i *jx.Decoder) (v uuid.UUID, err error) {
-	s, err := i.Str()
+	s, err := i.StrBytes()
 	if err != nil {
 		return v, err
 	}
-	return uuid.Parse(s)
+	return uuid.ParseBytes(s)
 }
 
 // EncodeUUID encodes UUID to json.
 func EncodeUUID(s *jx.Encoder, v uuid.UUID) {
-	s.Str(v.String())
+	const (
+		// Hexed length (16 * 2) + 4 hyphens
+		length       = len(v)*2 + 4
+		quotedLength = length + 2
+	)
+	var dst = [quotedLength]byte{
+		0:                '"',
+		quotedLength - 1: '"',
+	}
+	hexEncode((*[length]byte)(dst[1:length+1]), v)
+	s.Raw(dst[:])
+}
+
+func hexEncode(dst *[36]byte, v uuid.UUID) {
+	const hextable = "0123456789abcdef"
+
+	{
+		dst[0], dst[1] = hextable[v[0]>>4], hextable[v[0]&0x0f]
+		dst[2], dst[3] = hextable[v[1]>>4], hextable[v[1]&0x0f]
+		dst[4], dst[5] = hextable[v[2]>>4], hextable[v[2]&0x0f]
+		dst[6], dst[7] = hextable[v[3]>>4], hextable[v[3]&0x0f]
+	}
+	dst[8] = '-'
+	{
+		dst[9+0], dst[9+1] = hextable[v[4+0]>>4], hextable[v[4+0]&0x0f]
+		dst[9+2], dst[9+3] = hextable[v[4+1]>>4], hextable[v[4+1]&0x0f]
+	}
+	dst[13] = '-'
+	{
+		dst[14+0], dst[14+1] = hextable[v[6+0]>>4], hextable[v[6+0]&0x0f]
+		dst[14+2], dst[14+3] = hextable[v[6+1]>>4], hextable[v[6+1]&0x0f]
+	}
+	dst[18] = '-'
+	{
+		dst[19+0], dst[19+1] = hextable[v[8+0]>>4], hextable[v[8+0]&0x0f]
+		dst[19+2], dst[19+3] = hextable[v[8+1]>>4], hextable[v[8+1]&0x0f]
+	}
+	dst[23] = '-'
+	{
+		dst[24+0], dst[24+1] = hextable[v[10+0]>>4], hextable[v[10+0]&0x0f]
+		dst[24+2], dst[24+3] = hextable[v[10+1]>>4], hextable[v[10+1]&0x0f]
+		dst[24+4], dst[24+5] = hextable[v[10+2]>>4], hextable[v[10+2]&0x0f]
+		dst[24+6], dst[24+7] = hextable[v[10+3]>>4], hextable[v[10+3]&0x0f]
+		dst[24+8], dst[24+9] = hextable[v[10+4]>>4], hextable[v[10+4]&0x0f]
+		dst[24+10], dst[24+11] = hextable[v[10+5]>>4], hextable[v[10+5]&0x0f]
+	}
 }


Test patch:
diff --git a/json/uuid_test.go b/json/uuid_test.go
new file mode 100644
index 00000000..0f13591c
--- /dev/null
+++ b/json/uuid_test.go
@@ -0,0 +1,68 @@
+package json
+
+import (
+	"testing"
+
+	"github.com/go-faster/jx"
+	"github.com/google/uuid"
+	"github.com/stretchr/testify/require"
+)
+
+func BenchmarkDecodeUUID(b *testing.B) {
+	e := jx.GetEncoder()
+	defer jx.PutEncoder(e)
+
+	u, err := uuid.NewUUID()
+	if err != nil {
+		b.Fatal(err)
+	}
+	EncodeUUID(e, u)
+	data := e.Bytes()
+
+	d := jx.GetDecoder()
+	defer jx.PutDecoder(d)
+
+	b.ReportAllocs()
+	b.SetBytes(int64(len(data)))
+	b.ResetTimer()
+
+	for i := 0; i < b.N; i++ {
+		d.ResetBytes(data)
+		if _, err := DecodeUUID(d); err != nil {
+			b.Fatal(err)
+		}
+	}
+}
+
+func BenchmarkEncodeUUID(b *testing.B) {
+	e := jx.GetEncoder()
+	defer jx.PutEncoder(e)
+
+	u, err := uuid.NewUUID()
+	if err != nil {
+		b.Fatal(err)
+	}
+
+	EncodeUUID(e, u)
+	data := e.Bytes()
+
+	b.ReportAllocs()
+	b.SetBytes(int64(len(data)))
+	b.ResetTimer()
+
+	for i := 0; i < b.N; i++ {
+		e.Reset()
+		EncodeUUID(e, u)
+	}
+}
+
+func Test_hexEncode(t *testing.T) {
+	a := require.New(t)
+
+	u, err := uuid.NewUUID()
+	a.NoError(err)
+
+	var dst [36]byte
+	hexEncode(&dst, u)
+	a.Equal(u.String(), string(dst[:]))
+}