Skip to content

Commit

Permalink
codec: json: optimize bool encoding, and clean up bool decoding in na…
Browse files Browse the repository at this point in the history
…ked mode
  • Loading branch information
ugorji committed Jan 9, 2023
1 parent a7b31a0 commit 2a1ab51
Show file tree
Hide file tree
Showing 3 changed files with 44 additions and 24 deletions.
2 changes: 1 addition & 1 deletion codec/codec_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3570,7 +3570,7 @@ func doTestJsonDecodeNonStringScalarInStringContext(t *testing.T, h Handle) {
jh.SignedInteger = false

// Also test out decoding string values into naked interface
b = `{"true": true, "false": false, "null": null, "7700000000000000000": 7700000000000000000}`
b = `{"true": true, "false": false, null: null, "7700000000000000000": 7700000000000000000}`
const num = 7700000000000000000 // 77
var golden2 = map[interface{}]interface{}{
true: true,
Expand Down
8 changes: 8 additions & 0 deletions codec/helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -2463,6 +2463,14 @@ func implIntf(rt, iTyp reflect.Type) (base bool, indir bool) {
return
}

func bool2int(b bool) (v uint8) {
// MARKER: optimized to be a single instruction
if b {
v = 1
}
return
}

func isSliceBoundsError(s string) bool {
return strings.Contains(s, "index out of range") ||
strings.Contains(s, "slice bounds out of range")
Expand Down
58 changes: 35 additions & 23 deletions codec/json.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,11 @@ const (
// Both technically valid JSON, but bomb on JSONP, so fix here unconditionally.
jsonEscapeMultiByteUnicodeSep = true

// jsonRecognizeBoolNullInQuotedStr is used during decoding into a blank interface{}
// to control whether we detect quoted values of bools and null where a map key is expected,
// and treat as nil, true or false.
jsonNakedBoolNullInQuotedStr = true

// jsonManualInlineDecRdInHotZones controls whether we manually inline some decReader calls.
//
// encode performance is at par with libraries that just iterate over bytes directly,
Expand Down Expand Up @@ -238,7 +243,7 @@ func (e *jsonEncDriver) WriteMapElemValue() {

func (e *jsonEncDriver) EncodeNil() {
// We always encode nil as just null (never in quotes)
// so we can easily decode if a nil in the json streamie if initial token is n.
// so we can easily decode if a nil in the json stream ie if initial token is n.

e.e.encWr.writestr(jsonLits[jsonLitN : jsonLitN+4])
}
Expand Down Expand Up @@ -276,26 +281,32 @@ func (e *jsonEncDriver) EncodeRawExt(re *RawExt) {
}
}

func (e *jsonEncDriver) EncodeBool(b bool) {
// Use writen with an array instead of writeb with a slice
// i.e. in place of e.e.encWr.writeb(jsonLiteralTrueQ)
// OR jsonLiteralTrue, jsonLiteralFalse, jsonLiteralFalseQ, etc

if e.ks && e.e.c == containerMapKey {
if b {
e.e.encWr.writestr(jsonLits[jsonLitT-1 : jsonLitT+5])
} else {
e.e.encWr.writestr(jsonLits[jsonLitF-1 : jsonLitF+6])
}
} else {
if b {
e.e.encWr.writestr(jsonLits[jsonLitT : jsonLitT+4])
} else {
e.e.encWr.writestr(jsonLits[jsonLitF : jsonLitF+5])
}
}
var jsonEncBoolStrs = [2][2]string{
{jsonLits[jsonLitF : jsonLitF+5], jsonLits[jsonLitT : jsonLitT+4]},
{jsonLits[jsonLitF-1 : jsonLitF+6], jsonLits[jsonLitT-1 : jsonLitT+5]},
}

func (e *jsonEncDriver) EncodeBool(b bool) {
e.e.encWr.writestr(
jsonEncBoolStrs[bool2int(e.ks && e.e.c == containerMapKey)%2][bool2int(b)%2])
}

// func (e *jsonEncDriver) EncodeBool(b bool) {
// if e.ks && e.e.c == containerMapKey {
// if b {
// e.e.encWr.writestr(jsonLits[jsonLitT-1 : jsonLitT+5])
// } else {
// e.e.encWr.writestr(jsonLits[jsonLitF-1 : jsonLitF+6])
// }
// } else {
// if b {
// e.e.encWr.writestr(jsonLits[jsonLitT : jsonLitT+4])
// } else {
// e.e.encWr.writestr(jsonLits[jsonLitF : jsonLitF+5])
// }
// }
// }

func (e *jsonEncDriver) encodeFloat(f float64, bitsize, fmt byte, prec int8) {
var blen uint
if e.ks && e.e.c == containerMapKey {
Expand Down Expand Up @@ -1217,12 +1228,13 @@ func (d *jsonDecDriver) DecodeNaked() {
case '[':
z.v = valueTypeArray // don't consume. kInterfaceNaked will call ReadArrayStart
case '"':
// if a string, and MapKeyAsString, then try to decode it as a nil, bool or number first
// if a string, and MapKeyAsString, then try to decode it as a bool or number first
bs = d.dblQuoteStringAsBytes()
if len(bs) > 0 && d.d.c == containerMapKey && d.h.MapKeyAsString {
if jsonNakedBoolNullInQuotedStr &&
d.h.MapKeyAsString && len(bs) > 0 && d.d.c == containerMapKey {
switch string(bs) {
case "null":
z.v = valueTypeNil
// case "null": // nil is never quoted
// z.v = valueTypeNil
case "true":
z.v = valueTypeBool
z.b = true
Expand Down

0 comments on commit 2a1ab51

Please sign in to comment.