feat: improve performance when a payload contains escape sequence

This commit is contained in:
Nao Yonashiro 2022-02-12 17:25:52 +09:00
parent 116e62dc84
commit 4bd7d2399f
1 changed files with 39 additions and 27 deletions

View File

@ -1,6 +1,7 @@
package decoder package decoder
import ( import (
"bytes"
"reflect" "reflect"
"unicode" "unicode"
"unicode/utf16" "unicode/utf16"
@ -308,49 +309,30 @@ func (d *stringDecoder) decodeByte(buf []byte, cursor int64) ([]byte, int64, err
cursor++ cursor++
start := cursor start := cursor
b := (*sliceHeader)(unsafe.Pointer(&buf)).data b := (*sliceHeader)(unsafe.Pointer(&buf)).data
escaped := 0
for { for {
switch char(b, cursor) { switch char(b, cursor) {
case '\\': case '\\':
escaped++
cursor++ cursor++
switch char(b, cursor) { switch char(b, cursor) {
case '"': case '"', '\\', '/', 'b', 'f', 'n', 'r', 't':
buf[cursor] = '"' cursor++
buf = append(buf[:cursor-1], buf[cursor:]...)
case '\\':
buf[cursor] = '\\'
buf = append(buf[:cursor-1], buf[cursor:]...)
case '/':
buf[cursor] = '/'
buf = append(buf[:cursor-1], buf[cursor:]...)
case 'b':
buf[cursor] = '\b'
buf = append(buf[:cursor-1], buf[cursor:]...)
case 'f':
buf[cursor] = '\f'
buf = append(buf[:cursor-1], buf[cursor:]...)
case 'n':
buf[cursor] = '\n'
buf = append(buf[:cursor-1], buf[cursor:]...)
case 'r':
buf[cursor] = '\r'
buf = append(buf[:cursor-1], buf[cursor:]...)
case 't':
buf[cursor] = '\t'
buf = append(buf[:cursor-1], buf[cursor:]...)
case 'u': case 'u':
buflen := int64(len(buf)) buflen := int64(len(buf))
if cursor+5 >= buflen { if cursor+5 >= buflen {
return nil, 0, errors.ErrUnexpectedEndOfJSON("escaped string", cursor) return nil, 0, errors.ErrUnexpectedEndOfJSON("escaped string", cursor)
} }
code := unicodeToRune(buf[cursor+1 : cursor+5]) cursor += 5
unicode := []byte(string(code))
buf = append(append(buf[:cursor-1], unicode...), buf[cursor+5:]...)
default: default:
return nil, 0, errors.ErrUnexpectedEndOfJSON("escaped string", cursor) return nil, 0, errors.ErrUnexpectedEndOfJSON("escaped string", cursor)
} }
continue continue
case '"': case '"':
literal := buf[start:cursor] literal := buf[start:cursor]
if escaped > 0 {
literal = literal[:unescapeString(literal, escaped)]
}
cursor++ cursor++
return literal, cursor, nil return literal, cursor, nil
case nul: case nul:
@ -369,3 +351,33 @@ func (d *stringDecoder) decodeByte(buf []byte, cursor int64) ([]byte, int64, err
} }
} }
} }
var unescapeMap = [256]byte{
'"': '"',
'\\': '\\',
'/': '/',
'b': '\b',
'f': '\f',
'n': '\n',
'r': '\r',
't': '\t',
}
func unescapeString(buf []byte, escaped int) int {
cursor := 0
for i := 0; i < escaped; i++ {
cursor += bytes.IndexByte(buf[cursor:], '\\')
c := buf[cursor+1]
if c == 'u' {
code := unicodeToRune(buf[cursor+2 : cursor+6])
unicode := []byte(string(code))
buf = append(append(buf[:cursor], unicode...), buf[cursor+6:]...)
cursor += len(unicode)
} else {
buf[cursor+1] = unescapeMap[c]
buf = append(buf[:cursor], buf[cursor+1:]...)
cursor++
}
}
return len(buf)
}