summaryrefslogtreecommitdiff
path: root/internal/jsonstring/encode_string.go
diff options
context:
space:
mode:
Diffstat (limited to 'internal/jsonstring/encode_string.go')
-rw-r--r--internal/jsonstring/encode_string.go60
1 files changed, 44 insertions, 16 deletions
diff --git a/internal/jsonstring/encode_string.go b/internal/jsonstring/encode_string.go
index 2488cb2..1416b3e 100644
--- a/internal/jsonstring/encode_string.go
+++ b/internal/jsonstring/encode_string.go
@@ -31,22 +31,49 @@ type BackslashEscapeMode uint8
const (
BackslashEscapeNone BackslashEscapeMode = iota
BackslashEscapeShort
- BackslashEscapeUnicode
BackslashEscapeRawByte
+
+ // It is significant to the implementation that if X=binary-0
+ // and x=binary-1, then these "BackslashEscapeUnicode"
+ // constants are counting in-order from 0 to 15.
+
+ BackslashEscapeUnicodeXXXX
+ BackslashEscapeUnicodeXXXx
+ BackslashEscapeUnicodeXXxX
+ BackslashEscapeUnicodeXXxx
+ BackslashEscapeUnicodeXxXX
+ BackslashEscapeUnicodeXxXx
+ BackslashEscapeUnicodeXxxX
+ BackslashEscapeUnicodeXxxx
+ BackslashEscapeUnicodexXXX
+ BackslashEscapeUnicodexXXx
+ BackslashEscapeUnicodexXxX
+ BackslashEscapeUnicodexXxx
+ BackslashEscapeUnicodexxXX
+ BackslashEscapeUnicodexxXx
+ BackslashEscapeUnicodexxxX
+ BackslashEscapeUnicodexxxx
+
+ BackslashEscapeUnicodeMin = BackslashEscapeUnicodeXXXX
+ BackslashEscapeUnicodeMax = BackslashEscapeUnicodexxxx
+
+ BackslashEscapeUnicode = BackslashEscapeUnicodexxxx // back-compat
)
// BackslashEscaper is describe in the main lowmemjson package docs.
type BackslashEscaper = func(rune, BackslashEscapeMode) BackslashEscapeMode
-func WriteStringUnicodeEscape(w io.Writer, c rune) error {
- const alphabet = "0123456789abcdef"
+func WriteStringUnicodeEscape(w io.Writer, c rune, mode BackslashEscapeMode) error {
+ const alphabet = "0123456789ABCDEF"
+ _mode := byte(mode - BackslashEscapeUnicodeMin)
buf := [6]byte{
'\\',
'u',
- alphabet[(c>>12)&0xf],
- alphabet[(c>>8)&0xf],
- alphabet[(c>>4)&0xf],
- alphabet[(c>>0)&0xf],
+ // The 0b0010_0000 bit is the ASCII "lowercase bit".
+ alphabet[(c>>12)&0xf] | ((_mode << 2) & 0b0010_0000),
+ alphabet[(c>>8)&0xf] | ((_mode << 3) & 0b0010_0000),
+ alphabet[(c>>4)&0xf] | ((_mode << 4) & 0b0010_0000),
+ alphabet[(c>>0)&0xf] | ((_mode << 5) & 0b0010_0000),
}
_, err := noescape.Write(w, buf[:])
return err
@@ -84,7 +111,7 @@ func WriteStringChar(w fastio.AllWriter, c rune, escape BackslashEscapeMode) err
case '\b', '\f', '\n', '\r', '\t': // short-escape if possible
return writeStringShortEscape(w, c)
default:
- return WriteStringUnicodeEscape(w, c)
+ return WriteStringUnicodeEscape(w, c, BackslashEscapeUnicode)
}
case c == '"' || c == '\\': // override, gotta escape these
return writeStringShortEscape(w, c)
@@ -100,14 +127,6 @@ func WriteStringChar(w fastio.AllWriter, c rune, escape BackslashEscapeMode) err
_, err := w.WriteRune(c)
return err
}
- case BackslashEscapeUnicode:
- switch {
- case c > 0xFFFF: // override, can't escape these (TODO: unless we use UTF-16 surrogates?)
- _, err := w.WriteRune(c)
- return err
- default: // obey
- return WriteStringUnicodeEscape(w, c)
- }
case BackslashEscapeRawByte:
switch {
case c < utf8.RuneSelf:
@@ -118,6 +137,15 @@ func WriteStringChar(w fastio.AllWriter, c rune, escape BackslashEscapeMode) err
return w.WriteByte(byte(c))
}
default:
+ if BackslashEscapeUnicodeMin <= escape && escape <= BackslashEscapeUnicodeMax {
+ switch {
+ case c > 0xFFFF: // override, can't escape these (TODO: unless we use UTF-16 surrogates?)
+ _, err := w.WriteRune(c)
+ return err
+ default: // obey
+ return WriteStringUnicodeEscape(w, c, escape)
+ }
+ }
panic(fmt.Errorf("escaper returned an invalid escape mode=%d", escape))
}
}