|
|
@@ -50,6 +50,12 @@ const (
|
|
|
jsonLitNull = 14
|
|
|
)
|
|
|
|
|
|
+var (
|
|
|
+ jsonLiteral4True = jsonLiterals[jsonLitTrue+1 : jsonLitTrue+4]
|
|
|
+ jsonLiteral4False = jsonLiterals[jsonLitFalse+1 : jsonLitFalse+5]
|
|
|
+ jsonLiteral4Null = jsonLiterals[jsonLitNull+1 : jsonLitNull+4]
|
|
|
+)
|
|
|
+
|
|
|
const (
|
|
|
jsonU4Chk2 = '0'
|
|
|
jsonU4Chk1 = 'a' - 10
|
|
|
@@ -76,14 +82,15 @@ var (
|
|
|
// jsonTabs and jsonSpaces are used as caches for indents
|
|
|
jsonTabs, jsonSpaces [jsonSpacesOrTabsLen]byte
|
|
|
|
|
|
- jsonCharHtmlSafeSet bitset128
|
|
|
- jsonCharSafeSet bitset128
|
|
|
+ jsonCharHtmlSafeSet bitset256
|
|
|
+ jsonCharSafeSet bitset256
|
|
|
jsonCharWhitespaceSet bitset256
|
|
|
jsonNumSet bitset256
|
|
|
)
|
|
|
|
|
|
func init() {
|
|
|
- for i := 0; i < jsonSpacesOrTabsLen; i++ {
|
|
|
+ var i byte
|
|
|
+ for i = 0; i < jsonSpacesOrTabsLen; i++ {
|
|
|
jsonSpaces[i] = ' '
|
|
|
jsonTabs[i] = '\t'
|
|
|
}
|
|
|
@@ -91,7 +98,6 @@ func init() {
|
|
|
// populate the safe values as true: note: ASCII control characters are (0-31)
|
|
|
// jsonCharSafeSet: all true except (0-31) " \
|
|
|
// jsonCharHtmlSafeSet: all true except (0-31) " \ < > &
|
|
|
- var i byte
|
|
|
for i = 32; i < utf8.RuneSelf; i++ {
|
|
|
switch i {
|
|
|
case '"', '\\':
|
|
|
@@ -478,15 +484,15 @@ func (e *jsonEncDriver) EncodeStringBytes(c charEncoding, v []byte) {
|
|
|
return
|
|
|
}
|
|
|
|
|
|
- slen := base64.StdEncoding.EncodedLen(len(v))
|
|
|
- if cap(e.bs) >= slen+2 {
|
|
|
- e.bs = e.bs[:slen+2]
|
|
|
+ slen := base64.StdEncoding.EncodedLen(len(v)) + 2
|
|
|
+ if cap(e.bs) >= slen {
|
|
|
+ e.bs = e.bs[:slen]
|
|
|
} else {
|
|
|
- e.bs = make([]byte, slen+2)
|
|
|
+ e.bs = make([]byte, slen)
|
|
|
}
|
|
|
e.bs[0] = '"'
|
|
|
base64.StdEncoding.Encode(e.bs[1:], v)
|
|
|
- e.bs[slen+1] = '"'
|
|
|
+ e.bs[slen-1] = '"'
|
|
|
e.ew.writeb(e.bs)
|
|
|
} else {
|
|
|
e.quoteStr(stringView(v))
|
|
|
@@ -504,15 +510,15 @@ func (e *jsonEncDriver) EncodeStringBytesRaw(v []byte) {
|
|
|
return
|
|
|
}
|
|
|
|
|
|
- slen := base64.StdEncoding.EncodedLen(len(v))
|
|
|
- if cap(e.bs) >= slen+2 {
|
|
|
- e.bs = e.bs[:slen+2]
|
|
|
+ slen := base64.StdEncoding.EncodedLen(len(v)) + 2
|
|
|
+ if cap(e.bs) >= slen {
|
|
|
+ e.bs = e.bs[:slen]
|
|
|
} else {
|
|
|
- e.bs = make([]byte, slen+2)
|
|
|
+ e.bs = make([]byte, slen)
|
|
|
}
|
|
|
e.bs[0] = '"'
|
|
|
base64.StdEncoding.Encode(e.bs[1:], v)
|
|
|
- e.bs[slen+1] = '"'
|
|
|
+ e.bs[slen-1] = '"'
|
|
|
e.ew.writeb(e.bs)
|
|
|
}
|
|
|
|
|
|
@@ -733,12 +739,36 @@ func (d *jsonDecDriver) ReadMapEnd() {
|
|
|
d.c = containerMapEnd
|
|
|
}
|
|
|
|
|
|
-func (d *jsonDecDriver) readLit(length, fromIdx uint8) {
|
|
|
- // length here is always less than 8 (literals are: null, true, false)
|
|
|
- bs := d.r.readx(int(length))
|
|
|
+// func (d *jsonDecDriver) readLit(length, fromIdx uint8) {
|
|
|
+// // length here is always less than 8 (literals are: null, true, false)
|
|
|
+// bs := d.r.readx(int(length))
|
|
|
+// d.tok = 0
|
|
|
+// if jsonValidateSymbols && !bytes.Equal(bs, jsonLiterals[fromIdx:fromIdx+length]) {
|
|
|
+// d.d.errorf("expecting %s: got %s", jsonLiterals[fromIdx:fromIdx+length], bs)
|
|
|
+// }
|
|
|
+// }
|
|
|
+
|
|
|
+func (d *jsonDecDriver) readLit4True() {
|
|
|
+ bs := d.r.readx(3)
|
|
|
d.tok = 0
|
|
|
- if jsonValidateSymbols && !bytes.Equal(bs, jsonLiterals[fromIdx:fromIdx+length]) {
|
|
|
- d.d.errorf("expecting %s: got %s", jsonLiterals[fromIdx:fromIdx+length], bs)
|
|
|
+ if jsonValidateSymbols && !bytes.Equal(bs, jsonLiteral4True) {
|
|
|
+ d.d.errorf("expecting %s: got %s", jsonLiteral4True, bs)
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+func (d *jsonDecDriver) readLit4False() {
|
|
|
+ bs := d.r.readx(4)
|
|
|
+ d.tok = 0
|
|
|
+ if jsonValidateSymbols && !bytes.Equal(bs, jsonLiteral4False) {
|
|
|
+ d.d.errorf("expecting %s: got %s", jsonLiteral4False, bs)
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+func (d *jsonDecDriver) readLit4Null() {
|
|
|
+ bs := d.r.readx(3)
|
|
|
+ d.tok = 0
|
|
|
+ if jsonValidateSymbols && !bytes.Equal(bs, jsonLiteral4Null) {
|
|
|
+ d.d.errorf("expecting %s: got %s", jsonLiteral4Null, bs)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -749,7 +779,7 @@ func (d *jsonDecDriver) TryDecodeAsNil() bool {
|
|
|
// we shouldn't try to see if "null" was here, right?
|
|
|
// only the plain string: `null` denotes a nil (ie not quotes)
|
|
|
if d.tok == 'n' {
|
|
|
- d.readLit(3, jsonLitNull+1) // (n)ull
|
|
|
+ d.readLit4Null()
|
|
|
return true
|
|
|
}
|
|
|
return false
|
|
|
@@ -765,10 +795,10 @@ func (d *jsonDecDriver) DecodeBool() (v bool) {
|
|
|
}
|
|
|
switch d.tok {
|
|
|
case 'f':
|
|
|
- d.readLit(4, jsonLitFalse+1) // (f)alse
|
|
|
+ d.readLit4False()
|
|
|
// v = false
|
|
|
case 't':
|
|
|
- d.readLit(3, jsonLitTrue+1) // (t)rue
|
|
|
+ d.readLit4True()
|
|
|
v = true
|
|
|
default:
|
|
|
d.d.errorf("decode bool: got first char %c", d.tok)
|
|
|
@@ -991,15 +1021,15 @@ func (d *jsonDecDriver) appendStringAsBytes() {
|
|
|
// handle non-string scalar: null, true, false or a number
|
|
|
switch d.tok {
|
|
|
case 'n':
|
|
|
- d.readLit(3, jsonLitNull+1) // (n)ull
|
|
|
+ d.readLit4Null()
|
|
|
d.bs = d.bs[:0]
|
|
|
d.fnull = true
|
|
|
case 'f':
|
|
|
- d.readLit(4, jsonLitFalse+1) // (f)alse
|
|
|
+ d.readLit4False()
|
|
|
d.bs = d.bs[:5]
|
|
|
copy(d.bs, "false")
|
|
|
case 't':
|
|
|
- d.readLit(3, jsonLitTrue+1) // (t)rue
|
|
|
+ d.readLit4True()
|
|
|
d.bs = d.bs[:4]
|
|
|
copy(d.bs, "true")
|
|
|
default:
|
|
|
@@ -1018,7 +1048,7 @@ func (d *jsonDecDriver) appendStringAsBytes() {
|
|
|
d.tok = 0
|
|
|
r := d.r
|
|
|
var cs = r.readUntil(d.b2[:0], '"')
|
|
|
- var cslen = len(cs)
|
|
|
+ var cslen = uint(len(cs))
|
|
|
var c uint8
|
|
|
v := d.bs[:0]
|
|
|
// append on each byte seen can be expensive, so we just
|
|
|
@@ -1027,11 +1057,12 @@ func (d *jsonDecDriver) appendStringAsBytes() {
|
|
|
// and when we see a special byte
|
|
|
// e.g. end-of-slice, " or \,
|
|
|
// we will append the full range into the v slice before proceeding
|
|
|
- for i, cursor := 0, 0; ; {
|
|
|
+ var i, cursor uint
|
|
|
+ for {
|
|
|
if i == cslen {
|
|
|
v = append(v, cs[cursor:]...)
|
|
|
cs = r.readUntil(d.b2[:0], '"')
|
|
|
- cslen = len(cs)
|
|
|
+ cslen = uint(len(cs))
|
|
|
i, cursor = 0, 0
|
|
|
}
|
|
|
c = cs[i]
|
|
|
@@ -1062,14 +1093,13 @@ func (d *jsonDecDriver) appendStringAsBytes() {
|
|
|
case 'u':
|
|
|
var r rune
|
|
|
var rr uint32
|
|
|
- if len(cs) < i+4 { // may help reduce bounds-checking
|
|
|
+ if cslen < i+4 {
|
|
|
d.d.errorf("need at least 4 more bytes for unicode sequence")
|
|
|
}
|
|
|
- // c = cs[i+4] // may help reduce bounds-checking
|
|
|
- for j := 1; j < 5; j++ {
|
|
|
+ var j uint
|
|
|
+ for _, c = range cs[i+1 : i+5] { // bounds-check-elimination
|
|
|
// best to use explicit if-else
|
|
|
// - not a table, etc which involve memory loads, array lookup with bounds checks, etc
|
|
|
- c = cs[i+j]
|
|
|
if c >= '0' && c <= '9' {
|
|
|
rr = rr*16 + uint32(c-jsonU4Chk2)
|
|
|
} else if c >= 'a' && c <= 'f' {
|
|
|
@@ -1085,30 +1115,31 @@ func (d *jsonDecDriver) appendStringAsBytes() {
|
|
|
r = rune(rr)
|
|
|
i += 4
|
|
|
if utf16.IsSurrogate(r) {
|
|
|
- if len(cs) >= i+6 && cs[i+2] == 'u' && cs[i+1] == '\\' {
|
|
|
- i += 2
|
|
|
- // c = cs[i+4] // may help reduce bounds-checking
|
|
|
- var rr1 uint32
|
|
|
- for j := 1; j < 5; j++ {
|
|
|
- c = cs[i+j]
|
|
|
- if c >= '0' && c <= '9' {
|
|
|
- rr = rr*16 + uint32(c-jsonU4Chk2)
|
|
|
- } else if c >= 'a' && c <= 'f' {
|
|
|
- rr = rr*16 + uint32(c-jsonU4Chk1)
|
|
|
- } else if c >= 'A' && c <= 'F' {
|
|
|
- rr = rr*16 + uint32(c-jsonU4Chk0)
|
|
|
- } else {
|
|
|
- r = unicode.ReplacementChar
|
|
|
- i += 4
|
|
|
- goto encode_rune
|
|
|
+ if len(cs) >= int(i+6) {
|
|
|
+ var cx = cs[i+1 : i+7 : i+7][:6] // [:6] affords bounds-check-elimination
|
|
|
+ if cx[0] == '\\' && cx[1] == 'u' {
|
|
|
+ i += 2
|
|
|
+ var rr1 uint32
|
|
|
+ for j = 2; j < 6; j++ {
|
|
|
+ c = cx[j]
|
|
|
+ if c >= '0' && c <= '9' {
|
|
|
+ rr = rr*16 + uint32(c-jsonU4Chk2)
|
|
|
+ } else if c >= 'a' && c <= 'f' {
|
|
|
+ rr = rr*16 + uint32(c-jsonU4Chk1)
|
|
|
+ } else if c >= 'A' && c <= 'F' {
|
|
|
+ rr = rr*16 + uint32(c-jsonU4Chk0)
|
|
|
+ } else {
|
|
|
+ r = unicode.ReplacementChar
|
|
|
+ i += 4
|
|
|
+ goto encode_rune
|
|
|
+ }
|
|
|
}
|
|
|
+ r = utf16.DecodeRune(r, rune(rr1))
|
|
|
+ i += 4
|
|
|
+ goto encode_rune
|
|
|
}
|
|
|
- r = utf16.DecodeRune(r, rune(rr1))
|
|
|
- i += 4
|
|
|
- } else {
|
|
|
- r = unicode.ReplacementChar
|
|
|
- goto encode_rune
|
|
|
}
|
|
|
+ r = unicode.ReplacementChar
|
|
|
}
|
|
|
encode_rune:
|
|
|
w2 := utf8.EncodeRune(d.bstr[:], r)
|
|
|
@@ -1188,14 +1219,14 @@ func (d *jsonDecDriver) DecodeNaked() {
|
|
|
}
|
|
|
switch d.tok {
|
|
|
case 'n':
|
|
|
- d.readLit(3, jsonLitNull+1) // (n)ull
|
|
|
+ d.readLit4Null()
|
|
|
z.v = valueTypeNil
|
|
|
case 'f':
|
|
|
- d.readLit(4, jsonLitFalse+1) // (f)alse
|
|
|
+ d.readLit4False()
|
|
|
z.v = valueTypeBool
|
|
|
z.b = false
|
|
|
case 't':
|
|
|
- d.readLit(3, jsonLitTrue+1) // (t)rue
|
|
|
+ d.readLit4True()
|
|
|
z.v = valueTypeBool
|
|
|
z.b = true
|
|
|
case '{':
|