From 234e0836f1040f7724251b4120a2351bcbf64131 Mon Sep 17 00:00:00 2001 From: Luke Shumaker Date: Sat, 13 Aug 2022 15:11:17 -0600 Subject: set up as a separate repo --- misc.go | 131 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 131 insertions(+) create mode 100644 misc.go (limited to 'misc.go') diff --git a/misc.go b/misc.go new file mode 100644 index 0000000..132b177 --- /dev/null +++ b/misc.go @@ -0,0 +1,131 @@ +// Copyright (C) 2022 Luke Shumaker +// +// SPDX-License-Identifier: GPL-2.0-or-later + +package lowmemjson + +import ( + "encoding/json" + "io" + "reflect" + "unicode/utf8" +) + +const Tab = "\t" + +const hex = "0123456789abcdef" + +var ( + numberType = reflect.TypeOf(json.Number("")) + byteType = reflect.TypeOf(byte(0)) + byteSliceType = reflect.TypeOf(([]byte)(nil)) +) + +// generic I/O ///////////////////////////////////////////////////////////////// + +func decodeRune[T interface{ []byte | string }](s T) (r rune, size int) { + iface := any(s) + if str, ok := iface.(string); ok { + return utf8.DecodeRuneInString(str) + } else { + return utf8.DecodeRune(iface.([]byte)) + } +} + +func writeByte(w io.Writer, c byte) error { + if br, ok := w.(interface{ WriteByte(byte) error }); ok { + return br.WriteByte(c) + } + var buf [1]byte + buf[0] = c + if _, err := w.Write(buf[:]); err != nil { + return err + } + return nil +} + +func writeRune(w io.Writer, c rune) (int, error) { + if rw, ok := w.(interface{ WriteRune(rune) (int, error) }); ok { + return rw.WriteRune(c) + } + var buf [utf8.UTFMax]byte + n := utf8.EncodeRune(buf[:], c) + return w.Write(buf[:n]) +} + +// JSON string encoding //////////////////////////////////////////////////////// + +func UnicodeEscapeJSSafe(c rune, _ bool) bool { + // JSON is notionally a JS subset, but that's not actually + // true. + // + // http://timelessrepo.com/json-isnt-a-javascript-subset + switch c { + case '\u2028', '\u2029': + return true + default: + return false + } +} + +func UnicodeEscapeHTMLSafe(c rune, wasEscaped bool) bool { + switch c { + case '&', '<', '>': + return true + default: + return UnicodeEscapeJSSafe(c, wasEscaped) + } +} + +func UnicodeEscapeDefault(c rune, wasEscaped bool) bool { + switch c { + case '\b', '\f', utf8.RuneError: + return true + default: + return UnicodeEscapeHTMLSafe(c, wasEscaped) + } +} + +func writeStringUnicodeEscape(w io.Writer, c rune) (int, error) { + buf := [6]byte{ + '\\', + 'u', + hex[(c>>12)&0xf], + hex[(c>>8)&0xf], + hex[(c>>4)&0xf], + hex[(c>>0)&0xf], + } + return w.Write(buf[:]) +} +func writeStringShortEscape(w io.Writer, c byte) (int, error) { + buf := [2]byte{'\\', c} + return w.Write(buf[:]) +} +func writeStringChar(w io.Writer, c rune, wasEscaped bool, escaper func(rune, bool) bool) (int, error) { + if escaper == nil { + escaper = UnicodeEscapeDefault + } + switch { + case c <= 0xFFFF && escaper(c, wasEscaped): + return writeStringUnicodeEscape(w, c) + case c == '"' || c == '\\': + return writeStringShortEscape(w, byte(c)) + case c < 0x0020: + switch c { + case '\b': + return writeStringShortEscape(w, 'b') + case '\f': + return writeStringShortEscape(w, 'f') + case '\n': + return writeStringShortEscape(w, 'n') + case '\r': + return writeStringShortEscape(w, 'r') + case '\t': + return writeStringShortEscape(w, 't') + default: + return writeStringUnicodeEscape(w, c) + } + default: + return writeRune(w, c) + } +} -- cgit v1.2.3-2-g168b