mirror of
https://github.com/json-iterator/go.git
synced 2025-05-13 21:36:29 +02:00
document public symbols
This commit is contained in:
parent
46574e7d09
commit
f0487718f6
@ -160,8 +160,8 @@ type tolerateEmptyArrayDecoder struct {
|
|||||||
func (decoder *tolerateEmptyArrayDecoder) Decode(ptr unsafe.Pointer, iter *jsoniter.Iterator) {
|
func (decoder *tolerateEmptyArrayDecoder) Decode(ptr unsafe.Pointer, iter *jsoniter.Iterator) {
|
||||||
if iter.WhatIsNext() == jsoniter.Array {
|
if iter.WhatIsNext() == jsoniter.Array {
|
||||||
iter.Skip()
|
iter.Skip()
|
||||||
newIter := iter.Config().BorrowIterator([]byte("{}"))
|
newIter := iter.Pool().BorrowIterator([]byte("{}"))
|
||||||
defer iter.Config().ReturnIterator(newIter)
|
defer iter.Pool().ReturnIterator(newIter)
|
||||||
decoder.valDecoder.Decode(ptr, newIter)
|
decoder.valDecoder.Decode(ptr, newIter)
|
||||||
} else {
|
} else {
|
||||||
decoder.valDecoder.Decode(ptr, iter)
|
decoder.valDecoder.Decode(ptr, iter)
|
||||||
@ -202,8 +202,8 @@ func (decoder *fuzzyIntegerDecoder) Decode(ptr unsafe.Pointer, iter *jsoniter.It
|
|||||||
default:
|
default:
|
||||||
iter.ReportError("fuzzyIntegerDecoder", "not number or string")
|
iter.ReportError("fuzzyIntegerDecoder", "not number or string")
|
||||||
}
|
}
|
||||||
newIter := iter.Config().BorrowIterator([]byte(str))
|
newIter := iter.Pool().BorrowIterator([]byte(str))
|
||||||
defer iter.Config().ReturnIterator(newIter)
|
defer iter.Pool().ReturnIterator(newIter)
|
||||||
isFloat := strings.IndexByte(str, '.') != -1
|
isFloat := strings.IndexByte(str, '.') != -1
|
||||||
decoder.fun(isFloat, ptr, newIter)
|
decoder.fun(isFloat, ptr, newIter)
|
||||||
if newIter.Error != nil {
|
if newIter.Error != nil {
|
||||||
@ -222,8 +222,8 @@ func (decoder *fuzzyFloat32Decoder) Decode(ptr unsafe.Pointer, iter *jsoniter.It
|
|||||||
*((*float32)(ptr)) = iter.ReadFloat32()
|
*((*float32)(ptr)) = iter.ReadFloat32()
|
||||||
case jsoniter.String:
|
case jsoniter.String:
|
||||||
str = iter.ReadString()
|
str = iter.ReadString()
|
||||||
newIter := iter.Config().BorrowIterator([]byte(str))
|
newIter := iter.Pool().BorrowIterator([]byte(str))
|
||||||
defer iter.Config().ReturnIterator(newIter)
|
defer iter.Pool().ReturnIterator(newIter)
|
||||||
*((*float32)(ptr)) = newIter.ReadFloat32()
|
*((*float32)(ptr)) = newIter.ReadFloat32()
|
||||||
if newIter.Error != nil {
|
if newIter.Error != nil {
|
||||||
iter.Error = newIter.Error
|
iter.Error = newIter.Error
|
||||||
@ -244,8 +244,8 @@ func (decoder *fuzzyFloat64Decoder) Decode(ptr unsafe.Pointer, iter *jsoniter.It
|
|||||||
*((*float64)(ptr)) = iter.ReadFloat64()
|
*((*float64)(ptr)) = iter.ReadFloat64()
|
||||||
case jsoniter.String:
|
case jsoniter.String:
|
||||||
str = iter.ReadString()
|
str = iter.ReadString()
|
||||||
newIter := iter.Config().BorrowIterator([]byte(str))
|
newIter := iter.Pool().BorrowIterator([]byte(str))
|
||||||
defer iter.Config().ReturnIterator(newIter)
|
defer iter.Pool().ReturnIterator(newIter)
|
||||||
*((*float64)(ptr)) = newIter.ReadFloat64()
|
*((*float64)(ptr)) = newIter.ReadFloat64()
|
||||||
if newIter.Error != nil {
|
if newIter.Error != nil {
|
||||||
iter.Error = newIter.Error
|
iter.Error = newIter.Error
|
||||||
|
@ -1,14 +1,3 @@
|
|||||||
// Package jsoniter implements encoding and decoding of JSON as defined in
|
|
||||||
// RFC 4627 and provides interfaces with identical syntax of standard lib encoding/json.
|
|
||||||
// Converting from encoding/json to jsoniter is no more than replacing the package with jsoniter
|
|
||||||
// and variable type declarations (if any).
|
|
||||||
// jsoniter interfaces gives 100% compatibility with code using standard lib.
|
|
||||||
//
|
|
||||||
// "JSON and Go"
|
|
||||||
// (https://golang.org/doc/articles/json_and_go.html)
|
|
||||||
// gives a description of how Marshal/Unmarshal operate
|
|
||||||
// between arbitrary or predefined json objects and bytes,
|
|
||||||
// and it applies to jsoniter.Marshal/Unmarshal as well.
|
|
||||||
package jsoniter
|
package jsoniter
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@ -96,7 +85,7 @@ func (adapter *Decoder) Buffered() io.Reader {
|
|||||||
func (decoder *Decoder) UseNumber() {
|
func (decoder *Decoder) UseNumber() {
|
||||||
origCfg := decoder.iter.cfg.configBeforeFrozen
|
origCfg := decoder.iter.cfg.configBeforeFrozen
|
||||||
origCfg.UseNumber = true
|
origCfg.UseNumber = true
|
||||||
decoder.iter.cfg = origCfg.Froze()
|
decoder.iter.cfg = origCfg.Froze().(*frozenConfig)
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewEncoder(writer io.Writer) *Encoder {
|
func NewEncoder(writer io.Writer) *Encoder {
|
||||||
@ -119,6 +108,6 @@ func (adapter *Encoder) SetIndent(prefix, indent string) {
|
|||||||
|
|
||||||
func (adapter *Encoder) SetEscapeHTML(escapeHtml bool) {
|
func (adapter *Encoder) SetEscapeHTML(escapeHtml bool) {
|
||||||
config := adapter.stream.cfg.configBeforeFrozen
|
config := adapter.stream.cfg.configBeforeFrozen
|
||||||
config.EscapeHtml = escapeHtml
|
config.EscapeHTML = escapeHtml
|
||||||
adapter.stream.cfg = config.Froze()
|
adapter.stream.cfg = config.Froze().(*frozenConfig)
|
||||||
}
|
}
|
||||||
|
@ -9,10 +9,12 @@ import (
|
|||||||
"unsafe"
|
"unsafe"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Config customize how the API should behave.
|
||||||
|
// The API is created from Config by Froze.
|
||||||
type Config struct {
|
type Config struct {
|
||||||
IndentionStep int
|
IndentionStep int
|
||||||
MarshalFloatWith6Digits bool
|
MarshalFloatWith6Digits bool
|
||||||
EscapeHtml bool
|
EscapeHTML bool
|
||||||
SortMapKeys bool
|
SortMapKeys bool
|
||||||
UseNumber bool
|
UseNumber bool
|
||||||
}
|
}
|
||||||
@ -28,7 +30,11 @@ type frozenConfig struct {
|
|||||||
iteratorPool chan *Iterator
|
iteratorPool chan *Iterator
|
||||||
}
|
}
|
||||||
|
|
||||||
type Api interface {
|
// API the public interface of this package.
|
||||||
|
// Primary Marshal and Unmarshal.
|
||||||
|
type API interface {
|
||||||
|
IteratorPool
|
||||||
|
StreamPool
|
||||||
MarshalToString(v interface{}) (string, error)
|
MarshalToString(v interface{}) (string, error)
|
||||||
Marshal(v interface{}) ([]byte, error)
|
Marshal(v interface{}) ([]byte, error)
|
||||||
MarshalIndent(v interface{}, prefix, indent string) ([]byte, error)
|
MarshalIndent(v interface{}, prefix, indent string) ([]byte, error)
|
||||||
@ -39,22 +45,25 @@ type Api interface {
|
|||||||
NewDecoder(reader io.Reader) *Decoder
|
NewDecoder(reader io.Reader) *Decoder
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ConfigDefault the default API
|
||||||
var ConfigDefault = Config{
|
var ConfigDefault = Config{
|
||||||
EscapeHtml: true,
|
EscapeHTML: true,
|
||||||
}.Froze()
|
}.Froze()
|
||||||
|
|
||||||
// Trying to be 100% compatible with standard library behavior
|
// ConfigCompatibleWithStandardLibrary tries to be 100% compatible with standard library behavior
|
||||||
var ConfigCompatibleWithStandardLibrary = Config{
|
var ConfigCompatibleWithStandardLibrary = Config{
|
||||||
EscapeHtml: true,
|
EscapeHTML: true,
|
||||||
SortMapKeys: true,
|
SortMapKeys: true,
|
||||||
}.Froze()
|
}.Froze()
|
||||||
|
|
||||||
|
// ConfigFastest marshals float with only 6 digits precision
|
||||||
var ConfigFastest = Config{
|
var ConfigFastest = Config{
|
||||||
EscapeHtml: false,
|
EscapeHTML: false,
|
||||||
MarshalFloatWith6Digits: true,
|
MarshalFloatWith6Digits: true,
|
||||||
}.Froze()
|
}.Froze()
|
||||||
|
|
||||||
func (cfg Config) Froze() *frozenConfig {
|
// Froze forge API from config
|
||||||
|
func (cfg Config) Froze() API {
|
||||||
// TODO: cache frozen config
|
// TODO: cache frozen config
|
||||||
frozenConfig := &frozenConfig{
|
frozenConfig := &frozenConfig{
|
||||||
sortMapKeys: cfg.SortMapKeys,
|
sortMapKeys: cfg.SortMapKeys,
|
||||||
@ -67,8 +76,8 @@ func (cfg Config) Froze() *frozenConfig {
|
|||||||
if cfg.MarshalFloatWith6Digits {
|
if cfg.MarshalFloatWith6Digits {
|
||||||
frozenConfig.marshalFloatWith6Digits()
|
frozenConfig.marshalFloatWith6Digits()
|
||||||
}
|
}
|
||||||
if cfg.EscapeHtml {
|
if cfg.EscapeHTML {
|
||||||
frozenConfig.escapeHtml()
|
frozenConfig.escapeHTML()
|
||||||
}
|
}
|
||||||
if cfg.UseNumber {
|
if cfg.UseNumber {
|
||||||
frozenConfig.useNumber()
|
frozenConfig.useNumber()
|
||||||
@ -145,7 +154,7 @@ func (encoder *htmlEscapedStringEncoder) IsEmpty(ptr unsafe.Pointer) bool {
|
|||||||
return *((*string)(ptr)) == ""
|
return *((*string)(ptr)) == ""
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cfg *frozenConfig) escapeHtml() {
|
func (cfg *frozenConfig) escapeHTML() {
|
||||||
cfg.addEncoderToCache(reflect.TypeOf((*string)(nil)).Elem(), &htmlEscapedStringEncoder{})
|
cfg.addEncoderToCache(reflect.TypeOf((*string)(nil)).Elem(), &htmlEscapedStringEncoder{})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -189,18 +198,16 @@ func (cfg *frozenConfig) getEncoderFromCache(cacheKey reflect.Type) ValEncoder {
|
|||||||
return cache[cacheKey]
|
return cache[cacheKey]
|
||||||
}
|
}
|
||||||
|
|
||||||
// cleanDecoders cleans decoders registered or cached
|
|
||||||
func (cfg *frozenConfig) cleanDecoders() {
|
func (cfg *frozenConfig) cleanDecoders() {
|
||||||
typeDecoders = map[string]ValDecoder{}
|
typeDecoders = map[string]ValDecoder{}
|
||||||
fieldDecoders = map[string]ValDecoder{}
|
fieldDecoders = map[string]ValDecoder{}
|
||||||
*cfg = *cfg.configBeforeFrozen.Froze()
|
*cfg = *(cfg.configBeforeFrozen.Froze().(*frozenConfig))
|
||||||
}
|
}
|
||||||
|
|
||||||
// cleanEncoders cleans encoders registered or cached
|
|
||||||
func (cfg *frozenConfig) cleanEncoders() {
|
func (cfg *frozenConfig) cleanEncoders() {
|
||||||
typeEncoders = map[string]ValEncoder{}
|
typeEncoders = map[string]ValEncoder{}
|
||||||
fieldEncoders = map[string]ValEncoder{}
|
fieldEncoders = map[string]ValEncoder{}
|
||||||
*cfg = *cfg.configBeforeFrozen.Froze()
|
*cfg = *(cfg.configBeforeFrozen.Froze().(*frozenConfig))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cfg *frozenConfig) MarshalToString(v interface{}) (string, error) {
|
func (cfg *frozenConfig) MarshalToString(v interface{}) (string, error) {
|
||||||
|
@ -1,9 +1,3 @@
|
|||||||
//
|
|
||||||
// Besides, jsoniter.Iterator provides a different set of interfaces
|
|
||||||
// iterating given bytes/string/reader
|
|
||||||
// and yielding parsed elements one by one.
|
|
||||||
// This set of interfaces reads input as required and gives
|
|
||||||
// better performance.
|
|
||||||
package jsoniter
|
package jsoniter
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@ -11,15 +5,23 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// ValueType the type for JSON element
|
||||||
type ValueType int
|
type ValueType int
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
// Invalid invalid JSON element
|
||||||
Invalid ValueType = iota
|
Invalid ValueType = iota
|
||||||
|
// String JSON element "string"
|
||||||
String
|
String
|
||||||
|
// Number JSON element 100 or 0.10
|
||||||
Number
|
Number
|
||||||
|
// Nil JSON element null
|
||||||
Nil
|
Nil
|
||||||
|
// Bool JSON element true or false
|
||||||
Bool
|
Bool
|
||||||
|
// Array JSON element []
|
||||||
Array
|
Array
|
||||||
|
// Object JSON element {}
|
||||||
Object
|
Object
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -63,7 +65,8 @@ func init() {
|
|||||||
valueTypes['{'] = Object
|
valueTypes['{'] = Object
|
||||||
}
|
}
|
||||||
|
|
||||||
// Iterator is a fast and flexible JSON parser
|
// Iterator is a io.Reader like object, with JSON specific read functions.
|
||||||
|
// Error is not returned as return value, but stored as Error member on this iterator instance.
|
||||||
type Iterator struct {
|
type Iterator struct {
|
||||||
cfg *frozenConfig
|
cfg *frozenConfig
|
||||||
reader io.Reader
|
reader io.Reader
|
||||||
@ -75,10 +78,10 @@ type Iterator struct {
|
|||||||
Error error
|
Error error
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create creates an empty Iterator instance
|
// NewIterator creates an empty Iterator instance
|
||||||
func NewIterator(cfg *frozenConfig) *Iterator {
|
func NewIterator(cfg API) *Iterator {
|
||||||
return &Iterator{
|
return &Iterator{
|
||||||
cfg: cfg,
|
cfg: cfg.(*frozenConfig),
|
||||||
reader: nil,
|
reader: nil,
|
||||||
buf: nil,
|
buf: nil,
|
||||||
head: 0,
|
head: 0,
|
||||||
@ -86,10 +89,10 @@ func NewIterator(cfg *frozenConfig) *Iterator {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse parses a json buffer in io.Reader into an Iterator instance
|
// Parse creates an Iterator instance from io.Reader
|
||||||
func Parse(cfg *frozenConfig, reader io.Reader, bufSize int) *Iterator {
|
func Parse(cfg API, reader io.Reader, bufSize int) *Iterator {
|
||||||
return &Iterator{
|
return &Iterator{
|
||||||
cfg: cfg,
|
cfg: cfg.(*frozenConfig),
|
||||||
reader: reader,
|
reader: reader,
|
||||||
buf: make([]byte, bufSize),
|
buf: make([]byte, bufSize),
|
||||||
head: 0,
|
head: 0,
|
||||||
@ -97,10 +100,10 @@ func Parse(cfg *frozenConfig, reader io.Reader, bufSize int) *Iterator {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ParseBytes parses a json byte slice into an Iterator instance
|
// ParseBytes creates an Iterator instance from byte array
|
||||||
func ParseBytes(cfg *frozenConfig, input []byte) *Iterator {
|
func ParseBytes(cfg API, input []byte) *Iterator {
|
||||||
return &Iterator{
|
return &Iterator{
|
||||||
cfg: cfg,
|
cfg: cfg.(*frozenConfig),
|
||||||
reader: nil,
|
reader: nil,
|
||||||
buf: input,
|
buf: input,
|
||||||
head: 0,
|
head: 0,
|
||||||
@ -108,16 +111,17 @@ func ParseBytes(cfg *frozenConfig, input []byte) *Iterator {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ParseString parses a json string into an Iterator instance
|
// ParseString creates an Iterator instance from string
|
||||||
func ParseString(cfg *frozenConfig, input string) *Iterator {
|
func ParseString(cfg API, input string) *Iterator {
|
||||||
return ParseBytes(cfg, []byte(input))
|
return ParseBytes(cfg, []byte(input))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (iter *Iterator) Config() *frozenConfig {
|
// Pool returns a pool can provide more iterator with same configuration
|
||||||
|
func (iter *Iterator) Pool() IteratorPool {
|
||||||
return iter.cfg
|
return iter.cfg
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset can reset an Iterator instance for another json buffer in io.Reader
|
// Reset reuse iterator instance by specifying another reader
|
||||||
func (iter *Iterator) Reset(reader io.Reader) *Iterator {
|
func (iter *Iterator) Reset(reader io.Reader) *Iterator {
|
||||||
iter.reader = reader
|
iter.reader = reader
|
||||||
iter.head = 0
|
iter.head = 0
|
||||||
@ -125,7 +129,7 @@ func (iter *Iterator) Reset(reader io.Reader) *Iterator {
|
|||||||
return iter
|
return iter
|
||||||
}
|
}
|
||||||
|
|
||||||
// ResetBytes can reset an Iterator instance for another json byte slice
|
// ResetBytes reuse iterator instance by specifying another byte array as input
|
||||||
func (iter *Iterator) ResetBytes(input []byte) *Iterator {
|
func (iter *Iterator) ResetBytes(input []byte) *Iterator {
|
||||||
iter.reader = nil
|
iter.reader = nil
|
||||||
iter.buf = input
|
iter.buf = input
|
||||||
@ -134,7 +138,7 @@ func (iter *Iterator) ResetBytes(input []byte) *Iterator {
|
|||||||
return iter
|
return iter
|
||||||
}
|
}
|
||||||
|
|
||||||
// WhatIsNext gets ValueType of relatively next json object
|
// WhatIsNext gets ValueType of relatively next json element
|
||||||
func (iter *Iterator) WhatIsNext() ValueType {
|
func (iter *Iterator) WhatIsNext() ValueType {
|
||||||
valueType := valueTypes[iter.nextToken()]
|
valueType := valueTypes[iter.nextToken()]
|
||||||
iter.unreadByte()
|
iter.unreadByte()
|
||||||
@ -184,6 +188,7 @@ func (iter *Iterator) nextToken() byte {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ReportError record a error in iterator instance with current position.
|
||||||
func (iter *Iterator) ReportError(operation string, msg string) {
|
func (iter *Iterator) ReportError(operation string, msg string) {
|
||||||
if iter.Error != nil {
|
if iter.Error != nil {
|
||||||
if iter.Error != io.EOF {
|
if iter.Error != io.EOF {
|
||||||
@ -198,7 +203,7 @@ func (iter *Iterator) ReportError(operation string, msg string) {
|
|||||||
string(iter.buf[peekStart:iter.head]), string(iter.buf[0:iter.tail]))
|
string(iter.buf[peekStart:iter.head]), string(iter.buf[0:iter.tail]))
|
||||||
}
|
}
|
||||||
|
|
||||||
// CurrentBuffer gets current buffer as string
|
// CurrentBuffer gets current buffer as string for debugging purpose
|
||||||
func (iter *Iterator) CurrentBuffer() string {
|
func (iter *Iterator) CurrentBuffer() string {
|
||||||
peekStart := iter.head - 10
|
peekStart := iter.head - 10
|
||||||
if peekStart < 0 {
|
if peekStart < 0 {
|
||||||
@ -261,6 +266,7 @@ func (iter *Iterator) unreadByte() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Read read the next JSON element as generic interface{}.
|
||||||
func (iter *Iterator) Read() interface{} {
|
func (iter *Iterator) Read() interface{} {
|
||||||
valueType := iter.WhatIsNext()
|
valueType := iter.WhatIsNext()
|
||||||
switch valueType {
|
switch valueType {
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package jsoniter
|
package jsoniter
|
||||||
|
|
||||||
|
// ReadArray read array element, tells if the array has more element to read.
|
||||||
func (iter *Iterator) ReadArray() (ret bool) {
|
func (iter *Iterator) ReadArray() (ret bool) {
|
||||||
c := iter.nextToken()
|
c := iter.nextToken()
|
||||||
switch c {
|
switch c {
|
||||||
@ -23,6 +24,7 @@ func (iter *Iterator) ReadArray() (ret bool) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ReadArrayCB read array with callback
|
||||||
func (iter *Iterator) ReadArrayCB(callback func(*Iterator) bool) (ret bool) {
|
func (iter *Iterator) ReadArrayCB(callback func(*Iterator) bool) (ret bool) {
|
||||||
c := iter.nextToken()
|
c := iter.nextToken()
|
||||||
if c == '[' {
|
if c == '[' {
|
||||||
|
@ -30,6 +30,7 @@ func init() {
|
|||||||
floatDigits['.'] = dotInNumber
|
floatDigits['.'] = dotInNumber
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ReadBigFloat read big.Float
|
||||||
func (iter *Iterator) ReadBigFloat() (ret *big.Float) {
|
func (iter *Iterator) ReadBigFloat() (ret *big.Float) {
|
||||||
str := iter.readNumberAsString()
|
str := iter.readNumberAsString()
|
||||||
if iter.Error != nil && iter.Error != io.EOF {
|
if iter.Error != nil && iter.Error != io.EOF {
|
||||||
@ -47,6 +48,7 @@ func (iter *Iterator) ReadBigFloat() (ret *big.Float) {
|
|||||||
return val
|
return val
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ReadBigInt read big.Int
|
||||||
func (iter *Iterator) ReadBigInt() (ret *big.Int) {
|
func (iter *Iterator) ReadBigInt() (ret *big.Int) {
|
||||||
str := iter.readNumberAsString()
|
str := iter.readNumberAsString()
|
||||||
if iter.Error != nil && iter.Error != io.EOF {
|
if iter.Error != nil && iter.Error != io.EOF {
|
||||||
@ -62,14 +64,14 @@ func (iter *Iterator) ReadBigInt() (ret *big.Int) {
|
|||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//ReadFloat32 read float32
|
||||||
func (iter *Iterator) ReadFloat32() (ret float32) {
|
func (iter *Iterator) ReadFloat32() (ret float32) {
|
||||||
c := iter.nextToken()
|
c := iter.nextToken()
|
||||||
if c == '-' {
|
if c == '-' {
|
||||||
return -iter.readPositiveFloat32()
|
return -iter.readPositiveFloat32()
|
||||||
} else {
|
|
||||||
iter.unreadByte()
|
|
||||||
return iter.readPositiveFloat32()
|
|
||||||
}
|
}
|
||||||
|
iter.unreadByte()
|
||||||
|
return iter.readPositiveFloat32()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (iter *Iterator) readPositiveFloat32() (ret float32) {
|
func (iter *Iterator) readPositiveFloat32() (ret float32) {
|
||||||
@ -165,14 +167,14 @@ func (iter *Iterator) readFloat32SlowPath() (ret float32) {
|
|||||||
return float32(val)
|
return float32(val)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ReadFloat64 read float64
|
||||||
func (iter *Iterator) ReadFloat64() (ret float64) {
|
func (iter *Iterator) ReadFloat64() (ret float64) {
|
||||||
c := iter.nextToken()
|
c := iter.nextToken()
|
||||||
if c == '-' {
|
if c == '-' {
|
||||||
return -iter.readPositiveFloat64()
|
return -iter.readPositiveFloat64()
|
||||||
} else {
|
|
||||||
iter.unreadByte()
|
|
||||||
return iter.readPositiveFloat64()
|
|
||||||
}
|
}
|
||||||
|
iter.unreadByte()
|
||||||
|
return iter.readPositiveFloat64()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (iter *Iterator) readPositiveFloat64() (ret float64) {
|
func (iter *Iterator) readPositiveFloat64() (ret float64) {
|
||||||
|
@ -20,14 +20,17 @@ func init() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ReadUint read uint
|
||||||
func (iter *Iterator) ReadUint() uint {
|
func (iter *Iterator) ReadUint() uint {
|
||||||
return uint(iter.ReadUint64())
|
return uint(iter.ReadUint64())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ReadInt read int
|
||||||
func (iter *Iterator) ReadInt() int {
|
func (iter *Iterator) ReadInt() int {
|
||||||
return int(iter.ReadInt64())
|
return int(iter.ReadInt64())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ReadInt8 read int8
|
||||||
func (iter *Iterator) ReadInt8() (ret int8) {
|
func (iter *Iterator) ReadInt8() (ret int8) {
|
||||||
c := iter.nextToken()
|
c := iter.nextToken()
|
||||||
if c == '-' {
|
if c == '-' {
|
||||||
@ -37,16 +40,16 @@ func (iter *Iterator) ReadInt8() (ret int8) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
return -int8(val)
|
return -int8(val)
|
||||||
} else {
|
|
||||||
val := iter.readUint32(c)
|
|
||||||
if val > math.MaxInt8 {
|
|
||||||
iter.ReportError("ReadInt8", "overflow: "+strconv.FormatInt(int64(val), 10))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
return int8(val)
|
|
||||||
}
|
}
|
||||||
|
val := iter.readUint32(c)
|
||||||
|
if val > math.MaxInt8 {
|
||||||
|
iter.ReportError("ReadInt8", "overflow: "+strconv.FormatInt(int64(val), 10))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return int8(val)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ReadUint8 read uint8
|
||||||
func (iter *Iterator) ReadUint8() (ret uint8) {
|
func (iter *Iterator) ReadUint8() (ret uint8) {
|
||||||
val := iter.readUint32(iter.nextToken())
|
val := iter.readUint32(iter.nextToken())
|
||||||
if val > math.MaxUint8 {
|
if val > math.MaxUint8 {
|
||||||
@ -56,6 +59,7 @@ func (iter *Iterator) ReadUint8() (ret uint8) {
|
|||||||
return uint8(val)
|
return uint8(val)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ReadInt16 read int16
|
||||||
func (iter *Iterator) ReadInt16() (ret int16) {
|
func (iter *Iterator) ReadInt16() (ret int16) {
|
||||||
c := iter.nextToken()
|
c := iter.nextToken()
|
||||||
if c == '-' {
|
if c == '-' {
|
||||||
@ -65,16 +69,16 @@ func (iter *Iterator) ReadInt16() (ret int16) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
return -int16(val)
|
return -int16(val)
|
||||||
} else {
|
|
||||||
val := iter.readUint32(c)
|
|
||||||
if val > math.MaxInt16 {
|
|
||||||
iter.ReportError("ReadInt16", "overflow: "+strconv.FormatInt(int64(val), 10))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
return int16(val)
|
|
||||||
}
|
}
|
||||||
|
val := iter.readUint32(c)
|
||||||
|
if val > math.MaxInt16 {
|
||||||
|
iter.ReportError("ReadInt16", "overflow: "+strconv.FormatInt(int64(val), 10))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return int16(val)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ReadUint16 read uint16
|
||||||
func (iter *Iterator) ReadUint16() (ret uint16) {
|
func (iter *Iterator) ReadUint16() (ret uint16) {
|
||||||
val := iter.readUint32(iter.nextToken())
|
val := iter.readUint32(iter.nextToken())
|
||||||
if val > math.MaxUint16 {
|
if val > math.MaxUint16 {
|
||||||
@ -84,6 +88,7 @@ func (iter *Iterator) ReadUint16() (ret uint16) {
|
|||||||
return uint16(val)
|
return uint16(val)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ReadInt32 read int32
|
||||||
func (iter *Iterator) ReadInt32() (ret int32) {
|
func (iter *Iterator) ReadInt32() (ret int32) {
|
||||||
c := iter.nextToken()
|
c := iter.nextToken()
|
||||||
if c == '-' {
|
if c == '-' {
|
||||||
@ -93,16 +98,16 @@ func (iter *Iterator) ReadInt32() (ret int32) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
return -int32(val)
|
return -int32(val)
|
||||||
} else {
|
|
||||||
val := iter.readUint32(c)
|
|
||||||
if val > math.MaxInt32 {
|
|
||||||
iter.ReportError("ReadInt32", "overflow: "+strconv.FormatInt(int64(val), 10))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
return int32(val)
|
|
||||||
}
|
}
|
||||||
|
val := iter.readUint32(c)
|
||||||
|
if val > math.MaxInt32 {
|
||||||
|
iter.ReportError("ReadInt32", "overflow: "+strconv.FormatInt(int64(val), 10))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return int32(val)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ReadUint32 read uint32
|
||||||
func (iter *Iterator) ReadUint32() (ret uint32) {
|
func (iter *Iterator) ReadUint32() (ret uint32) {
|
||||||
return iter.readUint32(iter.nextToken())
|
return iter.readUint32(iter.nextToken())
|
||||||
}
|
}
|
||||||
@ -182,10 +187,9 @@ func (iter *Iterator) readUint32(c byte) (ret uint32) {
|
|||||||
if value2 < value {
|
if value2 < value {
|
||||||
iter.ReportError("readUint32", "overflow")
|
iter.ReportError("readUint32", "overflow")
|
||||||
return
|
return
|
||||||
} else {
|
|
||||||
value = value2
|
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
|
value = value2
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
value = (value << 3) + (value << 1) + uint32(ind)
|
value = (value << 3) + (value << 1) + uint32(ind)
|
||||||
}
|
}
|
||||||
@ -195,6 +199,7 @@ func (iter *Iterator) readUint32(c byte) (ret uint32) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ReadInt64 read int64
|
||||||
func (iter *Iterator) ReadInt64() (ret int64) {
|
func (iter *Iterator) ReadInt64() (ret int64) {
|
||||||
c := iter.nextToken()
|
c := iter.nextToken()
|
||||||
if c == '-' {
|
if c == '-' {
|
||||||
@ -204,16 +209,16 @@ func (iter *Iterator) ReadInt64() (ret int64) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
return -int64(val)
|
return -int64(val)
|
||||||
} else {
|
|
||||||
val := iter.readUint64(c)
|
|
||||||
if val > math.MaxInt64 {
|
|
||||||
iter.ReportError("ReadInt64", "overflow: "+strconv.FormatUint(uint64(val), 10))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
return int64(val)
|
|
||||||
}
|
}
|
||||||
|
val := iter.readUint64(c)
|
||||||
|
if val > math.MaxInt64 {
|
||||||
|
iter.ReportError("ReadInt64", "overflow: "+strconv.FormatUint(uint64(val), 10))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return int64(val)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ReadUint64 read uint64
|
||||||
func (iter *Iterator) ReadUint64() uint64 {
|
func (iter *Iterator) ReadUint64() uint64 {
|
||||||
return iter.readUint64(iter.nextToken())
|
return iter.readUint64(iter.nextToken())
|
||||||
}
|
}
|
||||||
@ -240,10 +245,9 @@ func (iter *Iterator) readUint64(c byte) (ret uint64) {
|
|||||||
if value2 < value {
|
if value2 < value {
|
||||||
iter.ReportError("readUint64", "overflow")
|
iter.ReportError("readUint64", "overflow")
|
||||||
return
|
return
|
||||||
} else {
|
|
||||||
value = value2
|
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
|
value = value2
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
value = (value << 3) + (value << 1) + uint64(ind)
|
value = (value << 3) + (value << 1) + uint64(ind)
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,9 @@ import (
|
|||||||
"unsafe"
|
"unsafe"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// ReadObject read one field from object.
|
||||||
|
// If object ended, returns empty string.
|
||||||
|
// Otherwise, returns the field name.
|
||||||
func (iter *Iterator) ReadObject() (ret string) {
|
func (iter *Iterator) ReadObject() (ret string) {
|
||||||
c := iter.nextToken()
|
c := iter.nextToken()
|
||||||
switch c {
|
switch c {
|
||||||
@ -74,6 +77,7 @@ func calcHash(str string) int32 {
|
|||||||
return int32(hash)
|
return int32(hash)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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 {
|
func (iter *Iterator) ReadObjectCB(callback func(*Iterator, string) bool) bool {
|
||||||
c := iter.nextToken()
|
c := iter.nextToken()
|
||||||
if c == '{' {
|
if c == '{' {
|
||||||
@ -106,6 +110,7 @@ func (iter *Iterator) ReadObjectCB(callback func(*Iterator, string) bool) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ReadMapCB read map with callback, the key can be any string
|
||||||
func (iter *Iterator) ReadMapCB(callback func(*Iterator, string) bool) bool {
|
func (iter *Iterator) ReadMapCB(callback func(*Iterator, string) bool) bool {
|
||||||
c := iter.nextToken()
|
c := iter.nextToken()
|
||||||
if c == '{' {
|
if c == '{' {
|
||||||
|
@ -29,6 +29,8 @@ func (iter *Iterator) ReadBool() (ret bool) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SkipAndReturnBytes skip next JSON element, and return its content as []byte.
|
||||||
|
// The []byte can be kept, it is a copy of data.
|
||||||
func (iter *Iterator) SkipAndReturnBytes() []byte {
|
func (iter *Iterator) SkipAndReturnBytes() []byte {
|
||||||
iter.startCapture(iter.head)
|
iter.startCapture(iter.head)
|
||||||
iter.Skip()
|
iter.Skip()
|
||||||
@ -58,10 +60,9 @@ func (iter *Iterator) stopCapture() []byte {
|
|||||||
iter.captured = nil
|
iter.captured = nil
|
||||||
if len(captured) == 0 {
|
if len(captured) == 0 {
|
||||||
return remaining
|
return remaining
|
||||||
} else {
|
|
||||||
captured = append(captured, remaining...)
|
|
||||||
return captured
|
|
||||||
}
|
}
|
||||||
|
captured = append(captured, remaining...)
|
||||||
|
return captured
|
||||||
}
|
}
|
||||||
|
|
||||||
// Skip skips a json object and positions to relatively the next json object
|
// Skip skips a json object and positions to relatively the next json object
|
||||||
|
@ -4,6 +4,7 @@ import (
|
|||||||
"unicode/utf16"
|
"unicode/utf16"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// ReadString read string from iterator
|
||||||
func (iter *Iterator) ReadString() (ret string) {
|
func (iter *Iterator) ReadString() (ret string) {
|
||||||
c := iter.nextToken()
|
c := iter.nextToken()
|
||||||
if c == '"' {
|
if c == '"' {
|
||||||
@ -96,6 +97,8 @@ func (iter *Iterator) readStringSlowPath() (ret string) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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) {
|
func (iter *Iterator) ReadStringAsSlice() (ret []byte) {
|
||||||
c := iter.nextToken()
|
c := iter.nextToken()
|
||||||
if c == '"' {
|
if c == '"' {
|
||||||
|
@ -4,6 +4,18 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// IteratorPool a thread safe pool of iterators with same configuration
|
||||||
|
type IteratorPool interface {
|
||||||
|
BorrowIterator(data []byte) *Iterator
|
||||||
|
ReturnIterator(iter *Iterator)
|
||||||
|
}
|
||||||
|
|
||||||
|
// StreamPool a thread safe pool of streams with same configuration
|
||||||
|
type StreamPool interface {
|
||||||
|
BorrowStream(writer io.Writer) *Stream
|
||||||
|
ReturnStream(stream *Stream)
|
||||||
|
}
|
||||||
|
|
||||||
func (cfg *frozenConfig) BorrowStream(writer io.Writer) *Stream {
|
func (cfg *frozenConfig) BorrowStream(writer io.Writer) *Stream {
|
||||||
select {
|
select {
|
||||||
case stream := <-cfg.streamPool:
|
case stream := <-cfg.streamPool:
|
||||||
|
@ -36,6 +36,7 @@ type checkIsEmpty interface {
|
|||||||
IsEmpty(ptr unsafe.Pointer) bool
|
IsEmpty(ptr unsafe.Pointer) bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WriteToStream the default implementation for TypeEncoder method EncodeInterface
|
||||||
func WriteToStream(val interface{}, stream *Stream, encoder ValEncoder) {
|
func WriteToStream(val interface{}, stream *Stream, encoder ValEncoder) {
|
||||||
e := (*emptyInterface)(unsafe.Pointer(&val))
|
e := (*emptyInterface)(unsafe.Pointer(&val))
|
||||||
if e.word == nil {
|
if e.word == nil {
|
||||||
@ -49,9 +50,6 @@ func WriteToStream(val interface{}, stream *Stream, encoder ValEncoder) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type DecoderFunc func(ptr unsafe.Pointer, iter *Iterator)
|
|
||||||
type EncoderFunc func(ptr unsafe.Pointer, stream *Stream)
|
|
||||||
|
|
||||||
var jsonNumberType reflect.Type
|
var jsonNumberType reflect.Type
|
||||||
var jsonRawMessageType reflect.Type
|
var jsonRawMessageType reflect.Type
|
||||||
var jsoniterRawMessageType reflect.Type
|
var jsoniterRawMessageType reflect.Type
|
||||||
@ -132,9 +130,8 @@ func (encoder *optionalEncoder) EncodeInterface(val interface{}, stream *Stream)
|
|||||||
func (encoder *optionalEncoder) IsEmpty(ptr unsafe.Pointer) bool {
|
func (encoder *optionalEncoder) IsEmpty(ptr unsafe.Pointer) bool {
|
||||||
if *((*unsafe.Pointer)(ptr)) == nil {
|
if *((*unsafe.Pointer)(ptr)) == nil {
|
||||||
return true
|
return true
|
||||||
} else {
|
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
type placeholderEncoder struct {
|
type placeholderEncoder struct {
|
||||||
@ -206,7 +203,7 @@ type nonEmptyInterface struct {
|
|||||||
word unsafe.Pointer
|
word unsafe.Pointer
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read converts an Iterator instance into go interface, same as json.Unmarshal
|
// ReadVal copy the underlying JSON into go interface, same as json.Unmarshal
|
||||||
func (iter *Iterator) ReadVal(obj interface{}) {
|
func (iter *Iterator) ReadVal(obj interface{}) {
|
||||||
typ := reflect.TypeOf(obj)
|
typ := reflect.TypeOf(obj)
|
||||||
cacheKey := typ.Elem()
|
cacheKey := typ.Elem()
|
||||||
@ -219,6 +216,7 @@ func (iter *Iterator) ReadVal(obj interface{}) {
|
|||||||
decoder.Decode(e.word, iter)
|
decoder.Decode(e.word, iter)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WriteVal copy the go interface into underlying JSON, same as json.Marshal
|
||||||
func (stream *Stream) WriteVal(val interface{}) {
|
func (stream *Stream) WriteVal(val interface{}) {
|
||||||
if nil == val {
|
if nil == val {
|
||||||
stream.WriteNil()
|
stream.WriteNil()
|
||||||
@ -393,9 +391,8 @@ func createDecoderOfType(cfg *frozenConfig, typ reflect.Type) (ValDecoder, error
|
|||||||
case reflect.Interface:
|
case reflect.Interface:
|
||||||
if typ.NumMethod() == 0 {
|
if typ.NumMethod() == 0 {
|
||||||
return &emptyInterfaceCodec{}, nil
|
return &emptyInterfaceCodec{}, nil
|
||||||
} else {
|
|
||||||
return &nonEmptyInterfaceCodec{}, nil
|
|
||||||
}
|
}
|
||||||
|
return &nonEmptyInterfaceCodec{}, nil
|
||||||
case reflect.Struct:
|
case reflect.Struct:
|
||||||
return prefix(fmt.Sprintf("[%s]", typeName)).addToDecoder(decoderOfStruct(cfg, typ))
|
return prefix(fmt.Sprintf("[%s]", typeName)).addToDecoder(decoderOfStruct(cfg, typ))
|
||||||
case reflect.Array:
|
case reflect.Array:
|
||||||
@ -517,9 +514,8 @@ func createCheckIsEmpty(typ reflect.Type) (checkIsEmpty, error) {
|
|||||||
case reflect.Interface:
|
case reflect.Interface:
|
||||||
if typ.NumMethod() == 0 {
|
if typ.NumMethod() == 0 {
|
||||||
return &emptyInterfaceCodec{}, nil
|
return &emptyInterfaceCodec{}, nil
|
||||||
} else {
|
|
||||||
return &nonEmptyInterfaceCodec{}, nil
|
|
||||||
}
|
}
|
||||||
|
return &nonEmptyInterfaceCodec{}, nil
|
||||||
case reflect.Struct:
|
case reflect.Struct:
|
||||||
return &structEncoder{}, nil
|
return &structEncoder{}, nil
|
||||||
case reflect.Array:
|
case reflect.Array:
|
||||||
@ -617,9 +613,8 @@ func createEncoderOfSimpleType(cfg *frozenConfig, typ reflect.Type) (ValEncoder,
|
|||||||
case reflect.Interface:
|
case reflect.Interface:
|
||||||
if typ.NumMethod() == 0 {
|
if typ.NumMethod() == 0 {
|
||||||
return &emptyInterfaceCodec{}, nil
|
return &emptyInterfaceCodec{}, nil
|
||||||
} else {
|
|
||||||
return &nonEmptyInterfaceCodec{}, nil
|
|
||||||
}
|
}
|
||||||
|
return &nonEmptyInterfaceCodec{}, nil
|
||||||
case reflect.Struct:
|
case reflect.Struct:
|
||||||
return prefix(fmt.Sprintf("[%s]", typeName)).addToEncoder(encoderOfStruct(cfg, typ))
|
return prefix(fmt.Sprintf("[%s]", typeName)).addToEncoder(encoderOfStruct(cfg, typ))
|
||||||
case reflect.Array:
|
case reflect.Array:
|
||||||
@ -679,7 +674,6 @@ func encoderOfMap(cfg *frozenConfig, typ reflect.Type) (ValEncoder, error) {
|
|||||||
mapInterface := reflect.New(typ).Elem().Interface()
|
mapInterface := reflect.New(typ).Elem().Interface()
|
||||||
if cfg.sortMapKeys {
|
if cfg.sortMapKeys {
|
||||||
return &sortKeysMapEncoder{typ, elemType, encoder, *((*emptyInterface)(unsafe.Pointer(&mapInterface)))}, nil
|
return &sortKeysMapEncoder{typ, elemType, encoder, *((*emptyInterface)(unsafe.Pointer(&mapInterface)))}, nil
|
||||||
} else {
|
|
||||||
return &mapEncoder{typ, elemType, encoder, *((*emptyInterface)(unsafe.Pointer(&mapInterface)))}, nil
|
|
||||||
}
|
}
|
||||||
|
return &mapEncoder{typ, elemType, encoder, *((*emptyInterface)(unsafe.Pointer(&mapInterface)))}, nil
|
||||||
}
|
}
|
||||||
|
@ -110,6 +110,11 @@ func (encoder *funcEncoder) IsEmpty(ptr unsafe.Pointer) bool {
|
|||||||
return encoder.isEmptyFunc(ptr)
|
return encoder.isEmptyFunc(ptr)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DecoderFunc the function form of TypeDecoder
|
||||||
|
type DecoderFunc func(ptr unsafe.Pointer, iter *Iterator)
|
||||||
|
// EncoderFunc the function form of TypeEncoder
|
||||||
|
type EncoderFunc func(ptr unsafe.Pointer, stream *Stream)
|
||||||
|
|
||||||
// RegisterTypeDecoderFunc register TypeDecoder for a type with function
|
// RegisterTypeDecoderFunc register TypeDecoder for a type with function
|
||||||
func RegisterTypeDecoderFunc(typ string, fun DecoderFunc) {
|
func RegisterTypeDecoderFunc(typ string, fun DecoderFunc) {
|
||||||
typeDecoders[typ] = &funcDecoder{fun}
|
typeDecoders[typ] = &funcDecoder{fun}
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
package jsoniter
|
package jsoniter
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"reflect"
|
|
||||||
"io"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"reflect"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -71,7 +71,7 @@ func createStructDecoder(typ reflect.Type, fields map[string]*structFieldDecoder
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return &threeFieldsStructDecoder{typ,
|
return &threeFieldsStructDecoder{typ,
|
||||||
fieldName1, fieldDecoder1, fieldName2, fieldDecoder2, fieldName3, fieldDecoder3}, nil
|
fieldName1, fieldDecoder1, fieldName2, fieldDecoder2, fieldName3, fieldDecoder3}, nil
|
||||||
case 4:
|
case 4:
|
||||||
var fieldName1 int32
|
var fieldName1 int32
|
||||||
var fieldName2 int32
|
var fieldName2 int32
|
||||||
@ -103,8 +103,8 @@ func createStructDecoder(typ reflect.Type, fields map[string]*structFieldDecoder
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return &fourFieldsStructDecoder{typ,
|
return &fourFieldsStructDecoder{typ,
|
||||||
fieldName1, fieldDecoder1, fieldName2, fieldDecoder2, fieldName3, fieldDecoder3,
|
fieldName1, fieldDecoder1, fieldName2, fieldDecoder2, fieldName3, fieldDecoder3,
|
||||||
fieldName4, fieldDecoder4}, nil
|
fieldName4, fieldDecoder4}, nil
|
||||||
case 5:
|
case 5:
|
||||||
var fieldName1 int32
|
var fieldName1 int32
|
||||||
var fieldName2 int32
|
var fieldName2 int32
|
||||||
@ -141,8 +141,8 @@ func createStructDecoder(typ reflect.Type, fields map[string]*structFieldDecoder
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return &fiveFieldsStructDecoder{typ,
|
return &fiveFieldsStructDecoder{typ,
|
||||||
fieldName1, fieldDecoder1, fieldName2, fieldDecoder2, fieldName3, fieldDecoder3,
|
fieldName1, fieldDecoder1, fieldName2, fieldDecoder2, fieldName3, fieldDecoder3,
|
||||||
fieldName4, fieldDecoder4, fieldName5, fieldDecoder5}, nil
|
fieldName4, fieldDecoder4, fieldName5, fieldDecoder5}, nil
|
||||||
case 6:
|
case 6:
|
||||||
var fieldName1 int32
|
var fieldName1 int32
|
||||||
var fieldName2 int32
|
var fieldName2 int32
|
||||||
@ -184,8 +184,8 @@ func createStructDecoder(typ reflect.Type, fields map[string]*structFieldDecoder
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return &sixFieldsStructDecoder{typ,
|
return &sixFieldsStructDecoder{typ,
|
||||||
fieldName1, fieldDecoder1, fieldName2, fieldDecoder2, fieldName3, fieldDecoder3,
|
fieldName1, fieldDecoder1, fieldName2, fieldDecoder2, fieldName3, fieldDecoder3,
|
||||||
fieldName4, fieldDecoder4, fieldName5, fieldDecoder5, fieldName6, fieldDecoder6}, nil
|
fieldName4, fieldDecoder4, fieldName5, fieldDecoder5, fieldName6, fieldDecoder6}, nil
|
||||||
case 7:
|
case 7:
|
||||||
var fieldName1 int32
|
var fieldName1 int32
|
||||||
var fieldName2 int32
|
var fieldName2 int32
|
||||||
@ -232,9 +232,9 @@ func createStructDecoder(typ reflect.Type, fields map[string]*structFieldDecoder
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return &sevenFieldsStructDecoder{typ,
|
return &sevenFieldsStructDecoder{typ,
|
||||||
fieldName1, fieldDecoder1, fieldName2, fieldDecoder2, fieldName3, fieldDecoder3,
|
fieldName1, fieldDecoder1, fieldName2, fieldDecoder2, fieldName3, fieldDecoder3,
|
||||||
fieldName4, fieldDecoder4, fieldName5, fieldDecoder5, fieldName6, fieldDecoder6,
|
fieldName4, fieldDecoder4, fieldName5, fieldDecoder5, fieldName6, fieldDecoder6,
|
||||||
fieldName7, fieldDecoder7}, nil
|
fieldName7, fieldDecoder7}, nil
|
||||||
case 8:
|
case 8:
|
||||||
var fieldName1 int32
|
var fieldName1 int32
|
||||||
var fieldName2 int32
|
var fieldName2 int32
|
||||||
@ -286,9 +286,9 @@ func createStructDecoder(typ reflect.Type, fields map[string]*structFieldDecoder
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return &eightFieldsStructDecoder{typ,
|
return &eightFieldsStructDecoder{typ,
|
||||||
fieldName1, fieldDecoder1, fieldName2, fieldDecoder2, fieldName3, fieldDecoder3,
|
fieldName1, fieldDecoder1, fieldName2, fieldDecoder2, fieldName3, fieldDecoder3,
|
||||||
fieldName4, fieldDecoder4, fieldName5, fieldDecoder5, fieldName6, fieldDecoder6,
|
fieldName4, fieldDecoder4, fieldName5, fieldDecoder5, fieldName6, fieldDecoder6,
|
||||||
fieldName7, fieldDecoder7, fieldName8, fieldDecoder8}, nil
|
fieldName7, fieldDecoder7, fieldName8, fieldDecoder8}, nil
|
||||||
case 9:
|
case 9:
|
||||||
var fieldName1 int32
|
var fieldName1 int32
|
||||||
var fieldName2 int32
|
var fieldName2 int32
|
||||||
@ -345,9 +345,9 @@ func createStructDecoder(typ reflect.Type, fields map[string]*structFieldDecoder
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return &nineFieldsStructDecoder{typ,
|
return &nineFieldsStructDecoder{typ,
|
||||||
fieldName1, fieldDecoder1, fieldName2, fieldDecoder2, fieldName3, fieldDecoder3,
|
fieldName1, fieldDecoder1, fieldName2, fieldDecoder2, fieldName3, fieldDecoder3,
|
||||||
fieldName4, fieldDecoder4, fieldName5, fieldDecoder5, fieldName6, fieldDecoder6,
|
fieldName4, fieldDecoder4, fieldName5, fieldDecoder5, fieldName6, fieldDecoder6,
|
||||||
fieldName7, fieldDecoder7, fieldName8, fieldDecoder8, fieldName9, fieldDecoder9}, nil
|
fieldName7, fieldDecoder7, fieldName8, fieldDecoder8, fieldName9, fieldDecoder9}, nil
|
||||||
case 10:
|
case 10:
|
||||||
var fieldName1 int32
|
var fieldName1 int32
|
||||||
var fieldName2 int32
|
var fieldName2 int32
|
||||||
@ -409,15 +409,14 @@ func createStructDecoder(typ reflect.Type, fields map[string]*structFieldDecoder
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return &tenFieldsStructDecoder{typ,
|
return &tenFieldsStructDecoder{typ,
|
||||||
fieldName1, fieldDecoder1, fieldName2, fieldDecoder2, fieldName3, fieldDecoder3,
|
fieldName1, fieldDecoder1, fieldName2, fieldDecoder2, fieldName3, fieldDecoder3,
|
||||||
fieldName4, fieldDecoder4, fieldName5, fieldDecoder5, fieldName6, fieldDecoder6,
|
fieldName4, fieldDecoder4, fieldName5, fieldDecoder5, fieldName6, fieldDecoder6,
|
||||||
fieldName7, fieldDecoder7, fieldName8, fieldDecoder8, fieldName9, fieldDecoder9,
|
fieldName7, fieldDecoder7, fieldName8, fieldDecoder8, fieldName9, fieldDecoder9,
|
||||||
fieldName10, fieldDecoder10}, nil
|
fieldName10, fieldDecoder10}, nil
|
||||||
}
|
}
|
||||||
return &generalStructDecoder{typ, fields}, nil
|
return &generalStructDecoder{typ, fields}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
type generalStructDecoder struct {
|
type generalStructDecoder struct {
|
||||||
typ reflect.Type
|
typ reflect.Type
|
||||||
fields map[string]*structFieldDecoder
|
fields map[string]*structFieldDecoder
|
||||||
@ -911,4 +910,4 @@ func (decoder *structFieldDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) {
|
|||||||
if iter.Error != nil && iter.Error != io.EOF {
|
if iter.Error != nil && iter.Error != io.EOF {
|
||||||
iter.Error = fmt.Errorf("%s: %s", decoder.field.Name, iter.Error.Error())
|
iter.Error = fmt.Errorf("%s: %s", decoder.field.Name, iter.Error.Error())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,7 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Stream is a writer like object, with JSON specific write functions.
|
// Stream is a io.Writer like object, with JSON specific write functions.
|
||||||
// Error is not returned as return value, but stored as Error member on this stream instance.
|
// Error is not returned as return value, but stored as Error member on this stream instance.
|
||||||
type Stream struct {
|
type Stream struct {
|
||||||
cfg *frozenConfig
|
cfg *frozenConfig
|
||||||
@ -19,9 +19,9 @@ type Stream struct {
|
|||||||
// cfg can be jsoniter.ConfigDefault.
|
// cfg can be jsoniter.ConfigDefault.
|
||||||
// out can be nil if write to internal buffer.
|
// out can be nil if write to internal buffer.
|
||||||
// bufSize is the initial size for the internal buffer in bytes.
|
// bufSize is the initial size for the internal buffer in bytes.
|
||||||
func NewStream(cfg *frozenConfig, out io.Writer, bufSize int) *Stream {
|
func NewStream(cfg API, out io.Writer, bufSize int) *Stream {
|
||||||
return &Stream{
|
return &Stream{
|
||||||
cfg: cfg,
|
cfg: cfg.(*frozenConfig),
|
||||||
out: out,
|
out: out,
|
||||||
buf: make([]byte, bufSize),
|
buf: make([]byte, bufSize),
|
||||||
n: 0,
|
n: 0,
|
||||||
@ -30,6 +30,11 @@ func NewStream(cfg *frozenConfig, out io.Writer, bufSize int) *Stream {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Pool returns a pool can provide more stream with same configuration
|
||||||
|
func (stream *Stream) Pool() StreamPool {
|
||||||
|
return stream.cfg
|
||||||
|
}
|
||||||
|
|
||||||
// Reset reuse this stream instance by assign a new writer
|
// Reset reuse this stream instance by assign a new writer
|
||||||
func (stream *Stream) Reset(out io.Writer) {
|
func (stream *Stream) Reset(out io.Writer) {
|
||||||
stream.out = out
|
stream.out = out
|
||||||
@ -196,7 +201,6 @@ func (stream *Stream) growAtLeast(minimal int) {
|
|||||||
stream.buf = newBuf
|
stream.buf = newBuf
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// WriteRaw write string out without quotes, just like []byte
|
// WriteRaw write string out without quotes, just like []byte
|
||||||
func (stream *Stream) WriteRaw(s string) {
|
func (stream *Stream) WriteRaw(s string) {
|
||||||
stream.ensure(len(s))
|
stream.ensure(len(s))
|
||||||
|
18
jsoniter.go
Normal file
18
jsoniter.go
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
// Package jsoniter implements encoding and decoding of JSON as defined in
|
||||||
|
// RFC 4627 and provides interfaces with identical syntax of standard lib encoding/json.
|
||||||
|
// Converting from encoding/json to jsoniter is no more than replacing the package with jsoniter
|
||||||
|
// and variable type declarations (if any).
|
||||||
|
// jsoniter interfaces gives 100% compatibility with code using standard lib.
|
||||||
|
//
|
||||||
|
// "JSON and Go"
|
||||||
|
// (https://golang.org/doc/articles/json_and_go.html)
|
||||||
|
// gives a description of how Marshal/Unmarshal operate
|
||||||
|
// between arbitrary or predefined json objects and bytes,
|
||||||
|
// and it applies to jsoniter.Marshal/Unmarshal as well.
|
||||||
|
//
|
||||||
|
// Besides, jsoniter.Iterator provides a different set of interfaces
|
||||||
|
// iterating given bytes/string/reader
|
||||||
|
// and yielding parsed elements one by one.
|
||||||
|
// This set of interfaces reads input as required and gives
|
||||||
|
// better performance.
|
||||||
|
package jsoniter
|
@ -26,7 +26,7 @@ func Test_new_encoder(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func Test_string_encode_with_std_without_html_escape(t *testing.T) {
|
func Test_string_encode_with_std_without_html_escape(t *testing.T) {
|
||||||
api := Config{EscapeHtml: false}.Froze()
|
api := Config{EscapeHTML: false}.Froze()
|
||||||
should := require.New(t)
|
should := require.New(t)
|
||||||
for i := 0; i < utf8.RuneSelf; i++ {
|
for i := 0; i < utf8.RuneSelf; i++ {
|
||||||
input := string([]byte{byte(i)})
|
input := string([]byte{byte(i)})
|
||||||
|
@ -7,25 +7,25 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var stringConvertMap = map[string]string{
|
var stringConvertMap = map[string]string{
|
||||||
"null": "",
|
"null": "",
|
||||||
"321.1": "321.1",
|
"321.1": "321.1",
|
||||||
`"1.1"`: "1.1",
|
`"1.1"`: "1.1",
|
||||||
`"-123.1"`: "-123.1",
|
`"-123.1"`: "-123.1",
|
||||||
"0.0": "0.0",
|
"0.0": "0.0",
|
||||||
"0": "0",
|
"0": "0",
|
||||||
`"0"`: "0",
|
`"0"`: "0",
|
||||||
`"0.0"`: "0.0",
|
`"0.0"`: "0.0",
|
||||||
`"00.0"`: "00.0",
|
`"00.0"`: "00.0",
|
||||||
"true": "true",
|
"true": "true",
|
||||||
"false": "false",
|
"false": "false",
|
||||||
`"true"`: "true",
|
`"true"`: "true",
|
||||||
`"false"`: "false",
|
`"false"`: "false",
|
||||||
`"true123"`: "true123",
|
`"true123"`: "true123",
|
||||||
`"+1"`: "+1",
|
`"+1"`: "+1",
|
||||||
"[]": "[]",
|
"[]": "[]",
|
||||||
"[1,2]": "[1,2]",
|
"[1,2]": "[1,2]",
|
||||||
"{}": "{}",
|
"{}": "{}",
|
||||||
"{1,2}": "{1,2}",
|
"{1,2}": "{1,2}",
|
||||||
`{"a":1, "stream":true}`: `{"a":1, "stream":true}`,
|
`{"a":1, "stream":true}`: `{"a":1, "stream":true}`,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,7 +18,7 @@ func Test_customize_type_decoder(t *testing.T) {
|
|||||||
}
|
}
|
||||||
*((*time.Time)(ptr)) = t
|
*((*time.Time)(ptr)) = t
|
||||||
})
|
})
|
||||||
defer ConfigDefault.cleanDecoders()
|
defer ConfigDefault.(*frozenConfig).cleanDecoders()
|
||||||
val := time.Time{}
|
val := time.Time{}
|
||||||
err := Unmarshal([]byte(`"2016-12-05 08:43:28"`), &val)
|
err := Unmarshal([]byte(`"2016-12-05 08:43:28"`), &val)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -36,7 +36,7 @@ func Test_customize_type_encoder(t *testing.T) {
|
|||||||
t := *((*time.Time)(ptr))
|
t := *((*time.Time)(ptr))
|
||||||
stream.WriteString(t.UTC().Format("2006-01-02 15:04:05"))
|
stream.WriteString(t.UTC().Format("2006-01-02 15:04:05"))
|
||||||
}, nil)
|
}, nil)
|
||||||
defer ConfigDefault.cleanEncoders()
|
defer ConfigDefault.(*frozenConfig).cleanEncoders()
|
||||||
val := time.Unix(0, 0)
|
val := time.Unix(0, 0)
|
||||||
str, err := MarshalToString(val)
|
str, err := MarshalToString(val)
|
||||||
should.Nil(err)
|
should.Nil(err)
|
||||||
@ -44,13 +44,13 @@ func Test_customize_type_encoder(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func Test_customize_byte_array_encoder(t *testing.T) {
|
func Test_customize_byte_array_encoder(t *testing.T) {
|
||||||
ConfigDefault.cleanEncoders()
|
ConfigDefault.(*frozenConfig).cleanEncoders()
|
||||||
should := require.New(t)
|
should := require.New(t)
|
||||||
RegisterTypeEncoderFunc("[]uint8", func(ptr unsafe.Pointer, stream *Stream) {
|
RegisterTypeEncoderFunc("[]uint8", func(ptr unsafe.Pointer, stream *Stream) {
|
||||||
t := *((*[]byte)(ptr))
|
t := *((*[]byte)(ptr))
|
||||||
stream.WriteString(string(t))
|
stream.WriteString(string(t))
|
||||||
}, nil)
|
}, nil)
|
||||||
defer ConfigDefault.cleanEncoders()
|
defer ConfigDefault.(*frozenConfig).cleanEncoders()
|
||||||
val := []byte("abc")
|
val := []byte("abc")
|
||||||
str, err := MarshalToString(val)
|
str, err := MarshalToString(val)
|
||||||
should.Nil(err)
|
should.Nil(err)
|
||||||
@ -73,7 +73,7 @@ func Test_customize_field_decoder(t *testing.T) {
|
|||||||
RegisterFieldDecoderFunc("jsoniter.Tom", "field1", func(ptr unsafe.Pointer, iter *Iterator) {
|
RegisterFieldDecoderFunc("jsoniter.Tom", "field1", func(ptr unsafe.Pointer, iter *Iterator) {
|
||||||
*((*string)(ptr)) = strconv.Itoa(iter.ReadInt())
|
*((*string)(ptr)) = strconv.Itoa(iter.ReadInt())
|
||||||
})
|
})
|
||||||
defer ConfigDefault.cleanDecoders()
|
defer ConfigDefault.(*frozenConfig).cleanDecoders()
|
||||||
tom := Tom{}
|
tom := Tom{}
|
||||||
err := Unmarshal([]byte(`{"field1": 100}`), &tom)
|
err := Unmarshal([]byte(`{"field1": 100}`), &tom)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -144,7 +144,7 @@ func Test_marshaler_and_encoder(t *testing.T) {
|
|||||||
type TestObject struct {
|
type TestObject struct {
|
||||||
Field *timeImplementedMarshaler
|
Field *timeImplementedMarshaler
|
||||||
}
|
}
|
||||||
ConfigDefault.cleanEncoders()
|
ConfigDefault.(*frozenConfig).cleanEncoders()
|
||||||
should := require.New(t)
|
should := require.New(t)
|
||||||
RegisterTypeEncoderFunc("jsoniter.timeImplementedMarshaler", func(ptr unsafe.Pointer, stream *Stream) {
|
RegisterTypeEncoderFunc("jsoniter.timeImplementedMarshaler", func(ptr unsafe.Pointer, stream *Stream) {
|
||||||
stream.WriteString("hello from encoder")
|
stream.WriteString("hello from encoder")
|
||||||
@ -184,7 +184,7 @@ func Test_unmarshaler_and_decoder(t *testing.T) {
|
|||||||
Field *ObjectImplementedUnmarshaler
|
Field *ObjectImplementedUnmarshaler
|
||||||
Field2 string
|
Field2 string
|
||||||
}
|
}
|
||||||
ConfigDefault.cleanDecoders()
|
ConfigDefault.(*frozenConfig).cleanDecoders()
|
||||||
should := require.New(t)
|
should := require.New(t)
|
||||||
RegisterTypeDecoderFunc("jsoniter.ObjectImplementedUnmarshaler", func(ptr unsafe.Pointer, iter *Iterator) {
|
RegisterTypeDecoderFunc("jsoniter.ObjectImplementedUnmarshaler", func(ptr unsafe.Pointer, iter *Iterator) {
|
||||||
*(*ObjectImplementedUnmarshaler)(ptr) = 10
|
*(*ObjectImplementedUnmarshaler)(ptr) = 10
|
||||||
|
@ -141,8 +141,8 @@ func Test_encode_object_contain_non_empty_interface(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func Test_nil_non_empty_interface(t *testing.T) {
|
func Test_nil_non_empty_interface(t *testing.T) {
|
||||||
ConfigDefault.cleanEncoders()
|
ConfigDefault.(*frozenConfig).cleanEncoders()
|
||||||
ConfigDefault.cleanDecoders()
|
ConfigDefault.(*frozenConfig).cleanDecoders()
|
||||||
type TestObject struct {
|
type TestObject struct {
|
||||||
Field []MyInterface
|
Field []MyInterface
|
||||||
}
|
}
|
||||||
|
@ -2,9 +2,9 @@ package jsoniter
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
"io"
|
"io"
|
||||||
"testing"
|
"testing"
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func Test_read_by_one(t *testing.T) {
|
func Test_read_by_one(t *testing.T) {
|
||||||
|
@ -127,7 +127,7 @@ func Test_unicode(t *testing.T) {
|
|||||||
should := require.New(t)
|
should := require.New(t)
|
||||||
output, _ := MarshalToString(map[string]interface{}{"a": "数字山谷"})
|
output, _ := MarshalToString(map[string]interface{}{"a": "数字山谷"})
|
||||||
should.Equal(`{"a":"数字山谷"}`, output)
|
should.Equal(`{"a":"数字山谷"}`, output)
|
||||||
output, _ = Config{EscapeHtml: false}.Froze().MarshalToString(map[string]interface{}{"a": "数字山谷"})
|
output, _ = Config{EscapeHTML: false}.Froze().MarshalToString(map[string]interface{}{"a": "数字山谷"})
|
||||||
should.Equal(`{"a":"数字山谷"}`, output)
|
should.Equal(`{"a":"数字山谷"}`, output)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -142,7 +142,7 @@ func Test_unicode_and_escape(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func Test_unsafe_unicode(t *testing.T) {
|
func Test_unsafe_unicode(t *testing.T) {
|
||||||
ConfigDefault.cleanEncoders()
|
ConfigDefault.(*frozenConfig).cleanEncoders()
|
||||||
should := require.New(t)
|
should := require.New(t)
|
||||||
output, err := ConfigDefault.MarshalToString("he\u2029\u2028he")
|
output, err := ConfigDefault.MarshalToString("he\u2029\u2028he")
|
||||||
should.Nil(err)
|
should.Nil(err)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user