summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLuke Shumaker <lukeshu@lukeshu.com>2023-02-16 22:56:11 -0700
committerLuke Shumaker <lukeshu@lukeshu.com>2023-02-16 22:56:11 -0700
commit6f8e7db1ac5ddd21b8e3fcc39a1e30fde9b62c3a (patch)
tree8f0247b646291577f54f7d164b7fed675388c72c
parentdebef01cc500fb9368e1d6d0206a32ca358a8c98 (diff)
parent0d23080e1f2af81d8d4656c3b72791b26f52f361 (diff)
Merge branch 'lukeshu/perf'
-rw-r--r--borrowed_misc.go4
-rw-r--r--compat/json/compat.go3
-rw-r--r--compat/json/testcompat_test.go4
-rw-r--r--encode.go13
-rw-r--r--internal/base64dec/base64.go3
-rw-r--r--internal/fastio/allwriter.go8
-rw-r--r--internal/fastio/noescape/noescape.go30
-rw-r--r--internal/jsonstring/encode_string.go5
-rw-r--r--reencode.go12
9 files changed, 64 insertions, 18 deletions
diff --git a/borrowed_misc.go b/borrowed_misc.go
index 52f4a12..e4cda6d 100644
--- a/borrowed_misc.go
+++ b/borrowed_misc.go
@@ -11,6 +11,8 @@ import (
"math"
"reflect"
"strconv"
+
+ "git.lukeshu.com/go/lowmemjson/internal/fastio/noescape"
)
// isEmptyValue is borrowed from encode.go.
@@ -66,7 +68,7 @@ func encodeFloat(w io.Writer, bits int, v reflect.Value) error {
}
}
- if _, err := w.Write(b); err != nil {
+ if _, err := noescape.Write(w, b); err != nil {
return err
}
return nil
diff --git a/compat/json/compat.go b/compat/json/compat.go
index c96470d..0b86732 100644
--- a/compat/json/compat.go
+++ b/compat/json/compat.go
@@ -15,6 +15,7 @@ import (
"strconv"
"git.lukeshu.com/go/lowmemjson"
+ "git.lukeshu.com/go/lowmemjson/internal/fastio"
)
//nolint:stylecheck // ST1021 False positive; these aren't comments on individual types.
@@ -172,7 +173,7 @@ func Indent(dst *bytes.Buffer, src []byte, prefix, indent string) error {
}
func Valid(data []byte) bool {
- formatter := lowmemjson.NewReEncoder(io.Discard, lowmemjson.ReEncoderConfig{
+ formatter := lowmemjson.NewReEncoder(fastio.Discard, lowmemjson.ReEncoderConfig{
Compact: true,
})
_, err := formatter.Write(data)
diff --git a/compat/json/testcompat_test.go b/compat/json/testcompat_test.go
index 42cbf5c..c186678 100644
--- a/compat/json/testcompat_test.go
+++ b/compat/json/testcompat_test.go
@@ -7,10 +7,10 @@ package json
import (
"bytes"
"encoding/json"
- "io"
_ "unsafe"
"git.lukeshu.com/go/lowmemjson"
+ "git.lukeshu.com/go/lowmemjson/internal/fastio"
"git.lukeshu.com/go/lowmemjson/internal/jsonparse"
"git.lukeshu.com/go/lowmemjson/internal/jsonstring"
"git.lukeshu.com/go/lowmemjson/internal/jsonstruct"
@@ -25,7 +25,7 @@ var (
type scanner = lowmemjson.ReEncoderConfig
func checkValid(in []byte, scan *lowmemjson.ReEncoderConfig) error {
- return reencode(io.Discard, in, *scan)
+ return reencode(fastio.Discard, in, *scan)
}
func isValidNumber(s string) bool {
diff --git a/encode.go b/encode.go
index 2e10134..38a2e93 100644
--- a/encode.go
+++ b/encode.go
@@ -228,7 +228,12 @@ func encode(w *ReEncoder, val reflect.Value, escaper BackslashEscaper, quote boo
return err
}
}
- if _, err := w.WriteString(strconv.FormatInt(val.Int(), 10)); err != nil {
+ // MaxInt64 = 9223372036854775807
+ // MinInt64 = -9223372036854775808
+ // 0 1 2
+ // 12345678901234567890
+ var buf [20]byte
+ if _, err := w.Write(strconv.AppendInt(buf[:0], val.Int(), 10)); err != nil {
return err
}
if quote {
@@ -242,7 +247,11 @@ func encode(w *ReEncoder, val reflect.Value, escaper BackslashEscaper, quote boo
return err
}
}
- if _, err := w.WriteString(strconv.FormatUint(val.Uint(), 10)); err != nil {
+ // MaxUint64 = 18446744073709551615
+ // 0 1 2
+ // 12345678901234567890
+ var buf [20]byte
+ if _, err := w.Write(strconv.AppendUint(buf[:0], val.Uint(), 10)); err != nil {
return err
}
if quote {
diff --git a/internal/base64dec/base64.go b/internal/base64dec/base64.go
index dcb4b1c..0d278ad 100644
--- a/internal/base64dec/base64.go
+++ b/internal/base64dec/base64.go
@@ -10,6 +10,7 @@ import (
"strings"
"git.lukeshu.com/go/lowmemjson/internal/fastio"
+ "git.lukeshu.com/go/lowmemjson/internal/fastio/noescape"
)
type base64Decoder struct {
@@ -84,7 +85,7 @@ func (dec *base64Decoder) decodeTuple(a, b, c, d byte) error {
decoded[1] = byte(val >> 8)
decoded[2] = byte(val >> 0)
- _, err := dec.dst.Write(decoded[:decodedLen])
+ _, err := noescape.Write(dec.dst, decoded[:decodedLen])
return err
}
diff --git a/internal/fastio/allwriter.go b/internal/fastio/allwriter.go
index 9de8fdc..c587531 100644
--- a/internal/fastio/allwriter.go
+++ b/internal/fastio/allwriter.go
@@ -7,6 +7,8 @@ package fastio
import (
"io"
"unicode/utf8"
+
+ "git.lukeshu.com/go/lowmemjson/internal/fastio/noescape"
)
// interfaces /////////////////////////////////////////////////////////////////
@@ -28,18 +30,18 @@ type AllWriter interface {
func WriteByte(w io.Writer, b byte) error {
var buf [1]byte
buf[0] = b
- _, err := w.Write(buf[:])
+ _, err := noescape.Write(w, buf[:])
return err
}
func WriteRune(w io.Writer, r rune) (int, error) {
var buf [utf8.UTFMax]byte
n := utf8.EncodeRune(buf[:], r)
- return w.Write(buf[:n])
+ return noescape.Write(w, buf[:n])
}
func WriteString(w io.Writer, s string) (int, error) {
- return w.Write([]byte(s))
+ return noescape.Write(w, []byte(s))
}
// wrappers ///////////////////////////////////////////////////////////////////
diff --git a/internal/fastio/noescape/noescape.go b/internal/fastio/noescape/noescape.go
new file mode 100644
index 0000000..02d25b5
--- /dev/null
+++ b/internal/fastio/noescape/noescape.go
@@ -0,0 +1,30 @@
+// Copyright (C) 2023 Luke Shumaker <lukeshu@lukeshu.com>
+//
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+package noescape
+
+import (
+ "io"
+ _ "unsafe"
+)
+
+//go:noescape
+//go:linkname Write io.Writer.Write
+func Write(w io.Writer, dat []byte) (int, error)
+
+//go:noescape
+//go:linkname WriteString io.StringWriter.WriteString
+func WriteString(w io.Writer, dat string) (int, error)
+
+//go:noescape
+//go:linkname WriteAt io.WriterAt.WriteAt
+func WriteAt(w io.WriterAt, dat []byte, off int64) (int, error)
+
+//go:noescape
+//go:linkname Read io.Reader.Read
+func Read(w io.Reader, dat []byte) (int, error)
+
+//go:noescape
+//go:linkname ReadAt io.ReaderAt.ReadAt
+func ReadAt(w io.WriterAt, dat []byte, off int64) (int, error)
diff --git a/internal/jsonstring/encode_string.go b/internal/jsonstring/encode_string.go
index 1b0c68a..fec2cc0 100644
--- a/internal/jsonstring/encode_string.go
+++ b/internal/jsonstring/encode_string.go
@@ -10,6 +10,7 @@ import (
"unicode/utf8"
"git.lukeshu.com/go/lowmemjson/internal/fastio"
+ "git.lukeshu.com/go/lowmemjson/internal/fastio/noescape"
)
// BackslashEscapeMode is describe in the main lowmemjson package
@@ -35,7 +36,7 @@ func writeStringUnicodeEscape(w io.Writer, c rune) error {
alphabet[(c>>4)&0xf],
alphabet[(c>>0)&0xf],
}
- _, err := w.Write(buf[:])
+ _, err := noescape.Write(w, buf[:])
return err
}
@@ -58,7 +59,7 @@ func writeStringShortEscape(w io.Writer, c rune) error {
panic(fmt.Errorf("should not happen: writeStringShortEscape called with invalid rune: %q", c))
}
buf := [2]byte{'\\', b}
- _, err := w.Write(buf[:])
+ _, err := noescape.Write(w, buf[:])
return err
}
diff --git a/reencode.go b/reencode.go
index f18888c..d8cdb71 100644
--- a/reencode.go
+++ b/reencode.go
@@ -165,14 +165,14 @@ func (enc *ReEncoder) Write(p []byte) (int, error) {
c, size := utf8.DecodeRune(enc.buf[:])
n += size - enc.bufLen
enc.bufLen = 0
- enc.handleRune(c)
+ enc.handleRune(c, size)
if enc.err != nil {
return 0, enc.err
}
}
for utf8.FullRune(p[n:]) {
c, size := utf8.DecodeRune(p[n:])
- enc.handleRune(c)
+ enc.handleRune(c, size)
if enc.err != nil {
return n, enc.err
}
@@ -194,14 +194,14 @@ func (enc *ReEncoder) WriteString(p string) (int, error) {
c, size := utf8.DecodeRune(enc.buf[:])
n += size - enc.bufLen
enc.bufLen = 0
- enc.handleRune(c)
+ enc.handleRune(c, size)
if enc.err != nil {
return 0, enc.err
}
}
for utf8.FullRuneInString(p[n:]) {
c, size := utf8.DecodeRuneInString(p[n:])
- enc.handleRune(c)
+ enc.handleRune(c, size)
if enc.err != nil {
return n, enc.err
}
@@ -253,7 +253,7 @@ func (enc *ReEncoder) Close() error {
return nil
}
-func (enc *ReEncoder) handleRune(c rune) {
+func (enc *ReEncoder) handleRune(c rune, size int) {
rehandle:
t, err := enc.par.HandleRune(c)
if err != nil {
@@ -283,7 +283,7 @@ rehandle:
}
}
- enc.inputPos += int64(utf8.RuneLen(c))
+ enc.inputPos += int64(size)
}
// semi-public API /////////////////////////////////////////////////////////////