From b774f31c2da7dfb8ef0cd676d03f336774fa6927 Mon Sep 17 00:00:00 2001 From: Richard Wilkes Date: Tue, 9 Jul 2024 21:24:40 -0700 Subject: [PATCH] Sync with Go 1.22.5 --- bench_test.go | 201 ++++++++++++-- decode.go | 165 +++++------- encode.go | 539 ++++++++++++++---------------------- encode_test.go | 705 +++++++++++++++++++++++++----------------------- example_test.go | 2 +- fold.go | 149 ++-------- fold_test.go | 138 +++------- fuzz.go | 43 --- fuzz_test.go | 83 ++++++ go.mod | 2 +- indent.go | 119 +++++--- number_test.go | 15 -- scanner.go | 6 +- scanner_test.go | 233 ++++++++-------- stream.go | 49 ++-- stream_test.go | 452 +++++++++++++++++-------------- tagkey_test.go | 107 +++----- tags.go | 16 +- tags_test.go | 2 +- 19 files changed, 1491 insertions(+), 1535 deletions(-) delete mode 100644 fuzz.go create mode 100644 fuzz_test.go diff --git a/bench_test.go b/bench_test.go index 2feb2c1..c37687b 100644 --- a/bench_test.go +++ b/bench_test.go @@ -17,6 +17,7 @@ import ( "io" "os" "reflect" + "regexp" "runtime" "strings" "sync" @@ -91,7 +92,37 @@ func BenchmarkCodeEncoder(b *testing.B) { enc := NewEncoder(io.Discard) for pb.Next() { if err := enc.Encode(&codeStruct); err != nil { - b.Fatal("Encode:", err) + b.Fatalf("Encode error: %v", err) + } + } + }) + b.SetBytes(int64(len(codeJSON))) +} + +func BenchmarkCodeEncoderError(b *testing.B) { + b.ReportAllocs() + if codeJSON == nil { + b.StopTimer() + codeInit() + b.StartTimer() + } + + // Trigger an error in Marshal with cyclic data. + type Dummy struct { + Name string + Next *Dummy + } + dummy := Dummy{Name: "Dummy"} + dummy.Next = &dummy + + b.RunParallel(func(pb *testing.PB) { + enc := NewEncoder(io.Discard) + for pb.Next() { + if err := enc.Encode(&codeStruct); err != nil { + b.Fatalf("Encode error: %v", err) + } + if _, err := Marshal(dummy); err == nil { + b.Fatal("Marshal error: got nil, want non-nil") } } }) @@ -108,7 +139,36 @@ func BenchmarkCodeMarshal(b *testing.B) { b.RunParallel(func(pb *testing.PB) { for pb.Next() { if _, err := Marshal(&codeStruct); err != nil { - b.Fatal("Marshal:", err) + b.Fatalf("Marshal error: %v", err) + } + } + }) + b.SetBytes(int64(len(codeJSON))) +} + +func BenchmarkCodeMarshalError(b *testing.B) { + b.ReportAllocs() + if codeJSON == nil { + b.StopTimer() + codeInit() + b.StartTimer() + } + + // Trigger an error in Marshal with cyclic data. + type Dummy struct { + Name string + Next *Dummy + } + dummy := Dummy{Name: "Dummy"} + dummy.Next = &dummy + + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + if _, err := Marshal(&codeStruct); err != nil { + b.Fatalf("Marshal error: %v", err) + } + if _, err := Marshal(dummy); err == nil { + b.Fatal("Marshal error: got nil, want non-nil") } } }) @@ -127,7 +187,37 @@ func benchMarshalBytes(n int) func(*testing.B) { return func(b *testing.B) { for i := 0; i < b.N; i++ { if _, err := Marshal(v); err != nil { - b.Fatal("Marshal:", err) + b.Fatalf("Marshal error: %v", err) + } + } + } +} + +func benchMarshalBytesError(n int) func(*testing.B) { + sample := []byte("hello world") + // Use a struct pointer, to avoid an allocation when passing it as an + // interface parameter to Marshal. + v := &struct { + Bytes []byte + }{ + bytes.Repeat(sample, (n/len(sample))+1)[:n], + } + + // Trigger an error in Marshal with cyclic data. + type Dummy struct { + Name string + Next *Dummy + } + dummy := Dummy{Name: "Dummy"} + dummy.Next = &dummy + + return func(b *testing.B) { + for i := 0; i < b.N; i++ { + if _, err := Marshal(v); err != nil { + b.Fatalf("Marshal error: %v", err) + } + if _, err := Marshal(dummy); err == nil { + b.Fatal("Marshal error: got nil, want non-nil") } } } @@ -144,6 +234,33 @@ func BenchmarkMarshalBytes(b *testing.B) { b.Run("4096", benchMarshalBytes(4096)) } +func BenchmarkMarshalBytesError(b *testing.B) { + b.ReportAllocs() + // 32 fits within encodeState.scratch. + b.Run("32", benchMarshalBytesError(32)) + // 256 doesn't fit in encodeState.scratch, but is small enough to + // allocate and avoid the slower base64.NewEncoder. + b.Run("256", benchMarshalBytesError(256)) + // 4096 is large enough that we want to avoid allocating for it. + b.Run("4096", benchMarshalBytesError(4096)) +} + +func BenchmarkMarshalMap(b *testing.B) { + b.ReportAllocs() + m := map[string]int{ + "key3": 3, + "key2": 2, + "key1": 1, + } + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + if _, err := Marshal(m); err != nil { + b.Fatal("Marshal:", err) + } + } + }) +} + func BenchmarkCodeDecoder(b *testing.B) { b.ReportAllocs() if codeJSON == nil { @@ -162,7 +279,7 @@ func BenchmarkCodeDecoder(b *testing.B) { buf.WriteByte('\n') buf.WriteByte('\n') if err := dec.Decode(&r); err != nil { - b.Fatal("Decode:", err) + b.Fatalf("Decode error: %v", err) } } }) @@ -179,7 +296,7 @@ func BenchmarkUnicodeDecoder(b *testing.B) { b.ResetTimer() for i := 0; i < b.N; i++ { if err := dec.Decode(&out); err != nil { - b.Fatal("Decode:", err) + b.Fatalf("Decode error: %v", err) } r.Seek(0, 0) } @@ -191,9 +308,9 @@ func BenchmarkDecoderStream(b *testing.B) { var buf bytes.Buffer dec := NewDecoder(&buf) buf.WriteString(`"` + strings.Repeat("x", 1000000) + `"` + "\n\n\n") - var x interface{} + var x any if err := dec.Decode(&x); err != nil { - b.Fatal("Decode:", err) + b.Fatalf("Decode error: %v", err) } ones := strings.Repeat(" 1\n", 300000) + "\n\n\n" b.StartTimer() @@ -202,8 +319,11 @@ func BenchmarkDecoderStream(b *testing.B) { buf.WriteString(ones) } x = nil - if err := dec.Decode(&x); err != nil || x != 1.0 { - b.Fatalf("Decode: %v after %d", err, i) + switch err := dec.Decode(&x); { + case err != nil: + b.Fatalf("Decode error: %v", err) + case x != 1.0: + b.Fatalf("Decode: got %v want 1.0", i) } } } @@ -219,7 +339,7 @@ func BenchmarkCodeUnmarshal(b *testing.B) { for pb.Next() { var r codeResponse if err := Unmarshal(codeJSON, &r); err != nil { - b.Fatal("Unmarshal:", err) + b.Fatalf("Unmarshal error: %v", err) } } }) @@ -237,7 +357,7 @@ func BenchmarkCodeUnmarshalReuse(b *testing.B) { var r codeResponse for pb.Next() { if err := Unmarshal(codeJSON, &r); err != nil { - b.Fatal("Unmarshal:", err) + b.Fatalf("Unmarshal error: %v", err) } } }) @@ -251,7 +371,7 @@ func BenchmarkUnmarshalString(b *testing.B) { var s string for pb.Next() { if err := Unmarshal(data, &s); err != nil { - b.Fatal("Unmarshal:", err) + b.Fatalf("Unmarshal error: %v", err) } } }) @@ -264,7 +384,7 @@ func BenchmarkUnmarshalFloat64(b *testing.B) { var f float64 for pb.Next() { if err := Unmarshal(data, &f); err != nil { - b.Fatal("Unmarshal:", err) + b.Fatalf("Unmarshal error: %v", err) } } }) @@ -277,7 +397,20 @@ func BenchmarkUnmarshalInt64(b *testing.B) { var x int64 for pb.Next() { if err := Unmarshal(data, &x); err != nil { - b.Fatal("Unmarshal:", err) + b.Fatalf("Unmarshal error: %v", err) + } + } + }) +} + +func BenchmarkUnmarshalMap(b *testing.B) { + b.ReportAllocs() + data := []byte(`{"key1":"value1","key2":"value2","key3":"value3"}`) + b.RunParallel(func(pb *testing.PB) { + x := make(map[string]string, 3) + for pb.Next() { + if err := Unmarshal(data, &x); err != nil { + b.Fatalf("Unmarshal error: %v", err) } } }) @@ -290,7 +423,7 @@ func BenchmarkIssue10335(b *testing.B) { var s struct{} for pb.Next() { if err := Unmarshal(j, &s); err != nil { - b.Fatal(err) + b.Fatalf("Unmarshal error: %v", err) } } }) @@ -306,7 +439,7 @@ func BenchmarkIssue34127(b *testing.B) { b.RunParallel(func(pb *testing.PB) { for pb.Next() { if _, err := Marshal(&j); err != nil { - b.Fatal(err) + b.Fatalf("Marshal error: %v", err) } } }) @@ -319,7 +452,7 @@ func BenchmarkUnmapped(b *testing.B) { var s struct{} for pb.Next() { if err := Unmarshal(j, &s); err != nil { - b.Fatal(err) + b.Fatalf("Unmarshal error: %v", err) } } }) @@ -332,7 +465,7 @@ func BenchmarkTypeFieldsCache(b *testing.B) { // Dynamically generate many new types. types := make([]reflect.Type, maxTypes) fs := []reflect.StructField{{ - Type: reflect.TypeOf(""), + Type: reflect.TypeFor[string](), Index: []int{0}, }} for i := range types { @@ -399,8 +532,38 @@ func BenchmarkEncodeMarshaler(b *testing.B) { for pb.Next() { if err := enc.Encode(&m); err != nil { - b.Fatal("Encode:", err) + b.Fatalf("Encode error: %v", err) + } + } + }) +} + +func BenchmarkEncoderEncode(b *testing.B) { + b.ReportAllocs() + type T struct { + X, Y string + } + v := &T{"foo", "bar"} + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + if err := NewEncoder(io.Discard).Encode(v); err != nil { + b.Fatalf("Encode error: %v", err) } } }) } + +func BenchmarkNumberIsValid(b *testing.B) { + s := "-61657.61667E+61673" + for i := 0; i < b.N; i++ { + isValidNumber(s) + } +} + +func BenchmarkNumberIsValidRegexp(b *testing.B) { + var jsonNumberRegexp = regexp.MustCompile(`^-?(?:0|[1-9]\d*)(?:\.\d+)?(?:[eE][+-]?\d+)?$`) + s := "-61657.61667E+61673" + for i := 0; i < b.N; i++ { + jsonNumberRegexp.MatchString(s) + } +} diff --git a/decode.go b/decode.go index 2adcb26..9c73d99 100644 --- a/decode.go +++ b/decode.go @@ -22,10 +22,10 @@ import ( // Unmarshal parses the JSON-encoded data and stores the result // in the value pointed to by v. If v is nil or not a pointer, -// Unmarshal returns an InvalidUnmarshalError. +// Unmarshal returns an [InvalidUnmarshalError]. // // Unmarshal uses the inverse of the encodings that -// Marshal uses, allocating maps, slices, and pointers as necessary, +// [Marshal] uses, allocating maps, slices, and pointers as necessary, // with the following additional rules: // // To unmarshal JSON into a pointer, Unmarshal first handles the case of @@ -34,28 +34,28 @@ import ( // the value pointed at by the pointer. If the pointer is nil, Unmarshal // allocates a new value for it to point to. // -// To unmarshal JSON into a value implementing the Unmarshaler interface, -// Unmarshal calls that value's UnmarshalJSON method, including +// To unmarshal JSON into a value implementing [Unmarshaler], +// Unmarshal calls that value's [Unmarshaler.UnmarshalJSON] method, including // when the input is a JSON null. -// Otherwise, if the value implements encoding.TextUnmarshaler -// and the input is a JSON quoted string, Unmarshal calls that value's -// UnmarshalText method with the unquoted form of the string. +// Otherwise, if the value implements [encoding.TextUnmarshaler] +// and the input is a JSON quoted string, Unmarshal calls +// [encoding.TextUnmarshaler.UnmarshalText] with the unquoted form of the string. // // To unmarshal JSON into a struct, Unmarshal matches incoming object -// keys to the keys used by Marshal (either the struct field name or its tag), +// keys to the keys used by [Marshal] (either the struct field name or its tag), // preferring an exact match but also accepting a case-insensitive match. By // default, object keys which don't have a corresponding struct field are -// ignored (see Decoder.DisallowUnknownFields for an alternative). +// ignored (see [Decoder.DisallowUnknownFields] for an alternative). // // To unmarshal JSON into an interface value, // Unmarshal stores one of these in the interface value: // -// bool, for JSON booleans -// float64, for JSON numbers -// string, for JSON strings -// []interface{}, for JSON arrays -// map[string]interface{}, for JSON objects -// nil for JSON null +// - bool, for JSON booleans +// - float64, for JSON numbers +// - string, for JSON strings +// - []interface{}, for JSON arrays +// - map[string]interface{}, for JSON objects +// - nil for JSON null // // To unmarshal a JSON array into a slice, Unmarshal resets the slice length // to zero and then appends each element to the slice. @@ -73,14 +73,16 @@ import ( // use. If the map is nil, Unmarshal allocates a new map. Otherwise Unmarshal // reuses the existing map, keeping existing entries. Unmarshal then stores // key-value pairs from the JSON object into the map. The map's key type must -// either be any string type, an integer, implement json.Unmarshaler, or -// implement encoding.TextUnmarshaler. +// either be any string type, an integer, implement [json.Unmarshaler], or +// implement [encoding.TextUnmarshaler]. +// +// If the JSON-encoded data contain a syntax error, Unmarshal returns a [SyntaxError]. // // If a JSON value is not appropriate for a given target type, // or if a JSON number overflows the target type, Unmarshal // skips that field and completes the unmarshaling as best it can. // If no more serious errors are encountered, Unmarshal returns -// an UnmarshalTypeError describing the earliest such error. In any +// an [UnmarshalTypeError] describing the earliest such error. In any // case, it's not guaranteed that all the remaining fields following // the problematic one will be unmarshaled into the target object. // @@ -93,7 +95,7 @@ import ( // invalid UTF-16 surrogate pairs are not treated as an error. // Instead, they are replaced by the Unicode replacement // character U+FFFD. -func Unmarshal(data []byte, v interface{}) error { +func Unmarshal(data []byte, v any) error { // Check for well-formedness. // Avoids filling out half a data structure // before discovering a JSON syntax error. @@ -108,8 +110,8 @@ func Unmarshal(data []byte, v interface{}) error { return d.unmarshal(v) } -// UnmarshalWithContext is like Unmarshal but allows a context to be passed through. -// To take advantage of this, you must implement UnmarshalerWithContext instead of Unmarshaler. +// UnmarshalWithContext is like [Unmarshal] but allows a context to be passed through. +// To take advantage of this, you must implement [UnmarshalerWithContext] instead of [Unmarshaler]. func UnmarshalWithContext(ctx context.Context, data []byte, v interface{}) error { // Check for well-formedness. // Avoids filling out half a data structure @@ -135,13 +137,13 @@ func UnmarshalWithContext(ctx context.Context, data []byte, v interface{}) error // a JSON value. UnmarshalJSON must copy the JSON data // if it wishes to retain the data after returning. // -// By convention, to approximate the behavior of Unmarshal itself, +// By convention, to approximate the behavior of [Unmarshal] itself, // Unmarshalers implement UnmarshalJSON([]byte("null")) as a no-op. type Unmarshaler interface { UnmarshalJSON([]byte) error } -// UnmarshalerWithContext is a version of Unmarshaler that +// UnmarshalerWithContext is a version of [Unmarshaler] that // passes a context through from the decoder. type UnmarshalerWithContext interface { UnmarshalJSONWithContext(context.Context, []byte) error @@ -164,8 +166,8 @@ func (e *UnmarshalTypeError) Error() string { return "json: cannot unmarshal " + e.Value + " into Go value of type " + e.Type.String() } -// An InvalidUnmarshalError describes an invalid argument passed to Unmarshal. -// (The argument to Unmarshal must be a non-nil pointer.) +// An InvalidUnmarshalError describes an invalid argument passed to [Unmarshal]. +// (The argument to [Unmarshal] must be a non-nil pointer.) type InvalidUnmarshalError struct { Type reflect.Type } @@ -175,15 +177,15 @@ func (e *InvalidUnmarshalError) Error() string { return "json: Unmarshal(nil)" } - if e.Type.Kind() != reflect.Ptr { + if e.Type.Kind() != reflect.Pointer { return "json: Unmarshal(non-pointer " + e.Type.String() + ")" } return "json: Unmarshal(nil " + e.Type.String() + ")" } -func (d *decodeState) unmarshal(v interface{}) error { +func (d *decodeState) unmarshal(v any) error { rv := reflect.ValueOf(v) - if rv.Kind() != reflect.Ptr || rv.IsNil() { + if rv.Kind() != reflect.Pointer || rv.IsNil() { return &InvalidUnmarshalError{reflect.TypeOf(v)} } @@ -413,7 +415,7 @@ type unquotedValue struct{} // quoted string literal or literal null into an interface value. // If it finds anything other than a quoted string literal or null, // valueQuoted returns unquotedValue{}. -func (d *decodeState) valueQuoted() interface{} { +func (d *decodeState) valueQuoted() any { switch d.opcode { default: panic(phasePanicMsg) @@ -455,7 +457,7 @@ func indirect(v reflect.Value, decodingNull bool) (UnmarshalerWithContext, Unmar // If v is a named type and is addressable, // start with its address, so that if the type has pointer methods, // we find them. - if v.Kind() != reflect.Ptr && v.Type().Name() != "" && v.CanAddr() { + if v.Kind() != reflect.Pointer && v.Type().Name() != "" && v.CanAddr() { haveAddr = true v = v.Addr() } @@ -464,14 +466,14 @@ func indirect(v reflect.Value, decodingNull bool) (UnmarshalerWithContext, Unmar // usefully addressable. if v.Kind() == reflect.Interface && !v.IsNil() { e := v.Elem() - if e.Kind() == reflect.Ptr && !e.IsNil() && (!decodingNull || e.Elem().Kind() == reflect.Ptr) { + if e.Kind() == reflect.Pointer && !e.IsNil() && (!decodingNull || e.Elem().Kind() == reflect.Pointer) { haveAddr = false v = e continue } } - if v.Kind() != reflect.Ptr { + if v.Kind() != reflect.Pointer { break } @@ -562,17 +564,10 @@ func (d *decodeState) array(v reflect.Value) error { break } - // Get element of array, growing if necessary. + // Expand slice length, growing the slice if necessary. if v.Kind() == reflect.Slice { - // Grow slice if necessary if i >= v.Cap() { - newcap := v.Cap() + v.Cap()/2 - if newcap < 4 { - newcap = 4 - } - newv := reflect.MakeSlice(v.Type(), v.Len(), newcap) - reflect.Copy(newv, v) - v.Set(newv) + v.Grow(1) } if i >= v.Len() { v.SetLen(i + 1) @@ -606,13 +601,11 @@ func (d *decodeState) array(v reflect.Value) error { if i < v.Len() { if v.Kind() == reflect.Array { - // Array. Zero the rest. - z := reflect.Zero(v.Type().Elem()) for ; i < v.Len(); i++ { - v.Index(i).Set(z) + v.Index(i).SetZero() // zero remainder of array } } else { - v.SetLen(i) + v.SetLen(i) // truncate the slice } } if i == 0 && v.Kind() == reflect.Slice { @@ -622,7 +615,7 @@ func (d *decodeState) array(v reflect.Value) error { } var nullLiteral = []byte("null") -var textUnmarshalerType = reflect.TypeOf((*encoding.TextUnmarshaler)(nil)).Elem() +var textUnmarshalerType = reflect.TypeFor[encoding.TextUnmarshaler]() // object consumes an object from d.data[d.off-1:], decoding into v. // The first byte ('{') of the object has been read already. @@ -669,7 +662,7 @@ func (d *decodeState) object(v reflect.Value) error { reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: default: - if !reflect.PtrTo(t.Key()).Implements(textUnmarshalerType) { + if !reflect.PointerTo(t.Key()).Implements(textUnmarshalerType) { d.saveError(&UnmarshalTypeError{Value: "object", Type: t, Offset: int64(d.off)}) d.skip() return nil @@ -722,30 +715,19 @@ func (d *decodeState) object(v reflect.Value) error { if !mapElem.IsValid() { mapElem = reflect.New(elemType).Elem() } else { - mapElem.Set(reflect.Zero(elemType)) + mapElem.SetZero() } subv = mapElem } else { - var f *field - if i, ok := fields.nameIndex[string(key)]; ok { - // Found an exact name match. - f = &fields.list[i] - } else { - // Fall back to the expensive case-insensitive - // linear search. - for i = range fields.list { - ff := &fields.list[i] - if ff.equalFold(ff.nameBytes, key) || (len(ff.altNameBytes) > 0 && ff.altEqualFold(ff.altNameBytes, key)) { - f = ff - break - } - } + f := fields.byExactName[string(key)] + if f == nil { + f = fields.byFoldedName[string(foldName(key))] } if f != nil { subv = v destring = f.quoted for _, i := range f.index { - if subv.Kind() == reflect.Ptr { + if subv.Kind() == reflect.Pointer { if subv.IsNil() { // If a struct embeds a pointer to an unexported type, // it is not possible to set a newly allocated value @@ -809,17 +791,17 @@ func (d *decodeState) object(v reflect.Value) error { if v.Kind() == reflect.Map { kt := t.Key() var kv reflect.Value - switch { - case reflect.PtrTo(kt).Implements(textUnmarshalerType): + if reflect.PointerTo(kt).Implements(textUnmarshalerType) { kv = reflect.New(kt) if err := d.literalStore(item, kv, true); err != nil { return err } kv = kv.Elem() - case kt.Kind() == reflect.String: - kv = reflect.ValueOf(key).Convert(kt) - default: + } else { switch kt.Kind() { + case reflect.String: + kv = reflect.New(kt).Elem() + kv.SetString(string(key)) case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: s := string(key) n, err := strconv.ParseInt(s, 10, 64) @@ -827,7 +809,8 @@ func (d *decodeState) object(v reflect.Value) error { d.saveError(&UnmarshalTypeError{Value: "number " + s, Type: kt, Offset: int64(start + 1)}) break } - kv = reflect.ValueOf(n).Convert(kt) + kv = reflect.New(kt).Elem() + kv.SetInt(n) case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: s := string(key) n, err := strconv.ParseUint(s, 10, 64) @@ -835,7 +818,8 @@ func (d *decodeState) object(v reflect.Value) error { d.saveError(&UnmarshalTypeError{Value: "number " + s, Type: kt, Offset: int64(start + 1)}) break } - kv = reflect.ValueOf(n).Convert(kt) + kv = reflect.New(kt).Elem() + kv.SetUint(n) default: panic("json: Unexpected key type") // should never occur } @@ -868,18 +852,18 @@ func (d *decodeState) object(v reflect.Value) error { // convertNumber converts the number literal s to a float64 or a Number // depending on the setting of d.useNumber. -func (d *decodeState) convertNumber(s string) (interface{}, error) { +func (d *decodeState) convertNumber(s string) (any, error) { if d.useNumber { return Number(s), nil } f, err := strconv.ParseFloat(s, 64) if err != nil { - return nil, &UnmarshalTypeError{Value: "number " + s, Type: reflect.TypeOf(0.0), Offset: int64(d.off)} + return nil, &UnmarshalTypeError{Value: "number " + s, Type: reflect.TypeFor[float64](), Offset: int64(d.off)} } return f, nil } -var numberType = reflect.TypeOf(Number("")) +var numberType = reflect.TypeFor[Number]() // literalStore decodes a literal stored in item into v. // @@ -889,7 +873,7 @@ var numberType = reflect.TypeOf(Number("")) func (d *decodeState) literalStore(item []byte, v reflect.Value, fromQuoted bool) error { // Check for unmarshaler. if len(item) == 0 { - // Empty string given + // Empty string given. d.saveError(fmt.Errorf("json: invalid use of ,string struct tag, trying to unmarshal %q into %v", item, v.Type())) return nil } @@ -938,8 +922,8 @@ func (d *decodeState) literalStore(item []byte, v reflect.Value, fromQuoted bool break } switch v.Kind() { - case reflect.Interface, reflect.Ptr, reflect.Map, reflect.Slice: - v.Set(reflect.Zero(v.Type())) + case reflect.Interface, reflect.Pointer, reflect.Map, reflect.Slice: + v.SetZero() // otherwise, ignore null for primitives/string } case 't', 'f': // true, false @@ -1010,13 +994,12 @@ func (d *decodeState) literalStore(item []byte, v reflect.Value, fromQuoted bool } panic(phasePanicMsg) } - s := string(item) switch v.Kind() { default: if v.Kind() == reflect.String && v.Type() == numberType { // s must be a valid number, because it's // already been tokenized. - v.SetString(s) + v.SetString(string(item)) break } if fromQuoted { @@ -1024,7 +1007,7 @@ func (d *decodeState) literalStore(item []byte, v reflect.Value, fromQuoted bool } d.saveError(&UnmarshalTypeError{Value: "number", Type: v.Type(), Offset: int64(d.readIndex())}) case reflect.Interface: - n, err := d.convertNumber(s) + n, err := d.convertNumber(string(item)) if err != nil { d.saveError(err) break @@ -1036,25 +1019,25 @@ func (d *decodeState) literalStore(item []byte, v reflect.Value, fromQuoted bool v.Set(reflect.ValueOf(n)) case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - n, err := strconv.ParseInt(s, 10, 64) + n, err := strconv.ParseInt(string(item), 10, 64) if err != nil || v.OverflowInt(n) { - d.saveError(&UnmarshalTypeError{Value: "number " + s, Type: v.Type(), Offset: int64(d.readIndex())}) + d.saveError(&UnmarshalTypeError{Value: "number " + string(item), Type: v.Type(), Offset: int64(d.readIndex())}) break } v.SetInt(n) case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: - n, err := strconv.ParseUint(s, 10, 64) + n, err := strconv.ParseUint(string(item), 10, 64) if err != nil || v.OverflowUint(n) { - d.saveError(&UnmarshalTypeError{Value: "number " + s, Type: v.Type(), Offset: int64(d.readIndex())}) + d.saveError(&UnmarshalTypeError{Value: "number " + string(item), Type: v.Type(), Offset: int64(d.readIndex())}) break } v.SetUint(n) case reflect.Float32, reflect.Float64: - n, err := strconv.ParseFloat(s, v.Type().Bits()) + n, err := strconv.ParseFloat(string(item), v.Type().Bits()) if err != nil || v.OverflowFloat(n) { - d.saveError(&UnmarshalTypeError{Value: "number " + s, Type: v.Type(), Offset: int64(d.readIndex())}) + d.saveError(&UnmarshalTypeError{Value: "number " + string(item), Type: v.Type(), Offset: int64(d.readIndex())}) break } v.SetFloat(n) @@ -1068,7 +1051,7 @@ func (d *decodeState) literalStore(item []byte, v reflect.Value, fromQuoted bool // but they avoid the weight of reflection in this common case. // valueInterface is like value but returns interface{} -func (d *decodeState) valueInterface() (val interface{}) { +func (d *decodeState) valueInterface() (val any) { switch d.opcode { default: panic(phasePanicMsg) @@ -1085,8 +1068,8 @@ func (d *decodeState) valueInterface() (val interface{}) { } // arrayInterface is like array but returns []interface{}. -func (d *decodeState) arrayInterface() []interface{} { - var v = make([]interface{}, 0) +func (d *decodeState) arrayInterface() []any { + var v = make([]any, 0) for { // Look ahead for ] - can only happen on first iteration. d.scanWhile(scanSkipSpace) @@ -1111,8 +1094,8 @@ func (d *decodeState) arrayInterface() []interface{} { } // objectInterface is like object but returns map[string]interface{}. -func (d *decodeState) objectInterface() map[string]interface{} { - m := make(map[string]interface{}) +func (d *decodeState) objectInterface() map[string]any { + m := make(map[string]any) for { // Read opening " of string key or closing }. d.scanWhile(scanSkipSpace) @@ -1162,7 +1145,7 @@ func (d *decodeState) objectInterface() map[string]interface{} { // literalInterface consumes and returns a literal from d.data[d.off-1:] and // it reads the following byte ahead. The first byte of the literal has been // read already (that's how the caller knows it's a literal). -func (d *decodeState) literalInterface() interface{} { +func (d *decodeState) literalInterface() any { // All bytes inside literal return scanContinue op code. start := d.readIndex() d.rescanLiteral() diff --git a/encode.go b/encode.go index ba00da4..baf4a19 100644 --- a/encode.go +++ b/encode.go @@ -18,6 +18,7 @@ import ( "fmt" "math" "reflect" + "slices" "sort" "strconv" "strings" @@ -35,29 +36,30 @@ type Omitter interface { // Marshal returns the JSON encoding of v. // // Marshal traverses the value v recursively. -// If an encountered value implements the Marshaler interface -// and is not a nil pointer, Marshal calls its MarshalJSON method -// to produce JSON. If no MarshalJSON method is present but the -// value implements encoding.TextMarshaler instead, Marshal calls -// its MarshalText method and encodes the result as a JSON string. +// If an encountered value implements [Marshaler] +// and is not a nil pointer, Marshal calls [Marshaler.MarshalJSON] +// to produce JSON. If no [Marshaler.MarshalJSON] method is present but the +// value implements [encoding.TextMarshaler] instead, Marshal calls +// [encoding.TextMarshaler.MarshalText] and encodes the result as a JSON string. // The nil pointer exception is not strictly necessary // but mimics a similar, necessary exception in the behavior of -// UnmarshalJSON. +// [Unmarshaler.UnmarshalJSON]. // // Otherwise, Marshal uses the following type-dependent default encodings: // // Boolean values encode as JSON booleans. // -// Floating point, integer, and Number values encode as JSON numbers. +// Floating point, integer, and [Number] values encode as JSON numbers. +// NaN and +/-Inf values will return an [UnsupportedValueError]. // // String values encode as JSON strings coerced to valid UTF-8, // replacing invalid bytes with the Unicode replacement rune. // So that the JSON will be safe to embed inside HTML