From 00187950437a10952b82353405e5ba4b4515fb29 Mon Sep 17 00:00:00 2001 From: Luke Shumaker Date: Thu, 16 Feb 2023 19:06:46 -0700 Subject: reencode: Don't normalize the capitalization of \uXXXX hex escapes --- internal/jsonstring/encode_string.go | 60 ++++++++++++++++++++++++++---------- 1 file changed, 44 insertions(+), 16 deletions(-) (limited to 'internal/jsonstring/encode_string.go') 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)) } } -- cgit v1.2.3-2-g168b