diff options
author | Luke Shumaker <lukeshu@lukeshu.com> | 2023-01-30 21:54:38 -0700 |
---|---|---|
committer | Luke Shumaker <lukeshu@lukeshu.com> | 2023-01-30 21:54:38 -0700 |
commit | 8467bdaa181257d031a258a05012dc85adbcb233 (patch) | |
tree | bc9bf437a34905f0b7249352043aff9e9d80ebe8 /encode_string.go | |
parent | 0b57145421e7e4f165f64e73ee7c5d8102945569 (diff) | |
parent | 2e48a42fb9b9e946958810cfbb90ae85bee997e4 (diff) |
Merge branch 'lukeshu/quality2'
Diffstat (limited to 'encode_string.go')
-rw-r--r-- | encode_string.go | 111 |
1 files changed, 111 insertions, 0 deletions
diff --git a/encode_string.go b/encode_string.go new file mode 100644 index 0000000..c5cb442 --- /dev/null +++ b/encode_string.go @@ -0,0 +1,111 @@ +// Copyright (C) 2022-2023 Luke Shumaker <lukeshu@lukeshu.com> +// +// SPDX-License-Identifier: GPL-2.0-or-later + +package lowmemjson + +import ( + "io" + "unicode/utf8" + + "git.lukeshu.com/go/lowmemjson/internal" +) + +func writeStringUnicodeEscape(w io.Writer, c rune) (int, error) { + buf := [6]byte{ + '\\', + 'u', + internal.Hex[(c>>12)&0xf], + internal.Hex[(c>>8)&0xf], + internal.Hex[(c>>4)&0xf], + internal.Hex[(c>>0)&0xf], + } + return w.Write(buf[:]) +} + +func writeStringShortEscape(w io.Writer, c rune) (int, error) { + var b byte + switch c { + case '"', '\\', '/': + b = byte(c) + case '\b': + b = 'b' + case '\f': + b = 'f' + case '\n': + b = 'n' + case '\r': + b = 'r' + case '\t': + b = 't' + default: + panic("should not happen") + } + buf := [2]byte{'\\', b} + return w.Write(buf[:]) +} + +func writeStringChar(w io.Writer, c rune, wasEscaped BackslashEscapeMode, escaper BackslashEscaper) (int, error) { + if escaper == nil { + escaper = EscapeDefault + } + switch escaper(c, wasEscaped) { + case BackslashEscapeNone: + switch { + case c < 0x0020: // override, gotta escape these + switch c { + case '\b', '\f', '\n', '\r', '\t': // short-escape if possible + return writeStringShortEscape(w, c) + default: + return writeStringUnicodeEscape(w, c) + } + case c == '"' || c == '\\': // override, gotta escape these + return writeStringShortEscape(w, c) + default: // obey + return writeRune(w, c) + } + case BackslashEscapeShort: + switch c { + case '"', '\\', '/', '\b', '\f', '\n', '\r', '\t': // obey + return writeStringShortEscape(w, c) + default: // override, can't short-escape these + return writeRune(w, c) + } + case BackslashEscapeUnicode: + switch { + case c > 0xFFFF: // override, can't escape these (TODO: unless we use UTF-16 surrogates?) + return writeRune(w, c) + default: // obey + return writeStringUnicodeEscape(w, c) + } + default: + panic("escaper returned an invalid escape mode") + } +} + +func encodeStringFromString(w io.Writer, escaper BackslashEscaper, str string) { + encodeWriteByte(w, '"') + for _, c := range str { + if _, err := writeStringChar(w, c, BackslashEscapeNone, escaper); err != nil { + panic(encodeError{err}) + } + } + encodeWriteByte(w, '"') +} + +func encodeStringFromBytes(w io.Writer, escaper BackslashEscaper, str []byte) { + encodeWriteByte(w, '"') + for i := 0; i < len(str); { + c, size := utf8.DecodeRune(str[i:]) + if _, err := writeStringChar(w, c, BackslashEscapeNone, escaper); err != nil { + panic(encodeError{err}) + } + i += size + } + encodeWriteByte(w, '"') +} + +func init() { + internal.EncodeStringFromString = func(w io.Writer, s string) { encodeStringFromString(w, nil, s) } + internal.EncodeStringFromBytes = func(w io.Writer, s []byte) { encodeStringFromBytes(w, nil, s) } +} |