summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorLuke Shumaker <lukeshu@lukeshu.com>2022-08-13 15:11:17 -0600
committerLuke Shumaker <lukeshu@lukeshu.com>2022-08-13 15:18:11 -0600
commit234e0836f1040f7724251b4120a2351bcbf64131 (patch)
tree201b30fcc99eed470ae345a9bbe594f7ee7a1178 /lib
parentf2769bd863521cf316ec9237a498bfa4ecaa115f (diff)
set up as a separate repo
Diffstat (limited to 'lib')
-rw-r--r--lib/lowmemjson.stock/borrowed_tags_test.go28
-rw-r--r--lib/lowmemjson/adapter_test.go120
-rw-r--r--lib/lowmemjson/base64.go121
-rw-r--r--lib/lowmemjson/base64_test.go44
-rw-r--r--lib/lowmemjson/borrowed_decode_test.go2590
-rw-r--r--lib/lowmemjson/borrowed_encode_test.go1208
-rw-r--r--lib/lowmemjson/borrowed_fuzz_test.go83
-rw-r--r--lib/lowmemjson/borrowed_misc.go42
-rw-r--r--lib/lowmemjson/borrowed_scanner_test.go305
-rw-r--r--lib/lowmemjson/borrowed_tagkey_test.go121
-rw-r--r--lib/lowmemjson/borrowed_tags.go38
-rw-r--r--lib/lowmemjson/borrowed_tags_test.go28
-rw-r--r--lib/lowmemjson/decode.go812
-rw-r--r--lib/lowmemjson/encode.go333
-rw-r--r--lib/lowmemjson/misc.go131
-rw-r--r--lib/lowmemjson/reencode.go598
-rw-r--r--lib/lowmemjson/struct.go164
-rw-r--r--lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/06e2c9db80a08b67fad7f1a4606dc7419750995a57828aa25ea57fe7099d5c032
-rw-r--r--lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/24f53a36f8832fec65cac0aa0f3b43ec1c904414fa6d38f6fc288b0bbd69588a2
-rw-r--r--lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/2d49311ef22319f70a3590a86b406b9f2565987a4a3b6d7660ddc308b5b2fae22
-rw-r--r--lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/356e28f5914a0f16f3cef81330f1d92060be4d694a93dedd654bf48743a7d2bd2
-rw-r--r--lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/582528ddfad69eb57775199a43e0f9fd5c94bba343ce7bb6724d4ebafe311ed42
-rw-r--r--lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/60c81ee499a7f1e151b66b08f0a4ff81edd7cb53d00dce8ee0eaf316839960262
-rw-r--r--lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/66498f377f38b53eebe1ceaa4a53e4de01a04efc02ac9cfda60f9815f80e9b9d2
-rw-r--r--lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/731951fe84fa6f3a7f6ee8adaa585d4f6a01f359a04737e51ffc70f16f480b9b2
-rw-r--r--lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/7d6367ba84cd18550920b5202cd1269174416ce32769c7f59376e76b7dd3129c2
-rw-r--r--lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/8727b16d337d7b8187433233f3a90099024e580a6ba319ea2bf539880c50bd7c2
-rw-r--r--lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/9201a772731543760326638b8915f80863feab0ba0108183b3093934bdc0420c2
-rw-r--r--lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/92f75f690317ace34aeaae3fe39f5f2ff9830777253ff371c5ef6f403a0f8f0f2
-rw-r--r--lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/93d6f7bc0d93f998c7b7fe654ff46010d6fa76f0a142c3523c42454f8ad10b072
-rw-r--r--lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/a7450fd77fc7c53cc5bd136874415dddfff5c586e662f21420caa7a94131a56a2
-rw-r--r--lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/a95d2a0f87501a643d54218d2ad8112204672cc1fb30be297853616788208a5c2
-rw-r--r--lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/beed435aa2fee4819eab217543561dfd8001d4a44f53ceb664aaba86cebfaf212
-rw-r--r--lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/c2501043394e49f2477408be5ef9389790e33ed1886073dec445d4cf05bcd4b42
-rw-r--r--lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/caf81e9797b19c76c1fc4dbf537d4d81f389524539f402d13aa01f93a65ac7e92
-rw-r--r--lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/cc90a4a40ae9b3beac70baf6d7821a5a6f3a90cabb033575790be917235936802
-rw-r--r--lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/ec72f669d648d8d9b9f75a3b303897c59b11e4bfb7622f25ff251a92f182bc2a2
-rw-r--r--lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/f34630c44c11bb13d27531927c5c1e65d159b70f39cd161da0dba348c1221ab32
-rw-r--r--lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/fd67efb09d433a1351a201281dbf6568628b4135c35c811dd9bce97620a75d432
39 files changed, 0 insertions, 6810 deletions
diff --git a/lib/lowmemjson.stock/borrowed_tags_test.go b/lib/lowmemjson.stock/borrowed_tags_test.go
deleted file mode 100644
index 8ba8ddd..0000000
--- a/lib/lowmemjson.stock/borrowed_tags_test.go
+++ /dev/null
@@ -1,28 +0,0 @@
-// Copyright 2011 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package json
-
-import (
- "testing"
-)
-
-func TestTagParsing(t *testing.T) {
- name, opts := parseTag("field,foobar,foo")
- if name != "field" {
- t.Fatalf("name = %q, want field", name)
- }
- for _, tt := range []struct {
- opt string
- want bool
- }{
- {"foobar", true},
- {"foo", true},
- {"bar", false},
- } {
- if opts.Contains(tt.opt) != tt.want {
- t.Errorf("Contains(%q) = %v", tt.opt, !tt.want)
- }
- }
-}
diff --git a/lib/lowmemjson/adapter_test.go b/lib/lowmemjson/adapter_test.go
deleted file mode 100644
index b1aec3e..0000000
--- a/lib/lowmemjson/adapter_test.go
+++ /dev/null
@@ -1,120 +0,0 @@
-// Copyright (C) 2022 Luke Shumaker <lukeshu@lukeshu.com>
-//
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-package lowmemjson
-
-import (
- "bufio"
- "bytes"
- "encoding/json"
- "io"
-)
-
-func MarshalIndent(v any, prefix, indent string) ([]byte, error) {
- var buf bytes.Buffer
- formatter := &ReEncoder{
- Out: &buf,
- Indent: indent,
- prefix: prefix,
- }
- err := Encode(formatter, v)
- return buf.Bytes(), err
-}
-
-func Marshal(v any) ([]byte, error) {
- var buf bytes.Buffer
- formatter := &ReEncoder{
- Out: &buf,
- Compact: true,
- }
- err := Encode(formatter, v)
- return buf.Bytes(), err
-}
-
-func HTMLEscape(dst *bytes.Buffer, src []byte) {
- formatter := &ReEncoder{
- Out: dst,
- }
- _, _ = formatter.Write(src)
-}
-
-func Compact(dst *bytes.Buffer, src []byte) error {
- formatter := &ReEncoder{
- Out: dst,
- Compact: true,
- }
- _, err := formatter.Write(src)
- return err
-}
-
-func Indent(dst *bytes.Buffer, src []byte, prefix, indent string) error {
- formatter := &ReEncoder{
- Out: dst,
- Indent: indent,
- prefix: prefix,
- }
- _, err := formatter.Write(src)
- return err
-}
-
-func Valid(data []byte) bool {
- formatter := &ReEncoder{
- Out: io.Discard,
- Compact: true,
- }
- _, err := formatter.Write(data)
- return err == nil
-}
-
-func Unmarshal(data []byte, ptr any) error {
- return Decode(bytes.NewReader(data), ptr)
-}
-
-func init() {
- forceBufio = true
-}
-
-func (dec *Decoder) Buffered() io.Reader {
- buf := dec.r.(*bufio.Reader)
- dat, _ := buf.Peek(buf.Buffered())
- return bytes.NewReader(dat)
-}
-
-//func (dec *Decoder) Token() (Token, error)
-
-/////////////////////////////////////////////////////////////////////
-
-type (
- Number = json.Number
- Marshaler = json.Marshaler
- RawMessage = json.RawMessage
-
- UnsupportedValueError = json.UnsupportedValueError
- MarshalerError = json.MarshalerError
- UnmarshalTypeError = json.UnmarshalTypeError
-)
-
-const (
- startDetectingCyclesAfter = 1000
-)
-
-func isSpace(c byte) bool {
- switch c {
- case 0x0020, 0x000A, 0x000D, 0x0009:
- return true
- default:
- return false
- }
-}
-
-type encodeState struct {
- bytes.Buffer
-}
-
-func (es *encodeState) string(str string, _ bool) {
- encodeString(&es.Buffer, str)
-}
-func (es *encodeState) stringBytes(str []byte, _ bool) {
- encodeString(&es.Buffer, str)
-}
diff --git a/lib/lowmemjson/base64.go b/lib/lowmemjson/base64.go
deleted file mode 100644
index 86fc293..0000000
--- a/lib/lowmemjson/base64.go
+++ /dev/null
@@ -1,121 +0,0 @@
-// Copyright (C) 2022 Luke Shumaker <lukeshu@lukeshu.com>
-//
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-package lowmemjson
-
-import (
- "encoding/base64"
- "io"
- "strings"
-)
-
-type base64Decoder struct {
- dst io.Writer
-
- err error
- pos int64
- buf [4]byte
- bufLen int
-}
-
-func newBase64Decoder(w io.Writer) io.WriteCloser {
- return &base64Decoder{
- dst: w,
- }
-}
-
-func (dec *base64Decoder) decodeByte(b byte) (byte, bool) {
- const alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
- n := strings.IndexByte(alphabet, b)
- if n < 0 {
- return 0, false
- }
- dec.pos++
- return byte(n), true
-}
-
-func (dec *base64Decoder) decodeTuple(a, b, c, d byte) error {
- var decodedLen int
- var encoded [4]byte
- var ok bool
-
- if a != '=' {
- encoded[0], ok = dec.decodeByte(a)
- if !ok {
- return base64.CorruptInputError(dec.pos)
- }
- decodedLen++
- }
- if b != '=' {
- encoded[1], ok = dec.decodeByte(b)
- if !ok {
- return base64.CorruptInputError(dec.pos)
- }
- // do NOT increment decodedLen here
- }
- if c != '=' {
- encoded[2], ok = dec.decodeByte(c)
- if !ok {
- return base64.CorruptInputError(dec.pos)
- }
- decodedLen++
- }
- if d != '=' {
- encoded[3], ok = dec.decodeByte(d)
- if !ok {
- return base64.CorruptInputError(dec.pos)
- }
- decodedLen++
- }
-
- val := 0 |
- uint32(encoded[0])<<18 |
- uint32(encoded[1])<<12 |
- uint32(encoded[2])<<6 |
- uint32(encoded[3])<<0
- var decoded [3]byte
- decoded[0] = byte(val >> 16)
- decoded[1] = byte(val >> 8)
- decoded[2] = byte(val >> 0)
-
- _, err := dec.dst.Write(decoded[:decodedLen])
- return err
-}
-
-func (dec *base64Decoder) Write(dat []byte) (int, error) {
- if len(dat) == 0 {
- return 0, nil
- }
- if dec.err != nil {
- return 0, dec.err
- }
- var n int
- if dec.bufLen > 0 {
- n = copy(dec.buf[dec.bufLen:], dat)
- dec.bufLen += n
- if dec.bufLen < 4 {
- return len(dat), nil
- }
- if err := dec.decodeTuple(dec.buf[0], dec.buf[1], dec.buf[2], dec.buf[3]); err != nil {
- dec.err = err
- return 0, dec.err
- }
- }
- for ; n+3 < len(dat); n += 4 {
- if err := dec.decodeTuple(dat[n], dat[n+1], dat[n+2], dat[n+3]); err != nil {
- dec.err = err
- return n, dec.err
- }
- }
- dec.bufLen = copy(dec.buf[:], dat[n:])
- return len(dat), nil
-}
-
-func (dec *base64Decoder) Close() error {
- if dec.bufLen == 0 {
- return nil
- }
- copy(dec.buf[:], "====")
- return dec.decodeTuple(dec.buf[0], dec.buf[1], dec.buf[2], dec.buf[3])
-}
diff --git a/lib/lowmemjson/base64_test.go b/lib/lowmemjson/base64_test.go
deleted file mode 100644
index 43367af..0000000
--- a/lib/lowmemjson/base64_test.go
+++ /dev/null
@@ -1,44 +0,0 @@
-// Copyright (C) 2022 Luke Shumaker <lukeshu@lukeshu.com>
-//
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-package lowmemjson
-
-import (
- "bytes"
- "encoding/base64"
- "testing"
-
- "github.com/stretchr/testify/require"
-)
-
-func b64encode(t *testing.T, input []byte) []byte {
- var encoded bytes.Buffer
- enc := base64.NewEncoder(base64.StdEncoding, &encoded)
- _, err := enc.Write(input)
- require.NoError(t, err)
- require.NoError(t, enc.Close())
- return encoded.Bytes()
-}
-
-func b64decode(t *testing.T, input []byte) []byte {
- var decoded bytes.Buffer
- dec := newBase64Decoder(&decoded)
- _, err := dec.Write(input)
- require.NoError(t, err)
- require.NoError(t, dec.Close())
- return decoded.Bytes()
-}
-
-func FuzzBase64Decoder(f *testing.F) {
- f.Fuzz(func(t *testing.T, input []byte) {
- encoded := b64encode(t, input)
- decoded := b64decode(t, encoded)
- t.Logf("input b64 = %q", encoded)
- t.Logf("expected decoded = %#v", input)
- t.Logf("actual decoded = %#v", decoded)
- if !bytes.Equal(input, decoded) {
- t.Fail()
- }
- })
-}
diff --git a/lib/lowmemjson/borrowed_decode_test.go b/lib/lowmemjson/borrowed_decode_test.go
deleted file mode 100644
index a1fd695..0000000
--- a/lib/lowmemjson/borrowed_decode_test.go
+++ /dev/null
@@ -1,2590 +0,0 @@
-// Copyright 2010 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package lowmemjson // MODIFIED
-
-import (
- "bytes"
- "encoding"
- "errors"
- "fmt"
- "image"
- "io" // MODIFIED
- "math"
- "math/big"
- "net"
- "reflect"
- "strconv"
- "strings"
- "testing"
- "time"
-)
-
-type T struct {
- X string
- Y int
- Z int `json:"-"`
-}
-
-type U struct {
- Alphabet string `json:"alpha"`
-}
-
-type V struct {
- F1 any
- F2 int32
- F3 Number
- F4 *VOuter
-}
-
-type VOuter struct {
- V V
-}
-
-type W struct {
- S SS
-}
-
-type P struct {
- PP PP
-}
-
-type PP struct {
- T T
- Ts []T
-}
-
-type SS string
-
-func (*SS) UnmarshalJSON(data []byte) error {
- return &UnmarshalTypeError{Value: "number", Type: reflect.TypeOf(SS(""))}
-}
-
-// ifaceNumAsFloat64/ifaceNumAsNumber are used to test unmarshaling with and
-// without UseNumber
-var ifaceNumAsFloat64 = map[string]any{
- "k1": float64(1),
- "k2": "s",
- "k3": []any{float64(1), float64(2.0), float64(3e-3)},
- "k4": map[string]any{"kk1": "s", "kk2": float64(2)},
-}
-
-var ifaceNumAsNumber = map[string]any{
- "k1": Number("1"),
- "k2": "s",
- "k3": []any{Number("1"), Number("2.0"), Number("3e-3")},
- "k4": map[string]any{"kk1": "s", "kk2": Number("2")},
-}
-
-type tx struct {
- x int
-}
-
-type u8 uint8
-
-// A type that can unmarshal itself.
-
-type unmarshaler struct {
- T bool
-}
-
-func (u *unmarshaler) UnmarshalJSON(b []byte) error {
- *u = unmarshaler{true} // All we need to see that UnmarshalJSON is called.
- return nil
-}
-
-type ustruct struct {
- M unmarshaler
-}
-
-type unmarshalerText struct {
- A, B string
-}
-
-// needed for re-marshaling tests
-func (u unmarshalerText) MarshalText() ([]byte, error) {
- return []byte(u.A + ":" + u.B), nil
-}
-
-func (u *unmarshalerText) UnmarshalText(b []byte) error {
- pos := bytes.IndexByte(b, ':')
- if pos == -1 {
- return errors.New("missing separator")
- }
- u.A, u.B = string(b[:pos]), string(b[pos+1:])
- return nil
-}
-
-var _ encoding.TextUnmarshaler = (*unmarshalerText)(nil)
-
-type ustructText struct {
- M unmarshalerText
-}
-
-// u8marshal is an integer type that can marshal/unmarshal itself.
-type u8marshal uint8
-
-func (u8 u8marshal) MarshalText() ([]byte, error) {
- return []byte(fmt.Sprintf("u%d", u8)), nil
-}
-
-var errMissingU8Prefix = errors.New("missing 'u' prefix")
-
-func (u8 *u8marshal) UnmarshalText(b []byte) error {
- if !bytes.HasPrefix(b, []byte{'u'}) {
- return errMissingU8Prefix
- }
- n, err := strconv.Atoi(string(b[1:]))
- if err != nil {
- return err
- }
- *u8 = u8marshal(n)
- return nil
-}
-
-var _ encoding.TextUnmarshaler = (*u8marshal)(nil)
-
-var (
- umtrue = unmarshaler{true}
- umslice = []unmarshaler{{true}}
- umstruct = ustruct{unmarshaler{true}}
-
- umtrueXY = unmarshalerText{"x", "y"}
- umsliceXY = []unmarshalerText{{"x", "y"}}
- umstructXY = ustructText{unmarshalerText{"x", "y"}}
-
- ummapXY = map[unmarshalerText]bool{{"x", "y"}: true}
-)
-
-// Test data structures for anonymous fields.
-
-type Point struct {
- Z int
-}
-
-type Top struct {
- Level0 int
- Embed0
- *Embed0a
- *Embed0b `json:"e,omitempty"` // treated as named
- Embed0c `json:"-"` // ignored
- Loop
- Embed0p // has Point with X, Y, used
- Embed0q // has Point with Z, used
- embed // contains exported field
-}
-
-type Embed0 struct {
- Level1a int // overridden by Embed0a's Level1a with json tag
- Level1b int // used because Embed0a's Level1b is renamed
- Level1c int // used because Embed0a's Level1c is ignored
- Level1d int // annihilated by Embed0a's Level1d
- Level1e int `json:"x"` // annihilated by Embed0a.Level1e
-}
-
-type Embed0a struct {
- Level1a int `json:"Level1a,omitempty"`
- Level1b int `json:"LEVEL1B,omitempty"`
- Level1c int `json:"-"`
- Level1d int // annihilated by Embed0's Level1d
- Level1f int `json:"x"` // annihilated by Embed0's Level1e
-}
-
-type Embed0b Embed0
-
-type Embed0c Embed0
-
-type Embed0p struct {
- image.Point
-}
-
-type Embed0q struct {
- Point
-}
-
-type embed struct {
- Q int
-}
-
-type Loop struct {
- Loop1 int `json:",omitempty"`
- Loop2 int `json:",omitempty"`
- *Loop
-}
-
-// From reflect test:
-// The X in S6 and S7 annihilate, but they also block the X in S8.S9.
-type S5 struct {
- S6
- S7
- S8
-}
-
-type S6 struct {
- X int
-}
-
-type S7 S6
-
-type S8 struct {
- S9
-}
-
-type S9 struct {
- X int
- Y int
-}
-
-// From reflect test:
-// The X in S11.S6 and S12.S6 annihilate, but they also block the X in S13.S8.S9.
-type S10 struct {
- S11
- S12
- S13
-}
-
-type S11 struct {
- S6
-}
-
-type S12 struct {
- S6
-}
-
-type S13 struct {
- S8
-}
-
-type Ambig struct {
- // Given "hello", the first match should win.
- First int `json:"HELLO"`
- Second int `json:"Hello"`
-}
-
-type XYZ struct {
- X any
- Y any
- Z any
-}
-
-type unexportedWithMethods struct{}
-
-func (unexportedWithMethods) F() {}
-
-type byteWithMarshalJSON byte
-
-func (b byteWithMarshalJSON) MarshalJSON() ([]byte, error) {
- return []byte(fmt.Sprintf(`"Z%.2x"`, byte(b))), nil
-}
-
-func (b *byteWithMarshalJSON) UnmarshalJSON(data []byte) error {
- if len(data) != 5 || data[0] != '"' || data[1] != 'Z' || data[4] != '"' {
- return fmt.Errorf("bad quoted string")
- }
- i, err := strconv.ParseInt(string(data[2:4]), 16, 8)
- if err != nil {
- return fmt.Errorf("bad hex")
- }
- *b = byteWithMarshalJSON(i)
- return nil
-}
-
-type byteWithPtrMarshalJSON byte
-
-func (b *byteWithPtrMarshalJSON) MarshalJSON() ([]byte, error) {
- return byteWithMarshalJSON(*b).MarshalJSON()
-}
-
-func (b *byteWithPtrMarshalJSON) UnmarshalJSON(data []byte) error {
- return (*byteWithMarshalJSON)(b).UnmarshalJSON(data)
-}
-
-type byteWithMarshalText byte
-
-func (b byteWithMarshalText) MarshalText() ([]byte, error) {
- return []byte(fmt.Sprintf(`Z%.2x`, byte(b))), nil
-}
-
-func (b *byteWithMarshalText) UnmarshalText(data []byte) error {
- if len(data) != 3 || data[0] != 'Z' {
- return fmt.Errorf("bad quoted string")
- }
- i, err := strconv.ParseInt(string(data[1:3]), 16, 8)
- if err != nil {
- return fmt.Errorf("bad hex")
- }
- *b = byteWithMarshalText(i)
- return nil
-}
-
-type byteWithPtrMarshalText byte
-
-func (b *byteWithPtrMarshalText) MarshalText() ([]byte, error) {
- return byteWithMarshalText(*b).MarshalText()
-}
-
-func (b *byteWithPtrMarshalText) UnmarshalText(data []byte) error {
- return (*byteWithMarshalText)(b).UnmarshalText(data)
-}
-
-type intWithMarshalJSON int
-
-func (b intWithMarshalJSON) MarshalJSON() ([]byte, error) {
- return []byte(fmt.Sprintf(`"Z%.2x"`, int(b))), nil
-}
-
-func (b *intWithMarshalJSON) UnmarshalJSON(data []byte) error {
- if len(data) != 5 || data[0] != '"' || data[1] != 'Z' || data[4] != '"' {
- return fmt.Errorf("bad quoted string")
- }
- i, err := strconv.ParseInt(string(data[2:4]), 16, 8)
- if err != nil {
- return fmt.Errorf("bad hex")
- }
- *b = intWithMarshalJSON(i)
- return nil
-}
-
-type intWithPtrMarshalJSON int
-
-func (b *intWithPtrMarshalJSON) MarshalJSON() ([]byte, error) {
- return intWithMarshalJSON(*b).MarshalJSON()
-}
-
-func (b *intWithPtrMarshalJSON) UnmarshalJSON(data []byte) error {
- return (*intWithMarshalJSON)(b).UnmarshalJSON(data)
-}
-
-type intWithMarshalText int
-
-func (b intWithMarshalText) MarshalText() ([]byte, error) {
- return []byte(fmt.Sprintf(`Z%.2x`, int(b))), nil
-}
-
-func (b *intWithMarshalText) UnmarshalText(data []byte) error {
- if len(data) != 3 || data[0] != 'Z' {
- return fmt.Errorf("bad quoted string")
- }
- i, err := strconv.ParseInt(string(data[1:3]), 16, 8)
- if err != nil {
- return fmt.Errorf("bad hex")
- }
- *b = intWithMarshalText(i)
- return nil
-}
-
-type intWithPtrMarshalText int
-
-func (b *intWithPtrMarshalText) MarshalText() ([]byte, error) {
- return intWithMarshalText(*b).MarshalText()
-}
-
-func (b *intWithPtrMarshalText) UnmarshalText(data []byte) error {
- return (*intWithMarshalText)(b).UnmarshalText(data)
-}
-
-type mapStringToStringData struct {
- Data map[string]string `json:"data"`
-}
-
-type unmarshalTest struct {
- in string
- ptr any // new(type)
- out any
- err error
- useNumber bool
- golden bool
- disallowUnknownFields bool
-}
-
-type B struct {
- B bool `json:",string"`
-}
-
-type DoublePtr struct {
- I **int
- J **int
-}
-
-var unmarshalTests = []unmarshalTest{
- // basic types
- {in: `true`, ptr: new(bool), out: true},
- {in: `1`, ptr: new(int), out: 1},
- {in: `1.2`, ptr: new(float64), out: 1.2},
- {in: `-5`, ptr: new(int16), out: int16(-5)},
- {in: `2`, ptr: new(Number), out: Number("2"), useNumber: true},
- {in: `2`, ptr: new(Number), out: Number("2")},
- {in: `2`, ptr: new(any), out: float64(2.0)},
- {in: `2`, ptr: new(any), out: Number("2"), useNumber: true},
- {in: `"a\u1234"`, ptr: new(string), out: "a\u1234"},
- {in: `"http:\/\/"`, ptr: new(string), out: "http://"},
- {in: `"g-clef: \uD834\uDD1E"`, ptr: new(string), out: "g-clef: \U0001D11E"},
- {in: `"invalid: \uD834x\uDD1E"`, ptr: new(string), out: "invalid: \uFFFDx\uFFFD"},
- {in: "null", ptr: new(any), out: nil},
- {in: `{"X": [1,2,3], "Y": 4}`, ptr: new(T), out: T{Y: 4}, err: &UnmarshalTypeError{"array", reflect.TypeOf(""), 7, "T", "X"}},
- {in: `{"X": 23}`, ptr: new(T), out: T{}, err: &UnmarshalTypeError{"number", reflect.TypeOf(""), 8, "T", "X"}}, {in: `{"x": 1}`, ptr: new(tx), out: tx{}},
- {in: `{"x": 1}`, ptr: new(tx), out: tx{}},
- {in: `{"x": 1}`, ptr: new(tx), err: fmt.Errorf("json: unknown field \"x\""), disallowUnknownFields: true},
- {in: `{"S": 23}`, ptr: new(W), out: W{}, err: &UnmarshalTypeError{"number", reflect.TypeOf(SS("")), 0, "W", "S"}},
- {in: `{"F1":1,"F2":2,"F3":3}`, ptr: new(V), out: V{F1: float64(1), F2: int32(2), F3: Number("3")}},
- {in: `{"F1":1,"F2":2,"F3":3}`, ptr: new(V), out: V{F1: Number("1"), F2: int32(2), F3: Number("3")}, useNumber: true},
- {in: `{"k1":1,"k2":"s","k3":[1,2.0,3e-3],"k4":{"kk1":"s","kk2":2}}`, ptr: new(any), out: ifaceNumAsFloat64},
- {in: `{"k1":1,"k2":"s","k3":[1,2.0,3e-3],"k4":{"kk1":"s","kk2":2}}`, ptr: new(any), out: ifaceNumAsNumber, useNumber: true},
-
- // raw values with whitespace
- {in: "\n true ", ptr: new(bool), out: true},
- {in: "\t 1 ", ptr: new(int), out: 1},
- {in: "\r 1.2 ", ptr: new(float64), out: 1.2},
- {in: "\t -5 \n", ptr: new(int16), out: int16(-5)},
- {in: "\t \"a\\u1234\" \n", ptr: new(string), out: "a\u1234"},
-
- // Z has a "-" tag.
- {in: `{"Y": 1, "Z": 2}`, ptr: new(T), out: T{Y: 1}},
- {in: `{"Y": 1, "Z": 2}`, ptr: new(T), err: fmt.Errorf("json: unknown field \"Z\""), disallowUnknownFields: true},
-
- {in: `{"alpha": "abc", "alphabet": "xyz"}`, ptr: new(U), out: U{Alphabet: "abc"}},
- {in: `{"alpha": "abc", "alphabet": "xyz"}`, ptr: new(U), err: fmt.Errorf("json: unknown field \"alphabet\""), disallowUnknownFields: true},
- {in: `{"alpha": "abc"}`, ptr: new(U), out: U{Alphabet: "abc"}},
- {in: `{"alphabet": "xyz"}`, ptr: new(U), out: U{}},
- {in: `{"alphabet": "xyz"}`, ptr: new(U), err: fmt.Errorf("json: unknown field \"alphabet\""), disallowUnknownFields: true},
-
- // syntax errors
- {in: `{"X": "foo", "Y"}`, err: &SyntaxError{"invalid character '}' after object key", 17}},
- {in: `[1, 2, 3+]`, err: &SyntaxError{"invalid character '+' after array element", 9}},
- {in: `{"X":12x}`, err: &SyntaxError{"invalid character 'x' after object key:value pair", 8}, useNumber: true},
- {in: `[2, 3`, err: &SyntaxError{msg: "unexpected end of JSON input", Offset: 5}},
- {in: `{"F3": -}`, ptr: new(V), out: V{F3: Number("-")}, err: &SyntaxError{msg: "invalid character '}' in numeric literal", Offset: 9}},
-
- // raw value errors
- {in: "\x01 42", err: &SyntaxError{"invalid character '\\x01' looking for beginning of value", 1}},
- {in: " 42 \x01", err: &SyntaxError{"invalid character '\\x01' after top-level value", 5}},
- {in: "\x01 true", err: &SyntaxError{"invalid character '\\x01' looking for beginning of value", 1}},
- {in: " false \x01", err: &SyntaxError{"invalid character '\\x01' after top-level value", 8}},
- {in: "\x01 1.2", err: &SyntaxError{"invalid character '\\x01' looking for beginning of value", 1}},
- {in: " 3.4 \x01", err: &SyntaxError{"invalid character '\\x01' after top-level value", 6}},
- {in: "\x01 \"string\"", err: &SyntaxError{"invalid character '\\x01' looking for beginning of value", 1}},
- {in: " \"string\" \x01", err: &SyntaxError{"invalid character '\\x01' after top-level value", 11}},
-
- // array tests
- {in: `[1, 2, 3]`, ptr: new([3]int), out: [3]int{1, 2, 3}},
- {in: `[1, 2, 3]`, ptr: new([1]int), out: [1]int{1}},
- {in: `[1, 2, 3]`, ptr: new([5]int), out: [5]int{1, 2, 3, 0, 0}},
- {in: `[1, 2, 3]`, ptr: new(MustNotUnmarshalJSON), err: errors.New("MustNotUnmarshalJSON was used")},
-
- // empty array to interface test
- {in: `[]`, ptr: new([]any), out: []any{}},
- {in: `null`, ptr: new([]any), out: []any(nil)},
- {in: `{"T":[]}`, ptr: new(map[string]any), out: map[string]any{"T": []any{}}},
- {in: `{"T":null}`, ptr: new(map[string]any), out: map[string]any{"T": any(nil)}},
-
- // composite tests
- {in: allValueIndent, ptr: new(All), out: allValue},
- {in: allValueCompact, ptr: new(All), out: allValue},
- {in: allValueIndent, ptr: new(*All), out: &allValue},
- {in: allValueCompact, ptr: new(*All), out: &allValue},
- {in: pallValueIndent, ptr: new(All), out: pallValue},
- {in: pallValueCompact, ptr: new(All), out: pallValue},
- {in: pallValueIndent, ptr: new(*All), out: &pallValue},
- {in: pallValueCompact, ptr: new(*All), out: &pallValue},
-
- // unmarshal interface test
- {in: `{"T":false}`, ptr: new(unmarshaler), out: umtrue}, // use "false" so test will fail if custom unmarshaler is not called
- {in: `{"T":false}`, ptr: new(*unmarshaler), out: &umtrue},
- {in: `[{"T":false}]`, ptr: new([]unmarshaler), out: umslice},
- {in: `[{"T":false}]`, ptr: new(*[]unmarshaler), out: &umslice},
- {in: `{"M":{"T":"x:y"}}`, ptr: new(ustruct), out: umstruct},
-
- // UnmarshalText interface test
- {in: `"x:y"`, ptr: new(unmarshalerText), out: umtrueXY},
- {in: `"x:y"`, ptr: new(*unmarshalerText), out: &umtrueXY},
- {in: `["x:y"]`, ptr: new([]unmarshalerText), out: umsliceXY},
- {in: `["x:y"]`, ptr: new(*[]unmarshalerText), out: &umsliceXY},
- {in: `{"M":"x:y"}`, ptr: new(ustructText), out: umstructXY},
-
- // integer-keyed map test
- {
- in: `{"-1":"a","0":"b","1":"c"}`,
- ptr: new(map[int]string),
- out: map[int]string{-1: "a", 0: "b", 1: "c"},
- },
- {
- in: `{"0":"a","10":"c","9":"b"}`,
- ptr: new(map[u8]string),
- out: map[u8]string{0: "a", 9: "b", 10: "c"},
- },
- {
- in: `{"-9223372036854775808":"min","9223372036854775807":"max"}`,
- ptr: new(map[int64]string),
- out: map[int64]string{math.MinInt64: "min", math.MaxInt64: "max"},
- },
- {
- in: `{"18446744073709551615":"max"}`,
- ptr: new(map[uint64]string),
- out: map[uint64]string{math.MaxUint64: "max"},
- },
- {
- in: `{"0":false,"10":true}`,
- ptr: new(map[uintptr]bool),
- out: map[uintptr]bool{0: false, 10: true},
- },
-
- // Check that MarshalText and UnmarshalText take precedence
- // over default integer handling in map keys.
- {
- in: `{"u2":4}`,
- ptr: new(map[u8marshal]int),
- out: map[u8marshal]int{2: 4},
- },
- {
- in: `{"2":4}`,
- ptr: new(map[u8marshal]int),
- err: errMissingU8Prefix,
- },
-
- // integer-keyed map errors
- {
- in: `{"abc":"abc"}`,
- ptr: new(map[int]string),
- err: &UnmarshalTypeError{Value: "number abc", Type: reflect.TypeOf(0), Offset: 2},
- },
- {
- in: `{"256":"abc"}`,
- ptr: new(map[uint8]string),
- err: &UnmarshalTypeError{Value: "number 256", Type: reflect.TypeOf(uint8(0)), Offset: 2},
- },
- {
- in: `{"128":"abc"}`,
- ptr: new(map[int8]string),
- err: &UnmarshalTypeError{Value: "number 128", Type: reflect.TypeOf(int8(0)), Offset: 2},
- },
- {
- in: `{"-1":"abc"}`,
- ptr: new(map[uint8]string),
- err: &UnmarshalTypeError{Value: "number -1", Type: reflect.TypeOf(uint8(0)), Offset: 2},
- },
- {
- in: `{"F":{"a":2,"3":4}}`,
- ptr: new(map[string]map[int]int),
- err: &UnmarshalTypeError{Value: "number a", Type: reflect.TypeOf(int(0)), Offset: 7},
- },
- {
- in: `{"F":{"a":2,"3":4}}`,
- ptr: new(map[string]map[uint]int),
- err: &UnmarshalTypeError{Value: "number a", Type: reflect.TypeOf(uint(0)), Offset: 7},
- },
-
- // Map keys can be encoding.TextUnmarshalers.
- {in: `{"x:y":true}`, ptr: new(map[unmarshalerText]bool), out: ummapXY},
- // If multiple values for the same key exists, only the most recent value is used.
- {in: `{"x:y":false,"x:y":true}`, ptr: new(map[unmarshalerText]bool), out: ummapXY},
-
- {
- in: `{
- "Level0": 1,
- "Level1b": 2,
- "Level1c": 3,
- "x": 4,
- "Level1a": 5,
- "LEVEL1B": 6,
- "e": {
- "Level1a": 8,
- "Level1b": 9,
- "Level1c": 10,
- "Level1d": 11,
- "x": 12
- },
- "Loop1": 13,
- "Loop2": 14,
- "X": 15,
- "Y": 16,
- "Z": 17,
- "Q": 18
- }`,
- ptr: new(Top),
- out: Top{
- Level0: 1,
- Embed0: Embed0{
- Level1b: 2,
- Level1c: 3,
- },
- Embed0a: &Embed0a{
- Level1a: 5,
- Level1b: 6,
- },
- Embed0b: &Embed0b{
- Level1a: 8,
- Level1b: 9,
- Level1c: 10,
- Level1d: 11,
- Level1e: 12,
- },
- Loop: Loop{
- Loop1: 13,
- Loop2: 14,
- },
- Embed0p: Embed0p{
- Point: image.Point{X: 15, Y: 16},
- },
- Embed0q: Embed0q{
- Point: Point{Z: 17},
- },
- embed: embed{
- Q: 18,
- },
- },
- },
- {
- in: `{"hello": 1}`,
- ptr: new(Ambig),
- out: Ambig{First: 1},
- },
-
- {
- in: `{"X": 1,"Y":2}`,
- ptr: new(S5),
- out: S5{S8: S8{S9: S9{Y: 2}}},
- },
- {
- in: `{"X": 1,"Y":2}`,
- ptr: new(S5),
- err: fmt.Errorf("json: unknown field \"X\""),
- disallowUnknownFields: true,
- },
- {
- in: `{"X": 1,"Y":2}`,
- ptr: new(S10),
- out: S10{S13: S13{S8: S8{S9: S9{Y: 2}}}},
- },
- {
- in: `{"X": 1,"Y":2}`,
- ptr: new(S10),
- err: fmt.Errorf("json: unknown field \"X\""),
- disallowUnknownFields: true,
- },
- {
- in: `{"I": 0, "I": null, "J": null}`,
- ptr: new(DoublePtr),
- out: DoublePtr{I: nil, J: nil},
- },
-
- // invalid UTF-8 is coerced to valid UTF-8.
- {
- in: "\"hello\xffworld\"",
- ptr: new(string),
- out: "hello\ufffdworld",
- },
- {
- in: "\"hello\xc2\xc2world\"",
- ptr: new(string),
- out: "hello\ufffd\ufffdworld",
- },
- {
- in: "\"hello\xc2\xffworld\"",
- ptr: new(string),
- out: "hello\ufffd\ufffdworld",
- },
- {
- in: "\"hello\\ud800world\"",
- ptr: new(string),
- out: "hello\ufffdworld",
- },
- {
- in: "\"hello\\ud800\\ud800world\"",
- ptr: new(string),
- out: "hello\ufffd\ufffdworld",
- },
- {
- in: "\"hello\\ud800\\ud800world\"",
- ptr: new(string),
- out: "hello\ufffd\ufffdworld",
- },
- {
- in: "\"hello\xed\xa0\x80\xed\xb0\x80world\"",
- ptr: new(string),
- out: "hello\ufffd\ufffd\ufffd\ufffd\ufffd\ufffdworld",
- },
-
- // Used to be issue 8305, but time.Time implements encoding.TextUnmarshaler so this works now.
- {
- in: `{"2009-11-10T23:00:00Z": "hello world"}`,
- ptr: new(map[time.Time]string),
- out: map[time.Time]string{time.Date(2009, 11, 10, 23, 0, 0, 0, time.UTC): "hello world"},
- },
-
- // issue 8305
- {
- in: `{"2009-11-10T23:00:00Z": "hello world"}`,
- ptr: new(map[Point]string),
- err: &UnmarshalTypeError{Value: "object", Type: reflect.TypeOf(map[Point]string{}), Offset: 1},
- },
- {
- in: `{"asdf": "hello world"}`,
- ptr: new(map[unmarshaler]string),
- err: &UnmarshalTypeError{Value: "object", Type: reflect.TypeOf(map[unmarshaler]string{}), Offset: 1},
- },
-
- // related to issue 13783.
- // Go 1.7 changed marshaling a slice of typed byte to use the methods on the byte type,
- // similar to marshaling a slice of typed int.
- // These tests check that, assuming the byte type also has valid decoding methods,
- // either the old base64 string encoding or the new per-element encoding can be
- // successfully unmarshaled. The custom unmarshalers were accessible in earlier
- // versions of Go, even though the custom marshaler was not.
- {
- in: `"AQID"`,
- ptr: new([]byteWithMarshalJSON),
- out: []byteWithMarshalJSON{1, 2, 3},
- },
- {
- in: `["Z01","Z02","Z03"]`,
- ptr: new([]byteWithMarshalJSON),
- out: []byteWithMarshalJSON{1, 2, 3},
- golden: true,
- },
- {
- in: `"AQID"`,
- ptr: new([]byteWithMarshalText),
- out: []byteWithMarshalText{1, 2, 3},
- },
- {
- in: `["Z01","Z02","Z03"]`,
- ptr: new([]byteWithMarshalText),
- out: []byteWithMarshalText{1, 2, 3},
- golden: true,
- },
- {
- in: `"AQID"`,
- ptr: new([]byteWithPtrMarshalJSON),
- out: []byteWithPtrMarshalJSON{1, 2, 3},
- },
- {
- in: `["Z01","Z02","Z03"]`,
- ptr: new([]byteWithPtrMarshalJSON),
- out: []byteWithPtrMarshalJSON{1, 2, 3},
- golden: true,
- },
- {
- in: `"AQID"`,
- ptr: new([]byteWithPtrMarshalText),
- out: []byteWithPtrMarshalText{1, 2, 3},
- },
- {
- in: `["Z01","Z02","Z03"]`,
- ptr: new([]byteWithPtrMarshalText),
- out: []byteWithPtrMarshalText{1, 2, 3},
- golden: true,
- },
-
- // ints work with the marshaler but not the base64 []byte case
- {
- in: `["Z01","Z02","Z03"]`,
- ptr: new([]intWithMarshalJSON),
- out: []intWithMarshalJSON{1, 2, 3},
- golden: true,
- },
- {
- in: `["Z01","Z02","Z03"]`,
- ptr: new([]intWithMarshalText),
- out: []intWithMarshalText{1, 2, 3},
- golden: true,
- },
- {
- in: `["Z01","Z02","Z03"]`,
- ptr: new([]intWithPtrMarshalJSON),
- out: []intWithPtrMarshalJSON{1, 2, 3},
- golden: true,
- },
- {
- in: `["Z01","Z02","Z03"]`,
- ptr: new([]intWithPtrMarshalText),
- out: []intWithPtrMarshalText{1, 2, 3},
- golden: true,
- },
-
- {in: `0.000001`, ptr: new(float64), out: 0.000001, golden: true},
- {in: `1e-7`, ptr: new(float64), out: 1e-7, golden: true},
- {in: `100000000000000000000`, ptr: new(float64), out: 100000000000000000000.0, golden: true},
- {in: `1e+21`, ptr: new(float64), out: 1e21, golden: true},
- {in: `-0.000001`, ptr: new(float64), out: -0.000001, golden: true},
- {in: `-1e-7`, ptr: new(float64), out: -1e-7, golden: true},
- {in: `-100000000000000000000`, ptr: new(float64), out: -100000000000000000000.0, golden: true},
- {in: `-1e+21`, ptr: new(float64), out: -1e21, golden: true},
- {in: `999999999999999900000`, ptr: new(float64), out: 999999999999999900000.0, golden: true},
- {in: `9007199254740992`, ptr: new(float64), out: 9007199254740992.0, golden: true},
- {in: `9007199254740993`, ptr: new(float64), out: 9007199254740992.0, golden: false},
-
- {
- in: `{"V": {"F2": "hello"}}`,
- ptr: new(VOuter),
- err: &UnmarshalTypeError{
- Value: "string",
- Struct: "V",
- Field: "V.F2",
- Type: reflect.TypeOf(int32(0)),
- Offset: 20,
- },
- },
- {
- in: `{"V": {"F4": {}, "F2": "hello"}}`,
- ptr: new(VOuter),
- err: &UnmarshalTypeError{
- Value: "string",
- Struct: "V",
- Field: "V.F2",
- Type: reflect.TypeOf(int32(0)),
- Offset: 30,
- },
- },
-
- // issue 15146.
- // invalid inputs in wrongStringTests below.
- {in: `{"B":"true"}`, ptr: new(B), out: B{true}, golden: true},
- {in: `{"B":"false"}`, ptr: new(B), out: B{false}, golden: true},
- {in: `{"B": "maybe"}`, ptr: new(B), err: errors.New(`json: invalid use of ,string struct tag, trying to unmarshal "maybe" into bool`)},
- {in: `{"B": "tru"}`, ptr: new(B), err: errors.New(`json: invalid use of ,string struct tag, trying to unmarshal "tru" into bool`)},
- {in: `{"B": "False"}`, ptr: new(B), err: errors.New(`json: invalid use of ,string struct tag, trying to unmarshal "False" into bool`)},
- {in: `{"B": "null"}`, ptr: new(B), out: B{false}},
- {in: `{"B": "nul"}`, ptr: new(B), err: errors.New(`json: invalid use of ,string struct tag, trying to unmarshal "nul" into bool`)},
- {in: `{"B": [2, 3]}`, ptr: new(B), err: errors.New(`json: invalid use of ,string struct tag, trying to unmarshal unquoted value into bool`)},
-
- // additional tests for disallowUnknownFields
- {
- in: `{
- "Level0": 1,
- "Level1b": 2,
- "Level1c": 3,
- "x": 4,
- "Level1a": 5,
- "LEVEL1B": 6,
- "e": {
- "Level1a": 8,
- "Level1b": 9,
- "Level1c": 10,
- "Level1d": 11,
- "x": 12
- },
- "Loop1": 13,
- "Loop2": 14,
- "X": 15,
- "Y": 16,
- "Z": 17,
- "Q": 18,
- "extra": true
- }`,
- ptr: new(Top),
- err: fmt.Errorf("json: unknown field \"extra\""),
- disallowUnknownFields: true,
- },
- {
- in: `{
- "Level0": 1,
- "Level1b": 2,
- "Level1c": 3,
- "x": 4,
- "Level1a": 5,
- "LEVEL1B": 6,
- "e": {
- "Level1a": 8,
- "Level1b": 9,
- "Level1c": 10,
- "Level1d": 11,
- "x": 12,
- "extra": null
- },
- "Loop1": 13,
- "Loop2": 14,
- "X": 15,
- "Y": 16,
- "Z": 17,
- "Q": 18
- }`,
- ptr: new(Top),
- err: fmt.Errorf("json: unknown field \"extra\""),
- disallowUnknownFields: true,
- },
- // issue 26444
- // UnmarshalTypeError without field & struct values
- {
- in: `{"data":{"test1": "bob", "test2": 123}}`,
- ptr: new(mapStringToStringData),
- err: &UnmarshalTypeError{Value: "number", Type: reflect.TypeOf(""), Offset: 37, Struct: "mapStringToStringData", Field: "data"},
- },
- {
- in: `{"data":{"test1": 123, "test2": "bob"}}`,
- ptr: new(mapStringToStringData),
- err: &UnmarshalTypeError{Value: "number", Type: reflect.TypeOf(""), Offset: 21, Struct: "mapStringToStringData", Field: "data"},
- },
-
- // trying to decode JSON arrays or objects via TextUnmarshaler
- {
- in: `[1, 2, 3]`,
- ptr: new(MustNotUnmarshalText),
- err: &UnmarshalTypeError{Value: "array", Type: reflect.TypeOf(&MustNotUnmarshalText{}), Offset: 1},
- },
- {
- in: `{"foo": "bar"}`,
- ptr: new(MustNotUnmarshalText),
- err: &UnmarshalTypeError{Value: "object", Type: reflect.TypeOf(&MustNotUnmarshalText{}), Offset: 1},
- },
- // #22369
- {
- in: `{"PP": {"T": {"Y": "bad-type"}}}`,
- ptr: new(P),
- err: &UnmarshalTypeError{
- Value: "string",
- Struct: "T",
- Field: "PP.T.Y",
- Type: reflect.TypeOf(int(0)),
- Offset: 29,
- },
- },
- {
- in: `{"Ts": [{"Y": 1}, {"Y": 2}, {"Y": "bad-type"}]}`,
- ptr: new(PP),
- err: &UnmarshalTypeError{
- Value: "string",
- Struct: "T",
- Field: "Ts.Y",
- Type: reflect.TypeOf(int(0)),
- Offset: 29,
- },
- },
- // #14702
- {
- in: `invalid`,
- ptr: new(Number),
- err: &SyntaxError{
- msg: "invalid character 'i' looking for beginning of value",
- Offset: 1,
- },
- },
- {
- in: `"invalid"`,
- ptr: new(Number),
- err: fmt.Errorf("json: invalid number literal, trying to unmarshal %q into Number", `"invalid"`),
- },
- {
- in: `{"A":"invalid"}`,
- ptr: new(struct{ A Number }),
- err: fmt.Errorf("json: invalid number literal, trying to unmarshal %q into Number", `"invalid"`),
- },
- {
- in: `{"A":"invalid"}`,
- ptr: new(struct {
- A Number `json:",string"`
- }),
- err: fmt.Errorf("json: invalid use of ,string struct tag, trying to unmarshal %q into json.Number", `invalid`),
- },
- {
- in: `{"A":"invalid"}`,
- ptr: new(map[string]Number),
- err: fmt.Errorf("json: invalid number literal, trying to unmarshal %q into Number", `"invalid"`),
- },
-}
-
-func TestMarshal(t *testing.T) {
- b, err := Marshal(allValue)
- if err != nil {
- t.Fatalf("Marshal allValue: %v", err)
- }
- if string(b) != allValueCompact {
- t.Errorf("Marshal allValueCompact")
- diff(t, b, []byte(allValueCompact))
- return
- }
-
- b, err = Marshal(pallValue)
- if err != nil {
- t.Fatalf("Marshal pallValue: %v", err)
- }
- if string(b) != pallValueCompact {
- t.Errorf("Marshal pallValueCompact")
- diff(t, b, []byte(pallValueCompact))
- return
- }
-}
-
-var badUTF8 = []struct {
- in, out string
-}{
- {"hello\xffworld", `"hello\ufffdworld"`},
- {"", `""`},
- {"\xff", `"\ufffd"`},
- {"\xff\xff", `"\ufffd\ufffd"`},
- {"a\xffb", `"a\ufffdb"`},
- {"\xe6\x97\xa5\xe6\x9c\xac\xff\xaa\x9e", `"日本\ufffd\ufffd\ufffd"`},
-}
-
-func TestMarshalBadUTF8(t *testing.T) {
- for _, tt := range badUTF8 {
- b, err := Marshal(tt.in)
- if string(b) != tt.out || err != nil {
- t.Errorf("Marshal(%q) = %#q, %v, want %#q, nil", tt.in, b, err, tt.out)
- }
- }
-}
-
-func TestMarshalNumberZeroVal(t *testing.T) {
- var n Number
- out, err := Marshal(n)
- if err != nil {
- t.Fatal(err)
- }
- outStr := string(out)
- if outStr != "0" {
- t.Fatalf("Invalid zero val for Number: %q", outStr)
- }
-}
-
-func TestMarshalEmbeds(t *testing.T) {
- t.Skip() // TODO
- top := &Top{
- Level0: 1,
- Embed0: Embed0{
- Level1b: 2,
- Level1c: 3,
- },
- Embed0a: &Embed0a{
- Level1a: 5,
- Level1b: 6,
- },
- Embed0b: &Embed0b{
- Level1a: 8,
- Level1b: 9,
- Level1c: 10,
- Level1d: 11,
- Level1e: 12,
- },
- Loop: Loop{
- Loop1: 13,
- Loop2: 14,
- },
- Embed0p: Embed0p{
- Point: image.Point{X: 15, Y: 16},
- },
- Embed0q: Embed0q{
- Point: Point{Z: 17},
- },
- embed: embed{
- Q: 18,
- },
- }
- b, err := Marshal(top)
- if err != nil {
- t.Fatal(err)
- }
- want := "{\"Level0\":1,\"Level1b\":2,\"Level1c\":3,\"Level1a\":5,\"LEVEL1B\":6,\"e\":{\"Level1a\":8,\"Level1b\":9,\"Level1c\":10,\"Level1d\":11,\"x\":12},\"Loop1\":13,\"Loop2\":14,\"X\":15,\"Y\":16,\"Z\":17,\"Q\":18}"
- if string(b) != want {
- t.Errorf("Wrong marshal result.\n got: %q\nwant: %q", b, want)
- }
-}
-
-func equalError(a, b error) bool {
- if a == nil {
- return b == nil
- }
- if b == nil {
- return a == nil
- }
- return true // a.Error() == b.Error() // MODIFIED
-}
-
-func TestUnmarshal(t *testing.T) {
- t.Skip() // TODO
- for i, tt := range unmarshalTests {
- scan := &ReEncoder{
- Out: io.Discard,
- }
- in := []byte(tt.in)
- if _, err := scan.Write(in); err != nil {
- if !equalError(err, tt.err) {
- t.Errorf("#%d: checkValid: %#v\n\n%s", i, err, tt.in)
- continue
- }
- }
- if tt.ptr == nil {
- continue
- }
-
- typ := reflect.TypeOf(tt.ptr)
- if typ.Kind() != reflect.Pointer {
- t.Errorf("#%d: unmarshalTest.ptr %T is not a pointer type", i, tt.ptr)
- continue
- }
- typ = typ.Elem()
-
- // v = new(right-type)
- v := reflect.New(typ)
-
- if !reflect.DeepEqual(tt.ptr, v.Interface()) {
- // There's no reason for ptr to point to non-zero data,
- // as we decode into new(right-type), so the data is
- // discarded.
- // This can easily mean tests that silently don't test
- // what they should. To test decoding into existing
- // data, see TestPrefilled.
- t.Errorf("#%d: unmarshalTest.ptr %#v is not a pointer to a zero value", i, tt.ptr)
- continue
- }
-
- dec := NewDecoder(bytes.NewReader(in))
- if tt.useNumber {
- dec.UseNumber()
- }
- if tt.disallowUnknownFields {
- dec.DisallowUnknownFields()
- }
- if err := dec.Decode(v.Interface()); !equalError(err, tt.err) {
- t.Errorf("#%d: %v, want %v", i, err, tt.err)
- continue
- } else if err != nil {
- continue
- }
- if !reflect.DeepEqual(v.Elem().Interface(), tt.out) {
- t.Errorf("#%d: mismatch\nhave: %#+v\nwant: %#+v\n\n%s", i, v.Elem().Interface(), tt.out, tt.in)
- data, _ := Marshal(v.Elem().Interface())
- println(string(data))
- data, _ = Marshal(tt.out)
- println(string(data))
- continue
- }
-
- // Check round trip also decodes correctly.
- if tt.err == nil {
- enc, err := Marshal(v.Interface())
- if err != nil {
- t.Errorf("#%d: error re-marshaling: %v", i, err)
- continue
- }
- if tt.golden && !bytes.Equal(enc, in) {
- t.Errorf("#%d: remarshal mismatch:\nhave: %s\nwant: %s", i, enc, in)
- }
- vv := reflect.New(reflect.TypeOf(tt.ptr).Elem())
- dec = NewDecoder(bytes.NewReader(enc))
- if tt.useNumber {
- dec.UseNumber()
- }
- if err := dec.Decode(vv.Interface()); err != nil {
- t.Errorf("#%d: error re-unmarshaling %#q: %v", i, enc, err)
- continue
- }
- if !reflect.DeepEqual(v.Elem().Interface(), vv.Elem().Interface()) {
- t.Errorf("#%d: mismatch\nhave: %#+v\nwant: %#+v", i, v.Elem().Interface(), vv.Elem().Interface())
- t.Errorf(" In: %q", strings.Map(noSpace, string(in)))
- t.Errorf("Marshal: %q", strings.Map(noSpace, string(enc)))
- continue
- }
- }
- }
-}
-
-func TestUnmarshalMarshal(t *testing.T) {
- initBig()
- var v any
- if err := Unmarshal(jsonBig, &v); err != nil {
- t.Fatalf("Unmarshal: %v", err)
- }
- b, err := Marshal(v)
- if err != nil {
- t.Fatalf("Marshal: %v", err)
- }
- if !bytes.Equal(jsonBig, b) {
- t.Errorf("Marshal jsonBig")
- diff(t, b, jsonBig)
- return
- }
-}
-
-var numberTests = []struct {
- in string
- i int64
- intErr string
- f float64
- floatErr string
-}{
- {in: "-1.23e1", intErr: "strconv.ParseInt: parsing \"-1.23e1\": invalid syntax", f: -1.23e1},
- {in: "-12", i: -12, f: -12.0},
- {in: "1e1000", intErr: "strconv.ParseInt: parsing \"1e1000\": invalid syntax", floatErr: "strconv.ParseFloat: parsing \"1e1000\": value out of range"},
-}
-
-// Independent of Decode, basic coverage of the accessors in Number
-func TestNumberAccessors(t *testing.T) {
- for _, tt := range numberTests {
- n := Number(tt.in)
- if s := n.String(); s != tt.in {
- t.Errorf("Number(%q).String() is %q", tt.in, s)
- }
- if i, err := n.Int64(); err == nil && tt.intErr == "" && i != tt.i {
- t.Errorf("Number(%q).Int64() is %d", tt.in, i)
- } else if (err == nil && tt.intErr != "") || (err != nil && err.Error() != tt.intErr) {
- t.Errorf("Number(%q).Int64() wanted error %q but got: %v", tt.in, tt.intErr, err)
- }
- if f, err := n.Float64(); err == nil && tt.floatErr == "" && f != tt.f {
- t.Errorf("Number(%q).Float64() is %g", tt.in, f)
- } else if (err == nil && tt.floatErr != "") || (err != nil && err.Error() != tt.floatErr) {
- t.Errorf("Number(%q).Float64() wanted error %q but got: %v", tt.in, tt.floatErr, err)
- }
- }
-}
-
-func TestLargeByteSlice(t *testing.T) {
- s0 := make([]byte, 2000)
- for i := range s0 {
- s0[i] = byte(i)
- }
- b, err := Marshal(s0)
- if err != nil {
- t.Fatalf("Marshal: %v", err)
- }
- var s1 []byte
- if err := Unmarshal(b, &s1); err != nil {
- t.Fatalf("Unmarshal: %v", err)
- }
- if !bytes.Equal(s0, s1) {
- t.Errorf("Marshal large byte slice")
- diff(t, s0, s1)
- }
-}
-
-type Xint struct {
- X int
-}
-
-func TestUnmarshalInterface(t *testing.T) {
- var xint Xint
- var i any = &xint
- if err := Unmarshal([]byte(`{"X":1}`), &i); err != nil {
- t.Fatalf("Unmarshal: %v", err)
- }
- if xint.X != 1 {
- t.Fatalf("Did not write to xint")
- }
-}
-
-func TestUnmarshalPtrPtr(t *testing.T) {
- var xint Xint
- pxint := &xint
- if err := Unmarshal([]byte(`{"X":1}`), &pxint); err != nil {
- t.Fatalf("Unmarshal: %v", err)
- }
- if xint.X != 1 {
- t.Fatalf("Did not write to xint")
- }
-}
-
-func TestEscape(t *testing.T) {
- const input = `"foobar"<html>` + " [\u2028 \u2029]"
- const expected = `"\"foobar\"\u003chtml\u003e [\u2028 \u2029]"`
- b, err := Marshal(input)
- if err != nil {
- t.Fatalf("Marshal error: %v", err)
- }
- if s := string(b); s != expected {
- t.Errorf("Encoding of [%s]:\n got [%s]\nwant [%s]", input, s, expected)
- }
-}
-
-// WrongString is a struct that's misusing the ,string modifier.
-type WrongString struct {
- Message string `json:"result,string"`
-}
-
-type wrongStringTest struct {
- in, err string
-}
-
-var wrongStringTests = []wrongStringTest{
- {`{"result":"x"}`, `json: invalid use of ,string struct tag, trying to unmarshal "x" into string`},
- {`{"result":"foo"}`, `json: invalid use of ,string struct tag, trying to unmarshal "foo" into string`},
- {`{"result":"123"}`, `json: invalid use of ,string struct tag, trying to unmarshal "123" into string`},
- {`{"result":123}`, `json: invalid use of ,string struct tag, trying to unmarshal unquoted value into string`},
- {`{"result":"\""}`, `json: invalid use of ,string struct tag, trying to unmarshal "\"" into string`},
- {`{"result":"\"foo"}`, `json: invalid use of ,string struct tag, trying to unmarshal "\"foo" into string`},
-}
-
-// If people misuse the ,string modifier, the error message should be
-// helpful, telling the user that they're doing it wrong.
-func TestErrorMessageFromMisusedString(t *testing.T) {
- for n, tt := range wrongStringTests {
- r := strings.NewReader(tt.in)
- var s WrongString
- err := NewDecoder(r).Decode(&s)
- got := fmt.Sprintf("%v", err)
- if err == nil { // if got != tt.err { // MODIFIED
- t.Errorf("%d. got err = %q, want %q", n, got, tt.err)
- }
- }
-}
-
-func noSpace(c rune) rune {
- if isSpace(byte(c)) { //only used for ascii
- return -1
- }
- return c
-}
-
-type All struct {
- Bool bool
- Int int
- Int8 int8
- Int16 int16
- Int32 int32
- Int64 int64
- Uint uint
- Uint8 uint8
- Uint16 uint16
- Uint32 uint32
- Uint64 uint64
- Uintptr uintptr
- Float32 float32
- Float64 float64
-
- Foo string `json:"bar"`
- Foo2 string `json:"bar2,dummyopt"`
-
- IntStr int64 `json:",string"`
- UintptrStr uintptr `json:",string"`
-
- PBool *bool
- PInt *int
- PInt8 *int8
- PInt16 *int16
- PInt32 *int32
- PInt64 *int64
- PUint *uint
- PUint8 *uint8
- PUint16 *uint16
- PUint32 *uint32
- PUint64 *uint64
- PUintptr *uintptr
- PFloat32 *float32
- PFloat64 *float64
-
- String string
- PString *string
-
- Map map[string]Small
- MapP map[string]*Small
- PMap *map[string]Small
- PMapP *map[string]*Small
-
- EmptyMap map[string]Small
- NilMap map[string]Small
-
- Slice []Small
- SliceP []*Small
- PSlice *[]Small
- PSliceP *[]*Small
-
- EmptySlice []Small
- NilSlice []Small
-
- StringSlice []string
- ByteSlice []byte
-
- Small Small
- PSmall *Small
- PPSmall **Small
-
- Interface any
- PInterface *any
-
- unexported int
-}
-
-type Small struct {
- Tag string
-}
-
-var allValue = All{
- Bool: true,
- Int: 2,
- Int8: 3,
- Int16: 4,
- Int32: 5,
- Int64: 6,
- Uint: 7,
- Uint8: 8,
- Uint16: 9,
- Uint32: 10,
- Uint64: 11,
- Uintptr: 12,
- Float32: 14.1,
- Float64: 15.1,
- Foo: "foo",
- Foo2: "foo2",
- IntStr: 42,
- UintptrStr: 44,
- String: "16",
- Map: map[string]Small{
- "17": {Tag: "tag17"},
- "18": {Tag: "tag18"},
- },
- MapP: map[string]*Small{
- "19": {Tag: "tag19"},
- "20": nil,
- },
- EmptyMap: map[string]Small{},
- Slice: []Small{{Tag: "tag20"}, {Tag: "tag21"}},
- SliceP: []*Small{{Tag: "tag22"}, nil, {Tag: "tag23"}},
- EmptySlice: []Small{},
- StringSlice: []string{"str24", "str25", "str26"},
- ByteSlice: []byte{27, 28, 29},
- Small: Small{Tag: "tag30"},
- PSmall: &Small{Tag: "tag31"},
- Interface: 5.2,
-}
-
-var pallValue = All{
- PBool: &allValue.Bool,
- PInt: &allValue.Int,
- PInt8: &allValue.Int8,
- PInt16: &allValue.Int16,
- PInt32: &allValue.Int32,
- PInt64: &allValue.Int64,
- PUint: &allValue.Uint,
- PUint8: &allValue.Uint8,
- PUint16: &allValue.Uint16,
- PUint32: &allValue.Uint32,
- PUint64: &allValue.Uint64,
- PUintptr: &allValue.Uintptr,
- PFloat32: &allValue.Float32,
- PFloat64: &allValue.Float64,
- PString: &allValue.String,
- PMap: &allValue.Map,
- PMapP: &allValue.MapP,
- PSlice: &allValue.Slice,
- PSliceP: &allValue.SliceP,
- PPSmall: &allValue.PSmall,
- PInterface: &allValue.Interface,
-}
-
-var allValueIndent = `{
- "Bool": true,
- "Int": 2,
- "Int8": 3,
- "Int16": 4,
- "Int32": 5,
- "Int64": 6,
- "Uint": 7,
- "Uint8": 8,
- "Uint16": 9,
- "Uint32": 10,
- "Uint64": 11,
- "Uintptr": 12,
- "Float32": 14.1,
- "Float64": 15.1,
- "bar": "foo",
- "bar2": "foo2",
- "IntStr": "42",
- "UintptrStr": "44",
- "PBool": null,
- "PInt": null,
- "PInt8": null,
- "PInt16": null,
- "PInt32": null,
- "PInt64": null,
- "PUint": null,
- "PUint8": null,
- "PUint16": null,
- "PUint32": null,
- "PUint64": null,
- "PUintptr": null,
- "PFloat32": null,
- "PFloat64": null,
- "String": "16",
- "PString": null,
- "Map": {
- "17": {
- "Tag": "tag17"
- },
- "18": {
- "Tag": "tag18"
- }
- },
- "MapP": {
- "19": {
- "Tag": "tag19"
- },
- "20": null
- },
- "PMap": null,
- "PMapP": null,
- "EmptyMap": {},
- "NilMap": null,
- "Slice": [
- {
- "Tag": "tag20"
- },
- {
- "Tag": "tag21"
- }
- ],
- "SliceP": [
- {
- "Tag": "tag22"
- },
- null,
- {
- "Tag": "tag23"
- }
- ],
- "PSlice": null,
- "PSliceP": null,
- "EmptySlice": [],
- "NilSlice": null,
- "StringSlice": [
- "str24",
- "str25",
- "str26"
- ],
- "ByteSlice": "Gxwd",
- "Small": {
- "Tag": "tag30"
- },
- "PSmall": {
- "Tag": "tag31"
- },
- "PPSmall": null,
- "Interface": 5.2,
- "PInterface": null
-}`
-
-var allValueCompact = strings.Map(noSpace, allValueIndent)
-
-var pallValueIndent = `{
- "Bool": false,
- "Int": 0,
- "Int8": 0,
- "Int16": 0,
- "Int32": 0,
- "Int64": 0,
- "Uint": 0,
- "Uint8": 0,
- "Uint16": 0,
- "Uint32": 0,
- "Uint64": 0,
- "Uintptr": 0,
- "Float32": 0,
- "Float64": 0,
- "bar": "",
- "bar2": "",
- "IntStr": "0",
- "UintptrStr": "0",
- "PBool": true,
- "PInt": 2,
- "PInt8": 3,
- "PInt16": 4,
- "PInt32": 5,
- "PInt64": 6,
- "PUint": 7,
- "PUint8": 8,
- "PUint16": 9,
- "PUint32": 10,
- "PUint64": 11,
- "PUintptr": 12,
- "PFloat32": 14.1,
- "PFloat64": 15.1,
- "String": "",
- "PString": "16",
- "Map": null,
- "MapP": null,
- "PMap": {
- "17": {
- "Tag": "tag17"
- },
- "18": {
- "Tag": "tag18"
- }
- },
- "PMapP": {
- "19": {
- "Tag": "tag19"
- },
- "20": null
- },
- "EmptyMap": null,
- "NilMap": null,
- "Slice": null,
- "SliceP": null,
- "PSlice": [
- {
- "Tag": "tag20"
- },
- {
- "Tag": "tag21"
- }
- ],
- "PSliceP": [
- {
- "Tag": "tag22"
- },
- null,
- {
- "Tag": "tag23"
- }
- ],
- "EmptySlice": null,
- "NilSlice": null,
- "StringSlice": null,
- "ByteSlice": null,
- "Small": {
- "Tag": ""
- },
- "PSmall": null,
- "PPSmall": {
- "Tag": "tag31"
- },
- "Interface": null,
- "PInterface": 5.2
-}`
-
-var pallValueCompact = strings.Map(noSpace, pallValueIndent)
-
-func TestRefUnmarshal(t *testing.T) {
- type S struct {
- // Ref is defined in encode_test.go.
- R0 Ref
- R1 *Ref
- R2 RefText
- R3 *RefText
- }
- want := S{
- R0: 12,
- R1: new(Ref),
- R2: 13,
- R3: new(RefText),
- }
- *want.R1 = 12
- *want.R3 = 13
-
- var got S
- if err := Unmarshal([]byte(`{"R0":"ref","R1":"ref","R2":"ref","R3":"ref"}`), &got); err != nil {
- t.Fatalf("Unmarshal: %v", err)
- }
- if !reflect.DeepEqual(got, want) {
- t.Errorf("got %+v, want %+v", got, want)
- }
-}
-
-// Test that the empty string doesn't panic decoding when ,string is specified
-// Issue 3450
-func TestEmptyString(t *testing.T) {
- type T2 struct {
- Number1 int `json:",string"`
- Number2 int `json:",string"`
- }
- data := `{"Number1":"1", "Number2":""}`
- dec := NewDecoder(strings.NewReader(data))
- var t2 T2
- err := dec.Decode(&t2)
- if err == nil {
- t.Fatal("Decode: did not return error")
- }
- if t2.Number1 != 1 {
- t.Fatal("Decode: did not set Number1")
- }
-}
-
-// Test that a null for ,string is not replaced with the previous quoted string (issue 7046).
-// It should also not be an error (issue 2540, issue 8587).
-func TestNullString(t *testing.T) {
- type T struct {
- A int `json:",string"`
- B int `json:",string"`
- C *int `json:",string"`
- }
- data := []byte(`{"A": "1", "B": null, "C": null}`)
- var s T
- s.B = 1
- s.C = new(int)
- *s.C = 2
- err := Unmarshal(data, &s)
- if err != nil {
- t.Fatalf("Unmarshal: %v", err)
- }
- if s.B != 1 || s.C != nil {
- t.Fatalf("after Unmarshal, s.B=%d, s.C=%p, want 1, nil", s.B, s.C)
- }
-}
-
-func intp(x int) *int {
- p := new(int)
- *p = x
- return p
-}
-
-func intpp(x *int) **int {
- pp := new(*int)
- *pp = x
- return pp
-}
-
-var interfaceSetTests = []struct {
- pre any
- json string
- post any
-}{
- {"foo", `"bar"`, "bar"},
- {"foo", `2`, 2.0},
- {"foo", `true`, true},
- {"foo", `null`, nil},
-
- {nil, `null`, nil},
- {new(int), `null`, nil},
- {(*int)(nil), `null`, nil},
- {new(*int), `null`, new(*int)},
- {(**int)(nil), `null`, nil},
- {intp(1), `null`, nil},
- {intpp(nil), `null`, intpp(nil)},
- {intpp(intp(1)), `null`, intpp(nil)},
-}
-
-func TestInterfaceSet(t *testing.T) {
- t.Skip() // TODO
- for _, tt := range interfaceSetTests {
- b := struct{ X any }{tt.pre}
- blob := `{"X":` + tt.json + `}`
- if err := Unmarshal([]byte(blob), &b); err != nil {
- t.Errorf("Unmarshal %#q: %v", blob, err)
- continue
- }
- if !reflect.DeepEqual(b.X, tt.post) {
- t.Errorf("Unmarshal %#q into %#v: X=%#v, want %#v", blob, tt.pre, b.X, tt.post)
- }
- }
-}
-
-type NullTest struct {
- Bool bool
- Int int
- Int8 int8
- Int16 int16
- Int32 int32
- Int64 int64
- Uint uint
- Uint8 uint8
- Uint16 uint16
- Uint32 uint32
- Uint64 uint64
- Float32 float32
- Float64 float64
- String string
- PBool *bool
- Map map[string]string
- Slice []string
- Interface any
-
- PRaw *RawMessage
- PTime *time.Time
- PBigInt *big.Int
- PText *MustNotUnmarshalText
- PBuffer *bytes.Buffer // has methods, just not relevant ones
- PStruct *struct{}
-
- Raw RawMessage
- Time time.Time
- BigInt big.Int
- Text MustNotUnmarshalText
- Buffer bytes.Buffer
- Struct struct{}
-}
-
-// JSON null values should be ignored for primitives and string values instead of resulting in an error.
-// Issue 2540
-func TestUnmarshalNulls(t *testing.T) {
- // Unmarshal docs:
- // The JSON null value unmarshals into an interface, map, pointer, or slice
- // by setting that Go value to nil. Because null is often used in JSON to mean
- // ``not present,'' unmarshaling a JSON null into any other Go type has no effect
- // on the value and produces no error.
-
- jsonData := []byte(`{
- "Bool" : null,
- "Int" : null,
- "Int8" : null,
- "Int16" : null,
- "Int32" : null,
- "Int64" : null,
- "Uint" : null,
- "Uint8" : null,
- "Uint16" : null,
- "Uint32" : null,
- "Uint64" : null,
- "Float32" : null,
- "Float64" : null,
- "String" : null,
- "PBool": null,
- "Map": null,
- "Slice": null,
- "Interface": null,
- "PRaw": null,
- "PTime": null,
- "PBigInt": null,
- "PText": null,
- "PBuffer": null,
- "PStruct": null,
- "Raw": null,
- "Time": null,
- "BigInt": null,
- "Text": null,
- "Buffer": null,
- "Struct": null
- }`)
- nulls := NullTest{
- Bool: true,
- Int: 2,
- Int8: 3,
- Int16: 4,
- Int32: 5,
- Int64: 6,
- Uint: 7,
- Uint8: 8,
- Uint16: 9,
- Uint32: 10,
- Uint64: 11,
- Float32: 12.1,
- Float64: 13.1,
- String: "14",
- PBool: new(bool),
- Map: map[string]string{},
- Slice: []string{},
- Interface: new(MustNotUnmarshalJSON),
- PRaw: new(RawMessage),
- PTime: new(time.Time),
- PBigInt: new(big.Int),
- PText: new(MustNotUnmarshalText),
- PStruct: new(struct{}),
- PBuffer: new(bytes.Buffer),
- Raw: RawMessage("123"),
- Time: time.Unix(123456789, 0),
- BigInt: *big.NewInt(123),
- }
-
- before := nulls.Time.String()
-
- err := Unmarshal(jsonData, &nulls)
- if err != nil {
- t.Errorf("Unmarshal of null values failed: %v", err)
- }
- if !nulls.Bool || nulls.Int != 2 || nulls.Int8 != 3 || nulls.Int16 != 4 || nulls.Int32 != 5 || nulls.Int64 != 6 ||
- nulls.Uint != 7 || nulls.Uint8 != 8 || nulls.Uint16 != 9 || nulls.Uint32 != 10 || nulls.Uint64 != 11 ||
- nulls.Float32 != 12.1 || nulls.Float64 != 13.1 || nulls.String != "14" {
- t.Errorf("Unmarshal of null values affected primitives")
- }
-
- if nulls.PBool != nil {
- t.Errorf("Unmarshal of null did not clear nulls.PBool")
- }
- if nulls.Map != nil {
- t.Errorf("Unmarshal of null did not clear nulls.Map")
- }
- if nulls.Slice != nil {
- t.Errorf("Unmarshal of null did not clear nulls.Slice")
- }
- if nulls.Interface != nil {
- t.Errorf("Unmarshal of null did not clear nulls.Interface")
- }
- if nulls.PRaw != nil {
- t.Errorf("Unmarshal of null did not clear nulls.PRaw")
- }
- if nulls.PTime != nil {
- t.Errorf("Unmarshal of null did not clear nulls.PTime")
- }
- if nulls.PBigInt != nil {
- t.Errorf("Unmarshal of null did not clear nulls.PBigInt")
- }
- if nulls.PText != nil {
- t.Errorf("Unmarshal of null did not clear nulls.PText")
- }
- if nulls.PBuffer != nil {
- t.Errorf("Unmarshal of null did not clear nulls.PBuffer")
- }
- if nulls.PStruct != nil {
- t.Errorf("Unmarshal of null did not clear nulls.PStruct")
- }
-
- if string(nulls.Raw) != "null" {
- t.Errorf("Unmarshal of RawMessage null did not record null: %v", string(nulls.Raw))
- }
- if nulls.Time.String() != before {
- t.Errorf("Unmarshal of time.Time null set time to %v", nulls.Time.String())
- }
- if nulls.BigInt.String() != "123" {
- t.Errorf("Unmarshal of big.Int null set int to %v", nulls.BigInt.String())
- }
-}
-
-type MustNotUnmarshalJSON struct{}
-
-func (x MustNotUnmarshalJSON) UnmarshalJSON(data []byte) error {
- return errors.New("MustNotUnmarshalJSON was used")
-}
-
-type MustNotUnmarshalText struct{}
-
-func (x MustNotUnmarshalText) UnmarshalText(text []byte) error {
- return errors.New("MustNotUnmarshalText was used")
-}
-
-func TestStringKind(t *testing.T) {
- type stringKind string
-
- var m1, m2 map[stringKind]int
- m1 = map[stringKind]int{
- "foo": 42,
- }
-
- data, err := Marshal(m1)
- if err != nil {
- t.Errorf("Unexpected error marshaling: %v", err)
- }
-
- err = Unmarshal(data, &m2)
- if err != nil {
- t.Errorf("Unexpected error unmarshaling: %v", err)
- }
-
- if !reflect.DeepEqual(m1, m2) {
- t.Error("Items should be equal after encoding and then decoding")
- }
-}
-
-// Custom types with []byte as underlying type could not be marshaled
-// and then unmarshaled.
-// Issue 8962.
-func TestByteKind(t *testing.T) {
- type byteKind []byte
-
- a := byteKind("hello")
-
- data, err := Marshal(a)
- if err != nil {
- t.Error(err)
- }
- if !reflect.DeepEqual(data, []byte(`"aGVsbG8="`)) { // MODIFIED
- t.Errorf("expected %q == %q", data, `"aGVsbG8="`) // MODIFIED
- } // MODIFIED
- var b byteKind
- err = Unmarshal(data, &b)
- if err != nil {
- t.Fatal(err)
- }
- if !reflect.DeepEqual(a, b) {
- t.Errorf("expected %v == %v", a, b)
- }
-}
-
-// The fix for issue 8962 introduced a regression.
-// Issue 12921.
-func TestSliceOfCustomByte(t *testing.T) {
- type Uint8 uint8
-
- a := []Uint8("hello")
-
- data, err := Marshal(a)
- if err != nil {
- t.Fatal(err)
- }
- if !reflect.DeepEqual(data, []byte(`"aGVsbG8="`)) { // MODIFIED
- t.Errorf("expected %q == %q", data, `"aGVsbG8="`) // MODIFIED
- } // MODIFIED
- var b []Uint8
- err = Unmarshal(data, &b)
- if err != nil {
- t.Fatal(err)
- }
- if !reflect.DeepEqual(a, b) {
- t.Fatalf("expected %v == %v", a, b)
- }
-}
-
-var decodeTypeErrorTests = []struct {
- dest any
- src string
-}{
- {new(string), `{"user": "name"}`}, // issue 4628.
- {new(error), `{}`}, // issue 4222
- {new(error), `[]`},
- {new(error), `""`},
- {new(error), `123`},
- {new(error), `true`},
-}
-
-func TestUnmarshalTypeError(t *testing.T) {
- for _, item := range decodeTypeErrorTests {
- err := Unmarshal([]byte(item.src), item.dest)
- if err == nil { // if _, ok := err.(*UnmarshalTypeError); !ok { // MODIFIED
- t.Errorf("expected type error for Unmarshal(%q, type %T): got %T",
- item.src, item.dest, err)
- }
- }
-}
-
-var unmarshalSyntaxTests = []string{
- "tru",
- "fals",
- "nul",
- "123e",
- `"hello`,
- `[1,2,3`,
- `{"key":1`,
- `{"key":1,`,
-}
-
-func TestUnmarshalSyntax(t *testing.T) {
- var x any
- for _, src := range unmarshalSyntaxTests {
- err := Unmarshal([]byte(src), &x)
- if err == nil { // _, ok := err.(*SyntaxError); !ok { // MODIFIED
- t.Errorf("expected syntax error for Unmarshal(%q): got %T", src, err)
- }
- }
-}
-
-// Test handling of unexported fields that should be ignored.
-// Issue 4660
-type unexportedFields struct {
- Name string
- m map[string]any `json:"-"`
- m2 map[string]any `json:"abcd"`
-
- s []int `json:"-"`
-}
-
-func TestUnmarshalUnexported(t *testing.T) {
- input := `{"Name": "Bob", "m": {"x": 123}, "m2": {"y": 456}, "abcd": {"z": 789}, "s": [2, 3]}`
- want := &unexportedFields{Name: "Bob"}
-
- out := &unexportedFields{}
- err := Unmarshal([]byte(input), out)
- if err != nil {
- t.Errorf("got error %v, expected nil", err)
- }
- if !reflect.DeepEqual(out, want) {
- t.Errorf("got %q, want %q", out, want)
- }
-}
-
-// Time3339 is a time.Time which encodes to and from JSON
-// as an RFC 3339 time in UTC.
-type Time3339 time.Time
-
-func (t *Time3339) UnmarshalJSON(b []byte) error {
- if len(b) < 2 || b[0] != '"' || b[len(b)-1] != '"' {
- return fmt.Errorf("types: failed to unmarshal non-string value %q as an RFC 3339 time", b)
- }
- tm, err := time.Parse(time.RFC3339, string(b[1:len(b)-1]))
- if err != nil {
- return err
- }
- *t = Time3339(tm)
- return nil
-}
-
-func TestUnmarshalJSONLiteralError(t *testing.T) {
- var t3 Time3339
- err := Unmarshal([]byte(`"0000-00-00T00:00:00Z"`), &t3)
- if err == nil {
- t.Fatalf("expected error; got time %v", time.Time(t3))
- }
- if !strings.Contains(err.Error(), "range") {
- t.Errorf("got err = %v; want out of range error", err)
- }
-}
-
-// Test that extra object elements in an array do not result in a
-// "data changing underfoot" error.
-// Issue 3717
-func TestSkipArrayObjects(t *testing.T) {
- json := `[{}]`
- var dest [0]any
-
- err := Unmarshal([]byte(json), &dest)
- if err != nil {
- t.Errorf("got error %q, want nil", err)
- }
-}
-
-// Test semantics of pre-filled data, such as struct fields, map elements,
-// slices, and arrays.
-// Issues 4900 and 8837, among others.
-func TestPrefilled(t *testing.T) {
- // Values here change, cannot reuse table across runs.
- var prefillTests = []struct {
- in string
- ptr any
- out any
- }{
- {
- in: `{"X": 1, "Y": 2}`,
- ptr: &XYZ{X: float32(3), Y: int16(4), Z: 1.5},
- out: &XYZ{X: float64(1), Y: float64(2), Z: 1.5},
- },
- {
- in: `{"X": 1, "Y": 2}`,
- ptr: &map[string]any{"X": float32(3), "Y": int16(4), "Z": 1.5},
- out: &map[string]any{"X": float64(1), "Y": float64(2), "Z": 1.5},
- },
- {
- in: `[2]`,
- ptr: &[]int{1},
- out: &[]int{2},
- },
- {
- in: `[2, 3]`,
- ptr: &[]int{1},
- out: &[]int{2, 3},
- },
- {
- in: `[2, 3]`,
- ptr: &[...]int{1},
- out: &[...]int{2},
- },
- {
- in: `[3]`,
- ptr: &[...]int{1, 2},
- out: &[...]int{3, 0},
- },
- }
-
- for _, tt := range prefillTests {
- ptrstr := fmt.Sprintf("%v", tt.ptr)
- err := Unmarshal([]byte(tt.in), tt.ptr) // tt.ptr edited here
- if err != nil {
- t.Errorf("Unmarshal: %v", err)
- }
- if !reflect.DeepEqual(tt.ptr, tt.out) {
- t.Errorf("Unmarshal(%#q, %s): have %v, want %v", tt.in, ptrstr, tt.ptr, tt.out)
- }
- }
-}
-
-var invalidUnmarshalTests = []struct {
- v any
- want string
-}{
- {nil, "json: Unmarshal(nil)"},
- {struct{}{}, "json: Unmarshal(non-pointer struct {})"},
- {(*int)(nil), "json: Unmarshal(nil *int)"},
-}
-
-func TestInvalidUnmarshal(t *testing.T) {
- buf := []byte(`{"a":"1"}`)
- for _, tt := range invalidUnmarshalTests {
- err := Unmarshal(buf, tt.v)
- if err == nil {
- t.Errorf("Unmarshal expecting error, got nil")
- continue
- }
- if got := err.Error(); got != tt.want {
- t.Errorf("Unmarshal = %q; want %q", got, tt.want)
- }
- }
-}
-
-var invalidUnmarshalTextTests = []struct {
- v any
- want string
-}{
- {nil, "json: Unmarshal(nil)"},
- {struct{}{}, "json: Unmarshal(non-pointer struct {})"},
- {(*int)(nil), "json: Unmarshal(nil *int)"},
- {new(net.IP), "json: cannot unmarshal number into Go value of type *net.IP"},
-}
-
-func TestInvalidUnmarshalText(t *testing.T) {
- buf := []byte(`123`)
- for _, tt := range invalidUnmarshalTextTests {
- err := Unmarshal(buf, tt.v)
- if err == nil {
- t.Errorf("Unmarshal expecting error, got nil")
- continue
- }
- // if got := err.Error(); got != tt.want { // MODIFIED
- // t.Errorf("Unmarshal = %q; want %q", got, tt.want) // MODIFIED
- // } // MODIFIED
- }
-}
-
-// Test that string option is ignored for invalid types.
-// Issue 9812.
-func TestInvalidStringOption(t *testing.T) {
- num := 0
- item := struct {
- T time.Time `json:",string"`
- M map[string]string `json:",string"`
- S []string `json:",string"`
- A [1]string `json:",string"`
- I any `json:",string"`
- P *int `json:",string"`
- }{M: make(map[string]string), S: make([]string, 0), I: num, P: &num}
-
- data, err := Marshal(item)
- if err != nil {
- t.Fatalf("Marshal: %v", err)
- }
-
- err = Unmarshal(data, &item)
- if err != nil {
- t.Fatalf("Unmarshal: %v", err)
- }
-}
-
-// Test unmarshal behavior with regards to embedded unexported structs.
-//
-// (Issue 21357) If the embedded struct is a pointer and is unallocated,
-// this returns an error because unmarshal cannot set the field.
-//
-// (Issue 24152) If the embedded struct is given an explicit name,
-// ensure that the normal unmarshal logic does not panic in reflect.
-//
-// (Issue 28145) If the embedded struct is given an explicit name and has
-// exported methods, don't cause a panic trying to get its value.
-func TestUnmarshalEmbeddedUnexported(t *testing.T) {
- t.Skip() // TODO
- type (
- embed1 struct{ Q int }
- embed2 struct{ Q int }
- embed3 struct {
- Q int64 `json:",string"`
- }
- S1 struct {
- *embed1
- R int
- }
- S2 struct {
- *embed1
- Q int
- }
- S3 struct {
- embed1
- R int
- }
- S4 struct {
- *embed1
- embed2
- }
- S5 struct {
- *embed3
- R int
- }
- S6 struct {
- embed1 `json:"embed1"`
- }
- S7 struct {
- embed1 `json:"embed1"`
- embed2
- }
- S8 struct {
- embed1 `json:"embed1"`
- embed2 `json:"embed2"`
- Q int
- }
- S9 struct {
- unexportedWithMethods `json:"embed"`
- }
- )
-
- tests := []struct {
- in string
- ptr any
- out any
- err error
- }{{
- // Error since we cannot set S1.embed1, but still able to set S1.R.
- in: `{"R":2,"Q":1}`,
- ptr: new(S1),
- out: &S1{R: 2},
- err: fmt.Errorf("json: cannot set embedded pointer to unexported struct: json.embed1"),
- }, {
- // The top level Q field takes precedence.
- in: `{"Q":1}`,
- ptr: new(S2),
- out: &S2{Q: 1},
- }, {
- // No issue with non-pointer variant.
- in: `{"R":2,"Q":1}`,
- ptr: new(S3),
- out: &S3{embed1: embed1{Q: 1}, R: 2},
- }, {
- // No error since both embedded structs have field R, which annihilate each other.
- // Thus, no attempt is made at setting S4.embed1.
- in: `{"R":2}`,
- ptr: new(S4),
- out: new(S4),
- }, {
- // Error since we cannot set S5.embed1, but still able to set S5.R.
- in: `{"R":2,"Q":1}`,
- ptr: new(S5),
- out: &S5{R: 2},
- err: fmt.Errorf("json: cannot set embedded pointer to unexported struct: json.embed3"),
- }, {
- // Issue 24152, ensure decodeState.indirect does not panic.
- in: `{"embed1": {"Q": 1}}`,
- ptr: new(S6),
- out: &S6{embed1{1}},
- }, {
- // Issue 24153, check that we can still set forwarded fields even in
- // the presence of a name conflict.
- //
- // This relies on obscure behavior of reflect where it is possible
- // to set a forwarded exported field on an unexported embedded struct
- // even though there is a name conflict, even when it would have been
- // impossible to do so according to Go visibility rules.
- // Go forbids this because it is ambiguous whether S7.Q refers to
- // S7.embed1.Q or S7.embed2.Q. Since embed1 and embed2 are unexported,
- // it should be impossible for an external package to set either Q.
- //
- // It is probably okay for a future reflect change to break this.
- in: `{"embed1": {"Q": 1}, "Q": 2}`,
- ptr: new(S7),
- out: &S7{embed1{1}, embed2{2}},
- }, {
- // Issue 24153, similar to the S7 case.
- in: `{"embed1": {"Q": 1}, "embed2": {"Q": 2}, "Q": 3}`,
- ptr: new(S8),
- out: &S8{embed1{1}, embed2{2}, 3},
- }, {
- // Issue 228145, similar to the cases above.
- in: `{"embed": {}}`,
- ptr: new(S9),
- out: &S9{},
- }}
-
- for i, tt := range tests {
- err := Unmarshal([]byte(tt.in), tt.ptr)
- if !equalError(err, tt.err) {
- t.Errorf("#%d: %v, want %v", i, err, tt.err)
- }
- if !reflect.DeepEqual(tt.ptr, tt.out) {
- t.Errorf("#%d: mismatch\ngot: %#+v\nwant: %#+v", i, tt.ptr, tt.out)
- }
- }
-}
-
-func TestUnmarshalErrorAfterMultipleJSON(t *testing.T) {
- t.Skip() // TODO
- tests := []struct {
- in string
- err error
- }{{
- in: `1 false null :`,
- err: &SyntaxError{"invalid character ':' looking for beginning of value", 14},
- }, {
- in: `1 [] [,]`,
- err: &SyntaxError{"invalid character ',' looking for beginning of value", 7},
- }, {
- in: `1 [] [true:]`,
- err: &SyntaxError{"invalid character ':' after array element", 11},
- }, {
- in: `1 {} {"x"=}`,
- err: &SyntaxError{"invalid character '=' after object key", 14},
- }, {
- in: `falsetruenul#`,
- err: &SyntaxError{"invalid character '#' in literal null (expecting 'l')", 13},
- }}
- for i, tt := range tests {
- dec := NewDecoder(strings.NewReader(tt.in))
- var err error
- for {
- var v any
- if err = dec.Decode(&v); err != nil {
- break
- }
- }
- if !reflect.DeepEqual(err, tt.err) {
- t.Errorf("#%d: got %#v, want %#v", i, err, tt.err)
- }
- }
-}
-
-type unmarshalPanic struct{}
-
-func (unmarshalPanic) UnmarshalJSON([]byte) error { panic(0xdead) }
-
-func TestUnmarshalPanic(t *testing.T) {
- defer func() {
- if got := recover(); !reflect.DeepEqual(got, 0xdead) {
- t.Errorf("panic() = (%T)(%v), want 0xdead", got, got)
- }
- }()
- Unmarshal([]byte("{}"), &unmarshalPanic{})
- t.Fatalf("Unmarshal should have panicked")
-}
-
-// The decoder used to hang if decoding into an interface pointing to its own address.
-// See golang.org/issues/31740.
-func TestUnmarshalRecursivePointer(t *testing.T) {
- t.Skip() // TODO
- var v any
- v = &v
- data := []byte(`{"a": "b"}`)
-
- if err := Unmarshal(data, v); err != nil {
- t.Fatal(err)
- }
-}
-
-type textUnmarshalerString string
-
-func (m *textUnmarshalerString) UnmarshalText(text []byte) error {
- *m = textUnmarshalerString(strings.ToLower(string(text)))
- return nil
-}
-
-// Test unmarshal to a map, where the map key is a user defined type.
-// See golang.org/issues/34437.
-func TestUnmarshalMapWithTextUnmarshalerStringKey(t *testing.T) {
- var p map[textUnmarshalerString]string
- if err := Unmarshal([]byte(`{"FOO": "1"}`), &p); err != nil {
- t.Fatalf("Unmarshal unexpected error: %v", err)
- }
-
- if _, ok := p["foo"]; !ok {
- t.Errorf(`Key "foo" does not exist in map: %v`, p)
- }
-}
-
-func TestUnmarshalRescanLiteralMangledUnquote(t *testing.T) {
- // See golang.org/issues/38105.
- var p map[textUnmarshalerString]string
- if err := Unmarshal([]byte(`{"开源":"12345开源"}`), &p); err != nil {
- t.Fatalf("Unmarshal unexpected error: %v", err)
- }
- if _, ok := p["开源"]; !ok {
- t.Errorf(`Key "开源" does not exist in map: %v`, p)
- }
-
- // See golang.org/issues/38126.
- type T struct {
- F1 string `json:"F1,string"`
- }
- t1 := T{"aaa\tbbb"}
-
- b, err := Marshal(t1)
- if err != nil {
- t.Fatalf("Marshal unexpected error: %v", err)
- }
- var t2 T
- if err := Unmarshal(b, &t2); err != nil {
- t.Fatalf("Unmarshal unexpected error: %v", err)
- }
- if t1 != t2 {
- t.Errorf("Marshal and Unmarshal roundtrip mismatch: want %q got %q", t1, t2)
- }
-
- // See golang.org/issues/39555.
- input := map[textUnmarshalerString]string{"FOO": "", `"`: ""}
-
- encoded, err := Marshal(input)
- if err != nil {
- t.Fatalf("Marshal unexpected error: %v", err)
- }
- var got map[textUnmarshalerString]string
- if err := Unmarshal(encoded, &got); err != nil {
- t.Fatalf("Unmarshal unexpected error: %v", err)
- }
- want := map[textUnmarshalerString]string{"foo": "", `"`: ""}
- if !reflect.DeepEqual(want, got) {
- t.Fatalf("Unexpected roundtrip result:\nwant: %q\ngot: %q", want, got)
- }
-}
-
-func TestUnmarshalMaxDepth(t *testing.T) {
- t.Skip() // TODO
- testcases := []struct {
- name string
- data string
- errMaxDepth bool
- }{
- {
- name: "ArrayUnderMaxNestingDepth",
- data: `{"a":` + strings.Repeat(`[`, 10000-1) + strings.Repeat(`]`, 10000-1) + `}`,
- errMaxDepth: false,
- },
- {
- name: "ArrayOverMaxNestingDepth",
- data: `{"a":` + strings.Repeat(`[`, 10000) + strings.Repeat(`]`, 10000) + `}`,
- errMaxDepth: true,
- },
- {
- name: "ArrayOverStackDepth",
- data: `{"a":` + strings.Repeat(`[`, 3000000) + strings.Repeat(`]`, 3000000) + `}`,
- errMaxDepth: true,
- },
- {
- name: "ObjectUnderMaxNestingDepth",
- data: `{"a":` + strings.Repeat(`{"a":`, 10000-1) + `0` + strings.Repeat(`}`, 10000-1) + `}`,
- errMaxDepth: false,
- },
- {
- name: "ObjectOverMaxNestingDepth",
- data: `{"a":` + strings.Repeat(`{"a":`, 10000) + `0` + strings.Repeat(`}`, 10000) + `}`,
- errMaxDepth: true,
- },
- {
- name: "ObjectOverStackDepth",
- data: `{"a":` + strings.Repeat(`{"a":`, 3000000) + `0` + strings.Repeat(`}`, 3000000) + `}`,
- errMaxDepth: true,
- },
- }
-
- targets := []struct {
- name string
- newValue func() any
- }{
- {
- name: "unstructured",
- newValue: func() any {
- var v any
- return &v
- },
- },
- {
- name: "typed named field",
- newValue: func() any {
- v := struct {
- A any `json:"a"`
- }{}
- return &v
- },
- },
- {
- name: "typed missing field",
- newValue: func() any {
- v := struct {
- B any `json:"b"`
- }{}
- return &v
- },
- },
- {
- name: "custom unmarshaler",
- newValue: func() any {
- v := unmarshaler{}
- return &v
- },
- },
- }
-
- for _, tc := range testcases {
- for _, target := range targets {
- t.Run(target.name+"-"+tc.name, func(t *testing.T) {
- err := Unmarshal([]byte(tc.data), target.newValue())
- if !tc.errMaxDepth {
- if err != nil {
- t.Errorf("unexpected error: %v", err)
- }
- } else {
- if err == nil {
- t.Errorf("expected error containing 'exceeded max depth', got none")
- } else if !strings.Contains(err.Error(), "exceeded max depth") {
- t.Errorf("expected error containing 'exceeded max depth', got: %v", err)
- }
- }
- })
- }
- }
-}
diff --git a/lib/lowmemjson/borrowed_encode_test.go b/lib/lowmemjson/borrowed_encode_test.go
deleted file mode 100644
index 9659910..0000000
--- a/lib/lowmemjson/borrowed_encode_test.go
+++ /dev/null
@@ -1,1208 +0,0 @@
-// Copyright 2011 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package lowmemjson // MODIFIED
-
-import (
- "bytes"
- "encoding"
- "fmt"
- "log"
- "math"
- "reflect"
- "regexp"
- "strconv"
- "testing"
- "unicode"
-)
-
-type Optionals struct {
- Sr string `json:"sr"`
- So string `json:"so,omitempty"`
- Sw string `json:"-"`
-
- Ir int `json:"omitempty"` // actually named omitempty, not an option
- Io int `json:"io,omitempty"`
-
- Slr []string `json:"slr,random"` //nolint:staticcheck // testing handling of unknown options // MODIFIED
- Slo []string `json:"slo,omitempty"`
-
- Mr map[string]any `json:"mr"`
- Mo map[string]any `json:",omitempty"`
-
- Fr float64 `json:"fr"`
- Fo float64 `json:"fo,omitempty"`
-
- Br bool `json:"br"`
- Bo bool `json:"bo,omitempty"`
-
- Ur uint `json:"ur"`
- Uo uint `json:"uo,omitempty"`
-
- Str struct{} `json:"str"`
- Sto struct{} `json:"sto,omitempty"`
-}
-
-var optionalsExpected = `{
- "sr": "",
- "omitempty": 0,
- "slr": null,
- "mr": {},
- "fr": 0,
- "br": false,
- "ur": 0,
- "str": {},
- "sto": {}
-}`
-
-func TestOmitEmpty(t *testing.T) {
- var o Optionals
- o.Sw = "something"
- o.Mr = map[string]any{}
- o.Mo = map[string]any{}
-
- got, err := MarshalIndent(&o, "", " ")
- if err != nil {
- t.Fatal(err)
- }
- if got := string(got); got != optionalsExpected {
- t.Errorf(" got: %s\nwant: %s\n", got, optionalsExpected)
- }
-}
-
-type StringTag struct {
- BoolStr bool `json:",string"`
- IntStr int64 `json:",string"`
- UintptrStr uintptr `json:",string"`
- StrStr string `json:",string"`
- NumberStr Number `json:",string"`
-}
-
-func TestRoundtripStringTag(t *testing.T) {
- tests := []struct {
- name string
- in StringTag
- want string // empty to just test that we roundtrip
- }{
- {
- name: "AllTypes",
- in: StringTag{
- BoolStr: true,
- IntStr: 42,
- UintptrStr: 44,
- StrStr: "xzbit",
- NumberStr: "46",
- },
- want: `{
- "BoolStr": "true",
- "IntStr": "42",
- "UintptrStr": "44",
- "StrStr": "\"xzbit\"",
- "NumberStr": "46"
- }`,
- },
- {
- // See golang.org/issues/38173.
- name: "StringDoubleEscapes",
- in: StringTag{
- StrStr: "\b\f\n\r\t\"\\",
- NumberStr: "0", // just to satisfy the roundtrip
- },
- want: `{
- "BoolStr": "false",
- "IntStr": "0",
- "UintptrStr": "0",
- "StrStr": "\"\\u0008\\u000c\\n\\r\\t\\\"\\\\\"",
- "NumberStr": "0"
- }`,
- },
- }
- for _, test := range tests {
- t.Run(test.name, func(t *testing.T) {
- // Indent with a tab prefix to make the multi-line string
- // literals in the table nicer to read.
- got, err := MarshalIndent(&test.in, "\t\t\t", "\t")
- if err != nil {
- t.Fatal(err)
- }
- if got := string(got); got != test.want {
- t.Fatalf(" got: %s\nwant: %s\n", got, test.want)
- }
-
- // Verify that it round-trips.
- var s2 StringTag
- if err := Unmarshal(got, &s2); err != nil {
- t.Fatalf("Decode: %v", err)
- }
- if !reflect.DeepEqual(test.in, s2) {
- t.Fatalf("decode didn't match.\nsource: %#v\nEncoded as:\n%s\ndecode: %#v", test.in, string(got), s2)
- }
- })
- }
-}
-
-// byte slices are special even if they're renamed types.
-type renamedByte byte
-type renamedByteSlice []byte
-type renamedRenamedByteSlice []renamedByte
-
-func TestEncodeRenamedByteSlice(t *testing.T) {
- s := renamedByteSlice("abc")
- result, err := Marshal(s)
- if err != nil {
- t.Fatal(err)
- }
- expect := `"YWJj"`
- if string(result) != expect {
- t.Errorf(" got %s want %s", result, expect)
- }
- r := renamedRenamedByteSlice("abc")
- result, err = Marshal(r)
- if err != nil {
- t.Fatal(err)
- }
- if string(result) != expect {
- t.Errorf(" got %s want %s", result, expect)
- }
-}
-
-type SamePointerNoCycle struct {
- Ptr1, Ptr2 *SamePointerNoCycle
-}
-
-var samePointerNoCycle = &SamePointerNoCycle{}
-
-type PointerCycle struct {
- Ptr *PointerCycle
-}
-
-var pointerCycle = &PointerCycle{}
-
-type PointerCycleIndirect struct {
- Ptrs []any
-}
-
-type RecursiveSlice []RecursiveSlice
-
-var (
- pointerCycleIndirect = &PointerCycleIndirect{}
- mapCycle = make(map[string]any)
- sliceCycle = []any{nil}
- sliceNoCycle = []any{nil, nil}
- recursiveSliceCycle = []RecursiveSlice{nil}
-)
-
-func init() {
- ptr := &SamePointerNoCycle{}
- samePointerNoCycle.Ptr1 = ptr
- samePointerNoCycle.Ptr2 = ptr
-
- pointerCycle.Ptr = pointerCycle
- pointerCycleIndirect.Ptrs = []any{pointerCycleIndirect}
-
- mapCycle["x"] = mapCycle
- sliceCycle[0] = sliceCycle
- sliceNoCycle[1] = sliceNoCycle[:1]
- for i := startDetectingCyclesAfter; i > 0; i-- {
- sliceNoCycle = []any{sliceNoCycle}
- }
- recursiveSliceCycle[0] = recursiveSliceCycle
-}
-
-func TestSamePointerNoCycle(t *testing.T) {
- if _, err := Marshal(samePointerNoCycle); err != nil {
- t.Fatalf("unexpected error: %v", err)
- }
-}
-
-func TestSliceNoCycle(t *testing.T) {
- if _, err := Marshal(sliceNoCycle); err != nil {
- t.Fatalf("unexpected error: %v", err)
- }
-}
-
-var unsupportedValues = []any{
- math.NaN(),
- math.Inf(-1),
- math.Inf(1),
- //pointerCycle, // MODIFIED
- //pointerCycleIndirect, // MODIFIED
- //mapCycle, // MODIFIED
- //sliceCycle, // MODIFIED
- //recursiveSliceCycle, // MODIFIED
-}
-
-func TestUnsupportedValues(t *testing.T) {
- for _, v := range unsupportedValues {
- if _, err := Marshal(v); err != nil {
- if _, ok := err.(*UnsupportedValueError); !ok {
- t.Errorf("for %v, got %T want UnsupportedValueError", v, err)
- }
- } else {
- t.Errorf("for %v, expected error", v)
- }
- }
-}
-
-// Issue 43207
-func TestMarshalTextFloatMap(t *testing.T) {
- m := map[textfloat]string{
- textfloat(math.NaN()): "1",
- textfloat(math.NaN()): "1",
- }
- got, err := Marshal(m)
- if err != nil {
- t.Errorf("Marshal() error: %v", err)
- }
- want := `{"TF:NaN":"1","TF:NaN":"1"}`
- if string(got) != want {
- t.Errorf("Marshal() = %s, want %s", got, want)
- }
-}
-
-// Ref has Marshaler and Unmarshaler methods with pointer receiver.
-type Ref int
-
-func (*Ref) MarshalJSON() ([]byte, error) {
- return []byte(`"ref"`), nil
-}
-
-func (r *Ref) UnmarshalJSON([]byte) error {
- *r = 12
- return nil
-}
-
-// Val has Marshaler methods with value receiver.
-type Val int
-
-func (Val) MarshalJSON() ([]byte, error) {
- return []byte(`"val"`), nil
-}
-
-// RefText has Marshaler and Unmarshaler methods with pointer receiver.
-type RefText int
-
-func (*RefText) MarshalText() ([]byte, error) {
- return []byte(`"ref"`), nil
-}
-
-func (r *RefText) UnmarshalText([]byte) error {
- *r = 13
- return nil
-}
-
-// ValText has Marshaler methods with value receiver.
-type ValText int
-
-func (ValText) MarshalText() ([]byte, error) {
- return []byte(`"val"`), nil
-}
-
-func TestRefValMarshal(t *testing.T) {
- var s = struct {
- R0 Ref
- R1 *Ref
- R2 RefText
- R3 *RefText
- V0 Val
- V1 *Val
- V2 ValText
- V3 *ValText
- }{
- R0: 12,
- R1: new(Ref),
- R2: 14,
- R3: new(RefText),
- V0: 13,
- V1: new(Val),
- V2: 15,
- V3: new(ValText),
- }
- const want = `{"R0":"ref","R1":"ref","R2":"\"ref\"","R3":"\"ref\"","V0":"val","V1":"val","V2":"\"val\"","V3":"\"val\""}`
- b, err := Marshal(&s)
- if err != nil {
- t.Fatalf("Marshal: %v", err)
- }
- if got := string(b); got != want {
- t.Errorf("got %q, want %q", got, want)
- }
-}
-
-// C implements Marshaler and returns unescaped JSON.
-type C int
-
-func (C) MarshalJSON() ([]byte, error) {
- return []byte(`"<&>"`), nil
-}
-
-// CText implements Marshaler and returns unescaped text.
-type CText int
-
-func (CText) MarshalText() ([]byte, error) {
- return []byte(`"<&>"`), nil
-}
-
-func TestMarshalerEscaping(t *testing.T) {
- t.Skip() // MODIFIED
- var c C
- want := `"\u003c\u0026\u003e"`
- b, err := Marshal(c)
- if err != nil {
- t.Fatalf("Marshal(c): %v", err)
- }
- if got := string(b); got != want {
- t.Errorf("Marshal(c) = %#q, want %#q", got, want)
- }
-
- var ct CText
- want = `"\"\u003c\u0026\u003e\""`
- b, err = Marshal(ct)
- if err != nil {
- t.Fatalf("Marshal(ct): %v", err)
- }
- if got := string(b); got != want {
- t.Errorf("Marshal(ct) = %#q, want %#q", got, want)
- }
-}
-
-func TestAnonymousFields(t *testing.T) {
- tests := []struct {
- label string // Test name
- makeInput func() any // Function to create input value
- want string // Expected JSON output
- }{{
- // Both S1 and S2 have a field named X. From the perspective of S,
- // it is ambiguous which one X refers to.
- // This should not serialize either field.
- label: "AmbiguousField",
- makeInput: func() any {
- type (
- S1 struct{ x, X int }
- S2 struct{ x, X int }
- S struct {
- S1
- S2
- }
- )
- return S{S1{1, 2}, S2{3, 4}}
- },
- want: `{}`,
- }, {
- label: "DominantField",
- // Both S1 and S2 have a field named X, but since S has an X field as
- // well, it takes precedence over S1.X and S2.X.
- makeInput: func() any {
- type (
- S1 struct{ x, X int }
- S2 struct{ x, X int }
- S struct {
- S1
- S2
- x, X int
- }
- )
- return S{S1{1, 2}, S2{3, 4}, 5, 6}
- },
- want: `{"X":6}`,
- }, {
- // Unexported embedded field of non-struct type should not be serialized.
- label: "UnexportedEmbeddedInt",
- makeInput: func() any {
- type (
- myInt int
- S struct{ myInt }
- )
- return S{5}
- },
- want: `{}`,
- }, {
- // Exported embedded field of non-struct type should be serialized.
- label: "ExportedEmbeddedInt",
- makeInput: func() any {
- type (
- MyInt int
- S struct{ MyInt }
- )
- return S{5}
- },
- want: `{"MyInt":5}`,
- }, {
- // Unexported embedded field of pointer to non-struct type
- // should not be serialized.
- label: "UnexportedEmbeddedIntPointer",
- makeInput: func() any {
- type (
- myInt int
- S struct{ *myInt }
- )
- s := S{new(myInt)}
- *s.myInt = 5
- return s
- },
- want: `{}`,
- }, {
- // Exported embedded field of pointer to non-struct type
- // should be serialized.
- label: "ExportedEmbeddedIntPointer",
- makeInput: func() any {
- type (
- MyInt int
- S struct{ *MyInt }
- )
- s := S{new(MyInt)}
- *s.MyInt = 5
- return s
- },
- want: `{"MyInt":5}`,
- }, {
- // Exported fields of embedded structs should have their
- // exported fields be serialized regardless of whether the struct types
- // themselves are exported.
- label: "EmbeddedStruct",
- makeInput: func() any {
- type (
- s1 struct{ x, X int }
- S2 struct{ y, Y int }
- S struct {
- s1
- S2
- }
- )
- return S{s1{1, 2}, S2{3, 4}}
- },
- want: `{"X":2,"Y":4}`,
- }, {
- // Exported fields of pointers to embedded structs should have their
- // exported fields be serialized regardless of whether the struct types
- // themselves are exported.
- label: "EmbeddedStructPointer",
- makeInput: func() any {
- type (
- s1 struct{ x, X int }
- S2 struct{ y, Y int }
- S struct {
- *s1
- *S2
- }
- )
- return S{&s1{1, 2}, &S2{3, 4}}
- },
- want: `{"X":2,"Y":4}`,
- }, {
- // Exported fields on embedded unexported structs at multiple levels
- // of nesting should still be serialized.
- label: "NestedStructAndInts",
- makeInput: func() any {
- type (
- MyInt1 int
- MyInt2 int
- myInt int
- s2 struct {
- MyInt2
- myInt
- }
- s1 struct {
- MyInt1
- myInt
- s2
- }
- S struct {
- s1
- myInt
- }
- )
- return S{s1{1, 2, s2{3, 4}}, 6}
- },
- want: `{"MyInt1":1,"MyInt2":3}`,
- }, {
- // If an anonymous struct pointer field is nil, we should ignore
- // the embedded fields behind it. Not properly doing so may
- // result in the wrong output or reflect panics.
- label: "EmbeddedFieldBehindNilPointer",
- makeInput: func() any {
- type (
- S2 struct{ Field string }
- S struct{ *S2 }
- )
- return S{}
- },
- want: `{}`,
- }}
-
- for _, tt := range tests {
- t.Run(tt.label, func(t *testing.T) {
- b, err := Marshal(tt.makeInput())
- if err != nil {
- t.Fatalf("Marshal() = %v, want nil error", err)
- }
- if string(b) != tt.want {
- t.Fatalf("Marshal() = %q, want %q", b, tt.want)
- }
- })
- }
-}
-
-type BugA struct {
- S string
-}
-
-type BugB struct {
- BugA
- S string
-}
-
-type BugC struct {
- S string
-}
-
-// Legal Go: We never use the repeated embedded field (S).
-type BugX struct {
- A int
- BugA
- BugB
-}
-
-// golang.org/issue/16042.
-// Even if a nil interface value is passed in, as long as
-// it implements Marshaler, it should be marshaled.
-type nilJSONMarshaler string
-
-func (nm *nilJSONMarshaler) MarshalJSON() ([]byte, error) {
- if nm == nil {
- return Marshal("0zenil0")
- }
- return Marshal("zenil:" + string(*nm))
-}
-
-// golang.org/issue/34235.
-// Even if a nil interface value is passed in, as long as
-// it implements encoding.TextMarshaler, it should be marshaled.
-type nilTextMarshaler string
-
-func (nm *nilTextMarshaler) MarshalText() ([]byte, error) {
- if nm == nil {
- return []byte("0zenil0"), nil
- }
- return []byte("zenil:" + string(*nm)), nil
-}
-
-// See golang.org/issue/16042 and golang.org/issue/34235.
-func TestNilMarshal(t *testing.T) {
- testCases := []struct {
- v any
- want string
- }{
- {v: nil, want: `null`},
- {v: new(float64), want: `0`},
- {v: []any(nil), want: `null`},
- {v: []string(nil), want: `null`},
- {v: map[string]string(nil), want: `null`},
- {v: []byte(nil), want: `null`},
- {v: struct{ M string }{"gopher"}, want: `{"M":"gopher"}`},
- {v: struct{ M Marshaler }{}, want: `{"M":null}`},
- {v: struct{ M Marshaler }{(*nilJSONMarshaler)(nil)}, want: `{"M":"0zenil0"}`},
- {v: struct{ M any }{(*nilJSONMarshaler)(nil)}, want: `{"M":null}`},
- {v: struct{ M encoding.TextMarshaler }{}, want: `{"M":null}`},
- {v: struct{ M encoding.TextMarshaler }{(*nilTextMarshaler)(nil)}, want: `{"M":"0zenil0"}`},
- {v: struct{ M any }{(*nilTextMarshaler)(nil)}, want: `{"M":null}`},
- }
-
- for _, tt := range testCases {
- out, err := Marshal(tt.v)
- if err != nil || string(out) != tt.want {
- t.Errorf("Marshal(%#v) = %#q, %#v, want %#q, nil", tt.v, out, err, tt.want)
- continue
- }
- }
-}
-
-// Issue 5245.
-func TestEmbeddedBug(t *testing.T) {
- v := BugB{
- BugA{"A"},
- "B",
- }
- b, err := Marshal(v)
- if err != nil {
- t.Fatal("Marshal:", err)
- }
- want := `{"S":"B"}`
- got := string(b)
- if got != want {
- t.Fatalf("Marshal: got %s want %s", got, want)
- }
- // Now check that the duplicate field, S, does not appear.
- x := BugX{
- A: 23,
- }
- b, err = Marshal(x)
- if err != nil {
- t.Fatal("Marshal:", err)
- }
- want = `{"A":23}`
- got = string(b)
- if got != want {
- t.Fatalf("Marshal: got %s want %s", got, want)
- }
-}
-
-type BugD struct { // Same as BugA after tagging.
- XXX string `json:"S"`
-}
-
-// BugD's tagged S field should dominate BugA's.
-type BugY struct {
- BugA
- BugD
-}
-
-// Test that a field with a tag dominates untagged fields.
-func TestTaggedFieldDominates(t *testing.T) {
- v := BugY{
- BugA{"BugA"},
- BugD{"BugD"},
- }
- b, err := Marshal(v)
- if err != nil {
- t.Fatal("Marshal:", err)
- }
- want := `{"S":"BugD"}`
- got := string(b)
- if got != want {
- t.Fatalf("Marshal: got %s want %s", got, want)
- }
-}
-
-// There are no tags here, so S should not appear.
-type BugZ struct {
- BugA
- BugC
- BugY // Contains a tagged S field through BugD; should not dominate.
-}
-
-func TestDuplicatedFieldDisappears(t *testing.T) {
- v := BugZ{
- BugA{"BugA"},
- BugC{"BugC"},
- BugY{
- BugA{"nested BugA"},
- BugD{"nested BugD"},
- },
- }
- b, err := Marshal(v)
- if err != nil {
- t.Fatal("Marshal:", err)
- }
- want := `{}`
- got := string(b)
- if got != want {
- t.Fatalf("Marshal: got %s want %s", got, want)
- }
-}
-
-func TestStringBytes(t *testing.T) {
- t.Parallel()
- // Test that encodeState.stringBytes and encodeState.string use the same encoding.
- var r []rune
- for i := '\u0000'; i <= unicode.MaxRune; i++ {
- if testing.Short() && i > 1000 {
- i = unicode.MaxRune
- }
- r = append(r, i)
- }
- s := string(r) + "\xff\xff\xffhello" // some invalid UTF-8 too
-
- for _, escapeHTML := range []bool{true, false} {
- es := &encodeState{}
- es.string(s, escapeHTML)
-
- esBytes := &encodeState{}
- esBytes.stringBytes([]byte(s), escapeHTML)
-
- enc := es.Buffer.String()
- encBytes := esBytes.Buffer.String()
- if enc != encBytes {
- i := 0
- for i < len(enc) && i < len(encBytes) && enc[i] == encBytes[i] {
- i++
- }
- enc = enc[i:]
- encBytes = encBytes[i:]
- i = 0
- for i < len(enc) && i < len(encBytes) && enc[len(enc)-i-1] == encBytes[len(encBytes)-i-1] {
- i++
- }
- enc = enc[:len(enc)-i]
- encBytes = encBytes[:len(encBytes)-i]
-
- if len(enc) > 20 {
- enc = enc[:20] + "..."
- }
- if len(encBytes) > 20 {
- encBytes = encBytes[:20] + "..."
- }
-
- t.Errorf("with escapeHTML=%t, encodings differ at %#q vs %#q",
- escapeHTML, enc, encBytes)
- }
- }
-}
-
-func TestIssue10281(t *testing.T) {
- type Foo struct {
- N Number
- }
- x := Foo{Number(`invalid`)}
-
- b, err := Marshal(&x)
- if err == nil {
- t.Errorf("Marshal(&x) = %#q; want error", b)
- }
-}
-
-func TestHTMLEscape(t *testing.T) {
- var b, want bytes.Buffer
- m := `{"M":"<html>foo &` + "\xe2\x80\xa8 \xe2\x80\xa9" + `</html>"}`
- want.Write([]byte(`{"M":"\u003chtml\u003efoo \u0026\u2028 \u2029\u003c/html\u003e"}`))
- HTMLEscape(&b, []byte(m))
- if !bytes.Equal(b.Bytes(), want.Bytes()) {
- t.Errorf("HTMLEscape(&b, []byte(m)) = %s; want %s", b.Bytes(), want.Bytes())
- }
-}
-
-// golang.org/issue/8582
-func TestEncodePointerString(t *testing.T) {
- type stringPointer struct {
- N *int64 `json:"n,string"`
- }
- var n int64 = 42
- b, err := Marshal(stringPointer{N: &n})
- if err != nil {
- t.Fatalf("Marshal: %v", err)
- }
- if got, want := string(b), `{"n":"42"}`; got != want {
- t.Errorf("Marshal = %s, want %s", got, want)
- }
- var back stringPointer
- err = Unmarshal(b, &back)
- if err != nil {
- t.Fatalf("Unmarshal: %v", err)
- }
- if back.N == nil {
- t.Fatalf("Unmarshaled nil N field")
- }
- if *back.N != 42 {
- t.Fatalf("*N = %d; want 42", *back.N)
- }
-}
-
-var encodeStringTests = []struct {
- in string
- out string
-}{
- {"\x00", `"\u0000"`},
- {"\x01", `"\u0001"`},
- {"\x02", `"\u0002"`},
- {"\x03", `"\u0003"`},
- {"\x04", `"\u0004"`},
- {"\x05", `"\u0005"`},
- {"\x06", `"\u0006"`},
- {"\x07", `"\u0007"`},
- {"\x08", `"\u0008"`},
- {"\x09", `"\t"`},
- {"\x0a", `"\n"`},
- {"\x0b", `"\u000b"`},
- {"\x0c", `"\u000c"`},
- {"\x0d", `"\r"`},
- {"\x0e", `"\u000e"`},
- {"\x0f", `"\u000f"`},
- {"\x10", `"\u0010"`},
- {"\x11", `"\u0011"`},
- {"\x12", `"\u0012"`},
- {"\x13", `"\u0013"`},
- {"\x14", `"\u0014"`},
- {"\x15", `"\u0015"`},
- {"\x16", `"\u0016"`},
- {"\x17", `"\u0017"`},
- {"\x18", `"\u0018"`},
- {"\x19", `"\u0019"`},
- {"\x1a", `"\u001a"`},
- {"\x1b", `"\u001b"`},
- {"\x1c", `"\u001c"`},
- {"\x1d", `"\u001d"`},
- {"\x1e", `"\u001e"`},
- {"\x1f", `"\u001f"`},
-}
-
-func TestEncodeString(t *testing.T) {
- for _, tt := range encodeStringTests {
- b, err := Marshal(tt.in)
- if err != nil {
- t.Errorf("Marshal(%q): %v", tt.in, err)
- continue
- }
- out := string(b)
- if out != tt.out {
- t.Errorf("Marshal(%q) = %#q, want %#q", tt.in, out, tt.out)
- }
- }
-}
-
-type jsonbyte byte
-
-func (b jsonbyte) MarshalJSON() ([]byte, error) { return tenc(`{"JB":%d}`, b) }
-
-type textbyte byte
-
-func (b textbyte) MarshalText() ([]byte, error) { return tenc(`TB:%d`, b) }
-
-type jsonint int
-
-func (i jsonint) MarshalJSON() ([]byte, error) { return tenc(`{"JI":%d}`, i) }
-
-type textint int
-
-func (i textint) MarshalText() ([]byte, error) { return tenc(`TI:%d`, i) }
-
-func tenc(format string, a ...any) ([]byte, error) {
- var buf bytes.Buffer
- fmt.Fprintf(&buf, format, a...)
- return buf.Bytes(), nil
-}
-
-type textfloat float64
-
-func (f textfloat) MarshalText() ([]byte, error) { return tenc(`TF:%0.2f`, f) }
-
-// Issue 13783
-func TestEncodeBytekind(t *testing.T) {
- t.Skip() // TODO
- testdata := []struct {
- data any
- want string
- }{
- {byte(7), "7"},
- {jsonbyte(7), `{"JB":7}`},
- {textbyte(4), `"TB:4"`},
- {jsonint(5), `{"JI":5}`},
- {textint(1), `"TI:1"`},
- {[]byte{0, 1}, `"AAE="`},
- {[]jsonbyte{0, 1}, `[{"JB":0},{"JB":1}]`},
- {[][]jsonbyte{{0, 1}, {3}}, `[[{"JB":0},{"JB":1}],[{"JB":3}]]`},
- {[]textbyte{2, 3}, `["TB:2","TB:3"]`},
- {[]jsonint{5, 4}, `[{"JI":5},{"JI":4}]`},
- {[]textint{9, 3}, `["TI:9","TI:3"]`},
- {[]int{9, 3}, `[9,3]`},
- {[]textfloat{12, 3}, `["TF:12.00","TF:3.00"]`},
- }
- for _, d := range testdata {
- js, err := Marshal(d.data)
- if err != nil {
- t.Error(err)
- continue
- }
- got, want := string(js), d.want
- if got != want {
- t.Errorf("got %s, want %s", got, want)
- }
- }
-}
-
-func TestTextMarshalerMapKeysAreSorted(t *testing.T) {
- b, err := Marshal(map[unmarshalerText]int{
- {"x", "y"}: 1,
- {"y", "x"}: 2,
- {"a", "z"}: 3,
- {"z", "a"}: 4,
- })
- if err != nil {
- t.Fatalf("Failed to Marshal text.Marshaler: %v", err)
- }
- const want = `{"a:z":3,"x:y":1,"y:x":2,"z:a":4}`
- if string(b) != want {
- t.Errorf("Marshal map with text.Marshaler keys: got %#q, want %#q", b, want)
- }
-}
-
-// https://golang.org/issue/33675
-func TestNilMarshalerTextMapKey(t *testing.T) {
- b, err := Marshal(map[*unmarshalerText]int{
- (*unmarshalerText)(nil): 1,
- {"A", "B"}: 2,
- })
- if err != nil {
- t.Fatalf("Failed to Marshal *text.Marshaler: %v", err)
- }
- const want = `{"":1,"A:B":2}`
- if string(b) != want {
- t.Errorf("Marshal map with *text.Marshaler keys: got %#q, want %#q", b, want)
- }
-}
-
-var re = regexp.MustCompile
-
-// syntactic checks on form of marshaled floating point numbers.
-var badFloatREs = []*regexp.Regexp{
- re(`p`), // no binary exponential notation
- re(`^\+`), // no leading + sign
- re(`^-?0[^.]`), // no unnecessary leading zeros
- re(`^-?\.`), // leading zero required before decimal point
- re(`\.(e|$)`), // no trailing decimal
- re(`\.[0-9]+0(e|$)`), // no trailing zero in fraction
- re(`^-?(0|[0-9]{2,})\..*e`), // exponential notation must have normalized mantissa
- re(`e[0-9]`), // positive exponent must be signed
- re(`e[+-]0`), // exponent must not have leading zeros
- re(`e-[1-6]$`), // not tiny enough for exponential notation
- re(`e+(.|1.|20)$`), // not big enough for exponential notation
- re(`^-?0\.0000000`), // too tiny, should use exponential notation
- re(`^-?[0-9]{22}`), // too big, should use exponential notation
- re(`[1-9][0-9]{16}[1-9]`), // too many significant digits in integer
- re(`[1-9][0-9.]{17}[1-9]`), // too many significant digits in decimal
- // below here for float32 only
- re(`[1-9][0-9]{8}[1-9]`), // too many significant digits in integer
- re(`[1-9][0-9.]{9}[1-9]`), // too many significant digits in decimal
-}
-
-func TestMarshalFloat(t *testing.T) {
- t.Parallel()
- nfail := 0
- test := func(f float64, bits int) {
- vf := any(f)
- if bits == 32 {
- f = float64(float32(f)) // round
- vf = float32(f)
- }
- bout, err := Marshal(vf)
- if err != nil {
- t.Errorf("Marshal(%T(%g)): %v", vf, vf, err)
- nfail++
- return
- }
- out := string(bout)
-
- // result must convert back to the same float
- g, err := strconv.ParseFloat(out, bits)
- if err != nil {
- t.Errorf("Marshal(%T(%g)) = %q, cannot parse back: %v", vf, vf, out, err)
- nfail++
- return
- }
- if f != g || fmt.Sprint(f) != fmt.Sprint(g) { // fmt.Sprint handles ±0
- t.Errorf("Marshal(%T(%g)) = %q (is %g, not %g)", vf, vf, out, float32(g), vf)
- nfail++
- return
- }
-
- bad := badFloatREs
- if bits == 64 {
- bad = bad[:len(bad)-2]
- }
- for _, re := range bad {
- if re.MatchString(out) {
- t.Errorf("Marshal(%T(%g)) = %q, must not match /%s/", vf, vf, out, re)
- nfail++
- return
- }
- }
- }
-
- var (
- bigger = math.Inf(+1)
- smaller = math.Inf(-1)
- )
-
- var digits = "1.2345678901234567890123"
- for i := len(digits); i >= 2; i-- {
- if testing.Short() && i < len(digits)-4 {
- break
- }
- for exp := -30; exp <= 30; exp++ {
- for _, sign := range "+-" {
- for bits := 32; bits <= 64; bits += 32 {
- s := fmt.Sprintf("%c%se%d", sign, digits[:i], exp)
- f, err := strconv.ParseFloat(s, bits)
- if err != nil {
- log.Fatal(err)
- }
- next := math.Nextafter
- if bits == 32 {
- next = func(g, h float64) float64 {
- return float64(math.Nextafter32(float32(g), float32(h)))
- }
- }
- test(f, bits)
- test(next(f, bigger), bits)
- test(next(f, smaller), bits)
- if nfail > 50 {
- t.Fatalf("stopping test early")
- }
- }
- }
- }
- }
- test(0, 64)
- test(math.Copysign(0, -1), 64)
- test(0, 32)
- test(math.Copysign(0, -1), 32)
-}
-
-func TestMarshalRawMessageValue(t *testing.T) {
- type (
- T1 struct {
- M RawMessage `json:",omitempty"`
- }
- T2 struct {
- M *RawMessage `json:",omitempty"`
- }
- )
-
- var (
- rawNil = RawMessage(nil)
- rawEmpty = RawMessage([]byte{})
- rawText = RawMessage([]byte(`"foo"`))
- )
-
- tests := []struct {
- in any
- want string
- ok bool
- }{
- // Test with nil RawMessage.
- {rawNil, "null", true},
- {&rawNil, "null", true},
- {[]any{rawNil}, "[null]", true},
- {&[]any{rawNil}, "[null]", true},
- {[]any{&rawNil}, "[null]", true},
- {&[]any{&rawNil}, "[null]", true},
- {struct{ M RawMessage }{rawNil}, `{"M":null}`, true},
- {&struct{ M RawMessage }{rawNil}, `{"M":null}`, true},
- {struct{ M *RawMessage }{&rawNil}, `{"M":null}`, true},
- {&struct{ M *RawMessage }{&rawNil}, `{"M":null}`, true},
- {map[string]any{"M": rawNil}, `{"M":null}`, true},
- {&map[string]any{"M": rawNil}, `{"M":null}`, true},
- {map[string]any{"M": &rawNil}, `{"M":null}`, true},
- {&map[string]any{"M": &rawNil}, `{"M":null}`, true},
- {T1{rawNil}, "{}", true},
- {T2{&rawNil}, `{"M":null}`, true},
- {&T1{rawNil}, "{}", true},
- {&T2{&rawNil}, `{"M":null}`, true},
-
- // Test with empty, but non-nil, RawMessage.
- {rawEmpty, "", false},
- {&rawEmpty, "", false},
- {[]any{rawEmpty}, "", false},
- {&[]any{rawEmpty}, "", false},
- {[]any{&rawEmpty}, "", false},
- {&[]any{&rawEmpty}, "", false},
- {struct{ X RawMessage }{rawEmpty}, "", false},
- {&struct{ X RawMessage }{rawEmpty}, "", false},
- {struct{ X *RawMessage }{&rawEmpty}, "", false},
- {&struct{ X *RawMessage }{&rawEmpty}, "", false},
- {map[string]any{"nil": rawEmpty}, "", false},
- {&map[string]any{"nil": rawEmpty}, "", false},
- {map[string]any{"nil": &rawEmpty}, "", false},
- {&map[string]any{"nil": &rawEmpty}, "", false},
- {T1{rawEmpty}, "{}", true},
- {T2{&rawEmpty}, "", false},
- {&T1{rawEmpty}, "{}", true},
- {&T2{&rawEmpty}, "", false},
-
- // Test with RawMessage with some text.
- //
- // The tests below marked with Issue6458 used to generate "ImZvbyI=" instead "foo".
- // This behavior was intentionally changed in Go 1.8.
- // See https://golang.org/issues/14493#issuecomment-255857318
- {rawText, `"foo"`, true}, // Issue6458
- {&rawText, `"foo"`, true},
- {[]any{rawText}, `["foo"]`, true}, // Issue6458
- {&[]any{rawText}, `["foo"]`, true}, // Issue6458
- {[]any{&rawText}, `["foo"]`, true},
- {&[]any{&rawText}, `["foo"]`, true},
- {struct{ M RawMessage }{rawText}, `{"M":"foo"}`, true}, // Issue6458
- {&struct{ M RawMessage }{rawText}, `{"M":"foo"}`, true},
- {struct{ M *RawMessage }{&rawText}, `{"M":"foo"}`, true},
- {&struct{ M *RawMessage }{&rawText}, `{"M":"foo"}`, true},
- {map[string]any{"M": rawText}, `{"M":"foo"}`, true}, // Issue6458
- {&map[string]any{"M": rawText}, `{"M":"foo"}`, true}, // Issue6458
- {map[string]any{"M": &rawText}, `{"M":"foo"}`, true},
- {&map[string]any{"M": &rawText}, `{"M":"foo"}`, true},
- {T1{rawText}, `{"M":"foo"}`, true}, // Issue6458
- {T2{&rawText}, `{"M":"foo"}`, true},
- {&T1{rawText}, `{"M":"foo"}`, true},
- {&T2{&rawText}, `{"M":"foo"}`, true},
- }
-
- for i, tt := range tests {
- b, err := Marshal(tt.in)
- if ok := (err == nil); ok != tt.ok {
- if err != nil {
- t.Errorf("test %d, unexpected failure: %v", i, err)
- } else {
- t.Skip() // MODIFIED
- t.Errorf("test %d, unexpected success", i)
- }
- }
- if got := string(b); got != tt.want {
- t.Errorf("test %d, Marshal(%#v) = %q, want %q", i, tt.in, got, tt.want)
- }
- }
-}
-
-type marshalPanic struct{}
-
-func (marshalPanic) MarshalJSON() ([]byte, error) { panic(0xdead) }
-
-func TestMarshalPanic(t *testing.T) {
- defer func() {
- if got := recover(); !reflect.DeepEqual(got, 0xdead) {
- t.Errorf("panic() = (%T)(%v), want 0xdead", got, got)
- }
- }()
- _, _ = Marshal(&marshalPanic{}) // MODIFIED
- t.Error("Marshal should have panicked")
-}
-
-func TestMarshalUncommonFieldNames(t *testing.T) {
- v := struct {
- A0, À, Aβ int
- }{}
- b, err := Marshal(v)
- if err != nil {
- t.Fatal("Marshal:", err)
- }
- want := `{"A0":0,"À":0,"Aβ":0}`
- got := string(b)
- if got != want {
- t.Fatalf("Marshal: got %s want %s", got, want)
- }
-}
-
-/* // MODIFIED
-func TestMarshalerError(t *testing.T) {
- s := "test variable"
- st := reflect.TypeOf(s)
- errText := "json: test error"
-
- tests := []struct {
- err *MarshalerError
- want string
- }{
- {
- &MarshalerError{st, fmt.Errorf(errText), ""},
- "json: error calling MarshalJSON for type " + st.String() + ": " + errText,
- },
- {
- &MarshalerError{st, fmt.Errorf(errText), "TestMarshalerError"},
- "json: error calling TestMarshalerError for type " + st.String() + ": " + errText,
- },
- }
-
- for i, tt := range tests {
- got := tt.err.Error()
- if got != tt.want {
- t.Errorf("MarshalerError test %d, got: %s, want: %s", i, got, tt.want)
- }
- }
-}
-*/ // MODIFIED
diff --git a/lib/lowmemjson/borrowed_fuzz_test.go b/lib/lowmemjson/borrowed_fuzz_test.go
deleted file mode 100644
index 21756a7..0000000
--- a/lib/lowmemjson/borrowed_fuzz_test.go
+++ /dev/null
@@ -1,83 +0,0 @@
-// Copyright 2021 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package lowmemjson // MODIFIED
-
-import (
- "testing"
-)
-
-func FuzzUnmarshalJSON(f *testing.F) {
- f.Add([]byte(`{
-"object": {
- "slice": [
- 1,
- 2.0,
- "3",
- [4],
- {5: {}}
- ]
-},
-"slice": [[]],
-"string": ":)",
-"int": 1e5,
-"float": 3e-9"
-}`))
-
- f.Fuzz(func(t *testing.T, b []byte) {
- for _, typ := range []func() interface{}{
- func() interface{} { return new(interface{}) },
- func() interface{} { return new(map[string]interface{}) },
- func() interface{} { return new([]interface{}) },
- } {
- i := typ()
- if err := Unmarshal(b, i); err != nil {
- return
- }
-
- encoded, err := Marshal(i)
- if err != nil {
- t.Fatalf("failed to marshal: %s", err)
- }
-
- if err := Unmarshal(encoded, i); err != nil {
- t.Fatalf("failed to roundtrip: %s", err)
- }
- }
- })
-}
-
-/* // MODIFIED
-func FuzzDecoderToken(f *testing.F) {
- f.Add([]byte(`{
-"object": {
- "slice": [
- 1,
- 2.0,
- "3",
- [4],
- {5: {}}
- ]
-},
-"slice": [[]],
-"string": ":)",
-"int": 1e5,
-"float": 3e-9"
-}`))
-
- f.Fuzz(func(t *testing.T, b []byte) {
- r := bytes.NewReader(b)
- d := NewDecoder(r)
- for {
- _, err := d.Token()
- if err != nil {
- if err == io.EOF {
- break
- }
- return
- }
- }
- })
-}
-*/ // MODIFIED
diff --git a/lib/lowmemjson/borrowed_misc.go b/lib/lowmemjson/borrowed_misc.go
deleted file mode 100644
index 5c6bbb6..0000000
--- a/lib/lowmemjson/borrowed_misc.go
+++ /dev/null
@@ -1,42 +0,0 @@
-// Copyright 2010 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package lowmemjson
-
-import (
- "fmt"
- "reflect"
-)
-
-// A SyntaxError is a description of a JSON syntax error.
-//
-// from scanner.go
-type SyntaxError struct {
- msg string // description of error
- Offset int64 // error occurred after reading Offset bytes
-}
-
-func (e *SyntaxError) Error() string {
- return fmt.Sprintf("JSON syntax error at input byte %v: %v",
- e.Offset, e.msg)
-}
-
-// from encode.go
-func isEmptyValue(v reflect.Value) bool {
- switch v.Kind() {
- case reflect.Array, reflect.Map, reflect.Slice, reflect.String:
- return v.Len() == 0
- case reflect.Bool:
- return !v.Bool()
- case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
- return v.Int() == 0
- case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
- return v.Uint() == 0
- case reflect.Float32, reflect.Float64:
- return v.Float() == 0
- case reflect.Interface, reflect.Pointer:
- return v.IsNil()
- }
- return false
-}
diff --git a/lib/lowmemjson/borrowed_scanner_test.go b/lib/lowmemjson/borrowed_scanner_test.go
deleted file mode 100644
index c5d67e6..0000000
--- a/lib/lowmemjson/borrowed_scanner_test.go
+++ /dev/null
@@ -1,305 +0,0 @@
-// Copyright 2010 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package lowmemjson // MODIFIED
-
-import (
- "bytes"
- "math"
- "math/rand"
- "reflect"
- "testing"
-)
-
-var validTests = []struct {
- data string
- ok bool
-}{
- {`foo`, false},
- {`}{`, false},
- {`{]`, false},
- {`{}`, true},
- {`{"foo":"bar"}`, true},
- {`{"foo":"bar","bar":{"baz":["qux"]}}`, true},
-}
-
-func TestValid(t *testing.T) {
- for _, tt := range validTests {
- if ok := Valid([]byte(tt.data)); ok != tt.ok {
- t.Errorf("Valid(%#q) = %v, want %v", tt.data, ok, tt.ok)
- }
- }
-}
-
-// Tests of simple examples.
-
-type example struct {
- compact string
- indent string
-}
-
-var examples = []example{
- {`1`, `1`},
- {`{}`, `{}`},
- {`[]`, `[]`},
- {`{"":2}`, "{\n\t\"\": 2\n}"},
- {`[3]`, "[\n\t3\n]"},
- {`[1,2,3]`, "[\n\t1,\n\t2,\n\t3\n]"},
- {`{"x":1}`, "{\n\t\"x\": 1\n}"},
- {ex1, ex1i},
- {"{\"\":\"<>&\u2028\u2029\"}", "{\n\t\"\": \"<>&\u2028\u2029\"\n}"}, // See golang.org/issue/34070
-}
-
-var ex1 = `[true,false,null,"x",1,1.5,0,-5e+2]`
-
-var ex1i = `[
- true,
- false,
- null,
- "x",
- 1,
- 1.5,
- 0,
- -5e+2
-]`
-
-func TestCompact(t *testing.T) {
- t.Skip() // TODO
- var buf bytes.Buffer
- for _, tt := range examples {
- buf.Reset()
- if err := Compact(&buf, []byte(tt.compact)); err != nil {
- t.Errorf("Compact(%#q): %v", tt.compact, err)
- } else if s := buf.String(); s != tt.compact {
- t.Errorf("Compact(%#q) = %#q, want original", tt.compact, s)
- }
-
- buf.Reset()
- if err := Compact(&buf, []byte(tt.indent)); err != nil {
- t.Errorf("Compact(%#q): %v", tt.indent, err)
- continue
- } else if s := buf.String(); s != tt.compact {
- t.Errorf("Compact(%#q) = %#q, want %#q", tt.indent, s, tt.compact)
- }
- }
-}
-
-func TestCompactSeparators(t *testing.T) {
- t.Skip() // TODO
- // U+2028 and U+2029 should be escaped inside strings.
- // They should not appear outside strings.
- tests := []struct {
- in, compact string
- }{
- {"{\"\u2028\": 1}", "{\"\u2028\":1}"},
- {"{\"\u2029\" :2}", "{\"\u2029\":2}"},
- }
- for _, tt := range tests {
- var buf bytes.Buffer
- if err := Compact(&buf, []byte(tt.in)); err != nil {
- t.Errorf("Compact(%q): %v", tt.in, err)
- } else if s := buf.String(); s != tt.compact {
- t.Errorf("Compact(%q) = %q, want %q", tt.in, s, tt.compact)
- }
- }
-}
-
-func TestIndent(t *testing.T) {
- t.Skip() // TODO
- var buf bytes.Buffer
- for _, tt := range examples {
- buf.Reset()
- if err := Indent(&buf, []byte(tt.indent), "", "\t"); err != nil {
- t.Errorf("Indent(%#q): %v", tt.indent, err)
- } else if s := buf.String(); s != tt.indent {
- t.Errorf("Indent(%#q) = %#q, want original", tt.indent, s)
- }
-
- buf.Reset()
- if err := Indent(&buf, []byte(tt.compact), "", "\t"); err != nil {
- t.Errorf("Indent(%#q): %v", tt.compact, err)
- continue
- } else if s := buf.String(); s != tt.indent {
- t.Errorf("Indent(%#q) = %#q, want %#q", tt.compact, s, tt.indent)
- }
- }
-}
-
-// Tests of a large random structure.
-
-func TestCompactBig(t *testing.T) {
- initBig()
- var buf bytes.Buffer
- if err := Compact(&buf, jsonBig); err != nil {
- t.Fatalf("Compact: %v", err)
- }
- b := buf.Bytes()
- if !bytes.Equal(b, jsonBig) {
- t.Error("Compact(jsonBig) != jsonBig")
- diff(t, b, jsonBig)
- return
- }
-}
-
-func TestIndentBig(t *testing.T) {
- t.Parallel()
- initBig()
- var buf bytes.Buffer
- if err := Indent(&buf, jsonBig, "", "\t"); err != nil {
- t.Fatalf("Indent1: %v", err)
- }
- b := buf.Bytes()
- if len(b) == len(jsonBig) {
- // jsonBig is compact (no unnecessary spaces);
- // indenting should make it bigger
- t.Fatalf("Indent(jsonBig) did not get bigger")
- }
-
- // should be idempotent
- var buf1 bytes.Buffer
- if err := Indent(&buf1, b, "", "\t"); err != nil {
- t.Fatalf("Indent2: %v", err)
- }
- b1 := buf1.Bytes()
- if !bytes.Equal(b1, b) {
- t.Error("Indent(Indent(jsonBig)) != Indent(jsonBig)")
- diff(t, b1, b)
- return
- }
-
- // should get back to original
- buf1.Reset()
- if err := Compact(&buf1, b); err != nil {
- t.Fatalf("Compact: %v", err)
- }
- b1 = buf1.Bytes()
- if !bytes.Equal(b1, jsonBig) {
- t.Error("Compact(Indent(jsonBig)) != jsonBig")
- diff(t, b1, jsonBig)
- return
- }
-}
-
-type indentErrorTest struct {
- in string
- err error
-}
-
-var indentErrorTests = []indentErrorTest{
- {`{"X": "foo", "Y"}`, &SyntaxError{"invalid character '}' after object key", 17}},
- {`{"X": "foo" "Y": "bar"}`, &SyntaxError{"invalid character '\"' after object key:value pair", 13}},
-}
-
-func TestIndentErrors(t *testing.T) {
- t.Skip() // TODO
- for i, tt := range indentErrorTests {
- slice := make([]uint8, 0)
- buf := bytes.NewBuffer(slice)
- if err := Indent(buf, []uint8(tt.in), "", ""); err != nil {
- if !reflect.DeepEqual(err, tt.err) {
- t.Errorf("#%d: Indent: %#v", i, err)
- continue
- }
- }
- }
-}
-
-func diff(t *testing.T, a, b []byte) {
- for i := 0; ; i++ {
- if i >= len(a) || i >= len(b) || a[i] != b[i] {
- j := i - 10
- if j < 0 {
- j = 0
- }
- t.Errorf("diverge at %d: «%s» vs «%s»", i, trim(a[j:]), trim(b[j:]))
- return
- }
- }
-}
-
-func trim(b []byte) []byte {
- if len(b) > 20 {
- return b[0:20]
- }
- return b
-}
-
-// Generate a random JSON object.
-
-var jsonBig []byte
-
-func initBig() {
- n := 10000
- if testing.Short() {
- n = 100
- }
- b, err := Marshal(genValue(n))
- if err != nil {
- panic(err)
- }
- jsonBig = b
-}
-
-func genValue(n int) any {
- if n > 1 {
- switch rand.Intn(2) {
- case 0:
- return genArray(n)
- case 1:
- return genMap(n)
- }
- }
- switch rand.Intn(3) {
- case 0:
- return rand.Intn(2) == 0
- case 1:
- return rand.NormFloat64()
- case 2:
- return genString(30)
- }
- panic("unreachable")
-}
-
-func genString(stddev float64) string {
- n := int(math.Abs(rand.NormFloat64()*stddev + stddev/2))
- c := make([]rune, n)
- for i := range c {
- f := math.Abs(rand.NormFloat64()*64 + 32)
- if f > 0x10ffff {
- f = 0x10ffff
- }
- c[i] = rune(f)
- }
- return string(c)
-}
-
-func genArray(n int) []any {
- f := int(math.Abs(rand.NormFloat64()) * math.Min(10, float64(n/2)))
- if f > n {
- f = n
- }
- if f < 1 {
- f = 1
- }
- x := make([]any, f)
- for i := range x {
- x[i] = genValue(((i+1)*n)/f - (i*n)/f)
- }
- return x
-}
-
-func genMap(n int) map[string]any {
- f := int(math.Abs(rand.NormFloat64()) * math.Min(10, float64(n/2)))
- if f > n {
- f = n
- }
- if n > 0 && f == 0 {
- f = 1
- }
- x := make(map[string]any)
- for i := 0; i < f; i++ {
- x[genString(10)] = genValue(((i+1)*n)/f - (i*n)/f)
- }
- return x
-}
diff --git a/lib/lowmemjson/borrowed_tagkey_test.go b/lib/lowmemjson/borrowed_tagkey_test.go
deleted file mode 100644
index 2d4d2c0..0000000
--- a/lib/lowmemjson/borrowed_tagkey_test.go
+++ /dev/null
@@ -1,121 +0,0 @@
-// Copyright 2011 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package lowmemjson // MODIFIED
-
-import (
- "testing"
-)
-
-type basicLatin2xTag struct {
- V string `json:"$%-/"`
-}
-
-type basicLatin3xTag struct {
- V string `json:"0123456789"`
-}
-
-type basicLatin4xTag struct {
- V string `json:"ABCDEFGHIJKLMO"`
-}
-
-type basicLatin5xTag struct {
- V string `json:"PQRSTUVWXYZ_"`
-}
-
-type basicLatin6xTag struct {
- V string `json:"abcdefghijklmno"`
-}
-
-type basicLatin7xTag struct {
- V string `json:"pqrstuvwxyz"`
-}
-
-type miscPlaneTag struct {
- V string `json:"色は匂へど"`
-}
-
-type percentSlashTag struct {
- V string `json:"text/html%"` // https://golang.org/issue/2718
-}
-
-type punctuationTag struct {
- V string `json:"!#$%&()*+-./:;<=>?@[]^_{|}~ "` // https://golang.org/issue/3546
-}
-
-type dashTag struct {
- V string `json:"-,"`
-}
-
-type emptyTag struct {
- W string
-}
-
-type misnamedTag struct {
- X string `jsom:"Misnamed"`
-}
-
-type badFormatTag struct {
- Y string `:"BadFormat"`
-}
-
-type badCodeTag struct {
- Z string `json:" !\"#&'()*+,."`
-}
-
-type spaceTag struct {
- Q string `json:"With space"`
-}
-
-type unicodeTag struct {
- W string `json:"Ελλάδα"`
-}
-
-var structTagObjectKeyTests = []struct {
- raw any
- value string
- key string
-}{
- {basicLatin2xTag{"2x"}, "2x", "$%-/"},
- {basicLatin3xTag{"3x"}, "3x", "0123456789"},
- {basicLatin4xTag{"4x"}, "4x", "ABCDEFGHIJKLMO"},
- {basicLatin5xTag{"5x"}, "5x", "PQRSTUVWXYZ_"},
- {basicLatin6xTag{"6x"}, "6x", "abcdefghijklmno"},
- {basicLatin7xTag{"7x"}, "7x", "pqrstuvwxyz"},
- {miscPlaneTag{"いろはにほへと"}, "いろはにほへと", "色は匂へど"},
- {dashTag{"foo"}, "foo", "-"},
- {emptyTag{"Pour Moi"}, "Pour Moi", "W"},
- {misnamedTag{"Animal Kingdom"}, "Animal Kingdom", "X"},
- {badFormatTag{"Orfevre"}, "Orfevre", "Y"},
- {badCodeTag{"Reliable Man"}, "Reliable Man", "Z"},
- {percentSlashTag{"brut"}, "brut", "text/html%"},
- {punctuationTag{"Union Rags"}, "Union Rags", "!#$%&()*+-./:;<=>?@[]^_{|}~ "},
- {spaceTag{"Perreddu"}, "Perreddu", "With space"},
- {unicodeTag{"Loukanikos"}, "Loukanikos", "Ελλάδα"},
-}
-
-func TestStructTagObjectKey(t *testing.T) {
- t.Skip() // TODO
- for _, tt := range structTagObjectKeyTests {
- b, err := Marshal(tt.raw)
- if err != nil {
- t.Fatalf("Marshal(%#q) failed: %v", tt.raw, err)
- }
- var f any
- err = Unmarshal(b, &f)
- if err != nil {
- t.Fatalf("Unmarshal(%#q) failed: %v", b, err)
- }
- for i, v := range f.(map[string]any) {
- switch i {
- case tt.key:
- if s, ok := v.(string); !ok || s != tt.value {
- t.Fatalf("Unexpected value: %#q, want %v", s, tt.value)
- }
- default:
- t.Fatalf("Unexpected key: %#q, from %#q", i, b)
- }
- }
- }
-}
diff --git a/lib/lowmemjson/borrowed_tags.go b/lib/lowmemjson/borrowed_tags.go
deleted file mode 100644
index 07292b1..0000000
--- a/lib/lowmemjson/borrowed_tags.go
+++ /dev/null
@@ -1,38 +0,0 @@
-// Copyright 2011 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package lowmemjson
-
-import (
- "strings"
-)
-
-// tagOptions is the string following a comma in a struct field's "json"
-// tag, or the empty string. It does not include the leading comma.
-type tagOptions string
-
-// parseTag splits a struct field's json tag into its name and
-// comma-separated options.
-func parseTag(tag string) (string, tagOptions) {
- tag, opt, _ := strings.Cut(tag, ",")
- return tag, tagOptions(opt)
-}
-
-// Contains reports whether a comma-separated list of options
-// contains a particular substr flag. substr must be surrounded by a
-// string boundary or commas.
-func (o tagOptions) Contains(optionName string) bool {
- if len(o) == 0 {
- return false
- }
- s := string(o)
- for s != "" {
- var name string
- name, s, _ = strings.Cut(s, ",")
- if name == optionName {
- return true
- }
- }
- return false
-}
diff --git a/lib/lowmemjson/borrowed_tags_test.go b/lib/lowmemjson/borrowed_tags_test.go
deleted file mode 100644
index 28d76e9..0000000
--- a/lib/lowmemjson/borrowed_tags_test.go
+++ /dev/null
@@ -1,28 +0,0 @@
-// Copyright 2011 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package lowmemjson // MODIFIED
-
-import (
- "testing"
-)
-
-func TestTagParsing(t *testing.T) {
- name, opts := parseTag("field,foobar,foo")
- if name != "field" {
- t.Fatalf("name = %q, want field", name)
- }
- for _, tt := range []struct {
- opt string
- want bool
- }{
- {"foobar", true},
- {"foo", true},
- {"bar", false},
- } {
- if opts.Contains(tt.opt) != tt.want {
- t.Errorf("Contains(%q) = %v", tt.opt, !tt.want)
- }
- }
-}
diff --git a/lib/lowmemjson/decode.go b/lib/lowmemjson/decode.go
deleted file mode 100644
index 03d5b7a..0000000
--- a/lib/lowmemjson/decode.go
+++ /dev/null
@@ -1,812 +0,0 @@
-// Copyright (C) 2022 Luke Shumaker <lukeshu@lukeshu.com>
-//
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-package lowmemjson
-
-import (
- "bufio"
- "bytes"
- "encoding"
- "encoding/json"
- "fmt"
- "io"
- "reflect"
- "strconv"
- "strings"
-)
-
-type Decodable interface {
- DecodeJSON(io.RuneScanner) error
-}
-
-type runeBuffer interface {
- io.Writer
- WriteRune(rune) (int, error)
- Reset()
-}
-
-type Decoder struct {
- r io.RuneScanner
-
- // config
- disallowUnknownFields bool
- useNumber bool
-
- // state
- err error
- curPos int64
- nxtPos int64
- stack []any
-}
-
-var forceBufio bool
-
-func NewDecoder(r io.Reader) *Decoder {
- rs, ok := r.(io.RuneScanner)
- if forceBufio || !ok {
- rs = bufio.NewReader(r)
- }
- return &Decoder{
- r: rs,
- }
-}
-
-func (dec *Decoder) DisallowUnknownFields() { dec.disallowUnknownFields = true }
-func (dec *Decoder) UseNumber() { dec.useNumber = true }
-func (dec *Decoder) InputOffset() int64 { return dec.curPos }
-
-func (dec *Decoder) More() bool {
- dec.decodeWS()
- _, ok := dec.peekRuneOrEOF()
- return ok
-}
-
-func (dec *Decoder) stackStr() string {
- var buf strings.Builder
- buf.WriteString("v")
- for _, item := range dec.stack {
- fmt.Fprintf(&buf, "[%#v]", item)
- }
- return buf.String()
-}
-
-func (dec *Decoder) stackPush(idx any) {
- dec.stack = append(dec.stack, idx)
-}
-func (dec *Decoder) stackPop() {
- dec.stack = dec.stack[:len(dec.stack)-1]
-}
-
-type decodeError struct {
- Err error
-}
-
-func (dec *Decoder) panicIO(err error) {
- panic(decodeError{fmt.Errorf("json: I/O error at input byte %v: %s: %w",
- dec.nxtPos, dec.stackStr(), err)})
-}
-func (dec *Decoder) panicSyntax(err error) {
- panic(decodeError{fmt.Errorf("json: syntax error at input byte %v: %s: %w",
- dec.curPos, dec.stackStr(), err)})
-}
-func (dec *Decoder) panicType(typ reflect.Type, err error) {
- panic(decodeError{fmt.Errorf("json: type mismatch error at input byte %v: %s: type %v: %w",
- dec.curPos, dec.stackStr(), typ, err)})
-}
-
-func Decode(r io.Reader, ptr any) error {
- return NewDecoder(r).Decode(ptr)
-}
-
-func (dec *Decoder) Decode(ptr any) (err error) {
- ptrVal := reflect.ValueOf(ptr)
- if ptrVal.Kind() != reflect.Pointer || ptrVal.IsNil() || !ptrVal.Elem().CanSet() {
- return &json.InvalidUnmarshalError{
- // don't use ptrVal.Type() because ptrVal might be invalid if ptr==nil
- Type: reflect.TypeOf(ptr),
- }
- }
-
- if dec.err != nil {
- return dec.err
- }
-
- defer func() {
- if r := recover(); r != nil {
- if de, ok := r.(decodeError); ok {
- dec.err = de.Err
- err = dec.err
- } else {
- panic(r)
- }
- }
- }()
- dec.decodeWS()
- dec.decode(ptrVal.Elem(), false)
- return nil
-}
-
-func (dec *Decoder) readRune() rune {
- c, size, err := dec.r.ReadRune()
- if err != nil {
- if err == io.EOF {
- dec.panicSyntax(io.ErrUnexpectedEOF)
- }
- dec.panicIO(err)
- }
- dec.curPos = dec.nxtPos
- dec.nxtPos = dec.curPos + int64(size)
- return c
-}
-
-func (dec *Decoder) readRuneOrEOF() (c rune, ok bool) {
- c, size, err := dec.r.ReadRune()
- if err != nil {
- if err == io.EOF {
- return 0, false
- }
- dec.panicIO(err)
- }
- dec.curPos = dec.nxtPos
- dec.nxtPos = dec.curPos + int64(size)
- return c, true
-}
-
-func (dec *Decoder) unreadRune() {
- if err := dec.r.UnreadRune(); err != nil {
- // .UnreadRune() must succeed if the previous call was
- // .ReadRune(), which it always is for this code.
- panic(err)
- }
- dec.nxtPos = dec.curPos
-}
-
-func (dec *Decoder) peekRune() rune {
- c, _, err := dec.r.ReadRune()
- if err != nil {
- if err == io.EOF {
- dec.panicSyntax(io.ErrUnexpectedEOF)
- }
- dec.panicIO(err)
- }
- if err := dec.r.UnreadRune(); err != nil {
- // .UnreadRune() must succeed if the previous call was
- // .ReadRune(), which it always is for this code.
- panic(err)
- }
- return c
-}
-
-func (dec *Decoder) peekRuneOrEOF() (rune, bool) {
- c, _, err := dec.r.ReadRune()
- if err != nil {
- if err == io.EOF {
- return 0, false
- }
- dec.panicIO(err)
- }
- if err := dec.r.UnreadRune(); err != nil {
- // .UnreadRune() must succeed if the previous call was
- // .ReadRune(), which it always is for this code.
- panic(err)
- }
- return c, true
-}
-
-func (dec *Decoder) expectRune(exp rune) {
- act := dec.readRune()
- if act != exp {
- dec.panicSyntax(fmt.Errorf("expected %q but got %q", exp, act))
- }
-}
-
-var (
- rawMessagePtrType = reflect.TypeOf((*json.RawMessage)(nil))
- decodableType = reflect.TypeOf((*Decodable)(nil)).Elem()
- jsonUnmarshalerType = reflect.TypeOf((*json.Unmarshaler)(nil)).Elem()
- textUnmarshalerType = reflect.TypeOf((*encoding.TextUnmarshaler)(nil)).Elem()
-)
-
-var kind2bits = map[reflect.Kind]int{
- reflect.Int: int(32 << (^uint(0) >> 63)),
- reflect.Int8: 8,
- reflect.Int16: 16,
- reflect.Int32: 32,
- reflect.Int64: 64,
-
- reflect.Uint: int(32 << (^uint(0) >> 63)),
- reflect.Uint8: 8,
- reflect.Uint16: 16,
- reflect.Uint32: 32,
- reflect.Uint64: 64,
-
- reflect.Uintptr: int(32 << (^uintptr(0) >> 63)),
-
- reflect.Float32: 32,
- reflect.Float64: 64,
-}
-
-func (dec *Decoder) decode(val reflect.Value, nullOK bool) {
- typ := val.Type()
- switch {
- case val.CanAddr() && reflect.PointerTo(typ) == rawMessagePtrType:
- var buf bytes.Buffer
- dec.scan(&buf)
- if err := val.Addr().Interface().(*json.RawMessage).UnmarshalJSON(buf.Bytes()); err != nil {
- dec.panicSyntax(err)
- }
- case val.CanAddr() && reflect.PointerTo(typ).Implements(decodableType):
- obj := val.Addr().Interface().(Decodable)
- if err := obj.DecodeJSON(dec.r); err != nil {
- dec.panicSyntax(err)
- }
- case val.CanAddr() && reflect.PointerTo(typ).Implements(jsonUnmarshalerType):
- var buf bytes.Buffer
- dec.scan(&buf)
- obj := val.Addr().Interface().(json.Unmarshaler)
- if err := obj.UnmarshalJSON(buf.Bytes()); err != nil {
- dec.panicSyntax(err)
- }
- case val.CanAddr() && reflect.PointerTo(typ).Implements(textUnmarshalerType):
- if nullOK && dec.peekRune() == 'n' {
- dec.decodeNull()
- return
- }
- var buf bytes.Buffer
- dec.decodeString(&buf)
- obj := val.Addr().Interface().(encoding.TextUnmarshaler)
- if err := obj.UnmarshalText(buf.Bytes()); err != nil {
- dec.panicSyntax(err)
- }
- default:
- kind := typ.Kind()
- switch kind {
- case reflect.Bool:
- if nullOK && dec.peekRune() == 'n' {
- dec.decodeNull()
- return
- }
- val.SetBool(dec.decodeBool())
- case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
- if nullOK && dec.peekRune() == 'n' {
- dec.decodeNull()
- return
- }
- var buf strings.Builder
- dec.scanNumber(&buf)
- n, err := strconv.ParseInt(buf.String(), 10, kind2bits[kind])
- if err != nil {
- dec.panicSyntax(err)
- }
- val.SetInt(n)
- case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
- if nullOK && dec.peekRune() == 'n' {
- dec.decodeNull()
- return
- }
- var buf strings.Builder
- dec.scanNumber(&buf)
- n, err := strconv.ParseUint(buf.String(), 10, kind2bits[kind])
- if err != nil {
- dec.panicSyntax(err)
- }
- val.SetUint(n)
- case reflect.Float32, reflect.Float64:
- if nullOK && dec.peekRune() == 'n' {
- dec.decodeNull()
- return
- }
- var buf strings.Builder
- dec.scanNumber(&buf)
- n, err := strconv.ParseFloat(buf.String(), kind2bits[kind])
- if err != nil {
- dec.panicSyntax(err)
- }
- val.SetFloat(n)
- case reflect.String:
- if nullOK && dec.peekRune() == 'n' {
- dec.decodeNull()
- return
- }
- var buf strings.Builder
- if typ == numberType {
- dec.scanNumber(&buf)
- val.SetString(buf.String())
- } else {
- dec.decodeString(&buf)
- val.SetString(buf.String())
- }
- case reflect.Interface:
- if typ.NumMethod() > 0 {
- dec.panicType(typ, fmt.Errorf("cannot decode in to non-empty interface"))
- }
- switch dec.peekRune() {
- case 'n':
- if !val.IsNil() && val.Elem().Kind() == reflect.Pointer && val.Elem().Elem().Kind() == reflect.Pointer {
- // XXX: I can't justify this case, other than "it's what encoding/json does, but
- // I don't understand their rationale".
- dec.decode(val.Elem(), false)
- } else {
- dec.decodeNull()
- val.Set(reflect.Zero(typ))
- }
- default:
- if !val.IsNil() && val.Elem().Kind() == reflect.Pointer {
- dec.decode(val.Elem(), false)
- } else {
- val.Set(reflect.ValueOf(dec.decodeAny()))
- }
- }
- case reflect.Struct:
- if nullOK && dec.peekRune() == 'n' {
- dec.decodeNull()
- return
- }
- index := indexStruct(typ)
- var nameBuf strings.Builder
- dec.decodeObject(&nameBuf, func() {
- name := nameBuf.String()
- dec.stackPush(name)
- defer dec.stackPop()
- idx, ok := index.byName[name]
- if !ok {
- if dec.disallowUnknownFields {
- dec.panicType(typ, fmt.Errorf("unknown field %q", name))
- }
- dec.scan(io.Discard)
- return
- }
- field := index.byPos[idx]
- fVal := val
- for _, idx := range field.Path {
- if fVal.Kind() == reflect.Pointer {
- if fVal.IsNil() {
- if !fVal.CanSet() { // https://golang.org/issue/21357
- dec.panicType(fVal.Type().Elem(), fmt.Errorf("cannot set embedded pointer to unexported type"))
- }
- fVal.Set(reflect.New(fVal.Type().Elem()))
- }
- fVal = fVal.Elem()
- }
- fVal = fVal.Field(idx)
- }
- if field.Quote {
- switch dec.peekRune() {
- case 'n':
- dec.decodeNull()
- switch fVal.Kind() {
- // XXX: I can't justify this list, other than "it's what encoding/json
- // does, but I don't understand their rationale".
- case reflect.Interface, reflect.Pointer, reflect.Map, reflect.Slice:
- fVal.Set(reflect.Zero(fVal.Type()))
- }
- case '"':
- // TODO: Figure out how to do this without buffering.
- var buf bytes.Buffer
- subD := *dec // capture the .curPos *before* calling .decodeString
- dec.decodeString(&buf)
- subD.r = &buf
- subD.decode(fVal, false)
- default:
- dec.panicSyntax(fmt.Errorf(",string field: expected %q or %q but got %q",
- 'n', '"', dec.peekRune()))
- }
- } else {
- dec.decode(fVal, true)
- }
- })
- case reflect.Map:
- switch dec.peekRune() {
- case 'n':
- dec.decodeNull()
- val.Set(reflect.Zero(typ))
- case '{':
- if val.IsNil() {
- val.Set(reflect.MakeMap(typ))
- }
- var nameBuf bytes.Buffer
- dec.decodeObject(&nameBuf, func() {
- nameValTyp := typ.Key()
- nameValPtr := reflect.New(nameValTyp)
- switch {
- case reflect.PointerTo(nameValTyp).Implements(textUnmarshalerType):
- obj := nameValPtr.Interface().(encoding.TextUnmarshaler)
- if err := obj.UnmarshalText(nameBuf.Bytes()); err != nil {
- dec.panicSyntax(err)
- }
- default:
- switch nameValTyp.Kind() {
- case reflect.String:
- nameValPtr.Elem().SetString(nameBuf.String())
- case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
- n, err := strconv.ParseInt(nameBuf.String(), 10, kind2bits[nameValTyp.Kind()])
- if err != nil {
- dec.panicSyntax(err)
- }
- nameValPtr.Elem().SetInt(n)
- case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
- n, err := strconv.ParseUint(nameBuf.String(), 10, kind2bits[nameValTyp.Kind()])
- if err != nil {
- dec.panicSyntax(err)
- }
- nameValPtr.Elem().SetUint(n)
- default:
- dec.panicType(typ, fmt.Errorf("invalid map key type: %v", nameValTyp))
- }
- }
- dec.stackPush(nameValPtr.Elem())
- defer dec.stackPop()
-
- fValPtr := reflect.New(typ.Elem())
- dec.decode(fValPtr.Elem(), false)
-
- val.SetMapIndex(nameValPtr.Elem(), fValPtr.Elem())
- })
- default:
- dec.panicSyntax(fmt.Errorf("map: expected %q or %q bug got %q", 'n', '{', dec.peekRune()))
- }
- case reflect.Slice:
- switch {
- case typ.Elem().Kind() == reflect.Uint8:
- switch dec.peekRune() {
- case 'n':
- dec.decodeNull()
- val.Set(reflect.Zero(typ))
- case '"':
- var buf bytes.Buffer
- dec.decodeString(newBase64Decoder(&buf))
- if typ.Elem() == byteType {
- val.Set(reflect.ValueOf(buf.Bytes()))
- } else {
- bs := buf.Bytes()
- // TODO: Surely there's a better way.
- val.Set(reflect.MakeSlice(typ, len(bs), len(bs)))
- for i := 0; i < len(bs); i++ {
- val.Index(i).Set(reflect.ValueOf(bs[i]).Convert(typ.Elem()))
- }
- }
- default:
- dec.panicSyntax(fmt.Errorf("byte slice: expected %q or %q but got %q", 'n', '"', dec.peekRune()))
- }
- default:
- switch dec.peekRune() {
- case 'n':
- dec.decodeNull()
- val.Set(reflect.Zero(typ))
- case '[':
- if val.IsNil() {
- val.Set(reflect.MakeSlice(typ, 0, 0))
- }
- if val.Len() > 0 {
- val.Set(val.Slice(0, 0))
- }
- i := 0
- dec.decodeArray(func() {
- dec.stackPush(i)
- defer dec.stackPop()
- mValPtr := reflect.New(typ.Elem())
- dec.decode(mValPtr.Elem(), false)
- val.Set(reflect.Append(val, mValPtr.Elem()))
- i++
- })
- default:
- dec.panicSyntax(fmt.Errorf("slice: expected %q or %q but got %q", 'n', '[', dec.peekRune()))
- }
- }
- case reflect.Array:
- if nullOK && dec.peekRune() == 'n' {
- dec.decodeNull()
- return
- }
- i := 0
- n := val.Len()
- dec.decodeArray(func() {
- dec.stackPush(i)
- defer dec.stackPop()
- if i < n {
- mValPtr := reflect.New(typ.Elem())
- dec.decode(mValPtr.Elem(), false)
- val.Index(i).Set(mValPtr.Elem())
- } else {
- dec.scan(io.Discard)
- }
- i++
- })
- for ; i < n; i++ {
- val.Index(i).Set(reflect.Zero(typ.Elem()))
- }
- case reflect.Pointer:
- switch dec.peekRune() {
- case 'n':
- dec.decodeNull()
- /*
- for typ.Elem().Kind() == reflect.Pointer {
- if val.IsNil() || !val.Elem().CanSet() {
- val.Set(reflect.New(typ.Elem()))
- }
- val = val.Elem()
- typ = val.Type()
- }
- */
- val.Set(reflect.Zero(typ))
- default:
- if val.IsNil() {
- val.Set(reflect.New(typ.Elem()))
- }
- dec.decode(val.Elem(), false)
- }
- default:
- dec.panicType(typ, fmt.Errorf("unsupported type (kind=%v)", typ.Kind()))
- }
- }
-}
-
-func (dec *Decoder) decodeWS() {
- for {
- c, ok := dec.readRuneOrEOF()
- if !ok {
- return
- }
- switch c {
- // NB: The JSON definition of whitespace is more
- // narrow than unicode.IsSpace
- case 0x0020, 0x000A, 0x000D, 0x0009:
- // do nothing
- default:
- dec.unreadRune()
- return
- }
- }
-}
-
-func (dec *Decoder) scan(out io.Writer) {
- scanner := &ReEncoder{
- Out: out,
- Compact: true,
- }
- if _, err := scanner.WriteRune(dec.readRune()); err != nil {
- dec.panicSyntax(err)
- }
- scanner.bailAfterCurrent = true
- var err error
- var eof bool
- for err == nil {
- c, ok := dec.readRuneOrEOF()
- if ok {
- _, err = scanner.WriteRune(c)
- } else {
- eof = true
- err = scanner.Flush()
- break
- }
- }
- if err != nil {
- if err == errBailedAfterCurrent {
- if !eof {
- dec.unreadRune()
- }
- } else {
- dec.panicSyntax(err)
- }
- }
-}
-
-func (dec *Decoder) scanNumber(out io.Writer) {
- c := dec.peekRune()
- switch c {
- case '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
- dec.scan(out)
- default:
- dec.panicSyntax(fmt.Errorf("number: expected %q or a digit, but got %q", '-', c))
- }
-}
-
-func (dec *Decoder) decodeAny() any {
- c := dec.peekRune()
- switch c {
- case '{':
- ret := make(map[string]any)
- var nameBuf strings.Builder
- dec.decodeObject(&nameBuf, func() {
- name := nameBuf.String()
- dec.stackPush(name)
- defer dec.stackPop()
- ret[name] = dec.decodeAny()
- })
- return ret
- case '[':
- ret := []any{}
- dec.decodeArray(func() {
- dec.stackPush(len(ret))
- defer dec.stackPop()
- ret = append(ret, dec.decodeAny())
- })
- return ret
- case '"':
- var buf strings.Builder
- dec.decodeString(&buf)
- return buf.String()
- case '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
- var buf strings.Builder
- dec.scanNumber(&buf)
- num := json.Number(buf.String())
- if dec.useNumber {
- return num
- }
- f64, err := num.Float64()
- if err != nil {
- dec.panicSyntax(err)
- }
- return f64
- case 't', 'f':
- return dec.decodeBool()
- case 'n':
- dec.decodeNull()
- return nil
- default:
- dec.panicSyntax(fmt.Errorf("any: unexpected character: %c", c))
- panic("not reached")
- }
-}
-
-func (dec *Decoder) decodeObject(nameBuf runeBuffer, decodeKVal func()) {
- dec.expectRune('{')
- dec.decodeWS()
- c := dec.readRune()
- switch c {
- case '"':
- decodeMember:
- dec.unreadRune()
- nameBuf.Reset()
- dec.decodeString(nameBuf)
- dec.decodeWS()
- dec.expectRune(':')
- dec.decodeWS()
- decodeKVal()
- dec.decodeWS()
- c := dec.readRune()
- switch c {
- case ',':
- dec.decodeWS()
- dec.expectRune('"')
- goto decodeMember
- case '}':
- return
- default:
- dec.panicSyntax(fmt.Errorf("object: expected %q or %q but got %q", ',', '}', c))
- }
- case '}':
- return
- default:
- dec.panicSyntax(fmt.Errorf("object: expected %q or %q but got %q", '"', '}', c))
- }
-}
-
-func (dec *Decoder) decodeArray(decodeMember func()) {
- dec.expectRune('[')
- dec.decodeWS()
- c := dec.readRune()
- switch c {
- case ']':
- return
- default:
- dec.unreadRune()
- decodeNextMember:
- decodeMember()
- dec.decodeWS()
- c := dec.readRune()
- switch c {
- case ',':
- dec.decodeWS()
- goto decodeNextMember
- case ']':
- return
- default:
- dec.panicSyntax(fmt.Errorf("array: expected %c or %c but got %c", ',', ']', c))
- }
- }
-}
-
-func (dec *Decoder) decodeHex() rune {
- c := dec.readRune()
- switch {
- case '0' <= c && c <= '9':
- return c - '0'
- case 'a' <= c && c <= 'f':
- return c - 'a' + 10
- case 'A' <= c && c <= 'F':
- return c - 'A' + 10
- default:
- dec.panicSyntax(fmt.Errorf("string: expected a hex digit but got %q", c))
- panic("not reached")
- }
-}
-
-func (dec *Decoder) decodeString(out io.Writer) {
- dec.expectRune('"')
- for {
- c := dec.readRune()
- switch {
- case 0x0020 <= c && c <= 0x10FFFF && c != '"' && c != '\\':
- if _, err := writeRune(out, c); err != nil {
- dec.panicSyntax(err)
- }
- case c == '\\':
- c = dec.readRune()
- switch c {
- case '"':
- if _, err := writeRune(out, '"'); err != nil {
- dec.panicSyntax(err)
- }
- case '\\':
- if _, err := writeRune(out, '\\'); err != nil {
- dec.panicSyntax(err)
- }
- case '/':
- if _, err := writeRune(out, '/'); err != nil {
- dec.panicSyntax(err)
- }
- case 'b':
- if _, err := writeRune(out, '\b'); err != nil {
- dec.panicSyntax(err)
- }
- case 'f':
- if _, err := writeRune(out, '\f'); err != nil {
- dec.panicSyntax(err)
- }
- case 'n':
- if _, err := writeRune(out, '\n'); err != nil {
- dec.panicSyntax(err)
- }
- case 'r':
- if _, err := writeRune(out, '\r'); err != nil {
- dec.panicSyntax(err)
- }
- case 't':
- if _, err := writeRune(out, '\t'); err != nil {
- dec.panicSyntax(err)
- }
- case 'u':
- c = dec.decodeHex()
- c = (c << 4) | dec.decodeHex()
- c = (c << 4) | dec.decodeHex()
- c = (c << 4) | dec.decodeHex()
- if _, err := writeRune(out, c); err != nil {
- dec.panicSyntax(err)
- }
- }
- case c == '"':
- return
- default:
- dec.panicSyntax(fmt.Errorf("string: unexpected %c", c))
- }
- }
-}
-
-func (dec *Decoder) decodeBool() bool {
- c := dec.readRune()
- switch c {
- case 't':
- dec.expectRune('r')
- dec.expectRune('u')
- dec.expectRune('e')
- return true
- case 'f':
- dec.expectRune('a')
- dec.expectRune('l')
- dec.expectRune('s')
- dec.expectRune('e')
- return false
- default:
- dec.panicSyntax(fmt.Errorf("bool: expected %q or %q but got %q", 't', 'f', c))
- panic("not reached")
- }
-}
-
-func (dec *Decoder) decodeNull() {
- dec.expectRune('n')
- dec.expectRune('u')
- dec.expectRune('l')
- dec.expectRune('l')
-}
diff --git a/lib/lowmemjson/encode.go b/lib/lowmemjson/encode.go
deleted file mode 100644
index 377b9b9..0000000
--- a/lib/lowmemjson/encode.go
+++ /dev/null
@@ -1,333 +0,0 @@
-// Copyright (C) 2022 Luke Shumaker <lukeshu@lukeshu.com>
-//
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-package lowmemjson
-
-import (
- "bytes"
- "encoding"
- "encoding/base64"
- "encoding/json"
- "io"
- "reflect"
- "sort"
- "strconv"
- "strings"
-)
-
-type Encodable interface {
- EncodeJSON(w io.Writer) error
-}
-
-type encodeError struct {
- Err error
-}
-
-func encodeWriteByte(w io.Writer, b byte) {
- if err := writeByte(w, b); err != nil {
- panic(encodeError{err})
- }
-}
-
-func encodeWriteString(w io.Writer, str string) {
- if _, err := io.WriteString(w, str); err != nil {
- panic(encodeError{err})
- }
-}
-
-func Encode(w io.Writer, obj any) (err error) {
- defer func() {
- if r := recover(); r != nil {
- if e, ok := r.(encodeError); ok {
- err = e.Err
- } else {
- panic(r)
- }
- }
- }()
- encode(w, reflect.ValueOf(obj), false)
- if f, ok := w.(interface{ Flush() error }); ok {
- return f.Flush()
- }
- return nil
-}
-
-var (
- encodableType = reflect.TypeOf((*Encodable)(nil)).Elem()
- jsonMarshalerType = reflect.TypeOf((*json.Marshaler)(nil)).Elem()
- textMarshalerType = reflect.TypeOf((*encoding.TextMarshaler)(nil)).Elem()
-)
-
-func encode(w io.Writer, val reflect.Value, quote bool) {
- if !val.IsValid() {
- encodeWriteString(w, "null")
- return
- }
- switch {
-
- case val.Kind() != reflect.Pointer && val.CanAddr() && reflect.PointerTo(val.Type()).Implements(encodableType):
- val = val.Addr()
- fallthrough
- case val.Type().Implements(encodableType):
- if val.Kind() == reflect.Pointer && val.IsNil() {
- encodeWriteString(w, "null")
- return
- }
- obj, ok := val.Interface().(Encodable)
- if !ok {
- encodeWriteString(w, "null")
- return
- }
- if err := obj.EncodeJSON(w); err != nil {
- panic(encodeError{err})
- }
-
- case val.Kind() != reflect.Pointer && val.CanAddr() && reflect.PointerTo(val.Type()).Implements(jsonMarshalerType):
- val = val.Addr()
- fallthrough
- case val.Type().Implements(jsonMarshalerType):
- if val.Kind() == reflect.Pointer && val.IsNil() {
- encodeWriteString(w, "null")
- return
- }
- obj, ok := val.Interface().(json.Marshaler)
- if !ok {
- encodeWriteString(w, "null")
- return
- }
- dat, err := obj.MarshalJSON()
- if err != nil {
- panic(encodeError{err})
- }
- if _, err := w.Write(dat); err != nil {
- panic(encodeError{err})
- }
-
- case val.Kind() != reflect.Pointer && val.CanAddr() && reflect.PointerTo(val.Type()).Implements(textMarshalerType):
- val = val.Addr()
- fallthrough
- case val.Type().Implements(textMarshalerType):
- if val.Kind() == reflect.Pointer && val.IsNil() {
- encodeWriteString(w, "null")
- return
- }
- obj, ok := val.Interface().(encoding.TextMarshaler)
- if !ok {
- encodeWriteString(w, "null")
- return
- }
- text, err := obj.MarshalText()
- if err != nil {
- panic(encodeError{err})
- }
- encodeString(w, text)
-
- default:
- switch val.Kind() {
- case reflect.Bool:
- if quote {
- encodeWriteByte(w, '"')
- }
- if val.Bool() {
- encodeWriteString(w, "true")
- } else {
- encodeWriteString(w, "false")
- }
- if quote {
- encodeWriteByte(w, '"')
- }
- case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
- if quote {
- encodeWriteByte(w, '"')
- }
- encodeWriteString(w, strconv.FormatInt(val.Int(), 10))
- if quote {
- encodeWriteByte(w, '"')
- }
- case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
- if quote {
- encodeWriteByte(w, '"')
- }
- encodeWriteString(w, strconv.FormatUint(val.Uint(), 10))
- if quote {
- encodeWriteByte(w, '"')
- }
- case reflect.Float32, reflect.Float64:
- if quote {
- encodeWriteByte(w, '"')
- }
- encodeTODO(w, val)
- if quote {
- encodeWriteByte(w, '"')
- }
- case reflect.String:
- if val.Type() == numberType {
- numStr := val.String()
- if numStr == "" {
- numStr = "0"
- }
- if quote {
- encodeWriteByte(w, '"')
- }
- encodeWriteString(w, numStr)
- if quote {
- encodeWriteByte(w, '"')
- }
- } else {
- if quote {
- var buf bytes.Buffer
- encodeString(&buf, val.String())
- encodeString(w, buf.Bytes())
- } else {
- encodeString(w, val.String())
- }
- }
- case reflect.Interface:
- if val.IsNil() {
- encodeWriteString(w, "null")
- } else {
- encode(w, val.Elem(), quote)
- }
- case reflect.Struct:
- encodeWriteByte(w, '{')
- empty := true
- for _, field := range indexStruct(val.Type()).byPos {
- fVal, err := val.FieldByIndexErr(field.Path)
- if err != nil {
- continue
- }
- if field.OmitEmpty && isEmptyValue(fVal) {
- continue
- }
- if !empty {
- encodeWriteByte(w, ',')
- }
- empty = false
- encodeString(w, field.Name)
- encodeWriteByte(w, ':')
- encode(w, fVal, field.Quote)
- }
- encodeWriteByte(w, '}')
- case reflect.Map:
- if val.IsNil() {
- encodeWriteString(w, "null")
- return
- }
- if val.Len() == 0 {
- encodeWriteString(w, "{}")
- return
- }
- encodeWriteByte(w, '{')
-
- type kv struct {
- K string
- V reflect.Value
- }
- kvs := make([]kv, val.Len())
- iter := val.MapRange()
- for i := 0; iter.Next(); i++ {
- var k strings.Builder
- encode(&k, iter.Key(), false)
- kStr := k.String()
- if kStr == "null" {
- kStr = `""`
- }
- if !strings.HasPrefix(kStr, `"`) {
- k.Reset()
- encodeString(&k, kStr)
- kStr = k.String()
- }
- kvs[i].K = kStr
- kvs[i].V = iter.Value()
- }
- sort.Slice(kvs, func(i, j int) bool {
- return kvs[i].K < kvs[j].K
- })
-
- for i, kv := range kvs {
- if i > 0 {
- encodeWriteByte(w, ',')
- }
- encodeWriteString(w, kv.K)
- encodeWriteByte(w, ':')
- encode(w, kv.V, false)
- }
- encodeWriteByte(w, '}')
- case reflect.Slice:
- switch {
- case val.IsNil():
- encodeWriteString(w, "null")
- case val.Type().Elem().Kind() == reflect.Uint8:
- encodeWriteByte(w, '"')
- enc := base64.NewEncoder(base64.StdEncoding, w)
- if val.CanConvert(byteSliceType) {
- if _, err := enc.Write(val.Convert(byteSliceType).Interface().([]byte)); err != nil {
- panic(encodeError{err})
- }
- } else {
- // TODO: Surely there's a better way.
- for i, n := 0, val.Len(); i < n; i++ {
- var buf [1]byte
- buf[0] = val.Index(i).Convert(byteType).Interface().(byte)
- if _, err := enc.Write(buf[:]); err != nil {
- panic(encodeError{err})
- }
- }
- }
- if err := enc.Close(); err != nil {
- panic(encodeError{err})
- }
- encodeWriteByte(w, '"')
- default:
- encodeArray(w, val)
- }
- case reflect.Array:
- encodeArray(w, val)
- case reflect.Pointer:
- if val.IsNil() {
- encodeWriteString(w, "null")
- } else {
- encode(w, val.Elem(), quote)
- }
- default:
- panic(encodeError{&json.UnsupportedTypeError{
- Type: val.Type(),
- }})
- }
- }
-}
-
-func encodeString[T interface{ []byte | string }](w io.Writer, str T) {
- encodeWriteByte(w, '"')
- for i := 0; i < len(str); {
- c, size := decodeRune(str[i:])
- if _, err := writeStringChar(w, c, false, nil); err != nil {
- panic(encodeError{err})
- }
- i += size
- }
- encodeWriteByte(w, '"')
-}
-
-func encodeArray(w io.Writer, val reflect.Value) {
- encodeWriteByte(w, '[')
- n := val.Len()
- for i := 0; i < n; i++ {
- if i > 0 {
- encodeWriteByte(w, ',')
- }
- encode(w, val.Index(i), false)
- }
- encodeWriteByte(w, ']')
-}
-
-func encodeTODO(w io.Writer, val reflect.Value) {
- bs, err := json.Marshal(val.Interface())
- if err != nil {
- panic(encodeError{err})
- }
- if _, err := w.Write(bs); err != nil {
- panic(encodeError{err})
- }
-}
diff --git a/lib/lowmemjson/misc.go b/lib/lowmemjson/misc.go
deleted file mode 100644
index 132b177..0000000
--- a/lib/lowmemjson/misc.go
+++ /dev/null
@@ -1,131 +0,0 @@
-// Copyright (C) 2022 Luke Shumaker <lukeshu@lukeshu.com>
-//
-// 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)
- }
-}
diff --git a/lib/lowmemjson/reencode.go b/lib/lowmemjson/reencode.go
deleted file mode 100644
index 50c8ba3..0000000
--- a/lib/lowmemjson/reencode.go
+++ /dev/null
@@ -1,598 +0,0 @@
-// Copyright (C) 2022 Luke Shumaker <lukeshu@lukeshu.com>
-//
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-package lowmemjson
-
-import (
- "errors"
- "fmt"
- "io"
- "unicode/utf8"
-)
-
-type reencodeState func(rune) error
-
-type ReEncoder struct {
- Out io.Writer
-
- // Whether to minify the JSON.
- Compact bool
- // String to use to indent; ignored if Compact is true.
- Indent string
- // String to put before indents, for testing-compat with
- // encoding/json only.
- prefix string
- // Returns whether a given character in a string should be
- // "\uXXXX" escaped. The bool argument is whether it was
- // \u-escaped in the input. This does not affect characters
- // that must or must-not be \u-escaped to be valid JSON.
- //
- // If not set, then EscapeUnicodeDefault is used.
- UnicodeEscape func(rune, bool) bool
-
- bailAfterCurrent bool
-
- // state: .Write's utf8-decoding buffer
- buf [utf8.UTFMax]byte
- bufLen int
-
- // state: .WriteRune
- err error
- inputPos int64
- written int
- stack []reencodeState
- stack0IsNumber bool
- curIndent int
-
- // state: reencodeState-specific
- stateBuf []byte
-}
-
-// public API //////////////////////////////////////////////////////////////////
-
-func (enc *ReEncoder) Write(p []byte) (int, error) {
- if len(p) == 0 {
- return 0, nil
- }
- var n int
- if enc.bufLen > 0 {
- copy(enc.buf[enc.bufLen:], p)
- c, size := utf8.DecodeRune(enc.buf[:])
- n += size - enc.bufLen
- enc.bufLen = 0
- if _, err := enc.WriteRune(c); err != nil {
- return 0, err
- }
- }
- for utf8.FullRune(p[n:]) {
- c, size := utf8.DecodeRune(p[n:])
- if _, err := enc.WriteRune(c); err != nil {
- return n, err
- }
- n += size
- }
- enc.bufLen = copy(enc.buf[:], p[n:])
- return len(p), nil
-}
-
-func (enc *ReEncoder) Flush() error {
- if enc.bufLen > 0 {
- return &SyntaxError{fmt.Sprintf("EOF: unflushed unicode garbage: %q", enc.buf[:enc.bufLen]), enc.inputPos}
- }
- switch len(enc.stack) {
- case 0:
- return nil
- case 1:
- if enc.stack0IsNumber {
- enc.Compact = true
- return enc.state('\n')
- }
- fallthrough
- default:
- return &SyntaxError{fmt.Sprintf("EOF: in the middle of a value"), enc.inputPos}
- }
-}
-
-func (enc *ReEncoder) WriteRune(c rune) (n int, err error) {
- if enc.err != nil {
- return 0, enc.err
- }
- if enc.bufLen != 0 {
- enc.err = errors.New("lowmemjson.ReEncoder: cannot .WriteRune() when there is a partial rune that has been .Write()n")
- return 0, enc.err
- }
- enc.written = 0
- enc.err = enc.state(c)
- enc.inputPos += int64(utf8.RuneLen(c))
- return enc.written, enc.err
-}
-
-// io helpers //////////////////////////////////////////////////////////////////
-
-func (enc *ReEncoder) emitByte(c byte) error {
- err := writeByte(enc.Out, c)
- if err == nil {
- enc.written++
- }
- return err
-}
-
-func (enc *ReEncoder) emit(n int, err error) error {
- enc.written += n
- return err
-}
-
-func (enc *ReEncoder) nlIndent() error {
- if enc.Compact || enc.Indent == "" {
- return nil
- }
- if err := enc.emitByte('\n'); err != nil {
- return err
- }
- if enc.prefix != "" {
- if err := enc.emit(io.WriteString(enc.Out, enc.prefix)); err != nil {
- return err
- }
- }
- for i := 0; i < enc.curIndent; i++ {
- if err := enc.emit(io.WriteString(enc.Out, enc.Indent)); err != nil {
- return err
- }
- }
- return nil
-}
-
-// state helpers ///////////////////////////////////////////////////////////////
-
-func (enc *ReEncoder) pushState(state reencodeState, isNumber bool) {
- if len(enc.stack) == 0 {
- enc.stack0IsNumber = isNumber
- }
- enc.stack = append(enc.stack, state)
-}
-func (enc *ReEncoder) replaceState(state reencodeState, isNumber bool) {
- if len(enc.stack) == 1 {
- enc.stack0IsNumber = isNumber
- }
- enc.stack[len(enc.stack)-1] = state
-}
-func (enc *ReEncoder) popState() {
- if len(enc.stack) == 1 {
- enc.stack0IsNumber = false
- }
- enc.stack = enc.stack[:len(enc.stack)-1]
-}
-
-var errBailedAfterCurrent = errors.New("bailed after current")
-
-func (enc *ReEncoder) state(c rune) error {
- if len(enc.stack) == 0 {
- if enc.bailAfterCurrent {
- return errBailedAfterCurrent
- }
- enc.pushState(enc.stateAny, false)
- }
- return enc.stack[len(enc.stack)-1](c)
-}
-
-// any /////////////////////////////////////////////////////////////////////////////////////////////
-
-func (enc *ReEncoder) stateAny(c rune) error {
- switch c {
- case 0x0020, 0x000A, 0x000D, 0x0009:
- if enc.Compact || enc.Indent != "" {
- return nil
- }
- case '{':
- enc.replaceState(enc.stateInEmptyObject, false)
- enc.curIndent++
- case '[':
- enc.replaceState(enc.stateInEmptyArray, false)
- enc.curIndent++
- case '"':
- enc.replaceState(enc.stateInString, false)
- case '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
- enc.replaceState(enc.stateNumberA, true)
- return enc.state(c)
- case 't':
- enc.replaceState(enc.stateInTrue, false)
- enc.stateBuf = append(enc.stateBuf[:0], 't')
- case 'f':
- enc.replaceState(enc.stateInFalse, false)
- enc.stateBuf = append(enc.stateBuf[:0], 'f')
- case 'n':
- enc.replaceState(enc.stateInNull, false)
- enc.stateBuf = append(enc.stateBuf[:0], 'n')
- default:
- return &SyntaxError{fmt.Sprintf("any: unexpected character: %c", c), enc.inputPos}
- }
- return enc.emitByte(byte(c))
-}
-
-// object //////////////////////////////////////////////////////////////////////////////////////////
-
-func (enc *ReEncoder) stateInEmptyObject(c rune) error { return enc._stateInObject(c, false) }
-func (enc *ReEncoder) stateInNonEmptyObject(c rune) error { return enc._stateInObject(c, true) }
-func (enc *ReEncoder) _stateInObject(c rune, nonempty bool) error {
- switch c {
- case 0x0020, 0x000A, 0x000D, 0x0009:
- if enc.Compact || enc.Indent != "" {
- return nil
- }
- case '"':
- if err := enc.nlIndent(); err != nil {
- return err
- }
- enc.replaceState(enc.stateInKV, false)
- enc.pushState(enc.stateInString, false)
- case '}':
- enc.popState()
- enc.curIndent--
- if nonempty {
- if err := enc.nlIndent(); err != nil {
- return err
- }
- }
- default:
- return &SyntaxError{fmt.Sprintf("object: unexpected character: %c", c), enc.inputPos}
- }
- return enc.emitByte(byte(c))
-}
-func (enc *ReEncoder) stateInKV(c rune) error {
- switch c {
- case 0x0020, 0x000A, 0x000D, 0x0009:
- if enc.Compact || enc.Indent != "" {
- return nil
- }
- return enc.emitByte(byte(c))
- case ':':
- enc.replaceState(enc.stateAfterV, false)
- enc.pushState(enc.stateAny, false)
- if err := enc.emitByte(byte(c)); err != nil {
- return err
- }
- if !enc.Compact && enc.Indent != "" {
- return enc.emitByte(' ')
- }
- return nil
- default:
- return &SyntaxError{fmt.Sprintf("object member: unexpected character: %c", c), enc.inputPos}
- }
-}
-func (enc *ReEncoder) stateAfterV(c rune) error {
- switch c {
- case 0x0020, 0x000A, 0x000D, 0x0009:
- if enc.Compact || enc.Indent != "" {
- return nil
- }
- case ',':
- enc.replaceState(enc.stateInNonEmptyObject, false)
- case '}':
- enc.popState()
- enc.curIndent--
- if err := enc.nlIndent(); err != nil {
- return err
- }
- default:
- return &SyntaxError{fmt.Sprintf("object member: unexpected character: %c", c), enc.inputPos}
- }
- return enc.emitByte(byte(c))
-}
-
-// array ///////////////////////////////////////////////////////////////////////////////////////////
-
-func (enc *ReEncoder) stateInEmptyArray(c rune) error { return enc._stateInArray(c, false) }
-func (enc *ReEncoder) stateInNonEmptyArray(c rune) error { return enc._stateInArray(c, true) }
-func (enc *ReEncoder) _stateInArray(c rune, nonempty bool) error {
- switch c {
- case 0x0020, 0x000A, 0x000D, 0x0009:
- if enc.Compact || enc.Indent != "" {
- return nil
- }
- case ']':
- enc.popState()
- enc.curIndent--
- if nonempty {
- if err := enc.nlIndent(); err != nil {
- return err
- }
- }
- default:
- if err := enc.nlIndent(); err != nil {
- return err
- }
- enc.replaceState(enc.stateAfterItem, false)
- enc.pushState(enc.stateAny, false)
- return enc.state(c)
- }
- return enc.emitByte(byte(c))
-}
-func (enc *ReEncoder) stateAfterItem(c rune) error {
- switch c {
- case 0x0020, 0x000A, 0x000D, 0x0009:
- if enc.Compact || enc.Indent != "" {
- return nil
- }
- case ',':
- enc.replaceState(enc.stateInNonEmptyArray, false)
- case ']':
- enc.popState()
- enc.curIndent--
- if err := enc.nlIndent(); err != nil {
- return err
- }
- default:
- return &SyntaxError{fmt.Sprintf("array: unexpected character: %c", c), enc.inputPos}
- }
- return enc.emitByte(byte(c))
-}
-
-// string //////////////////////////////////////////////////////////////////////////////////////////
-
-func (enc *ReEncoder) stateInString(c rune) error {
- switch {
- case c == '\\':
- enc.replaceState(enc.stateInBackslash, false)
- return nil
- case c == '"':
- enc.popState()
- return enc.emitByte(byte(c))
- case 0x0020 <= c && c <= 0x10FFFF:
- return enc.emit(writeStringChar(enc.Out, c, false, enc.UnicodeEscape))
- default:
- return &SyntaxError{fmt.Sprintf("string: unexpected character: %c", c), enc.inputPos}
- }
-}
-func (enc *ReEncoder) stateInBackslash(c rune) error {
- switch c {
- case '"':
- enc.replaceState(enc.stateInString, false)
- return enc.emit(writeStringChar(enc.Out, '"', false, enc.UnicodeEscape))
- case '\\':
- enc.replaceState(enc.stateInString, false)
- return enc.emit(writeStringChar(enc.Out, '\\', false, enc.UnicodeEscape))
- case '/':
- enc.replaceState(enc.stateInString, false)
- return enc.emit(writeStringChar(enc.Out, '/', false, enc.UnicodeEscape))
- case 'b':
- enc.replaceState(enc.stateInString, false)
- return enc.emit(writeStringChar(enc.Out, '\b', false, enc.UnicodeEscape))
- case 'f':
- enc.replaceState(enc.stateInString, false)
- return enc.emit(writeStringChar(enc.Out, '\f', false, enc.UnicodeEscape))
- case 'n':
- enc.replaceState(enc.stateInString, false)
- return enc.emit(writeStringChar(enc.Out, '\n', false, enc.UnicodeEscape))
- case 'r':
- enc.replaceState(enc.stateInString, false)
- return enc.emit(writeStringChar(enc.Out, '\r', false, enc.UnicodeEscape))
- case 't':
- enc.replaceState(enc.stateInString, false)
- return enc.emit(writeStringChar(enc.Out, '\t', false, enc.UnicodeEscape))
- case 'u':
- enc.replaceState(enc.stateInUnicode, false)
- return nil
- default:
- return &SyntaxError{fmt.Sprintf("string backslash sequence: unexpected character: %c", c), enc.inputPos}
- }
-}
-func (enc *ReEncoder) stateInUnicode(c rune) error {
- switch {
- case '0' <= c && c <= '9':
- enc.stateBuf = append(enc.stateBuf, byte(c)-'0')
- case 'a' <= c && c <= 'f':
- enc.stateBuf = append(enc.stateBuf, byte(c)-'a'+10)
- case 'A' <= c && c <= 'F':
- enc.stateBuf = append(enc.stateBuf, byte(c)-'A'+10)
- default:
- return &SyntaxError{fmt.Sprintf("string unicode sequence: unexpected character: %c", c), enc.inputPos}
- }
- if len(enc.stateBuf) == 4 {
- enc.replaceState(enc.stateInString, false)
- c := 0 |
- rune(enc.stateBuf[0])<<12 |
- rune(enc.stateBuf[1])<<8 |
- rune(enc.stateBuf[2])<<4 |
- rune(enc.stateBuf[3])<<0
- enc.stateBuf = enc.stateBuf[:0]
- return enc.emit(writeStringChar(enc.Out, c, true, enc.UnicodeEscape))
- }
- return nil
-}
-
-// number //////////////////////////////////////////////////////////////////////////////////////////
-
-// Here's a flattened drawing of the syntax diagram from www.json.org :
-//
-// [------------ integer ----------][-- fraction ---][-------- exponent -------]
-// >─╮─────╭─╮─"0"───────╭─────────╭──╮─────────────╭──╮───────────────────────╭─>
-// │ │ │ │ │ │ │ │ │
-// ╰─"-"─╯ ╰─digit 1-9─╯─╭digit╮─╯ ╰─"."─╭digit╮─╯ ╰─"e"─╭─╮─────╭─╭digit╮─╯
-// ╰──<──╯ ╰──<──╯ │ │ │ │ ╰──<──╯
-// ╰─"E"─╯ ╰─"-"─╯
-// │ │
-// ╰─"+"─╯
-//
-// Now here it is slightly redrawn, and with each distinct state our
-// decoder can be in marked with a single-capital-letter:
-//
-// [-------------- integer ------------][--------- fraction --------][--------- exponent ---------]
-// >─A─╮───────╭──╮─"0"─────────C─╭─────────╮──────────────────╭─────────╮──────────────────────────╭─>
-// │ │ │ │ │ │ │ │
-// ╰─"-"─B─╯ ╰─digit 1-9─╭─D─╯─digit╮ ╰─"."─E─digit──╭─F─╯─digit╮ ╰─"e"─╭─G─╮─────╭─╭digit─H─╯
-// ╰────<─────╯ ╰────<─────╯ │ │ │ │ ╰────<───╯
-// ╰─"E"─╯ ╰─"-"─╯
-// │ │
-// ╰─"+"─╯
-//
-// Which state we're at is the 'X' in 'stateNumberX'.
-//
-// Besides just traversing that, there are a few compressions we want to make:
-//
-// - trim trailing 0s from fraction the (but don't remove the
-// fraction if it's all 0s); do this by making the F state a little
-// special. This requires a little more state, because when we
-// encounter the 0 we don't yet know if it's trailing. So, store
-// the number of maybe-trailing zeros in enc.stateBuf[0]; if that
-// reaches 255, then bleed over to enc.stateBuf[1] and so on.
-//
-// - trim leading 0s from the exponent (but don't remove the exponent
-// if it's all 0s); do this by making the H state a little special.
-// Record whether we've seen a non-zero digit in enc.stateBuf[0]
-// (0=false, 1=true).
-
-// integer-part ////////////////////////////////////////////////////////////////
-func (enc *ReEncoder) stateNumberA(c rune) error { // start
- switch c {
- case '-':
- enc.replaceState(enc.stateNumberB, true)
- case '0':
- enc.replaceState(enc.stateNumberC, true)
- case '1', '2', '3', '4', '5', '6', '7', '8', '9':
- enc.replaceState(enc.stateNumberD, true)
- default:
- return &SyntaxError{fmt.Sprintf("number: unexpected character: %c", c), enc.inputPos}
- }
- return enc.emitByte(byte(c))
-}
-func (enc *ReEncoder) stateNumberB(c rune) error { // got a leading "-"
- switch c {
- case '0':
- enc.replaceState(enc.stateNumberC, true)
- case '1', '2', '3', '4', '5', '6', '7', '8', '9':
- enc.replaceState(enc.stateNumberD, true)
- default:
- return &SyntaxError{fmt.Sprintf("number: unexpected character: %c", c), enc.inputPos}
- }
- return enc.emitByte(byte(c))
-}
-func (enc *ReEncoder) stateNumberC(c rune) error { // ready for the fraction or exponent part to start
- switch c {
- case '.':
- enc.replaceState(enc.stateNumberE, true)
- return enc.emitByte('.')
- case 'e', 'E':
- enc.replaceState(enc.stateNumberG, true)
- enc.stateBuf = append(enc.stateBuf[:0], 0)
- return enc.emitByte('e')
- default:
- enc.popState()
- return enc.state(c)
- }
-}
-func (enc *ReEncoder) stateNumberD(c rune) error { // in the integer part
- switch c {
- case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
- return enc.emitByte(byte(c))
- case '.':
- enc.replaceState(enc.stateNumberE, true)
- return enc.emitByte('.')
- case 'e', 'E':
- enc.replaceState(enc.stateNumberG, true)
- enc.stateBuf = append(enc.stateBuf[:0], 0)
- return enc.emitByte('e')
- default:
- enc.popState()
- return enc.state(c)
- }
-}
-
-// fraction-part ///////////////////////////////////////////////////////////////
-func (enc *ReEncoder) stateNumberE(c rune) error { // got a ".", ready to read a number for the fraction part
- switch c {
- case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
- enc.replaceState(enc.stateNumberF, true)
- return enc.emitByte(byte(c))
- default:
- return &SyntaxError{fmt.Sprintf("number: unexpected character: %c", c), enc.inputPos}
- }
-}
-func (enc *ReEncoder) stateNumberF(c rune) error { // in the fraction part
- switch c {
- case '0':
- if len(enc.stateBuf) > 0 && enc.stateBuf[len(enc.stateBuf)-1] < 255 {
- enc.stateBuf[len(enc.stateBuf)-1]++
- } else {
- enc.stateBuf = append(enc.stateBuf, 1)
- }
- return nil
- case '1', '2', '3', '4', '5', '6', '7', '8', '9':
- for len(enc.stateBuf) > 0 {
- if err := enc.emitByte('0'); err != nil {
- return err
- }
- if enc.stateBuf[len(enc.stateBuf)-1] == 1 {
- enc.stateBuf = enc.stateBuf[:len(enc.stateBuf)-1]
- } else {
- enc.stateBuf[len(enc.stateBuf)-1]--
- }
- }
- return enc.emitByte(byte(c))
- case 'e', 'E':
- enc.replaceState(enc.stateNumberG, true)
- enc.stateBuf = append(enc.stateBuf[:0], 0)
- return enc.emitByte('e')
- default:
- enc.stateBuf = enc.stateBuf[:0]
- enc.popState()
- return enc.state(c)
- }
-}
-
-// exponent-part ///////////////////////////////////////////////////////////////
-func (enc *ReEncoder) stateNumberG(c rune) error { // got a leading "e"
- switch c {
- case '-', '+':
- enc.replaceState(enc.stateNumberH, true)
- return enc.emitByte(byte(c))
- case '0':
- enc.replaceState(enc.stateNumberH, true)
- return nil
- case '1', '2', '3', '4', '5', '6', '7', '8', '9':
- enc.replaceState(enc.stateNumberH, true)
- enc.stateBuf[0] = 1
- return enc.emitByte(byte(c))
- default:
- enc.stateBuf = enc.stateBuf[:0]
- return &SyntaxError{fmt.Sprintf("number: unexpected character: %c", c), enc.inputPos}
- }
-}
-func (enc *ReEncoder) stateNumberH(c rune) error { // in the exponent's number part
- switch c {
- case '0':
- if enc.stateBuf[0] == 0 {
- return nil
- }
- return enc.emitByte('0')
- case '1', '2', '3', '4', '5', '6', '7', '8', '9':
- enc.stateBuf[0] = 1
- return enc.emitByte(byte(c))
- default:
- if enc.stateBuf[0] == 0 {
- if err := enc.emitByte('0'); err != nil {
- return err
- }
- }
- enc.stateBuf = enc.stateBuf[:0]
- enc.popState()
- return enc.state(c)
- }
-}
-
-// literals ////////////////////////////////////////////////////////////////////////////////////////
-
-func (enc *ReEncoder) stateInTrue(c rune) error { return enc._stateInLiteral(c, "true") }
-func (enc *ReEncoder) stateInFalse(c rune) error { return enc._stateInLiteral(c, "false") }
-func (enc *ReEncoder) stateInNull(c rune) error { return enc._stateInLiteral(c, "null") }
-func (enc *ReEncoder) _stateInLiteral(c rune, full string) error {
- if c != rune(full[len(enc.stateBuf)]) {
- return &SyntaxError{fmt.Sprintf("%s: unexpected character: %c", full, c), enc.inputPos}
- }
- enc.stateBuf = append(enc.stateBuf, byte(c))
- if len(enc.stateBuf) == len(full) {
- enc.stateBuf = enc.stateBuf[:0]
- enc.popState()
- }
- return enc.emitByte(byte(c))
-}
diff --git a/lib/lowmemjson/struct.go b/lib/lowmemjson/struct.go
deleted file mode 100644
index ad142d6..0000000
--- a/lib/lowmemjson/struct.go
+++ /dev/null
@@ -1,164 +0,0 @@
-// Copyright (C) 2022 Luke Shumaker <lukeshu@lukeshu.com>
-//
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-package lowmemjson
-
-import (
- "reflect"
-)
-
-type structField struct {
- Name string
- Path []int
- Tagged bool
- OmitEmpty bool
- Quote bool
-}
-
-type structIndex struct {
- byPos []structField
- byName map[string]int
-}
-
-func indexStruct(typ reflect.Type) structIndex {
- byName := make(map[string][]structField)
- var byPos []string
-
- indexStructInner(typ, nil, byName, &byPos)
-
- ret := structIndex{
- byName: make(map[string]int),
- }
-
- for _, name := range byPos {
- fields := byName[name]
- delete(byName, name)
- switch len(fields) {
- case 0:
- // do nothing
- case 1:
- ret.byName[name] = len(ret.byPos)
- ret.byPos = append(ret.byPos, fields[0])
- default:
- // To quote the encoding/json docs (version 1.18.4):
- //
- // If there are multiple fields at the same level, and that level is the
- // least nested (and would therefore be the nesting level selected by the
- // usual Go rules), the following extra rules apply:
- //
- // 1) Of those fields, if any are JSON-tagged, only tagged fields are
- // considered, even if there are multiple untagged fields that would
- // otherwise conflict.
- //
- // 2) If there is exactly one field (tagged or not according to the first
- // rule), that is selected.
- //
- // 3) Otherwise there are multiple fields, and all are ignored; no error
- // occurs.
- leastLevel := len(fields[0].Path)
- for _, field := range fields[1:] {
- if len(field.Path) < leastLevel {
- leastLevel = len(field.Path)
- }
- }
- var numUntagged, numTagged int
- var untaggedIdx, taggedIdx int
- for i, field := range fields {
- if len(field.Path) != leastLevel {
- continue
- }
- if field.Tagged {
- numTagged++
- taggedIdx = i
- if numTagged > 1 {
- break // optimization
- }
- } else {
- numUntagged++
- untaggedIdx = i
- }
- }
- switch numTagged {
- case 0:
- switch numUntagged {
- case 0:
- // do nothing
- case 1:
- ret.byName[name] = len(ret.byPos)
- ret.byPos = append(ret.byPos, fields[untaggedIdx])
- }
- case 1:
- ret.byName[name] = len(ret.byPos)
- ret.byPos = append(ret.byPos, fields[taggedIdx])
- }
- }
- }
-
- return ret
-}
-
-func indexStructInner(typ reflect.Type, prefix []int, byName map[string][]structField, byPos *[]string) {
- n := typ.NumField()
- for i := 0; i < n; i++ {
- path := append(append([]int(nil), prefix...), i)
-
- fTyp := typ.Field(i)
- var embed bool
- if fTyp.Anonymous {
- t := fTyp.Type
- if t.Kind() == reflect.Pointer {
- t = t.Elem()
- }
- if !fTyp.IsExported() && t.Kind() != reflect.Struct {
- continue
- }
- embed = t.Kind() == reflect.Struct
- } else if !fTyp.IsExported() {
- continue
- }
- tag := fTyp.Tag.Get("json")
- if tag == "-" {
- continue
- }
- tagName, opts := parseTag(tag)
- name := tagName
- if name == "" {
- name = fTyp.Name
- }
-
- if embed {
- t := fTyp.Type
- if t.Kind() == reflect.Pointer {
- t = t.Elem()
- }
- indexStructInner(t, path, byName, byPos)
- } else {
- byName[name] = append(byName[name], structField{
- Name: name,
- Path: path,
- Tagged: tagName != "",
- OmitEmpty: opts.Contains("omitempty"),
- Quote: opts.Contains("string") && isQuotable(fTyp.Type),
- })
- *byPos = append(*byPos, name)
- }
- }
-}
-
-func isQuotable(typ reflect.Type) bool {
- for typ.Kind() == reflect.Pointer {
- typ = typ.Elem()
- }
- switch typ.Kind() {
- case reflect.Bool,
- reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
- reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64,
- reflect.Uintptr,
- reflect.Float32, reflect.Float64,
- reflect.String:
- return true
- default:
- return false
- }
-}
diff --git a/lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/06e2c9db80a08b67fad7f1a4606dc7419750995a57828aa25ea57fe7099d5c03 b/lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/06e2c9db80a08b67fad7f1a4606dc7419750995a57828aa25ea57fe7099d5c03
deleted file mode 100644
index c3774e7..0000000
--- a/lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/06e2c9db80a08b67fad7f1a4606dc7419750995a57828aa25ea57fe7099d5c03
+++ /dev/null
@@ -1,2 +0,0 @@
-go test fuzz v1
-[]byte("0000000")
diff --git a/lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/24f53a36f8832fec65cac0aa0f3b43ec1c904414fa6d38f6fc288b0bbd69588a b/lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/24f53a36f8832fec65cac0aa0f3b43ec1c904414fa6d38f6fc288b0bbd69588a
deleted file mode 100644
index 4c861db..0000000
--- a/lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/24f53a36f8832fec65cac0aa0f3b43ec1c904414fa6d38f6fc288b0bbd69588a
+++ /dev/null
@@ -1,2 +0,0 @@
-go test fuzz v1
-[]byte("000000000000000000000000000000000")
diff --git a/lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/2d49311ef22319f70a3590a86b406b9f2565987a4a3b6d7660ddc308b5b2fae2 b/lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/2d49311ef22319f70a3590a86b406b9f2565987a4a3b6d7660ddc308b5b2fae2
deleted file mode 100644
index 3d32e14..0000000
--- a/lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/2d49311ef22319f70a3590a86b406b9f2565987a4a3b6d7660ddc308b5b2fae2
+++ /dev/null
@@ -1,2 +0,0 @@
-go test fuzz v1
-[]byte("00000000000000000")
diff --git a/lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/356e28f5914a0f16f3cef81330f1d92060be4d694a93dedd654bf48743a7d2bd b/lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/356e28f5914a0f16f3cef81330f1d92060be4d694a93dedd654bf48743a7d2bd
deleted file mode 100644
index d08ef92..0000000
--- a/lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/356e28f5914a0f16f3cef81330f1d92060be4d694a93dedd654bf48743a7d2bd
+++ /dev/null
@@ -1,2 +0,0 @@
-go test fuzz v1
-[]byte("00000000000000000000000000000000")
diff --git a/lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/582528ddfad69eb57775199a43e0f9fd5c94bba343ce7bb6724d4ebafe311ed4 b/lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/582528ddfad69eb57775199a43e0f9fd5c94bba343ce7bb6724d4ebafe311ed4
deleted file mode 100644
index a96f559..0000000
--- a/lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/582528ddfad69eb57775199a43e0f9fd5c94bba343ce7bb6724d4ebafe311ed4
+++ /dev/null
@@ -1,2 +0,0 @@
-go test fuzz v1
-[]byte("0")
diff --git a/lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/60c81ee499a7f1e151b66b08f0a4ff81edd7cb53d00dce8ee0eaf31683996026 b/lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/60c81ee499a7f1e151b66b08f0a4ff81edd7cb53d00dce8ee0eaf31683996026
deleted file mode 100644
index 87c024d..0000000
--- a/lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/60c81ee499a7f1e151b66b08f0a4ff81edd7cb53d00dce8ee0eaf31683996026
+++ /dev/null
@@ -1,2 +0,0 @@
-go test fuzz v1
-[]byte("0000000000000000000000000000000000000000000000000000")
diff --git a/lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/66498f377f38b53eebe1ceaa4a53e4de01a04efc02ac9cfda60f9815f80e9b9d b/lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/66498f377f38b53eebe1ceaa4a53e4de01a04efc02ac9cfda60f9815f80e9b9d
deleted file mode 100644
index 959401e..0000000
--- a/lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/66498f377f38b53eebe1ceaa4a53e4de01a04efc02ac9cfda60f9815f80e9b9d
+++ /dev/null
@@ -1,2 +0,0 @@
-go test fuzz v1
-[]byte("000000")
diff --git a/lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/731951fe84fa6f3a7f6ee8adaa585d4f6a01f359a04737e51ffc70f16f480b9b b/lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/731951fe84fa6f3a7f6ee8adaa585d4f6a01f359a04737e51ffc70f16f480b9b
deleted file mode 100644
index bd1ae59..0000000
--- a/lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/731951fe84fa6f3a7f6ee8adaa585d4f6a01f359a04737e51ffc70f16f480b9b
+++ /dev/null
@@ -1,2 +0,0 @@
-go test fuzz v1
-[]byte("000000000000000000000000000000000000000000000000")
diff --git a/lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/7d6367ba84cd18550920b5202cd1269174416ce32769c7f59376e76b7dd3129c b/lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/7d6367ba84cd18550920b5202cd1269174416ce32769c7f59376e76b7dd3129c
deleted file mode 100644
index 09e0ad2..0000000
--- a/lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/7d6367ba84cd18550920b5202cd1269174416ce32769c7f59376e76b7dd3129c
+++ /dev/null
@@ -1,2 +0,0 @@
-go test fuzz v1
-[]byte("000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000")
diff --git a/lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/8727b16d337d7b8187433233f3a90099024e580a6ba319ea2bf539880c50bd7c b/lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/8727b16d337d7b8187433233f3a90099024e580a6ba319ea2bf539880c50bd7c
deleted file mode 100644
index e8000f3..0000000
--- a/lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/8727b16d337d7b8187433233f3a90099024e580a6ba319ea2bf539880c50bd7c
+++ /dev/null
@@ -1,2 +0,0 @@
-go test fuzz v1
-[]byte("00")
diff --git a/lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/9201a772731543760326638b8915f80863feab0ba0108183b3093934bdc0420c b/lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/9201a772731543760326638b8915f80863feab0ba0108183b3093934bdc0420c
deleted file mode 100644
index aac6b7d..0000000
--- a/lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/9201a772731543760326638b8915f80863feab0ba0108183b3093934bdc0420c
+++ /dev/null
@@ -1,2 +0,0 @@
-go test fuzz v1
-[]byte("00000000000000")
diff --git a/lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/92f75f690317ace34aeaae3fe39f5f2ff9830777253ff371c5ef6f403a0f8f0f b/lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/92f75f690317ace34aeaae3fe39f5f2ff9830777253ff371c5ef6f403a0f8f0f
deleted file mode 100644
index f3bf6d9..0000000
--- a/lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/92f75f690317ace34aeaae3fe39f5f2ff9830777253ff371c5ef6f403a0f8f0f
+++ /dev/null
@@ -1,2 +0,0 @@
-go test fuzz v1
-[]byte("00000000000")
diff --git a/lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/93d6f7bc0d93f998c7b7fe654ff46010d6fa76f0a142c3523c42454f8ad10b07 b/lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/93d6f7bc0d93f998c7b7fe654ff46010d6fa76f0a142c3523c42454f8ad10b07
deleted file mode 100644
index 2e7f462..0000000
--- a/lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/93d6f7bc0d93f998c7b7fe654ff46010d6fa76f0a142c3523c42454f8ad10b07
+++ /dev/null
@@ -1,2 +0,0 @@
-go test fuzz v1
-[]byte("00000000")
diff --git a/lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/a7450fd77fc7c53cc5bd136874415dddfff5c586e662f21420caa7a94131a56a b/lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/a7450fd77fc7c53cc5bd136874415dddfff5c586e662f21420caa7a94131a56a
deleted file mode 100644
index c541f52..0000000
--- a/lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/a7450fd77fc7c53cc5bd136874415dddfff5c586e662f21420caa7a94131a56a
+++ /dev/null
@@ -1,2 +0,0 @@
-go test fuzz v1
-[]byte("000000000000000000000000000000000000000000000000000")
diff --git a/lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/a95d2a0f87501a643d54218d2ad8112204672cc1fb30be297853616788208a5c b/lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/a95d2a0f87501a643d54218d2ad8112204672cc1fb30be297853616788208a5c
deleted file mode 100644
index 5d56f29..0000000
--- a/lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/a95d2a0f87501a643d54218d2ad8112204672cc1fb30be297853616788208a5c
+++ /dev/null
@@ -1,2 +0,0 @@
-go test fuzz v1
-[]byte("0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000")
diff --git a/lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/beed435aa2fee4819eab217543561dfd8001d4a44f53ceb664aaba86cebfaf21 b/lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/beed435aa2fee4819eab217543561dfd8001d4a44f53ceb664aaba86cebfaf21
deleted file mode 100644
index 4b4d59f..0000000
--- a/lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/beed435aa2fee4819eab217543561dfd8001d4a44f53ceb664aaba86cebfaf21
+++ /dev/null
@@ -1,2 +0,0 @@
-go test fuzz v1
-[]byte("000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000")
diff --git a/lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/c2501043394e49f2477408be5ef9389790e33ed1886073dec445d4cf05bcd4b4 b/lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/c2501043394e49f2477408be5ef9389790e33ed1886073dec445d4cf05bcd4b4
deleted file mode 100644
index ef9f9d4..0000000
--- a/lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/c2501043394e49f2477408be5ef9389790e33ed1886073dec445d4cf05bcd4b4
+++ /dev/null
@@ -1,2 +0,0 @@
-go test fuzz v1
-[]byte("000")
diff --git a/lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/caf81e9797b19c76c1fc4dbf537d4d81f389524539f402d13aa01f93a65ac7e9 b/lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/caf81e9797b19c76c1fc4dbf537d4d81f389524539f402d13aa01f93a65ac7e9
deleted file mode 100644
index 67322c7..0000000
--- a/lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/caf81e9797b19c76c1fc4dbf537d4d81f389524539f402d13aa01f93a65ac7e9
+++ /dev/null
@@ -1,2 +0,0 @@
-go test fuzz v1
-[]byte("")
diff --git a/lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/cc90a4a40ae9b3beac70baf6d7821a5a6f3a90cabb033575790be91723593680 b/lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/cc90a4a40ae9b3beac70baf6d7821a5a6f3a90cabb033575790be91723593680
deleted file mode 100644
index f195330..0000000
--- a/lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/cc90a4a40ae9b3beac70baf6d7821a5a6f3a90cabb033575790be91723593680
+++ /dev/null
@@ -1,2 +0,0 @@
-go test fuzz v1
-[]byte("000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\x04000000000000\r00000000000000000000")
diff --git a/lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/ec72f669d648d8d9b9f75a3b303897c59b11e4bfb7622f25ff251a92f182bc2a b/lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/ec72f669d648d8d9b9f75a3b303897c59b11e4bfb7622f25ff251a92f182bc2a
deleted file mode 100644
index 5b0d392..0000000
--- a/lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/ec72f669d648d8d9b9f75a3b303897c59b11e4bfb7622f25ff251a92f182bc2a
+++ /dev/null
@@ -1,2 +0,0 @@
-go test fuzz v1
-[]byte("0000000000000000000000000000000000000000")
diff --git a/lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/f34630c44c11bb13d27531927c5c1e65d159b70f39cd161da0dba348c1221ab3 b/lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/f34630c44c11bb13d27531927c5c1e65d159b70f39cd161da0dba348c1221ab3
deleted file mode 100644
index a389d3c..0000000
--- a/lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/f34630c44c11bb13d27531927c5c1e65d159b70f39cd161da0dba348c1221ab3
+++ /dev/null
@@ -1,2 +0,0 @@
-go test fuzz v1
-[]byte("00000")
diff --git a/lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/fd67efb09d433a1351a201281dbf6568628b4135c35c811dd9bce97620a75d43 b/lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/fd67efb09d433a1351a201281dbf6568628b4135c35c811dd9bce97620a75d43
deleted file mode 100644
index 17d10b2..0000000
--- a/lib/lowmemjson/testdata/fuzz/FuzzBase64Decoder/fd67efb09d433a1351a201281dbf6568628b4135c35c811dd9bce97620a75d43
+++ /dev/null
@@ -1,2 +0,0 @@
-go test fuzz v1
-[]byte("000000000000")