Browse Source

int lazy any

Tao Wen 8 years ago
parent
commit
ba410b045b
7 changed files with 248 additions and 0 deletions
  1. 32 0
      feature_adapter.go
  2. 57 0
      feature_any.go
  3. 39 0
      feature_any_float.go
  4. 66 0
      feature_any_int.go
  5. 36 0
      feature_any_nil.go
  6. 11 0
      jsoniter_int_test.go
  7. 7 0
      jsoniter_null_test.go

+ 32 - 0
feature_adapter.go

@@ -13,9 +13,24 @@ func Unmarshal(data []byte, v interface{}) error {
 	if iter.Error == io.EOF {
 		return nil
 	}
+	if iter.Error == nil {
+		iter.reportError("UnmarshalAny", "there are bytes left after unmarshal")
+	}
 	return iter.Error
 }
 
+func UnmarshalAny(data []byte) (Any, error) {
+	iter := ParseBytes(data)
+	any := iter.ReadAny()
+	if iter.Error == io.EOF {
+		return any, nil
+	}
+	if iter.Error == nil {
+		iter.reportError("UnmarshalAny", "there are bytes left after unmarshal")
+	}
+	return any, iter.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))
@@ -24,9 +39,26 @@ func UnmarshalFromString(str string, v interface{}) error {
 	if iter.Error == io.EOF {
 		return nil
 	}
+	if iter.Error == nil {
+		iter.reportError("UnmarshalFromString", "there are bytes left after unmarshal")
+	}
 	return iter.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))
