diff options
-rw-r--r-- | common.go | 16 | ||||
-rw-r--r-- | encode.go | 23 | ||||
-rw-r--r-- | encode_escape.go (renamed from misc.go) | 108 | ||||
-rw-r--r-- | encode_string.go | 111 | ||||
-rw-r--r-- | internal/encode.go (renamed from internal/export_tags.go) | 4 | ||||
-rw-r--r-- | internal/tags.go | 7 | ||||
-rw-r--r-- | ioutil.go | 31 | ||||
-rw-r--r-- | struct.go | 4 | ||||
-rw-r--r-- | test_export.go | 18 |
9 files changed, 169 insertions, 153 deletions
diff --git a/common.go b/common.go new file mode 100644 index 0000000..90156b9 --- /dev/null +++ b/common.go @@ -0,0 +1,16 @@ +// Copyright (C) 2022-2023 Luke Shumaker <lukeshu@lukeshu.com> +// +// SPDX-License-Identifier: GPL-2.0-or-later + +package lowmemjson + +import ( + "encoding/json" + "reflect" +) + +var ( + numberType = reflect.TypeOf(json.Number("")) + byteType = reflect.TypeOf(byte(0)) + byteSliceType = reflect.TypeOf(([]byte)(nil)) +) @@ -17,7 +17,6 @@ import ( "sort" "strconv" "strings" - "unicode/utf8" "unsafe" ) @@ -426,28 +425,6 @@ func encode(w io.Writer, val reflect.Value, escaper BackslashEscaper, quote bool } } -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 encodeArray(w io.Writer, val reflect.Value, escaper BackslashEscaper, cycleDepth uint, cycleSeen map[any]struct{}) { encodeWriteByte(w, '[') n := val.Len() diff --git a/misc.go b/encode_escape.go index fb96b4e..ab0d9c1 100644 --- a/misc.go +++ b/encode_escape.go @@ -5,45 +5,9 @@ package lowmemjson import ( - "encoding/json" - "io" - "reflect" "unicode/utf8" - - "git.lukeshu.com/go/lowmemjson/internal" -) - -var ( - numberType = reflect.TypeOf(json.Number("")) - byteType = reflect.TypeOf(byte(0)) - byteSliceType = reflect.TypeOf(([]byte)(nil)) ) -// generic I/O ///////////////////////////////////////////////////////////////// - -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 //////////////////////////////////////////////////////// - // BackslashEscapeMode identifies one of the three ways that a // character may be represented in a JSON string: // @@ -137,75 +101,3 @@ func EscapeDefaultNonHTMLSafe(c rune, wasEscaped BackslashEscapeMode) BackslashE return EscapeJSSafe(c, wasEscaped) } } - -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") - } -} 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) } +} diff --git a/internal/export_tags.go b/internal/encode.go index d8cf622..8aae673 100644 --- a/internal/export_tags.go +++ b/internal/encode.go @@ -1,4 +1,4 @@ -// Copyright (C) 2022 Luke Shumaker <lukeshu@lukeshu.com> +// Copyright (C) 2022-2023 Luke Shumaker <lukeshu@lukeshu.com> // // SPDX-License-Identifier: GPL-2.0-or-later @@ -8,8 +8,6 @@ import ( "io" ) -var ParseTag = parseTag - var ( EncodeStringFromBytes func(io.Writer, []byte) EncodeStringFromString func(io.Writer, string) diff --git a/internal/tags.go b/internal/tags.go new file mode 100644 index 0000000..bdf1f72 --- /dev/null +++ b/internal/tags.go @@ -0,0 +1,7 @@ +// Copyright (C) 2022-2023 Luke Shumaker <lukeshu@lukeshu.com> +// +// SPDX-License-Identifier: GPL-2.0-or-later + +package internal + +var ParseTag = parseTag diff --git a/ioutil.go b/ioutil.go new file mode 100644 index 0000000..a53eac3 --- /dev/null +++ b/ioutil.go @@ -0,0 +1,31 @@ +// Copyright (C) 2022-2023 Luke Shumaker <lukeshu@lukeshu.com> +// +// SPDX-License-Identifier: GPL-2.0-or-later + +package lowmemjson + +import ( + "io" + "unicode/utf8" +) + +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]) +} @@ -6,6 +6,8 @@ package lowmemjson import ( "reflect" + + "git.lukeshu.com/go/lowmemjson/internal" ) type structField struct { @@ -143,7 +145,7 @@ func indexStructInner(typ reflect.Type, byPos *[]structField, byName map[string] if tag == "-" { continue } - tagName, opts := parseTag(tag) + tagName, opts := internal.ParseTag(tag) name := tagName if !isValidTag(name) { name = "" diff --git a/test_export.go b/test_export.go deleted file mode 100644 index 76d29d2..0000000 --- a/test_export.go +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright (C) 2022 Luke Shumaker <lukeshu@lukeshu.com> -// -// SPDX-License-Identifier: GPL-2.0-or-later - -package lowmemjson - -import ( - "io" - - "git.lukeshu.com/go/lowmemjson/internal" -) - -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) } -} - -var parseTag = internal.ParseTag |