mirror of
https://github.com/json-iterator/go.git
synced 2025-03-23 21:09:11 +02:00
array lazy fill and full fill
This commit is contained in:
parent
2d647f04ca
commit
8656482625
@ -1,7 +1,5 @@
|
||||
package jsoniter
|
||||
|
||||
import "fmt"
|
||||
|
||||
type Any interface {
|
||||
LastError() error
|
||||
ToBool() bool
|
||||
@ -12,12 +10,17 @@ type Any interface {
|
||||
ToFloat64() float64
|
||||
ToString() string
|
||||
Get(path ...interface{}) Any
|
||||
Size() int
|
||||
Keys() []string
|
||||
IterateObject() (func() (string, Any, bool), bool)
|
||||
}
|
||||
|
||||
type baseAny struct {}
|
||||
|
||||
func (any *baseAny) Size() int {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (any *baseAny) Keys() []string {
|
||||
return []string{}
|
||||
}
|
||||
@ -38,9 +41,6 @@ func (iter *Iterator) readAny(reusableIter *Iterator) Any {
|
||||
case 'n':
|
||||
iter.skipFixedBytes(3) // null
|
||||
return &nilAny{}
|
||||
case '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
|
||||
iter.unreadByte()
|
||||
return iter.readNumberAny(reusableIter)
|
||||
case 't':
|
||||
iter.skipFixedBytes(3) // true
|
||||
return &trueAny{}
|
||||
@ -49,9 +49,12 @@ func (iter *Iterator) readAny(reusableIter *Iterator) Any {
|
||||
return &falseAny{}
|
||||
case '{':
|
||||
return iter.readObjectAny(reusableIter)
|
||||
case '[':
|
||||
return iter.readArrayAny(reusableIter)
|
||||
default:
|
||||
iter.unreadByte()
|
||||
return iter.readNumberAny(reusableIter)
|
||||
}
|
||||
iter.reportError("ReadAny", fmt.Sprintf("unexpected character: %v", c))
|
||||
return &invalidAny{}
|
||||
}
|
||||
|
||||
func (iter *Iterator) readNumberAny(reusableIter *Iterator) Any {
|
||||
@ -130,7 +133,7 @@ func (iter *Iterator) readObjectAny(reusableIter *Iterator) Any {
|
||||
if level == 0 {
|
||||
iter.head = i + 1
|
||||
lazyBuf = append(lazyBuf, iter.buf[start:iter.head]...)
|
||||
return &objectLazyAny{lazyBuf, reusableIter, nil, nil, lazyBuf}
|
||||
return &objectLazyAny{baseAny{}, lazyBuf, reusableIter, nil, nil, lazyBuf}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -141,3 +144,36 @@ func (iter *Iterator) readObjectAny(reusableIter *Iterator) Any {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (iter *Iterator) readArrayAny(reusableIter *Iterator) Any {
|
||||
level := 1
|
||||
lazyBuf := make([]byte, 1, 32)
|
||||
lazyBuf[0] = '{'
|
||||
for {
|
||||
start := iter.head
|
||||
for i := iter.head; i < iter.tail; i++ {
|
||||
switch iter.buf[i] {
|
||||
case '"': // If inside string, skip it
|
||||
iter.head = i + 1
|
||||
iter.skipString()
|
||||
i = iter.head - 1 // it will be i++ soon
|
||||
case '[': // If open symbol, increase level
|
||||
level++
|
||||
case ']': // If close symbol, increase level
|
||||
level--
|
||||
|
||||
// If we have returned to the original level, we're done
|
||||
if level == 0 {
|
||||
iter.head = i + 1
|
||||
lazyBuf = append(lazyBuf, iter.buf[start:iter.head]...)
|
||||
return &arrayLazyAny{baseAny{},lazyBuf, reusableIter, nil, nil, lazyBuf}
|
||||
}
|
||||
}
|
||||
}
|
||||
lazyBuf = append(lazyBuf, iter.buf[iter.head:iter.tail]...)
|
||||
if !iter.loadMore() {
|
||||
iter.reportError("skipArray", "incomplete array")
|
||||
return &invalidAny{}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
127
feature_any_array.go
Normal file
127
feature_any_array.go
Normal file
@ -0,0 +1,127 @@
|
||||
package jsoniter
|
||||
|
||||
type arrayLazyAny struct {
|
||||
baseAny
|
||||
buf []byte
|
||||
iter *Iterator
|
||||
err error
|
||||
cache []Any
|
||||
remaining []byte
|
||||
}
|
||||
|
||||
func (any *arrayLazyAny) parse() *Iterator {
|
||||
iter := any.iter
|
||||
if iter == nil {
|
||||
iter = NewIterator()
|
||||
any.iter = iter
|
||||
}
|
||||
iter.ResetBytes(any.remaining)
|
||||
return iter
|
||||
}
|
||||
|
||||
func (any *arrayLazyAny) fillCacheUntil(target int) Any {
|
||||
if any.remaining == nil {
|
||||
if target >= len(any.cache) {
|
||||
return nil
|
||||
}
|
||||
return any.cache[target]
|
||||
}
|
||||
i := len(any.cache)
|
||||
if target < i {
|
||||
return any.cache[target]
|
||||
}
|
||||
iter := any.parse()
|
||||
if (len(any.remaining) == len(any.buf)) {
|
||||
iter.head++
|
||||
c := iter.nextToken()
|
||||
if c != ']' {
|
||||
iter.unreadByte()
|
||||
element := iter.readAny(iter)
|
||||
any.cache = append(any.cache, element)
|
||||
if target == 0 {
|
||||
any.remaining = iter.buf[iter.head:]
|
||||
return element
|
||||
}
|
||||
i = 1
|
||||
} else {
|
||||
any.remaining = nil
|
||||
return nil
|
||||
}
|
||||
}
|
||||
for iter.nextToken() == ',' {
|
||||
element := iter.readAny(iter)
|
||||
any.cache = append(any.cache, element)
|
||||
if i == target {
|
||||
any.remaining = iter.buf[iter.head:]
|
||||
return element
|
||||
}
|
||||
i++
|
||||
}
|
||||
any.remaining = nil
|
||||
return nil
|
||||
}
|
||||
|
||||
func (any *arrayLazyAny) fillCache() {
|
||||
if any.remaining == nil {
|
||||
return
|
||||
}
|
||||
iter := any.parse()
|
||||
if len(any.remaining) == len(any.buf) {
|
||||
iter.head++
|
||||
c := iter.nextToken()
|
||||
if c != ']' {
|
||||
iter.unreadByte()
|
||||
any.cache = append(any.cache, iter.readAny(iter))
|
||||
} else {
|
||||
any.remaining = nil
|
||||
return
|
||||
}
|
||||
}
|
||||
for iter.nextToken() == ',' {
|
||||
any.cache = append(any.cache, iter.readAny(iter))
|
||||
}
|
||||
any.remaining = nil
|
||||
return
|
||||
}
|
||||
|
||||
func (any *arrayLazyAny) LastError() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (any *arrayLazyAny) ToBool() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (any *arrayLazyAny) ToInt() int {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (any *arrayLazyAny) ToInt32() int32 {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (any *arrayLazyAny) ToInt64() int64 {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (any *arrayLazyAny) ToFloat32() float32 {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (any *arrayLazyAny) ToFloat64() float64 {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (any *arrayLazyAny) ToString() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (any *arrayLazyAny) Get(path ...interface{}) Any {
|
||||
idx := path[0].(int)
|
||||
return any.fillCacheUntil(idx)
|
||||
}
|
||||
|
||||
func (any *arrayLazyAny) Size() int {
|
||||
any.fillCache()
|
||||
return len(any.cache)
|
||||
}
|
@ -5,6 +5,7 @@ import (
|
||||
)
|
||||
|
||||
type objectLazyAny struct {
|
||||
baseAny
|
||||
buf []byte
|
||||
iter *Iterator
|
||||
err error
|
||||
@ -189,9 +190,6 @@ func (any *objectLazyAny) IterateObject() (func() (string, Any, bool), bool) {
|
||||
i++
|
||||
return key, value, true
|
||||
} else {
|
||||
if remaining == nil {
|
||||
return "", nil, false
|
||||
}
|
||||
// read from buffer
|
||||
iter := any.iter
|
||||
if iter == nil {
|
||||
|
@ -198,14 +198,18 @@ func (iter *Iterator) readByte() (ret byte) {
|
||||
|
||||
func (iter *Iterator) loadMore() bool {
|
||||
if iter.reader == nil {
|
||||
iter.Error = io.EOF
|
||||
if iter.Error == nil {
|
||||
iter.Error = io.EOF
|
||||
}
|
||||
return false
|
||||
}
|
||||
for {
|
||||
n, err := iter.reader.Read(iter.buf)
|
||||
if n == 0 {
|
||||
if err != nil {
|
||||
iter.Error = err
|
||||
if iter.Error == nil {
|
||||
iter.Error = err
|
||||
}
|
||||
return false
|
||||
}
|
||||
} else {
|
||||
|
@ -5,6 +5,7 @@ import (
|
||||
"testing"
|
||||
"github.com/json-iterator/go/require"
|
||||
"bytes"
|
||||
"io"
|
||||
)
|
||||
|
||||
func Test_empty_array(t *testing.T) {
|
||||
@ -44,10 +45,31 @@ func Test_two_elements(t *testing.T) {
|
||||
should.Equal([]interface{}{float64(1), float64(2)}, iter.Read())
|
||||
}
|
||||
|
||||
func Test_read_empty_array_as_any(t *testing.T) {
|
||||
should := require.New(t)
|
||||
any, err := UnmarshalAnyFromString("[]")
|
||||
should.Nil(err)
|
||||
should.Equal(0, any.Size())
|
||||
}
|
||||
|
||||
func Test_read_one_element_array_as_any(t *testing.T) {
|
||||
should := require.New(t)
|
||||
any, err := UnmarshalAnyFromString("[1]")
|
||||
should.Nil(err)
|
||||
should.Equal(1, any.Size())
|
||||
}
|
||||
|
||||
func Test_read_two_element_array_as_any(t *testing.T) {
|
||||
should := require.New(t)
|
||||
any, err := UnmarshalAnyFromString("[1,2]")
|
||||
should.Nil(err)
|
||||
should.Equal(1, any.Get(0).ToInt())
|
||||
should.Equal(2, any.Size())
|
||||
}
|
||||
|
||||
func Test_invalid_array(t *testing.T) {
|
||||
iter := ParseString(`[`)
|
||||
iter.ReadArray()
|
||||
if iter.Error == nil {
|
||||
_, err := UnmarshalAnyFromString("[")
|
||||
if err == nil || err == io.EOF {
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user