+	iter := ParseBytes(data)
+	any := iter.ReadAny()
+	if iter.Error == io.EOF {
+		return any, nil
+	}
+	if iter.Error == nil {
+		iter.reportError("UnmarshalAnyFromString", "there are bytes left after unmarshal")
+	}
+	return nil, iter.Error
+}
+
 func Marshal(v interface{}) ([]byte, error) {
 	buf := &bytes.Buffer{}
 	stream := NewStream(buf, 4096)

+ 57 - 0
feature_any.go

@@ -0,0 +1,57 @@
+package jsoniter
+
+import "fmt"
+
+type Any interface {
+	LastError() error
+	ToBool() bool
+	ToInt() int
+	ToInt32() int32
+	ToInt64() int64
+	ToFloat32() float32
+	ToFloat64() float64
+	ToString() string
+}
+
+func (iter *Iterator) ReadAny() Any {
+	valueType := iter.WhatIsNext()
+	switch valueType {
+	case Nil:
+		iter.skipFixedBytes(4)
+		return &nilAny{}
+	case Number:
+		dotFound, lazyBuf := iter.skipNumber()
+		if dotFound {
+			return &floatLazyAny{lazyBuf, nil, nil}
+		} else {
+			return &intLazyAny{lazyBuf, nil, nil, 0}
+		}
+	}
+	iter.reportError("ReadAny", fmt.Sprintf("unexpected value type: %v", valueType))
+	return nil
+}
+
+func (iter *Iterator) skipNumber() (bool, []byte) {
+	dotFound := false
+	var lazyBuf []byte
+	for {
+		for i := iter.head; i < iter.tail; i++ {
+			c := iter.buf[i]
+			if c == '.' {
+				dotFound = true
+				continue
+			}
+			switch c {
+			case ' ', '\n', '\r', '\t', ',', '}', ']':
+				lazyBuf = append(lazyBuf, iter.buf[iter.head:i]...)
+				iter.head = i
+				return dotFound, lazyBuf
+			}
+		}
+		lazyBuf = append(lazyBuf, iter.buf[iter.head:iter.tail]...)
+		if !iter.loadMore() {
+			iter.head = iter.tail;
+			return dotFound, lazyBuf
+		}
+	}
+}

+ 39 - 0
feature_any_float.go

@@ -0,0 +1,39 @@
+package jsoniter
+
+type floatLazyAny struct {
+	buf []byte
+	iter *Iterator
+	err error
+}
+
+func (any *floatLazyAny) LastError() error {
+	return any.err
+}
+
+func (any *floatLazyAny) ToBool() bool {
+	return false
+}
+
+func (any *floatLazyAny) ToInt() int {
+	return 0
+}
+
+func (any *floatLazyAny) ToInt32() int32 {
+	return 0
+}
+
+func (any *floatLazyAny) ToInt64() int64 {
+	return 0
+}
+
+func (any *floatLazyAny) ToFloat32() float32 {
+	return 0
+}
+
+func (any *floatLazyAny) ToFloat64() float64 {
+	return 0
+}
+
+func (any *floatLazyAny) ToString() string {
+	return ""
+}

+ 66 - 0
feature_any_int.go

@@ -0,0 +1,66 @@
+package jsoniter
+
+import (
+	"io"
+	"unsafe"
+)
+
+type intLazyAny struct {
+	buf   []byte
+	iter  *Iterator
+	err   error
+	cache int64
+}
+
+func (any *intLazyAny) fillCache() {
+	if any.err != nil {
+		return
+	}
+	iter := any.iter
+	if iter == nil {
+		iter = NewIterator()
+	}
+	iter.ResetBytes(any.buf)
+	any.cache = iter.ReadInt64()
+	if iter.Error != io.EOF {
+		iter.reportError("intLazyAny", "there are bytes left")
+	}
+	any.err = iter.Error
+}
+
+func (any *intLazyAny) LastError() error {
+	return any.err
+}
+
+func (any *intLazyAny) ToBool() bool {
+	return any.ToInt64() != 0
+}
+
+func (any *intLazyAny) ToInt() int {
+	any.fillCache()
+	return int(any.cache)
+}
+
+func (any *intLazyAny) ToInt32() int32 {
+	any.fillCache()
+	return int32(any.cache)
+}
+
+func (any *intLazyAny) ToInt64() int64 {
+	any.fillCache()
+	return any.cache
+}
+
+func (any *intLazyAny) ToFloat32() float32 {
+	any.fillCache()
+	return float32(any.cache)
+}
+
+func (any *intLazyAny) ToFloat64() float64 {
+	any.fillCache()
+	return float64(any.cache)
+}
+
+func (any *intLazyAny) ToString() string {
+	return *(*string)(unsafe.Pointer(&any.buf))
+}

+ 36 - 0
feature_any_nil.go

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

+ 11 - 0
jsoniter_int_test.go

@@ -8,6 +8,7 @@ import (
 	"fmt"
 	"strconv"
 	"io/ioutil"
+	"io"
 )
 
 func Test_read_uint64_invalid(t *testing.T) {
@@ -99,6 +100,16 @@ func Test_read_int64_overflow(t *testing.T) {
 	should.NotNil(iter.Error)
 }
 
+func Test_read_int64_as_any(t *testing.T) {
+	should := require.New(t)
+	any, err := UnmarshalAnyFromString("1234")
+	should.Nil(err)
+	should.Equal(1234, any.ToInt())
+	should.Equal(io.EOF, any.LastError())
+	should.Equal("1234", any.ToString())
+	should.True(any.ToBool())
+}
+
 func Test_write_uint8(t *testing.T) {
 	vals := []uint8{0, 1, 11, 111, 255}
 	for _, val := range vals {

+ 7 - 0
jsoniter_null_test.go

@@ -12,6 +12,13 @@ func Test_read_null(t *testing.T) {
 	should.True(iter.ReadNil())
 	iter = ParseString(`null`)
 	should.Nil(iter.Read())
+	iter = ParseString(`null`)
+	any, err := UnmarshalAnyFromString(`null`)
+	should.Nil(err)
+	should.Equal(0, any.ToInt())
+	should.Equal(float64(0), any.ToFloat64())
+	should.Equal("", any.ToString())
+	should.False(any.ToBool())
 }
 
 func Test_write_null(t *testing.T) {