Skip to content

Commit

Permalink
Remove jsontext.WithEscapeFunc (#313)
Browse files Browse the repository at this point in the history
WARNING: This commit includes breaking changes.

This option original existed as a way for users to implement
EscapeForHTML and EscapeForJS on their own.
Since we added first-class support for those two escaping,
drop the general purpose escape logic.
It carries a unjustified amount of complexity.
Generally speaking, users can trivially do this escaping
themselves for any character that is not ASCII.

Performance:

	name                                        old time/op    new time/op   delta
	Testdata/CanadaGeometry/Marshal/Concrete    1.39ms ± 2%    1.39ms ± 2%    ~     (p=0.745 n=55+49)
	Testdata/CitmCatalog/Marshal/Concrete       1.41ms ± 2%    1.42ms ± 2%  +0.95%  (p=0.000 n=57+59)
	Testdata/GolangSource/Marshal/Concrete      5.27ms ± 2%    5.16ms ± 1%  -2.18%  (p=0.000 n=58+56)
	Testdata/StringEscaped/Marshal/Concrete     26.9µs ± 2%    25.7µs ± 2%  -4.60%  (p=0.000 n=60+56)
	Testdata/StringUnicode/Marshal/Concrete     27.0µs ± 2%    25.6µs ± 2%  -5.01%  (p=0.000 n=56+60)
	Testdata/SyntheaFhir/Marshal/Concrete       8.35ms ± 2%    8.08ms ± 2%  -3.25%  (p=0.000 n=60+50)
	Testdata/TwitterStatus/Marshal/Concrete      925µs ± 2%     916µs ± 2%  -1.00%  (p=0.000 n=58+40)
  • Loading branch information
dsnet authored Sep 6, 2023
1 parent 0e811c2 commit 699550a
Show file tree
Hide file tree
Showing 19 changed files with 177 additions and 445 deletions.
6 changes: 3 additions & 3 deletions arshal_default.go
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ func makeStringArshaler(t reflect.Type) *arshaler {

// Optimize for marshaling without preceding whitespace or string escaping.
s := va.String()
if optimizeCommon && !xe.Flags.Get(jsonflags.Expand) && !xe.Tokens.Last.NeedObjectName() && !jsonwire.NeedEscape(s, xe.EscapeRunes) {
if optimizeCommon && !xe.Flags.Get(jsonflags.Expand) && !xe.Tokens.Last.NeedObjectName() && !jsonwire.NeedEscape(s) {
b := xe.Buf
b = xe.Tokens.MayAppendDelim(b, '"')
b = append(b, '"')
Expand Down Expand Up @@ -984,10 +984,10 @@ func makeStructArshaler(t reflect.Type) *arshaler {

// Append the token to the output and to the state machine.
n0 := len(b) // offset before calling AppendQuote
if xe.EscapeRunes.IsCanonical() {
if !xe.Flags.Get(jsonflags.EscapeForHTML | jsonflags.EscapeForJS) {
b = append(b, f.quotedName...)
} else {
b, _ = jsonwire.AppendQuote(b, f.name, false, xe.EscapeRunes)
b, _ = jsonwire.AppendQuote(b, f.name, &xe.Flags)
}
xe.Buf = b
if !xe.Flags.Get(jsonflags.AllowDuplicateNames) {
Expand Down
2 changes: 1 addition & 1 deletion arshal_inlined.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ func marshalInlinedFallbackAll(enc *jsontext.Encoder, va addressableValue, mo *j
mv := newAddressableValue(m.Type().Elem())
marshalKey := func(mk addressableValue) error {
xe := export.Encoder(enc)
b, err := jsonwire.AppendQuote(enc.UnusedBuffer(), mk.String(), !xe.Flags.Get(jsonflags.AllowInvalidUTF8), nil)
b, err := jsonwire.AppendQuote(enc.UnusedBuffer(), mk.String(), &xe.Flags)
if err != nil {
return err
}
Expand Down
67 changes: 11 additions & 56 deletions arshal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1056,9 +1056,17 @@ func TestMarshal(t *testing.T) {
want: `{"":"empty",",":"comma","\"":"quote"}`,
}, {
name: jsontest.Name("Structs/EscapedNames"),
opts: []Options{jsontext.WithEscapeFunc(func(rune) bool { return true })},
in: structWeirdNames{Empty: "empty", Comma: "comma", Quote: "quote"},
want: `{"":"\u0065\u006d\u0070\u0074\u0079","\u002c":"\u0063\u006f\u006d\u006d\u0061","\u0022":"\u0071\u0075\u006f\u0074\u0065"}`,
opts: []Options{jsontext.EscapeForHTML(true), jsontext.EscapeForJS(true)},
in: struct {
S string "json:\"'abc<>&\u2028\u2029xyz'\""
M any
I structInlineTextValue
}{
S: "abc<>&\u2028\u2029xyz",
M: map[string]string{"abc<>&\u2028\u2029xyz": "abc<>&\u2028\u2029xyz"},
I: structInlineTextValue{X: jsontext.Value(`{"abc<>&` + "\u2028\u2029" + `xyz":"abc<>&` + "\u2028\u2029" + `xyz"}`)},
},
want: `{"abc\u003c\u003e\u0026\u2028\u2029xyz":"abc\u003c\u003e\u0026\u2028\u2029xyz","M":{"abc\u003c\u003e\u0026\u2028\u2029xyz":"abc\u003c\u003e\u0026\u2028\u2029xyz"},"I":{"abc\u003c\u003e\u0026\u2028\u2029xyz":"abc\u003c\u003e\u0026\u2028\u2029xyz"}}`,
}, {
name: jsontest.Name("Structs/NoCase"),
in: structNoCase{AaA: "AaA", AAa: "AAa", AAA: "AAA"},
Expand Down Expand Up @@ -1354,59 +1362,6 @@ func TestMarshal(t *testing.T) {
"Interface": null
},
"Interface": null
}`,
}, {
name: jsontest.Name("Structs/Stringified/Escaped"),
opts: []Options{
jsontext.Expand(true),
jsontext.WithEscapeFunc(func(rune) bool { return true }),
},
in: structStringifiedAll{
Bool: true,
String: "hello",
Bytes: []byte{1, 2, 3},
Int: -64, // should be stringified and escaped
Uint: +64, // should be stringified and escaped
Float: 3.14159, // should be stringified and escaped
},
want: `{
"\u0042\u006f\u006f\u006c": true,
"\u0053\u0074\u0072\u0069\u006e\u0067": "\u0068\u0065\u006c\u006c\u006f",
"\u0042\u0079\u0074\u0065\u0073": "\u0041\u0051\u0049\u0044",
"\u0049\u006e\u0074": "\u002d\u0036\u0034",
"\u0055\u0069\u006e\u0074": "\u0036\u0034",
"\u0046\u006c\u006f\u0061\u0074": "\u0033\u002e\u0031\u0034\u0031\u0035\u0039",
"\u004d\u0061\u0070": {},
"\u0053\u0074\u0072\u0075\u0063\u0074\u0053\u0063\u0061\u006c\u0061\u0072\u0073": {
"\u0042\u006f\u006f\u006c": false,
"\u0053\u0074\u0072\u0069\u006e\u0067": "",
"\u0042\u0079\u0074\u0065\u0073": "",
"\u0049\u006e\u0074": "\u0030",
"\u0055\u0069\u006e\u0074": "\u0030",
"\u0046\u006c\u006f\u0061\u0074": "\u0030"
},
"\u0053\u0074\u0072\u0075\u0063\u0074\u004d\u0061\u0070\u0073": {
"\u004d\u0061\u0070\u0042\u006f\u006f\u006c": {},
"\u004d\u0061\u0070\u0053\u0074\u0072\u0069\u006e\u0067": {},
"\u004d\u0061\u0070\u0042\u0079\u0074\u0065\u0073": {},
"\u004d\u0061\u0070\u0049\u006e\u0074": {},
"\u004d\u0061\u0070\u0055\u0069\u006e\u0074": {},
"\u004d\u0061\u0070\u0046\u006c\u006f\u0061\u0074": {}
},
"\u0053\u0074\u0072\u0075\u0063\u0074\u0053\u006c\u0069\u0063\u0065\u0073": {
"\u0053\u006c\u0069\u0063\u0065\u0042\u006f\u006f\u006c": [],
"\u0053\u006c\u0069\u0063\u0065\u0053\u0074\u0072\u0069\u006e\u0067": [],
"\u0053\u006c\u0069\u0063\u0065\u0042\u0079\u0074\u0065\u0073": [],
"\u0053\u006c\u0069\u0063\u0065\u0049\u006e\u0074": [],
"\u0053\u006c\u0069\u0063\u0065\u0055\u0069\u006e\u0074": [],
"\u0053\u006c\u0069\u0063\u0065\u0046\u006c\u006f\u0061\u0074": []
},
"\u0053\u006c\u0069\u0063\u0065": [],
"\u0041\u0072\u0072\u0061\u0079": [
""
],
"\u0050\u006f\u0069\u006e\u0074\u0065\u0072": null,
"\u0049\u006e\u0074\u0065\u0072\u0066\u0061\u0063\u0065": null
}`,
}, {
name: jsontest.Name("Structs/OmitZero/Zero"),
Expand Down
3 changes: 1 addition & 2 deletions diff_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -731,8 +731,7 @@ func TestMapDeterminism(t *testing.T) {
// In v1, JSON string encoding escapes special characters related to HTML.
// In v2, JSON string encoding uses a normalized representation (per RFC 8785).
//
// Users of v2 can opt into the v1 behavior by setting WithEscapeFunc.
// See the EscapeHTML example.
// Users of v2 can opt into the v1 behavior by setting EscapeForHTML and EscapeForJS.
//
// Escaping HTML-specific characters in a JSON library is a layering violation.
// It presumes that JSON is always used with HTML and ignores other
Expand Down
3 changes: 2 additions & 1 deletion fields.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (
"unicode"
"unicode/utf8"

"github.com/go-json-experiment/json/internal/jsonflags"
"github.com/go-json-experiment/json/internal/jsonwire"
)

Expand Down Expand Up @@ -363,7 +364,7 @@ func parseFieldOptions(sf reflect.StructField) (out fieldOptions, ignored bool,
out.name = opt
tag = tag[n:]
}
b, _ := jsonwire.AppendQuote(nil, out.name, false, nil)
b, _ := jsonwire.AppendQuote(nil, out.name, &jsonflags.Flags{})
out.quotedName = string(b)

// Handle any additional tag options (if any).
Expand Down
34 changes: 12 additions & 22 deletions internal/jsonflags/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,11 @@ package jsonflags

import "github.com/go-json-experiment/json/internal"

// Bools represents zero or more boolean flag, all set to true or false.
// Bools represents zero or more boolean flags, all set to true or false.
// The least-significant bit is the boolean value of all flags in the set.
// The remaining bits identify a particular flag.
// The remaining bits identify which particular flags.
//
// In common usage, this is OR'd with 0 or 1. For example:
//
// - (AllowInvalidUTF8 | 0) means "AllowInvalidUTF8 is false"
// - (Expand | Indent | 1) means "Expand and Indent are true"
type Bools uint64
Expand All @@ -37,7 +36,6 @@ const (
// where the value is some other concrete Go type.
// The value of the flag is stored within jsonopts.Struct.
NonBooleanFlags = 0 |
EscapeFunc |
Indent |
IndentPrefix |
ByteLimit |
Expand Down Expand Up @@ -81,7 +79,6 @@ const (
CanonicalizeNumbers // encode only; for internal use by jsontext.Value.Canonicalize
EscapeForHTML // encode only
EscapeForJS // encode only
EscapeFunc // encode only; non-boolean flag
Expand // encode only
Indent // encode only; non-boolean flag
IndentPrefix // encode only; non-boolean flag
Expand Down Expand Up @@ -128,13 +125,13 @@ const (
maxArshalV1Flag
)

// Flags is a set boolean flags.
// Flags is a set of boolean flags.
// If the presence bit is zero, then the value bit must also be zero.
// The least-significant bit of both fields is always zero.
//
// Unlike Bools, which can represent a set of bools that are all true or false,
// Flags represents a set of bools, each individually may be true or false.
type Flags struct{ Presence, Value uint64 }
type Flags struct{ Presence, Values uint64 }

// Join joins two sets of flags such that the latter takes precedence.
func (dst *Flags) Join(src Flags) {
Expand All @@ -144,8 +141,8 @@ func (dst *Flags) Join(src Flags) {
// e.g., dst := Flags{Presence: 0b_1100_0011, Value: 0b_1000_0011}
// e.g., src := Flags{Presence: 0b_0101_1010, Value: 0b_1001_0010}
dst.Presence |= src.Presence // e.g., 0b_1100_0011 | 0b_0101_1010 -> 0b_110_11011
dst.Value &= ^src.Presence // e.g., 0b_1000_0011 & 0b_1010_0101 -> 0b_100_00001
dst.Value |= src.Value // e.g., 0b_1000_0001 | 0b_1001_0010 -> 0b_100_10011
dst.Values &= ^src.Presence // e.g., 0b_1000_0011 & 0b_1010_0101 -> 0b_100_00001
dst.Values |= src.Values // e.g., 0b_1000_0001 | 0b_1001_0010 -> 0b_100_10011
}

// Set sets both the presence and value for the provided bool (or set of bools).
Expand All @@ -156,24 +153,17 @@ func (fs *Flags) Set(f Bools) {
// then copy over all the identifier bits to the value if LSB is 1.
// e.g., fs := Flags{Presence: 0b_0101_0010, Value: 0b_0001_0010}
// e.g., f := 0b_1001_0001
id := uint64(f) &^ uint64(1) // e.g., 0b_1001_0001 & 0b_1111_1110 -> 0b_1001_0000
fs.Presence |= id // e.g., 0b_0101_0010 | 0b_1001_0000 -> 0b_1101_0011
fs.Value &= ^id // e.g., 0b_0001_0010 & 0b_0110_1111 -> 0b_0000_0010
fs.Value |= uint64(f&1) * id // e.g., 0b_0000_0010 | 0b_1001_0000 -> 0b_1001_0010
id := uint64(f) &^ uint64(1) // e.g., 0b_1001_0001 & 0b_1111_1110 -> 0b_1001_0000
fs.Presence |= id // e.g., 0b_0101_0010 | 0b_1001_0000 -> 0b_1101_0011
fs.Values &= ^id // e.g., 0b_0001_0010 & 0b_0110_1111 -> 0b_0000_0010
fs.Values |= uint64(f&1) * id // e.g., 0b_0000_0010 | 0b_1001_0000 -> 0b_1001_0010
}

// Get reports whether the bool (or any of the bools) is true.
// This is generally only used with a singular bool.
// The value bit of f (i.e., the LSB) is ignored.
func (fs Flags) Get(f Bools) bool {
return fs.Value&uint64(f) > 0
}

// GetOk reports the value of the bool and whether it was set.
// This is generally only used with a singular bool.
// The value bit of f (i.e., the LSB) is ignored.
func (fs Flags) GetOk(f Bools) (v, ok bool) {
return fs.Get(f), fs.Has(f)
return fs.Values&uint64(f) > 0
}

// Has reports whether the bool (or any of the bools) is set.
Expand All @@ -190,5 +180,5 @@ func (fs *Flags) Clear(f Bools) {
// e.g., f := 0b_0001_1000
mask := uint64(^f) // e.g., 0b_0001_1000 -> 0b_1110_0111
fs.Presence &= mask // e.g., 0b_0101_0010 & 0b_1110_0111 -> 0b_0100_0010
fs.Value &= mask // e.g., 0b_0001_0010 & 0b_1110_0111 -> 0b_0000_0010
fs.Values &= mask // e.g., 0b_0001_0010 & 0b_1110_0111 -> 0b_0000_0010
}
28 changes: 15 additions & 13 deletions internal/jsonflags/flags_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,31 +23,31 @@ func TestFlags(t *testing.T) {
Get{in: AllowDuplicateNames, want: false, wantOk: true},
Set{in: AllowDuplicateNames | 1},
Get{in: AllowDuplicateNames, want: true, wantOk: true},
Check{want: Flags{Presence: uint64(AllowDuplicateNames), Value: uint64(AllowDuplicateNames)}},
Check{want: Flags{Presence: uint64(AllowDuplicateNames), Values: uint64(AllowDuplicateNames)}},
Get{in: AllowInvalidUTF8, want: false, wantOk: false},
Set{in: AllowInvalidUTF8 | 1},
Get{in: AllowInvalidUTF8, want: true, wantOk: true},
Set{in: AllowInvalidUTF8 | 0},
Get{in: AllowInvalidUTF8, want: false, wantOk: true},
Get{in: AllowDuplicateNames, want: true, wantOk: true},
Check{want: Flags{Presence: uint64(AllowDuplicateNames | AllowInvalidUTF8), Value: uint64(AllowDuplicateNames)}},
Check{want: Flags{Presence: uint64(AllowDuplicateNames | AllowInvalidUTF8), Values: uint64(AllowDuplicateNames)}},
Set{in: AllowDuplicateNames | AllowInvalidUTF8 | 0},
Check{want: Flags{Presence: uint64(AllowDuplicateNames | AllowInvalidUTF8), Value: uint64(0)}},
Check{want: Flags{Presence: uint64(AllowDuplicateNames | AllowInvalidUTF8), Values: uint64(0)}},
Set{in: AllowDuplicateNames | AllowInvalidUTF8 | 0},
Check{want: Flags{Presence: uint64(AllowDuplicateNames | AllowInvalidUTF8), Value: uint64(0)}},
Check{want: Flags{Presence: uint64(AllowDuplicateNames | AllowInvalidUTF8), Values: uint64(0)}},
Set{in: AllowDuplicateNames | AllowInvalidUTF8 | 1},
Check{want: Flags{Presence: uint64(AllowDuplicateNames | AllowInvalidUTF8), Value: uint64(AllowDuplicateNames | AllowInvalidUTF8)}},
Join{in: Flags{Presence: 0, Value: 0}},
Check{want: Flags{Presence: uint64(AllowDuplicateNames | AllowInvalidUTF8), Value: uint64(AllowDuplicateNames | AllowInvalidUTF8)}},
Join{in: Flags{Presence: uint64(Expand | AllowInvalidUTF8), Value: uint64(AllowDuplicateNames)}},
Check{want: Flags{Presence: uint64(Expand | AllowDuplicateNames | AllowInvalidUTF8), Value: uint64(AllowDuplicateNames)}},
Check{want: Flags{Presence: uint64(AllowDuplicateNames | AllowInvalidUTF8), Values: uint64(AllowDuplicateNames | AllowInvalidUTF8)}},
Join{in: Flags{Presence: 0, Values: 0}},
Check{want: Flags{Presence: uint64(AllowDuplicateNames | AllowInvalidUTF8), Values: uint64(AllowDuplicateNames | AllowInvalidUTF8)}},
Join{in: Flags{Presence: uint64(Expand | AllowInvalidUTF8), Values: uint64(AllowDuplicateNames)}},
Check{want: Flags{Presence: uint64(Expand | AllowDuplicateNames | AllowInvalidUTF8), Values: uint64(AllowDuplicateNames)}},
Clear{in: AllowDuplicateNames | AllowInvalidUTF8},
Check{want: Flags{Presence: uint64(Expand), Value: uint64(0)}},
Check{want: Flags{Presence: uint64(Expand), Values: uint64(0)}},
Set{in: AllowInvalidUTF8 | Deterministic | IgnoreStructErrors | 1},
Set{in: Expand | StringifyNumbers | RejectFloatOverflow | 0},
Check{want: Flags{Presence: uint64(AllowInvalidUTF8 | Deterministic | IgnoreStructErrors | Expand | StringifyNumbers | RejectFloatOverflow), Value: uint64(AllowInvalidUTF8 | Deterministic | IgnoreStructErrors)}},
Check{want: Flags{Presence: uint64(AllowInvalidUTF8 | Deterministic | IgnoreStructErrors | Expand | StringifyNumbers | RejectFloatOverflow), Values: uint64(AllowInvalidUTF8 | Deterministic | IgnoreStructErrors)}},
Clear{in: ^AllCoderFlags},
Check{want: Flags{Presence: uint64(AllowInvalidUTF8 | Expand), Value: uint64(AllowInvalidUTF8)}},
Check{want: Flags{Presence: uint64(AllowInvalidUTF8 | Expand), Values: uint64(AllowInvalidUTF8)}},
}
var fs Flags
for i, call := range calls {
Expand All @@ -59,7 +59,9 @@ func TestFlags(t *testing.T) {
case Clear:
fs.Clear(call.in)
case Get:
if got, gotOk := fs.GetOk(call.in); got != call.want || gotOk != call.wantOk {
got := fs.Get(call.in)
gotOk := fs.Has(call.in)
if got != call.want || gotOk != call.wantOk {
t.Fatalf("%d: GetOk = (%v, %v), want (%v, %v)", i, got, gotOk, call.want, call.wantOk)
}
case Check:
Expand Down
37 changes: 12 additions & 25 deletions internal/jsonopts/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,10 @@ type Struct struct {
}

type CoderValues struct {
EscapeFunc func(rune) bool // jsonflags.EscapeFunc
Indent string // jsonflags.Indent
IndentPrefix string // jsonflags.IndentPrefix
ByteLimit int64 // jsonflags.ByteLimit
DepthLimit int // jsonflags.DepthLimit
Indent string // jsonflags.Indent
IndentPrefix string // jsonflags.IndentPrefix
ByteLimit int64 // jsonflags.ByteLimit
DepthLimit int // jsonflags.DepthLimit
}

type ArshalValues struct {
Expand All @@ -48,7 +47,7 @@ type ArshalValues struct {
var DefaultOptionsV2 = Struct{
Flags: jsonflags.Flags{
Presence: uint64(jsonflags.AllFlags),
Value: uint64(0),
Values: uint64(0),
},
CoderValues: CoderValues{Indent: "\t"}, // Indent is set, but Expand is set to false
}
Expand All @@ -57,7 +56,7 @@ var DefaultOptionsV2 = Struct{
var DefaultOptionsV1 = Struct{
Flags: jsonflags.Flags{
Presence: uint64(jsonflags.AllFlags),
Value: uint64(jsonflags.DefaultV1Flags),
Values: uint64(jsonflags.DefaultV1Flags),
},
CoderValues: CoderValues{Indent: "\t"}, // Indent is set, but Expand is set to false
}
Expand Down Expand Up @@ -92,13 +91,9 @@ func GetOption[T any](opts Options, setter func(T) Options) (T, bool) {
var zero T
switch opt := setter(zero).(type) {
case jsonflags.Bools:
v, ok := structOpts.Flags.GetOk(opt)
v := structOpts.Flags.Get(opt)
ok := structOpts.Flags.Has(opt)
return any(v).(T), ok
case EscapeFunc:
if !structOpts.Flags.Has(jsonflags.EscapeFunc) {
return zero, false
}
return any(structOpts.EscapeFunc).(T), true
case Indent:
if !structOpts.Flags.Has(jsonflags.Indent) {
return zero, false
Expand Down Expand Up @@ -136,9 +131,6 @@ func (dst *Struct) Join(srcs ...Options) {
continue
case jsonflags.Bools:
dst.Flags.Set(src)
case EscapeFunc:
dst.Flags.Set(jsonflags.EscapeFunc | 1)
dst.EscapeFunc = src
case Indent:
dst.Flags.Set(jsonflags.Expand | jsonflags.Indent | 1)
dst.Indent = string(src)
Expand All @@ -154,9 +146,6 @@ func (dst *Struct) Join(srcs ...Options) {
case *Struct:
dst.Flags.Join(src.Flags)
if src.Flags.Has(jsonflags.NonBooleanFlags) {
if src.Flags.Has(jsonflags.EscapeFunc) {
dst.EscapeFunc = src.EscapeFunc
}
if src.Flags.Has(jsonflags.Indent) {
dst.Indent = src.Indent
}
Expand Down Expand Up @@ -187,16 +176,14 @@ func (dst *Struct) Join(srcs ...Options) {
}

type (
EscapeFunc func(rune) bool // jsontext.WithEscapeFunc
Indent string // jsontext.WithIndent
IndentPrefix string // jsontext.WithIndentPrefix
ByteLimit int64 // jsontext.WithByteLimit
DepthLimit int // jsontext.WithDepthLimit
Indent string // jsontext.WithIndent
IndentPrefix string // jsontext.WithIndentPrefix
ByteLimit int64 // jsontext.WithByteLimit
DepthLimit int // jsontext.WithDepthLimit
// type for jsonflags.Marshalers declared in "json" package
// type for jsonflags.Unmarshalers declared in "json" package
)

func (EscapeFunc) JSONOptions(internal.NotForPublicUse) {}
func (Indent) JSONOptions(internal.NotForPublicUse) {}
func (IndentPrefix) JSONOptions(internal.NotForPublicUse) {}
func (ByteLimit) JSONOptions(internal.NotForPublicUse) {}
Expand Down
2 changes: 1 addition & 1 deletion internal/jsonwire/decode.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ func ConsumeSimpleString(b []byte) (n int) {
// NOTE: The arguments and logic are kept simple to keep this inlinable.
if len(b) > 0 && b[0] == '"' {
n++
for len(b) > n && b[n] < utf8.RuneSelf && !escapeHTML.needEscapeASCII(b[n]) {
for len(b) > n && b[n] < utf8.RuneSelf && escapeASCII[b[n]] == 0 {
n++
}
if uint(len(b)) > uint(n) && b[n] == '"' {
Expand Down
Loading

0 comments on commit 699550a

Please sign in to comment.