summaryrefslogtreecommitdiff
path: root/encode_escape.go
diff options
context:
space:
mode:
Diffstat (limited to 'encode_escape.go')
-rw-r--r--encode_escape.go103
1 files changed, 103 insertions, 0 deletions
diff --git a/encode_escape.go b/encode_escape.go
new file mode 100644
index 0000000..ab0d9c1
--- /dev/null
+++ b/encode_escape.go
@@ -0,0 +1,103 @@
+// Copyright (C) 2022-2023 Luke Shumaker <lukeshu@lukeshu.com>
+//
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+package lowmemjson
+
+import (
+ "unicode/utf8"
+)
+
+// BackslashEscapeMode identifies one of the three ways that a
+// character may be represented in a JSON string:
+//
+// - literally (no backslash escaping)
+//
+// - as a short "well-known" `\X` backslash sequence (where `X` is a
+// single-character)
+//
+// - as a long Unicode `\uXXXX` backslash sequence
+type BackslashEscapeMode uint8
+
+const (
+ BackslashEscapeNone BackslashEscapeMode = iota
+ BackslashEscapeShort
+ BackslashEscapeUnicode
+)
+
+// A BackslashEscaper controls how a ReEncoder emits a character in a
+// JSON string. The `rune` argument is the character being
+// considered, and the `BackslashEscapeMode` argument is how it was
+// originally encoded in the input.
+//
+// The ReEncoder will panic if a BackslashEscaper returns an unknown
+// BackslashEscapeMode.
+type BackslashEscaper = func(rune, BackslashEscapeMode) BackslashEscapeMode
+
+// EscapePreserve is a BackslashEscaper that preserves the original
+// input escaping.
+func EscapePreserve(_ rune, wasEscaped BackslashEscapeMode) BackslashEscapeMode {
+ return wasEscaped
+}
+
+// EscapeJSSafe is a BackslashEscaper that escapes strings such that
+// the JSON safe to embed in JS; it otherwise preserves the original
+// input escaping.
+//
+// JSON is notionally a JS subset, but that's not actually true; so
+// more conservative backslash-escaping is necessary to safely embed
+// it in JS. http://timelessrepo.com/json-isnt-a-javascript-subset
+func EscapeJSSafe(c rune, wasEscaped BackslashEscapeMode) BackslashEscapeMode {
+ switch c {
+ case '\u2028', '\u2029':
+ return BackslashEscapeUnicode
+ default:
+ return wasEscaped
+ }
+}
+
+// EscapeHTMLSafe is a BackslashEscaper that escapes strings such that
+// the JSON is safe to embed in HTML; it otherwise preserves the
+// original input escaping.
+func EscapeHTMLSafe(c rune, wasEscaped BackslashEscapeMode) BackslashEscapeMode {
+ switch c {
+ case '&', '<', '>':
+ return BackslashEscapeUnicode
+ default:
+ return EscapeJSSafe(c, wasEscaped)
+ }
+}
+
+// EscapeDefault is a BackslashEscaper that mimics the default
+// behavior of encoding/json.
+//
+// It is like EscapeHTMLSafe, but also uses long Unicode `\uXXXX`
+// sequences for `\b`, `\f`, and the `\uFFFD` Unicode replacement
+// character.
+//
+// A ReEncoder uses EscapeDefault if a BackslashEscaper is not
+// specified.
+func EscapeDefault(c rune, wasEscaped BackslashEscapeMode) BackslashEscapeMode {
+ switch c {
+ case '\b', '\f', utf8.RuneError:
+ return BackslashEscapeUnicode
+ default:
+ return EscapeHTMLSafe(c, wasEscaped)
+ }
+}
+
+// EscapeDefaultNonHTMLSafe is a BackslashEscaper that mimics the
+// default behavior of an encoding/json.Encoder that has had
+// SetEscapeHTML(false) called on it.
+//
+// It is like EscapeJSSafe, but also uses long Unicode `\uXXXX`
+// sequences for `\b`, `\f`, and the `\uFFFD` Unicode replacement
+// character.
+func EscapeDefaultNonHTMLSafe(c rune, wasEscaped BackslashEscapeMode) BackslashEscapeMode {
+ switch c {
+ case '\b', '\f', utf8.RuneError:
+ return BackslashEscapeUnicode
+ default:
+ return EscapeJSSafe(c, wasEscaped)
+ }
+}