Tao Wen 8 年 前
コミット
b9fe012eea
7 ファイル変更206 行追加18 行削除
  1. 14 5
      feature_adapter.go
  2. 39 11
      feature_any.go
  3. 36 0
      feature_any_invalid.go
  4. 98 0
      feature_any_string.go
  5. 3 1
      feature_iter.go
  6. 1 0
      feature_iter_skip.go
  7. 15 1
      jsoniter_string_test.go

+ 14 - 5
feature_adapter.go

@@ -2,7 +2,6 @@ package jsoniter
 
 import (
 	"io"
-	"unsafe"
 	"bytes"
 )
 
@@ -10,6 +9,9 @@ import (
 func Unmarshal(data []byte, v interface{}) error {
 	iter := ParseBytes(data)
 	iter.ReadVal(v)
+	if iter.head == iter.tail {
+		iter.loadMore()
+	}
 	if iter.Error == io.EOF {
 		return nil
 	}
@@ -22,6 +24,9 @@ func Unmarshal(data []byte, v interface{}) error {
 func UnmarshalAny(data []byte) (Any, error) {
 	iter := ParseBytes(data)
 	any := iter.ReadAny()
+	if iter.head == iter.tail {
+		iter.loadMore()
+	}
 	if iter.Error == io.EOF {
 		return any, nil
 	}
@@ -32,10 +37,12 @@ func UnmarshalAny(data []byte) (Any, error) {
 }
 
 func UnmarshalFromString(str string, v interface{}) error {
-	// safe to do the unsafe cast here, as str is always referenced in this scope
-	data := *(*[]byte)(unsafe.Pointer(&str))
+	data := []byte(str)
 	iter := ParseBytes(data)
 	iter.ReadVal(v)
+	if iter.head == iter.tail {
+		iter.loadMore()
+	}
 	if iter.Error == io.EOF {
 		return nil
 	}
@@ -46,10 +53,12 @@ func UnmarshalFromString(str string, v interface{}) error {
 }
 
 func UnmarshalAnyFromString(str string) (Any, error) {
-	// safe to do the unsafe cast here, as str is always referenced in this scope
-	data := *(*[]byte)(unsafe.Pointer(&str))
+	data := []byte(str)
 	iter := ParseBytes(data)
 	any := iter.ReadAny()
+	if iter.head == iter.tail {
+		iter.loadMore()
+	}
 	if iter.Error == io.EOF {
 		return any, nil
 	}

+ 39 - 11
feature_any.go

@@ -20,18 +20,15 @@ func (iter *Iterator) ReadAny() Any {
 		iter.skipFixedBytes(4)
 		return &nilAny{}
 	case Number:
-		dotFound, lazyBuf := iter.skipNumber()
-		if dotFound {
-			return &floatLazyAny{lazyBuf, nil, nil, 0}
-		} else {
-			return &intLazyAny{lazyBuf, nil, nil, 0}
-		}
+		return iter.readNumberAny()
+	case String:
+		return iter.readStringAny()
 	}
 	iter.reportError("ReadAny", fmt.Sprintf("unexpected value type: %v", valueType))
-	return nil
+	return &invalidAny{}
 }
 
-func (iter *Iterator) skipNumber() (bool, []byte) {
+func (iter *Iterator) readNumberAny() Any {
 	dotFound := false
 	var lazyBuf []byte
 	for {
@@ -45,13 +42,44 @@ func (iter *Iterator) skipNumber() (bool, []byte) {
 			case ' ', '\n', '\r', '\t', ',', '}', ']':
 				lazyBuf = append(lazyBuf, iter.buf[iter.head:i]...)
 				iter.head = i
-				return dotFound, lazyBuf
+				if dotFound {
+					return &floatLazyAny{lazyBuf, nil, nil, 0}
+				} else {
+					return &intLazyAny{lazyBuf, nil, nil, 0}
+				}
 			}
 		}
 		lazyBuf = append(lazyBuf, iter.buf[iter.head:iter.tail]...)
 		if !iter.loadMore() {
-			iter.head = iter.tail;
-			return dotFound, lazyBuf
+			iter.head = iter.tail
+			if dotFound {
+				return &floatLazyAny{lazyBuf, nil, nil, 0}
+			} else {
+				return &intLazyAny{lazyBuf, nil, nil, 0}
+			}
+		}
+	}
+}
+
+func (iter *Iterator) readStringAny() Any {
+	iter.head++
+	lazyBuf := make([]byte, 1, 8)
+	lazyBuf[0] = '"'
+	for {
+		end, escaped := iter.findStringEnd()
+		if end == -1 {
+			lazyBuf = append(lazyBuf, iter.buf[iter.head:iter.tail]...)
+			if !iter.loadMore() {
+				iter.reportError("readStringAny", "incomplete string")
+				return &invalidAny{}
+			}
+			if escaped {
+				iter.head = 1 // skip the first char as last char read is \
+			}
+		} else {
+			lazyBuf = append(lazyBuf, iter.buf[iter.head:end]...)
+			iter.head = end
+			return &stringLazyAny{lazyBuf, nil, nil, ""}
 		}
 	}
 }

+ 36 - 0
feature_any_invalid.go

@@ -0,0 +1,36 @@
+package jsoniter
+
+type invalidAny struct {
+}
+
+func (any *invalidAny) LastError() error {
+	return nil
+}
+
+func (any *invalidAny) ToBool() bool {
+	return false
+}
+
+func (any *invalidAny) ToInt() int {
+	return 0
+}
+
+func (any *invalidAny) ToInt32() int32 {
+	return 0
+}
+
+func (any *invalidAny) ToInt64() int64 {
+	return 0
+}
+
+func (any *invalidAny) ToFloat32() float32 {
+	return 0
+}
+
+func (any *invalidAny) ToFloat64() float64 {
+	return 0
+}
+
+func (any *invalidAny) ToString() string {
+	return ""
+}

+ 98 - 0
feature_any_string.go

@@ -0,0 +1,98 @@
+package jsoniter
+
+import (
+	"io"
+)
+
+type stringLazyAny struct{
+	buf   []byte
+	iter  *Iterator
+	err   error
+	cache string
+}
+
+func (any *stringLazyAny) fillCache() {
+	if any.err != nil {
+		return
+	}
+	iter := any.parse()
+	any.cache = iter.ReadString()
+	if iter.Error != io.EOF {
+		iter.reportError("stringLazyAny", "there are bytes left")
+	}
+	any.err = iter.Error
+}
+
+func (any *stringLazyAny) parse() *Iterator {
+	iter := any.iter
+	if iter == nil {
+		iter = NewIterator()
+		any.iter = iter
+	}
+	iter.ResetBytes(any.buf)
+	return iter
+}
+
+func (any *stringLazyAny) LastError() error {
+	return any.err
+}
+
+func (any *stringLazyAny) ToBool() bool {
+	str := any.ToString()
+	if str == "false" {
+		return false
+	}
+	for _, c := range str {
+		switch c {
+		case ' ', '\n', '\r', '\t':
+		default:
+			return true
+		}
+	}
+	return false
+}
+
+func (any *stringLazyAny) ToInt() int {
+	iter := any.parse()
+	iter.head++
+	val := iter.ReadInt()
+	any.err = iter.Error
+	return val
+}
+
+func (any *stringLazyAny) ToInt32() int32 {
+	iter := any.parse()
+	iter.head++
+	val := iter.ReadInt32()
+	any.err = iter.Error
+	return val
+}
+
+func (any *stringLazyAny) ToInt64() int64 {
+	iter := any.parse()
+	iter.head++
+	val := iter.ReadInt64()
+	any.err = iter.Error
+	return val
+}
+
+func (any *stringLazyAny) ToFloat32() float32 {
+	iter := any.parse()
+	iter.head++
+	val := iter.ReadFloat32()
+	any.err = iter.Error
+	return val
+}
+
+func (any *stringLazyAny) ToFloat64() float64 {
+	iter := any.parse()
+	iter.head++
+	val := iter.ReadFloat64()
+	any.err = iter.Error
+	return val
+}
+
+func (any *stringLazyAny) ToString() string {
+	any.fillCache()
+	return any.cache
+}

+ 3 - 1
feature_iter.go

@@ -160,7 +160,9 @@ func (iter *Iterator) nextToken() byte {
 
 func (iter *Iterator) reportError(operation string, msg string) {
 	if iter.Error != nil {
-		return
+		if iter.Error != io.EOF {
+			return
+		}
 	}
 	peekStart := iter.head - 10
 	if peekStart < 0 {

+ 1 - 0
feature_iter_skip.go

@@ -57,6 +57,7 @@ func (iter *Iterator) skipString() {
 		end, escaped := iter.findStringEnd()
 		if end == -1 {
 			if !iter.loadMore() {
+				iter.reportError("skipString", "incomplete string")
 				return
 			}
 			if escaped {

+ 15 - 1
jsoniter_string_test.go

@@ -59,12 +59,26 @@ func Test_read_exotic_string(t *testing.T) {
 	}
 }
 
-func Test_read_string_via_read(t *testing.T) {
+func Test_read_string_as_interface(t *testing.T) {
 	should := require.New(t)
 	iter := ParseString(`"hello"`)
 	should.Equal("hello", iter.Read())
 }
 
+func Test_read_string_as_any(t *testing.T) {
+	should := require.New(t)
+	any, err := UnmarshalAnyFromString(`"hello"`)
+	should.Nil(err)
+	should.Equal("hello", any.ToString())
+	should.True(any.ToBool())
+	any, err = UnmarshalAnyFromString(`" "`)
+	should.False(any.ToBool())
+	any, err = UnmarshalAnyFromString(`"false"`)
+	should.False(any.ToBool())
+	any, err = UnmarshalAnyFromString(`"123"`)
+	should.Equal(123, any.ToInt())
+}
+
 func Test_write_string(t *testing.T) {
 	should := require.New(t)
 	str, err := MarshalToString("hello")