mirror of
https://github.com/json-iterator/go.git
synced 2024-12-09 08:55:35 +02:00
241 lines
4.8 KiB
Go
241 lines
4.8 KiB
Go
package jsoniter
|
|
|
|
import "fmt"
|
|
|
|
// ReadNil reads a json object as nil and
|
|
// returns whether it's a nil or not
|
|
func (iter *Iterator) ReadNil() (ret bool) {
|
|
c := iter.nextToken()
|
|
if c == 'n' {
|
|
iter.skipFixedBytes(3) // null
|
|
return true
|
|
}
|
|
iter.unreadByte()
|
|
return false
|
|
}
|
|
|
|
// ReadBool reads a json object as Bool
|
|
func (iter *Iterator) ReadBool() (ret bool) {
|
|
c := iter.nextToken()
|
|
if c == 't' {
|
|
iter.skipFixedBytes(3)
|
|
return true
|
|
}
|
|
if c == 'f' {
|
|
iter.skipFixedBytes(4)
|
|
return false
|
|
}
|
|
iter.ReportError("ReadBool", "expect t or f")
|
|
return
|
|
}
|
|
|
|
func (iter *Iterator) SkipAndReturnBytes() []byte {
|
|
iter.startCapture(iter.head)
|
|
iter.Skip()
|
|
return iter.stopCapture()
|
|
}
|
|
|
|
type captureBuffer struct {
|
|
startedAt int
|
|
captured []byte
|
|
}
|
|
|
|
func (iter *Iterator) startCapture(captureStartedAt int) {
|
|
if iter.captured != nil {
|
|
panic("already in capture mode")
|
|
}
|
|
iter.captureStartedAt = captureStartedAt
|
|
iter.captured = make([]byte, 0, 32)
|
|
}
|
|
|
|
func (iter *Iterator) stopCapture() []byte {
|
|
if iter.captured == nil {
|
|
panic("not in capture mode")
|
|
}
|
|
captured := iter.captured
|
|
remaining := iter.buf[iter.captureStartedAt:iter.head]
|
|
iter.captureStartedAt = -1
|
|
iter.captured = nil
|
|
if len(captured) == 0 {
|
|
return remaining
|
|
} else {
|
|
captured = append(captured, remaining...)
|
|
return captured
|
|
}
|
|
}
|
|
|
|
// Skip skips a json object and positions to relatively the next json object
|
|
func (iter *Iterator) Skip() {
|
|
c := iter.nextToken()
|
|
switch c {
|
|
case '"':
|
|
iter.skipString()
|
|
case 'n', 't':
|
|
iter.skipFixedBytes(3) // null or true
|
|
case 'f':
|
|
iter.skipFixedBytes(4) // false
|
|
case '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
|
|
iter.skipNumber()
|
|
case '[':
|
|
iter.skipArray()
|
|
case '{':
|
|
iter.skipObject()
|
|
default:
|
|
iter.ReportError("Skip", fmt.Sprintf("do not know how to skip: %v", c))
|
|
return
|
|
}
|
|
}
|
|
|
|
func (iter *Iterator) skipString() {
|
|
for {
|
|
end, escaped := iter.findStringEnd()
|
|
if end == -1 {
|
|
if !iter.loadMore() {
|
|
iter.ReportError("skipString", "incomplete string")
|
|
return
|
|
}
|
|
if escaped {
|
|
iter.head = 1 // skip the first char as last char read is \
|
|
}
|
|
} else {
|
|
iter.head = end
|
|
return
|
|
}
|
|
}
|
|
}
|
|
|
|
// adapted from: https://github.com/buger/jsonparser/blob/master/parser.go
|
|
// Tries to find the end of string
|
|
// Support if string contains escaped quote symbols.
|
|
func (iter *Iterator) findStringEnd() (int, bool) {
|
|
escaped := false
|
|
for i := iter.head; i < iter.tail; i++ {
|
|
c := iter.buf[i]
|
|
if c == '"' {
|
|
if !escaped {
|
|
return i + 1, false
|
|
}
|
|
j := i - 1
|
|
for {
|
|
if j < iter.head || iter.buf[j] != '\\' {
|
|
// even number of backslashes
|
|
// either end of buffer, or " found
|
|
return i + 1, true
|
|
}
|
|
j--
|
|
if j < iter.head || iter.buf[j] != '\\' {
|
|
// odd number of backslashes
|
|
// it is \" or \\\"
|
|
break
|
|
}
|
|
j--
|
|
}
|
|
} else if c == '\\' {
|
|
escaped = true
|
|
}
|
|
}
|
|
j := iter.tail - 1
|
|
for {
|
|
if j < iter.head || iter.buf[j] != '\\' {
|
|
// even number of backslashes
|
|
// either end of buffer, or " found
|
|
return -1, false // do not end with \
|
|
}
|
|
j--
|
|
if j < iter.head || iter.buf[j] != '\\' {
|
|
// odd number of backslashes
|
|
// it is \" or \\\"
|
|
break
|
|
}
|
|
j--
|
|
|
|
}
|
|
return -1, true // end with \
|
|
}
|
|
|
|
func (iter *Iterator) skipArray() {
|
|
level := 1
|
|
for {
|
|
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
|
|
return
|
|
}
|
|
}
|
|
}
|
|
if !iter.loadMore() {
|
|
iter.ReportError("skipObject", "incomplete array")
|
|
return
|
|
}
|
|
}
|
|
}
|
|
|
|
func (iter *Iterator) skipObject() {
|
|
level := 1
|
|
for {
|
|
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
|
|
return
|
|
}
|
|
}
|
|
}
|
|
if !iter.loadMore() {
|
|
iter.ReportError("skipObject", "incomplete object")
|
|
return
|
|
}
|
|
}
|
|
}
|
|
|
|
func (iter *Iterator) skipNumber() {
|
|
for {
|
|
for i := iter.head; i < iter.tail; i++ {
|
|
c := iter.buf[i]
|
|
switch c {
|
|
case ' ', '\n', '\r', '\t', ',', '}', ']':
|
|
iter.head = i
|
|
return
|
|
}
|
|
}
|
|
if !iter.loadMore() {
|
|
return
|
|
}
|
|
}
|
|
}
|
|
|
|
func (iter *Iterator) skipFixedBytes(n int) {
|
|
iter.head += n
|
|
if iter.head >= iter.tail {
|
|
more := iter.head - iter.tail
|
|
if !iter.loadMore() {
|
|
if more > 0 {
|
|
iter.ReportError("skipFixedBytes", "unexpected end")
|
|
}
|
|
return
|
|
}
|
|
iter.head += more
|
|
}
|
|
}
|