diff options
author | Luke Shumaker <lukeshu@lukeshu.com> | 2022-08-13 15:11:17 -0600 |
---|---|---|
committer | Luke Shumaker <lukeshu@lukeshu.com> | 2022-08-13 15:18:11 -0600 |
commit | 234e0836f1040f7724251b4120a2351bcbf64131 (patch) | |
tree | 201b30fcc99eed470ae345a9bbe594f7ee7a1178 /lib | |
parent | f2769bd863521cf316ec9237a498bfa4ecaa115f (diff) |
set up as a separate repo
Diffstat (limited to 'lib')
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") |