Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use ReadStringAsSlice instead of ReadString #697

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
5 changes: 3 additions & 2 deletions any.go
Expand Up @@ -3,11 +3,12 @@ package jsoniter
import (
"errors"
"fmt"
"github.com/modern-go/reflect2"
"io"
"reflect"
"strconv"
"unsafe"

"github.com/modern-go/reflect2"
)

// Any generic object representation.
Expand Down Expand Up @@ -155,7 +156,7 @@ func (iter *Iterator) readAny() Any {
switch c {
case '"':
iter.unreadByte()
return &stringAny{baseAny{}, iter.ReadString()}
return &stringAny{baseAny{}, string(iter.ReadStringAsSlice())}
case 'n':
iter.skipThreeBytes('u', 'l', 'l') // null
return &nilAny{}
Expand Down
19 changes: 8 additions & 11 deletions extra/binary_as_string_codec.go
@@ -1,10 +1,12 @@
package extra

import (
"github.com/json-iterator/go"
"github.com/modern-go/reflect2"
"unicode/utf8"
"unsafe"

"github.com/modern-go/reflect2"

"github.com/json-iterator/go"
)

// safeSet holds the value true if the ASCII character with the given array
Expand Down Expand Up @@ -142,19 +144,14 @@ func (codec *binaryAsStringCodec) Decode(ptr unsafe.Pointer, iter *jsoniter.Iter
b := rawBytes[i]
if b == '\\' {
b2 := rawBytes[i+1]
if b2 != '\\' {
iter.ReportError("decode binary as string", `\\x is only supported escape`)
if b2 != 'x' {
iter.ReportError("decode binary as string", `\x is only supported escape`)
return
}
b3 := rawBytes[i+2]
if b3 != 'x' {
iter.ReportError("decode binary as string", `\\x is only supported escape`)
return
}
b4 := rawBytes[i+3]
b5 := rawBytes[i+4]
i += 4
b = readHex(iter, b4, b5)
i += 3
b = readHex(iter, b3, b4)
}
bytes = append(bytes, b)
}
Expand Down
19 changes: 9 additions & 10 deletions iter_object.go
Expand Up @@ -59,7 +59,7 @@ func (iter *Iterator) readFieldHash() int64 {
b := iter.buf[i]
if b == '\\' {
iter.head = i
for _, b := range iter.readStringSlowPath() {
for _, b := range iter.readStringSlowPathAsSlice() {
if 'A' <= b && b <= 'Z' && !iter.cfg.caseSensitive {
b += 'a' - 'A'
}
Expand Down Expand Up @@ -110,31 +110,30 @@ func calcHash(str string, caseSensitive bool) int64 {
// ReadObjectCB read object with callback, the key is ascii only and field name not copied
func (iter *Iterator) ReadObjectCB(callback func(*Iterator, string) bool) bool {
c := iter.nextToken()
var field string
if c == '{' {
if !iter.incrementDepth() {
return false
}
c = iter.nextToken()
if c == '"' {
iter.unreadByte()
field = iter.ReadString()
field := iter.ReadStringAsSlice()
c = iter.nextToken()
if c != ':' {
iter.ReportError("ReadObject", "expect : after object field, but found "+string([]byte{c}))
}
if !callback(iter, field) {
if !callback(iter, string(field)) {
iter.decrementDepth()
return false
}
c = iter.nextToken()
for c == ',' {
field = iter.ReadString()
field = iter.ReadStringAsSlice()
c = iter.nextToken()
if c != ':' {
iter.ReportError("ReadObject", "expect : after object field, but found "+string([]byte{c}))
}
if !callback(iter, field) {
if !callback(iter, string(field)) {
iter.decrementDepth()
return false
}
Expand Down Expand Up @@ -172,25 +171,25 @@ func (iter *Iterator) ReadMapCB(callback func(*Iterator, string) bool) bool {
c = iter.nextToken()
if c == '"' {
iter.unreadByte()
field := iter.ReadString()
field := iter.ReadStringAsSlice()
if iter.nextToken() != ':' {
iter.ReportError("ReadMapCB", "expect : after object field, but found "+string([]byte{c}))
iter.decrementDepth()
return false
}
if !callback(iter, field) {
if !callback(iter, string(field)) {
iter.decrementDepth()
return false
}
c = iter.nextToken()
for c == ',' {
field = iter.ReadString()
field = iter.ReadStringAsSlice()
if iter.nextToken() != ':' {
iter.ReportError("ReadMapCB", "expect : after object field, but found "+string([]byte{c}))
iter.decrementDepth()
return false
}
if !callback(iter, field) {
if !callback(iter, string(field)) {
iter.decrementDepth()
return false
}
Expand Down
5 changes: 3 additions & 2 deletions iter_skip_strict.go
@@ -1,4 +1,5 @@
//+build !jsoniter_sloppy
//go:build !jsoniter_sloppy
// +build !jsoniter_sloppy

package jsoniter

Expand Down Expand Up @@ -61,7 +62,7 @@ func (iter *Iterator) trySkipNumber() bool {
func (iter *Iterator) skipString() {
if !iter.trySkipString() {
iter.unreadByte()
iter.ReadString()
iter.ReadStringAsSlice()
}
}

Expand Down
56 changes: 15 additions & 41 deletions iter_str.go
Expand Up @@ -7,38 +7,44 @@ import (

// ReadString read string from iterator
func (iter *Iterator) ReadString() (ret string) {
return string(iter.ReadStringAsSlice())
}

// ReadStringAsSlice read string from iterator without copying into string form.
// The []byte can not be kept, as it will change after next iterator call.
func (iter *Iterator) ReadStringAsSlice() (ret []byte) {
c := iter.nextToken()
if c == '"' {
for i := iter.head; i < iter.tail; i++ {
c := iter.buf[i]
if c == '"' {
ret = string(iter.buf[iter.head:i])
head := iter.head
iter.head = i + 1
return ret
return iter.buf[head:i]
} else if c == '\\' {
break
} else if c < ' ' {
iter.ReportError("ReadString",
iter.ReportError("ReadStringAsSlice",
fmt.Sprintf(`invalid control character found: %d`, c))
return
}
}
return iter.readStringSlowPath()
return iter.readStringSlowPathAsSlice()
} else if c == 'n' {
iter.skipThreeBytes('u', 'l', 'l')
return ""
return
}
iter.ReportError("ReadString", `expects " or n, but found `+string([]byte{c}))
iter.ReportError("ReadStringAsSlice", `expects " or n, but found `+string([]byte{c}))
return
}

func (iter *Iterator) readStringSlowPath() (ret string) {
func (iter *Iterator) readStringSlowPathAsSlice() (ret []byte) {
var str []byte
var c byte
for iter.Error == nil {
c = iter.readByte()
if c == '"' {
return string(str)
return str
}
if c == '\\' {
c = iter.readByte()
Expand All @@ -47,7 +53,7 @@ func (iter *Iterator) readStringSlowPath() (ret string) {
str = append(str, c)
}
}
iter.ReportError("readStringSlowPath", "unexpected end of input")
iter.ReportError("readStringSlowPathAsSlice", "unexpected end of input")
return
}

Expand Down Expand Up @@ -111,38 +117,6 @@ func (iter *Iterator) readEscapedChar(c byte, str []byte) []byte {
return str
}

// ReadStringAsSlice read string from iterator without copying into string form.
// The []byte can not be kept, as it will change after next iterator call.
func (iter *Iterator) ReadStringAsSlice() (ret []byte) {
c := iter.nextToken()
if c == '"' {
for i := iter.head; i < iter.tail; i++ {
// require ascii string and no escape
// for: field name, base64, number
if iter.buf[i] == '"' {
// fast path: reuse the underlying buffer
ret = iter.buf[iter.head:i]
iter.head = i + 1
return ret
}
}
readLen := iter.tail - iter.head
copied := make([]byte, readLen, readLen*2)
copy(copied, iter.buf[iter.head:iter.tail])
iter.head = iter.tail
for iter.Error == nil {
c := iter.readByte()
if c == '"' {
return copied
}
copied = append(copied, c)
}
return copied
}
iter.ReportError("ReadStringAsSlice", `expects " or n, but found `+string([]byte{c}))
return
}

func (iter *Iterator) readU4() (ret rune) {
for i := 0; i < 4; i++ {
c := iter.readByte()
Expand Down
7 changes: 4 additions & 3 deletions misc_tests/jsoniter_object_test.go
Expand Up @@ -3,12 +3,13 @@ package misc_tests
import (
"bytes"
"reflect"
"strings"
"testing"
"time"

"github.com/json-iterator/go"
"github.com/stretchr/testify/require"
"strings"
"time"

"github.com/json-iterator/go"
)

func Test_empty_object(t *testing.T) {
Expand Down
11 changes: 7 additions & 4 deletions reflect_json_number.go
Expand Up @@ -2,9 +2,10 @@ package jsoniter

import (
"encoding/json"
"github.com/modern-go/reflect2"
"strconv"
"unsafe"

"github.com/modern-go/reflect2"
)

type Number string
Expand Down Expand Up @@ -61,7 +62,7 @@ type jsonNumberCodec struct {
func (codec *jsonNumberCodec) Decode(ptr unsafe.Pointer, iter *Iterator) {
switch iter.WhatIsNext() {
case StringValue:
*((*json.Number)(ptr)) = json.Number(iter.ReadString())
*((*json.Number)(ptr)) = json.Number(iter.ReadStringAsSlice())
case NilValue:
iter.skipFourBytes('n', 'u', 'l', 'l')
*((*json.Number)(ptr)) = ""
Expand Down Expand Up @@ -89,12 +90,14 @@ type jsoniterNumberCodec struct {
func (codec *jsoniterNumberCodec) Decode(ptr unsafe.Pointer, iter *Iterator) {
switch iter.WhatIsNext() {
case StringValue:
*((*Number)(ptr)) = Number(iter.ReadString())
num := iter.ReadStringAsSlice()
*((*Number)(ptr)) = Number(string(num))
case NilValue:
iter.skipFourBytes('n', 'u', 'l', 'l')
*((*Number)(ptr)) = ""
default:
*((*Number)(ptr)) = Number([]byte(iter.readNumberAsString()))
num := iter.ReadStringAsSlice()
*((*Number)(ptr)) = Number(string(num))
}
}

Expand Down
2 changes: 1 addition & 1 deletion reflect_map.go
Expand Up @@ -305,7 +305,7 @@ func (encoder *sortKeysMapEncoder) Encode(ptr unsafe.Pointer, stream *Stream) {
}
encodedKey := subStream.Buffer()[subStreamIndex:]
subIter.ResetBytes(encodedKey)
decodedKey := subIter.ReadString()
decodedKey := string(subIter.ReadStringAsSlice())
if stream.indention > 0 {
subStream.writeTwoBytes(byte(':'), byte(' '))
} else {
Expand Down
4 changes: 2 additions & 2 deletions reflect_marshaler.go
Expand Up @@ -217,8 +217,8 @@ func (decoder *textUnmarshalerDecoder) Decode(ptr unsafe.Pointer, iter *Iterator
obj = valType.UnsafeIndirect(ptr)
}
unmarshaler := (obj).(encoding.TextUnmarshaler)
str := iter.ReadString()
err := unmarshaler.UnmarshalText([]byte(str))
str := iter.ReadStringAsSlice()
err := unmarshaler.UnmarshalText(str)
if err != nil {
iter.ReportError("textUnmarshalerDecoder", err.Error())
}
Expand Down
7 changes: 4 additions & 3 deletions reflect_native.go
Expand Up @@ -206,7 +206,8 @@ type stringCodec struct {
}

func (codec *stringCodec) Decode(ptr unsafe.Pointer, iter *Iterator) {
*((*string)(ptr)) = iter.ReadString()
data := iter.ReadStringAsSlice()
*((*string)(ptr)) = string(data)
}

func (codec *stringCodec) Encode(ptr unsafe.Pointer, stream *Stream) {
Expand Down Expand Up @@ -417,8 +418,8 @@ func (codec *base64Codec) Decode(ptr unsafe.Pointer, iter *Iterator) {
}
switch iter.WhatIsNext() {
case StringValue:
src := iter.ReadString()
dst, err := base64.StdEncoding.DecodeString(src)
src := iter.ReadStringAsSlice()
dst, err := base64.StdEncoding.DecodeString(string(src))
if err != nil {
iter.ReportError("decode base64", err.Error())
} else {
Expand Down
22 changes: 6 additions & 16 deletions reflect_struct_decoder.go
Expand Up @@ -517,21 +517,10 @@ func (decoder *generalStructDecoder) Decode(ptr unsafe.Pointer, iter *Iterator)
}

func (decoder *generalStructDecoder) decodeOneField(ptr unsafe.Pointer, iter *Iterator) {
var field string
var fieldDecoder *structFieldDecoder
if iter.cfg.objectFieldMustBeSimpleString {
fieldBytes := iter.ReadStringAsSlice()
field = *(*string)(unsafe.Pointer(&fieldBytes))
fieldDecoder = decoder.fields[field]
if fieldDecoder == nil && !iter.cfg.caseSensitive {
fieldDecoder = decoder.fields[strings.ToLower(field)]
}
} else {
field = iter.ReadString()
fieldDecoder = decoder.fields[field]
if fieldDecoder == nil && !iter.cfg.caseSensitive {
fieldDecoder = decoder.fields[strings.ToLower(field)]
}
field := string(iter.ReadStringAsSlice())
fieldDecoder := decoder.fields[field]
if fieldDecoder == nil && !iter.cfg.caseSensitive {
fieldDecoder = decoder.fields[strings.ToLower(field)]
}
if fieldDecoder == nil {
if decoder.disallowUnknownFields {
Expand Down Expand Up @@ -1067,7 +1056,8 @@ func (decoder *stringModeStringDecoder) Decode(ptr unsafe.Pointer, iter *Iterato
str := *((*string)(ptr))
tempIter := decoder.cfg.BorrowIterator([]byte(str))
defer decoder.cfg.ReturnIterator(tempIter)
*((*string)(ptr)) = tempIter.ReadString()
data := tempIter.ReadStringAsSlice()
*((*string)(ptr)) = string(data)
}

type stringModeNumberDecoder struct {
Expand Down