Tao Wen 8 лет назад
Родитель
Сommit
8656482625
5 измененных файлов с 203 добавлено и 16 удалено
  1. 44 8
      feature_any.go
  2. 127 0
      feature_any_array.go
  3. 1 3
      feature_any_object.go
  4. 6 2
      feature_iter.go
  5. 25 3
      jsoniter_array_test.go

+ 44 - 8
feature_any.go

@@ -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 - 0
feature_any_array.go

@@ -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)
+}

+ 1 - 3
feature_any_object.go

@@ -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 {

+ 6 - 2
feature_iter.go

@@ -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 {

+ 25 - 3
jsoniter_array_test.go

@@ -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()
 	}
 }