summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLuke Shumaker <lukeshu@lukeshu.com>2023-02-16 21:05:24 -0700
committerLuke Shumaker <lukeshu@lukeshu.com>2023-02-16 21:16:32 -0700
commita6cd78ec94f76feba180fa75e942bb5cdeae115f (patch)
treee0b87c21a23a00c6c1245c269fcb157add1cf19b
parentc904fc3e8605ec95c0fac92654d773e8456bf130 (diff)
Move string-encoding to an internal/jsonstring package
-rw-r--r--compat/json/testcompat_test.go12
-rw-r--r--encode.go19
-rw-r--r--encode_escape.go10
-rw-r--r--internal/jsonstring/encode_string.go (renamed from encode_string.go)30
-rw-r--r--reencode.go28
5 files changed, 56 insertions, 43 deletions
diff --git a/compat/json/testcompat_test.go b/compat/json/testcompat_test.go
index 07c75bc..42cbf5c 100644
--- a/compat/json/testcompat_test.go
+++ b/compat/json/testcompat_test.go
@@ -11,8 +11,8 @@ import (
_ "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"
)
@@ -58,20 +58,14 @@ type encodeState struct {
bytes.Buffer
}
-//go:linkname encodeStringFromString git.lukeshu.com/go/lowmemjson.encodeStringFromString
-func encodeStringFromString(w fastio.AllWriter, escaper lowmemjson.BackslashEscaper, str string) error
-
-//go:linkname encodeStringFromBytes git.lukeshu.com/go/lowmemjson.encodeStringFromBytes
-func encodeStringFromBytes(w fastio.AllWriter, escaper lowmemjson.BackslashEscaper, str []byte) error
-
func (es *encodeState) string(str string, _ bool) {
- if err := encodeStringFromString(&es.Buffer, nil, str); err != nil {
+ if err := jsonstring.EncodeStringFromString(&es.Buffer, lowmemjson.EscapeDefault, str); err != nil {
panic(err)
}
}
func (es *encodeState) stringBytes(str []byte, _ bool) {
- if err := encodeStringFromBytes(&es.Buffer, nil, str); err != nil {
+ if err := jsonstring.EncodeStringFromBytes(&es.Buffer, lowmemjson.EscapeDefault, str); err != nil {
panic(err)
}
}
diff --git a/encode.go b/encode.go
index d39c862..ebb4568 100644
--- a/encode.go
+++ b/encode.go
@@ -16,6 +16,7 @@ import (
"strings"
"unsafe"
+ "git.lukeshu.com/go/lowmemjson/internal/jsonstring"
"git.lukeshu.com/go/lowmemjson/internal/jsonstruct"
)
@@ -82,7 +83,11 @@ func (enc *Encoder) Encode(obj any) (err error) {
if enc.isRoot {
enc.w.par.Reset()
}
- if err := encode(enc.w, reflect.ValueOf(obj), enc.w.BackslashEscape, false, 0, map[any]struct{}{}); err != nil {
+ escaper := enc.w.BackslashEscape
+ if escaper == nil {
+ escaper = EscapeDefault
+ }
+ if err := encode(enc.w, reflect.ValueOf(obj), escaper, false, 0, map[any]struct{}{}); err != nil {
if rwe, ok := err.(*ReEncodeWriteError); ok {
err = &EncodeWriteError{
Err: rwe.Err,
@@ -192,7 +197,7 @@ func encode(w *ReEncoder, val reflect.Value, escaper BackslashEscaper, quote boo
Err: err,
}
}
- if err := encodeStringFromBytes(w, escaper, text); err != nil {
+ if err := jsonstring.EncodeStringFromBytes(w, escaper, text); err != nil {
return err
}
default:
@@ -295,14 +300,14 @@ func encode(w *ReEncoder, val reflect.Value, escaper BackslashEscaper, quote boo
} else {
if quote {
var buf bytes.Buffer
- if err := encodeStringFromString(&buf, escaper, val.String()); err != nil {
+ if err := jsonstring.EncodeStringFromString(&buf, escaper, val.String()); err != nil {
return err
}
- if err := encodeStringFromBytes(w, escaper, buf.Bytes()); err != nil {
+ if err := jsonstring.EncodeStringFromBytes(w, escaper, buf.Bytes()); err != nil {
return err
}
} else {
- if err := encodeStringFromString(w, escaper, val.String()); err != nil {
+ if err := jsonstring.EncodeStringFromString(w, escaper, val.String()); err != nil {
return err
}
}
@@ -336,7 +341,7 @@ func encode(w *ReEncoder, val reflect.Value, escaper BackslashEscaper, quote boo
}
}
empty = false
- if err := encodeStringFromString(w, escaper, field.Name); err != nil {
+ if err := jsonstring.EncodeStringFromString(w, escaper, field.Name); err != nil {
return err
}
if err := w.WriteByte(':'); err != nil {
@@ -389,7 +394,7 @@ func encode(w *ReEncoder, val reflect.Value, escaper BackslashEscaper, quote boo
}
if !strings.HasPrefix(kStr, `"`) {
k.Reset()
- if err := encodeStringFromString(&k, escaper, kStr); err != nil {
+ if err := jsonstring.EncodeStringFromString(&k, escaper, kStr); err != nil {
return err
}
kStr = k.String()
diff --git a/encode_escape.go b/encode_escape.go
index ab0d9c1..0054e72 100644
--- a/encode_escape.go
+++ b/encode_escape.go
@@ -6,6 +6,8 @@ package lowmemjson
import (
"unicode/utf8"
+
+ "git.lukeshu.com/go/lowmemjson/internal/jsonstring"
)
// BackslashEscapeMode identifies one of the three ways that a
@@ -17,12 +19,12 @@ import (
// single-character)
//
// - as a long Unicode `\uXXXX` backslash sequence
-type BackslashEscapeMode uint8
+type BackslashEscapeMode = jsonstring.BackslashEscapeMode
const (
- BackslashEscapeNone BackslashEscapeMode = iota
- BackslashEscapeShort
- BackslashEscapeUnicode
+ BackslashEscapeNone = jsonstring.BackslashEscapeNone
+ BackslashEscapeShort = jsonstring.BackslashEscapeShort
+ BackslashEscapeUnicode = jsonstring.BackslashEscapeUnicode
)
// A BackslashEscaper controls how a ReEncoder emits a character in a
diff --git a/encode_string.go b/internal/jsonstring/encode_string.go
index 208aef4..f29dc3f 100644
--- a/encode_string.go
+++ b/internal/jsonstring/encode_string.go
@@ -2,7 +2,7 @@
//
// SPDX-License-Identifier: GPL-2.0-or-later
-package lowmemjson
+package jsonstring
import (
"fmt"
@@ -13,6 +13,19 @@ import (
"git.lukeshu.com/go/lowmemjson/internal/jsonparse"
)
+// BackslashEscapeMode is describe in the main lowmemjson package
+// docs.
+type BackslashEscapeMode uint8
+
+const (
+ BackslashEscapeNone BackslashEscapeMode = iota
+ BackslashEscapeShort
+ BackslashEscapeUnicode
+)
+
+// BackslashEscaper is describe in the main lowmemjson package docs.
+type BackslashEscaper = func(rune, BackslashEscapeMode) BackslashEscapeMode
+
func writeStringUnicodeEscape(w io.Writer, c rune) (int, error) {
buf := [6]byte{
'\\',
@@ -47,11 +60,8 @@ func writeStringShortEscape(w io.Writer, c rune) (int, error) {
return w.Write(buf[:])
}
-func writeStringChar(w fastio.AllWriter, c rune, wasEscaped BackslashEscapeMode, escaper BackslashEscaper) (int, error) {
- if escaper == nil {
- escaper = EscapeDefault
- }
- switch escaper(c, wasEscaped) {
+func WriteStringChar(w fastio.AllWriter, c rune, escape BackslashEscapeMode) (int, error) {
+ switch escape {
case BackslashEscapeNone:
switch {
case c < 0x0020: // override, gotta escape these
@@ -85,12 +95,12 @@ func writeStringChar(w fastio.AllWriter, c rune, wasEscaped BackslashEscapeMode,
}
}
-func encodeStringFromString(w fastio.AllWriter, escaper BackslashEscaper, str string) error {
+func EncodeStringFromString(w fastio.AllWriter, escaper BackslashEscaper, str string) error {
if err := w.WriteByte('"'); err != nil {
return err
}
for _, c := range str {
- if _, err := writeStringChar(w, c, BackslashEscapeNone, escaper); err != nil {
+ if _, err := WriteStringChar(w, c, escaper(c, BackslashEscapeNone)); err != nil {
return err
}
}
@@ -100,13 +110,13 @@ func encodeStringFromString(w fastio.AllWriter, escaper BackslashEscaper, str st
return nil
}
-func encodeStringFromBytes(w fastio.AllWriter, escaper BackslashEscaper, str []byte) error {
+func EncodeStringFromBytes(w fastio.AllWriter, escaper BackslashEscaper, str []byte) error {
if err := w.WriteByte('"'); err != nil {
return err
}
for i := 0; i < len(str); {
c, size := utf8.DecodeRune(str[i:])
- if _, err := writeStringChar(w, c, BackslashEscapeNone, escaper); err != nil {
+ if _, err := WriteStringChar(w, c, escaper(c, BackslashEscapeNone)); err != nil {
return err
}
i += size
diff --git a/reencode.go b/reencode.go
index d19dc1a..f100275 100644
--- a/reencode.go
+++ b/reencode.go
@@ -12,6 +12,7 @@ import (
"git.lukeshu.com/go/lowmemjson/internal/fastio"
"git.lukeshu.com/go/lowmemjson/internal/jsonparse"
+ "git.lukeshu.com/go/lowmemjson/internal/jsonstring"
)
// A ReEncoderConfig controls how a ReEncoder should behave.
@@ -499,34 +500,35 @@ func (enc *ReEncoder) handleRunePre(c rune, t jsonparse.RuneType) (error, bool)
// handleRuneMain handles the new rune itself, not buffered things.
func (enc *ReEncoder) handleRuneMain(c rune, t jsonparse.RuneType) error {
+ escaper := enc.BackslashEscape
+ if escaper == nil {
+ escaper = EscapeDefault
+ }
var err error
switch t {
case jsonparse.RuneTypeStringChar:
- err = enc.emit(writeStringChar(enc.out, c, BackslashEscapeNone, enc.BackslashEscape))
+ err = enc.emit(jsonstring.WriteStringChar(enc.out, c, escaper(c, BackslashEscapeNone)))
case jsonparse.RuneTypeStringEsc, jsonparse.RuneTypeStringEscU:
// do nothing
case jsonparse.RuneTypeStringEsc1:
switch c {
- case '"':
- err = enc.emit(writeStringChar(enc.out, '"', BackslashEscapeShort, enc.BackslashEscape))
- case '\\':
- err = enc.emit(writeStringChar(enc.out, '\\', BackslashEscapeShort, enc.BackslashEscape))
- case '/':
- err = enc.emit(writeStringChar(enc.out, '/', BackslashEscapeShort, enc.BackslashEscape))
+ case '"', '\\', '/':
+ // self
case 'b':
- err = enc.emit(writeStringChar(enc.out, '\b', BackslashEscapeShort, enc.BackslashEscape))
+ c = '\b'
case 'f':
- err = enc.emit(writeStringChar(enc.out, '\f', BackslashEscapeShort, enc.BackslashEscape))
+ c = '\f'
case 'n':
- err = enc.emit(writeStringChar(enc.out, '\n', BackslashEscapeShort, enc.BackslashEscape))
+ c = '\n'
case 'r':
- err = enc.emit(writeStringChar(enc.out, '\r', BackslashEscapeShort, enc.BackslashEscape))
+ c = '\r'
case 't':
- err = enc.emit(writeStringChar(enc.out, '\t', BackslashEscapeShort, enc.BackslashEscape))
+ c = '\t'
default:
panic(fmt.Errorf("should not happen: rune %q is not a RuneTypeStringEsc1", c))
}
+ err = enc.emit(jsonstring.WriteStringChar(enc.out, c, escaper(c, BackslashEscapeShort)))
case jsonparse.RuneTypeStringEscUA:
enc.uhex[0], _ = jsonparse.HexToInt(c)
case jsonparse.RuneTypeStringEscUB:
@@ -540,7 +542,7 @@ func (enc *ReEncoder) handleRuneMain(c rune, t jsonparse.RuneType) error {
rune(enc.uhex[1])<<8 |
rune(enc.uhex[2])<<4 |
rune(enc.uhex[3])<<0
- err = enc.emit(writeStringChar(enc.out, c, BackslashEscapeUnicode, enc.BackslashEscape))
+ err = enc.emit(jsonstring.WriteStringChar(enc.out, c, escaper(c, BackslashEscapeUnicode)))
case jsonparse.RuneTypeError: // EOF explicitly stated by .Close()
fallthrough