diff options
author | Luke Shumaker <lukeshu@lukeshu.com> | 2023-01-26 22:31:32 -0700 |
---|---|---|
committer | Luke Shumaker <lukeshu@lukeshu.com> | 2023-01-29 21:01:42 -0700 |
commit | ff6dc0bc519886905e758a84e572f5e34d6c03d1 (patch) | |
tree | 8906c4b8bb0c7dc468419efc17b872db62a4e068 /encode_string.go | |
parent | d1b5bc1f05624614f43ef85597f4aa9d7a166d23 (diff) |
Move things between files
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) } +} |