mirror of https://github.com/gogits/gogs.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
322 lines
7.1 KiB
322 lines
7.1 KiB
package jsoniter |
|
|
|
import ( |
|
"encoding/json" |
|
"fmt" |
|
"io" |
|
) |
|
|
|
// ValueType the type for JSON element |
|
type ValueType int |
|
|
|
const ( |
|
// InvalidValue invalid JSON element |
|
InvalidValue ValueType = iota |
|
// StringValue JSON element "string" |
|
StringValue |
|
// NumberValue JSON element 100 or 0.10 |
|
NumberValue |
|
// NilValue JSON element null |
|
NilValue |
|
// BoolValue JSON element true or false |
|
BoolValue |
|
// ArrayValue JSON element [] |
|
ArrayValue |
|
// ObjectValue JSON element {} |
|
ObjectValue |
|
) |
|
|
|
var hexDigits []byte |
|
var valueTypes []ValueType |
|
|
|
func init() { |
|
hexDigits = make([]byte, 256) |
|
for i := 0; i < len(hexDigits); i++ { |
|
hexDigits[i] = 255 |
|
} |
|
for i := '0'; i <= '9'; i++ { |
|
hexDigits[i] = byte(i - '0') |
|
} |
|
for i := 'a'; i <= 'f'; i++ { |
|
hexDigits[i] = byte((i - 'a') + 10) |
|
} |
|
for i := 'A'; i <= 'F'; i++ { |
|
hexDigits[i] = byte((i - 'A') + 10) |
|
} |
|
valueTypes = make([]ValueType, 256) |
|
for i := 0; i < len(valueTypes); i++ { |
|
valueTypes[i] = InvalidValue |
|
} |
|
valueTypes['"'] = StringValue |
|
valueTypes['-'] = NumberValue |
|
valueTypes['0'] = NumberValue |
|
valueTypes['1'] = NumberValue |
|
valueTypes['2'] = NumberValue |
|
valueTypes['3'] = NumberValue |
|
valueTypes['4'] = NumberValue |
|
valueTypes['5'] = NumberValue |
|
valueTypes['6'] = NumberValue |
|
valueTypes['7'] = NumberValue |
|
valueTypes['8'] = NumberValue |
|
valueTypes['9'] = NumberValue |
|
valueTypes['t'] = BoolValue |
|
valueTypes['f'] = BoolValue |
|
valueTypes['n'] = NilValue |
|
valueTypes['['] = ArrayValue |
|
valueTypes['{'] = ObjectValue |
|
} |
|
|
|
// 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 { |
|
cfg *frozenConfig |
|
reader io.Reader |
|
buf []byte |
|
head int |
|
tail int |
|
captureStartedAt int |
|
captured []byte |
|
Error error |
|
Attachment interface{} // open for customized decoder |
|
} |
|
|
|
// NewIterator creates an empty Iterator instance |
|
func NewIterator(cfg API) *Iterator { |
|
return &Iterator{ |
|
cfg: cfg.(*frozenConfig), |
|
reader: nil, |
|
buf: nil, |
|
head: 0, |
|
tail: 0, |
|
} |
|
} |
|
|
|
// Parse creates an Iterator instance from io.Reader |
|
func Parse(cfg API, reader io.Reader, bufSize int) *Iterator { |
|
return &Iterator{ |
|
cfg: cfg.(*frozenConfig), |
|
reader: reader, |
|
buf: make([]byte, bufSize), |
|
head: 0, |
|
tail: 0, |
|
} |
|
} |
|
|
|
// ParseBytes creates an Iterator instance from byte array |
|
func ParseBytes(cfg API, input []byte) *Iterator { |
|
return &Iterator{ |
|
cfg: cfg.(*frozenConfig), |
|
reader: nil, |
|
buf: input, |
|
head: 0, |
|
tail: len(input), |
|
} |
|
} |
|
|
|
// ParseString creates an Iterator instance from string |
|
func ParseString(cfg API, input string) *Iterator { |
|
return ParseBytes(cfg, []byte(input)) |
|
} |
|
|
|
// Pool returns a pool can provide more iterator with same configuration |
|
func (iter *Iterator) Pool() IteratorPool { |
|
return iter.cfg |
|
} |
|
|
|
// Reset reuse iterator instance by specifying another reader |
|
func (iter *Iterator) Reset(reader io.Reader) *Iterator { |
|
iter.reader = reader |
|
iter.head = 0 |
|
iter.tail = 0 |
|
return iter |
|
} |
|
|
|
// ResetBytes reuse iterator instance by specifying another byte array as input |
|
func (iter *Iterator) ResetBytes(input []byte) *Iterator { |
|
iter.reader = nil |
|
iter.buf = input |
|
iter.head = 0 |
|
iter.tail = len(input) |
|
return iter |
|
} |
|
|
|
// WhatIsNext gets ValueType of relatively next json element |
|
func (iter *Iterator) WhatIsNext() ValueType { |
|
valueType := valueTypes[iter.nextToken()] |
|
iter.unreadByte() |
|
return valueType |
|
} |
|
|
|
func (iter *Iterator) skipWhitespacesWithoutLoadMore() bool { |
|
for i := iter.head; i < iter.tail; i++ { |
|
c := iter.buf[i] |
|
switch c { |
|
case ' ', '\n', '\t', '\r': |
|
continue |
|
} |
|
iter.head = i |
|
return false |
|
} |
|
return true |
|
} |
|
|
|
func (iter *Iterator) isObjectEnd() bool { |
|
c := iter.nextToken() |
|
if c == ',' { |
|
return false |
|
} |
|
if c == '}' { |
|
return true |
|
} |
|
iter.ReportError("isObjectEnd", "object ended prematurely, unexpected char "+string([]byte{c})) |
|
return true |
|
} |
|
|
|
func (iter *Iterator) nextToken() byte { |
|
// a variation of skip whitespaces, returning the next non-whitespace token |
|
for { |
|
for i := iter.head; i < iter.tail; i++ { |
|
c := iter.buf[i] |
|
switch c { |
|
case ' ', '\n', '\t', '\r': |
|
continue |
|
} |
|
iter.head = i + 1 |
|
return c |
|
} |
|
if !iter.loadMore() { |
|
return 0 |
|
} |
|
} |
|
} |
|
|
|
// ReportError record a error in iterator instance with current position. |
|
func (iter *Iterator) ReportError(operation string, msg string) { |
|
if iter.Error != nil { |
|
if iter.Error != io.EOF { |
|
return |
|
} |
|
} |
|
peekStart := iter.head - 10 |
|
if peekStart < 0 { |
|
peekStart = 0 |
|
} |
|
peekEnd := iter.head + 10 |
|
if peekEnd > iter.tail { |
|
peekEnd = iter.tail |
|
} |
|
parsing := string(iter.buf[peekStart:peekEnd]) |
|
contextStart := iter.head - 50 |
|
if contextStart < 0 { |
|
contextStart = 0 |
|
} |
|
contextEnd := iter.head + 50 |
|
if contextEnd > iter.tail { |
|
contextEnd = iter.tail |
|
} |
|
context := string(iter.buf[contextStart:contextEnd]) |
|
iter.Error = fmt.Errorf("%s: %s, error found in #%v byte of ...|%s|..., bigger context ...|%s|...", |
|
operation, msg, iter.head-peekStart, parsing, context) |
|
} |
|
|
|
// CurrentBuffer gets current buffer as string for debugging purpose |
|
func (iter *Iterator) CurrentBuffer() string { |
|
peekStart := iter.head - 10 |
|
if peekStart < 0 { |
|
peekStart = 0 |
|
} |
|
return fmt.Sprintf("parsing #%v byte, around ...|%s|..., whole buffer ...|%s|...", iter.head, |
|
string(iter.buf[peekStart:iter.head]), string(iter.buf[0:iter.tail])) |
|
} |
|
|
|
func (iter *Iterator) readByte() (ret byte) { |
|
if iter.head == iter.tail { |
|
if iter.loadMore() { |
|
ret = iter.buf[iter.head] |
|
iter.head++ |
|
return ret |
|
} |
|
return 0 |
|
} |
|
ret = iter.buf[iter.head] |
|
iter.head++ |
|
return ret |
|
} |
|
|
|
func (iter *Iterator) loadMore() bool { |
|
if iter.reader == nil { |
|
if iter.Error == nil { |
|
iter.head = iter.tail |
|
iter.Error = io.EOF |
|
} |
|
return false |
|
} |
|
if iter.captured != nil { |
|
iter.captured = append(iter.captured, |
|
iter.buf[iter.captureStartedAt:iter.tail]...) |
|
iter.captureStartedAt = 0 |
|
} |
|
for { |
|
n, err := iter.reader.Read(iter.buf) |
|
if n == 0 { |
|
if err != nil { |
|
if iter.Error == nil { |
|
iter.Error = err |
|
} |
|
return false |
|
} |
|
} else { |
|
iter.head = 0 |
|
iter.tail = n |
|
return true |
|
} |
|
} |
|
} |
|
|
|
func (iter *Iterator) unreadByte() { |
|
if iter.Error != nil { |
|
return |
|
} |
|
iter.head-- |
|
return |
|
} |
|
|
|
// Read read the next JSON element as generic interface{}. |
|
func (iter *Iterator) Read() interface{} { |
|
valueType := iter.WhatIsNext() |
|
switch valueType { |
|
case StringValue: |
|
return iter.ReadString() |
|
case NumberValue: |
|
if iter.cfg.configBeforeFrozen.UseNumber { |
|
return json.Number(iter.readNumberAsString()) |
|
} |
|
return iter.ReadFloat64() |
|
case NilValue: |
|
iter.skipFourBytes('n', 'u', 'l', 'l') |
|
return nil |
|
case BoolValue: |
|
return iter.ReadBool() |
|
case ArrayValue: |
|
arr := []interface{}{} |
|
iter.ReadArrayCB(func(iter *Iterator) bool { |
|
var elem interface{} |
|
iter.ReadVal(&elem) |
|
arr = append(arr, elem) |
|
return true |
|
}) |
|
return arr |
|
case ObjectValue: |
|
obj := map[string]interface{}{} |
|
iter.ReadMapCB(func(Iter *Iterator, field string) bool { |
|
var elem interface{} |
|
iter.ReadVal(&elem) |
|
obj[field] = elem |
|
return true |
|
}) |
|
return obj |
|
default: |
|
iter.ReportError("Read", fmt.Sprintf("unexpected value type: %v", valueType)) |
|
return nil |
|
} |
|
}
|
|
|