mirror of
https://github.com/superseriousbusiness/gotosocial.git
synced 2024-11-24 18:25:57 +03:00
98263a7de6
* start fixing up tests * fix up tests + automate with drone * fiddle with linting * messing about with drone.yml * some more fiddling * hmmm * add cache * add vendor directory * verbose * ci updates * update some little things * update sig
215 lines
4.7 KiB
Go
215 lines
4.7 KiB
Go
package jsoniter
|
|
|
|
import (
|
|
"fmt"
|
|
"unicode/utf16"
|
|
)
|
|
|
|
// ReadString read string from iterator
|
|
func (iter *Iterator) ReadString() (ret string) {
|
|
c := iter.nextToken()
|
|
if c == '"' {
|
|
for i := iter.head; i < iter.tail; i++ {
|
|
c := iter.buf[i]
|
|
if c == '"' {
|
|
ret = string(iter.buf[iter.head:i])
|
|
iter.head = i + 1
|
|
return ret
|
|
} else if c == '\\' {
|
|
break
|
|
} else if c < ' ' {
|
|
iter.ReportError("ReadString",
|
|
fmt.Sprintf(`invalid control character found: %d`, c))
|
|
return
|
|
}
|
|
}
|
|
return iter.readStringSlowPath()
|
|
} else if c == 'n' {
|
|
iter.skipThreeBytes('u', 'l', 'l')
|
|
return ""
|
|
}
|
|
iter.ReportError("ReadString", `expects " or n, but found `+string([]byte{c}))
|
|
return
|
|
}
|
|
|
|
func (iter *Iterator) readStringSlowPath() (ret string) {
|
|
var str []byte
|
|
var c byte
|
|
for iter.Error == nil {
|
|
c = iter.readByte()
|
|
if c == '"' {
|
|
return string(str)
|
|
}
|
|
if c == '\\' {
|
|
c = iter.readByte()
|
|
str = iter.readEscapedChar(c, str)
|
|
} else {
|
|
str = append(str, c)
|
|
}
|
|
}
|
|
iter.ReportError("readStringSlowPath", "unexpected end of input")
|
|
return
|
|
}
|
|
|
|
func (iter *Iterator) readEscapedChar(c byte, str []byte) []byte {
|
|
switch c {
|
|
case 'u':
|
|
r := iter.readU4()
|
|
if utf16.IsSurrogate(r) {
|
|
c = iter.readByte()
|
|
if iter.Error != nil {
|
|
return nil
|
|
}
|
|
if c != '\\' {
|
|
iter.unreadByte()
|
|
str = appendRune(str, r)
|
|
return str
|
|
}
|
|
c = iter.readByte()
|
|
if iter.Error != nil {
|
|
return nil
|
|
}
|
|
if c != 'u' {
|
|
str = appendRune(str, r)
|
|
return iter.readEscapedChar(c, str)
|
|
}
|
|
r2 := iter.readU4()
|
|
if iter.Error != nil {
|
|
return nil
|
|
}
|
|
combined := utf16.DecodeRune(r, r2)
|
|
if combined == '\uFFFD' {
|
|
str = appendRune(str, r)
|
|
str = appendRune(str, r2)
|
|
} else {
|
|
str = appendRune(str, combined)
|
|
}
|
|
} else {
|
|
str = appendRune(str, r)
|
|
}
|
|
case '"':
|
|
str = append(str, '"')
|
|
case '\\':
|
|
str = append(str, '\\')
|
|
case '/':
|
|
str = append(str, '/')
|
|
case 'b':
|
|
str = append(str, '\b')
|
|
case 'f':
|
|
str = append(str, '\f')
|
|
case 'n':
|
|
str = append(str, '\n')
|
|
case 'r':
|
|
str = append(str, '\r')
|
|
case 't':
|
|
str = append(str, '\t')
|
|
default:
|
|
iter.ReportError("readEscapedChar",
|
|
`invalid escape char after \`)
|
|
return nil
|
|
}
|
|
return str
|
|
}
|
|
|
|
// 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) {
|
|
c := iter.nextToken()
|
|
if c == '"' {
|
|
for i := iter.head; i < iter.tail; i++ {
|
|
// require ascii string and no escape
|
|
// for: field name, base64, number
|
|
if iter.buf[i] == '"' {
|
|
// fast path: reuse the underlying buffer
|
|
ret = iter.buf[iter.head:i]
|
|
iter.head = i + 1
|
|
return ret
|
|
}
|
|
}
|
|
readLen := iter.tail - iter.head
|
|
copied := make([]byte, readLen, readLen*2)
|
|
copy(copied, iter.buf[iter.head:iter.tail])
|
|
iter.head = iter.tail
|
|
for iter.Error == nil {
|
|
c := iter.readByte()
|
|
if c == '"' {
|
|
return copied
|
|
}
|
|
copied = append(copied, c)
|
|
}
|
|
return copied
|
|
}
|
|
iter.ReportError("ReadStringAsSlice", `expects " or n, but found `+string([]byte{c}))
|
|
return
|
|
}
|
|
|
|
func (iter *Iterator) readU4() (ret rune) {
|
|
for i := 0; i < 4; i++ {
|
|
c := iter.readByte()
|
|
if iter.Error != nil {
|
|
return
|
|
}
|
|
if c >= '0' && c <= '9' {
|
|
ret = ret*16 + rune(c-'0')
|
|
} else if c >= 'a' && c <= 'f' {
|
|
ret = ret*16 + rune(c-'a'+10)
|
|
} else if c >= 'A' && c <= 'F' {
|
|
ret = ret*16 + rune(c-'A'+10)
|
|
} else {
|
|
iter.ReportError("readU4", "expects 0~9 or a~f, but found "+string([]byte{c}))
|
|
return
|
|
}
|
|
}
|
|
return ret
|
|
}
|
|
|
|
const (
|
|
t1 = 0x00 // 0000 0000
|
|
tx = 0x80 // 1000 0000
|
|
t2 = 0xC0 // 1100 0000
|
|
t3 = 0xE0 // 1110 0000
|
|
t4 = 0xF0 // 1111 0000
|
|
t5 = 0xF8 // 1111 1000
|
|
|
|
maskx = 0x3F // 0011 1111
|
|
mask2 = 0x1F // 0001 1111
|
|
mask3 = 0x0F // 0000 1111
|
|
mask4 = 0x07 // 0000 0111
|
|
|
|
rune1Max = 1<<7 - 1
|
|
rune2Max = 1<<11 - 1
|
|
rune3Max = 1<<16 - 1
|
|
|
|
surrogateMin = 0xD800
|
|
surrogateMax = 0xDFFF
|
|
|
|
maxRune = '\U0010FFFF' // Maximum valid Unicode code point.
|
|
runeError = '\uFFFD' // the "error" Rune or "Unicode replacement character"
|
|
)
|
|
|
|
func appendRune(p []byte, r rune) []byte {
|
|
// Negative values are erroneous. Making it unsigned addresses the problem.
|
|
switch i := uint32(r); {
|
|
case i <= rune1Max:
|
|
p = append(p, byte(r))
|
|
return p
|
|
case i <= rune2Max:
|
|
p = append(p, t2|byte(r>>6))
|
|
p = append(p, tx|byte(r)&maskx)
|
|
return p
|
|
case i > maxRune, surrogateMin <= i && i <= surrogateMax:
|
|
r = runeError
|
|
fallthrough
|
|
case i <= rune3Max:
|
|
p = append(p, t3|byte(r>>12))
|
|
p = append(p, tx|byte(r>>6)&maskx)
|
|
p = append(p, tx|byte(r)&maskx)
|
|
return p
|
|
default:
|
|
p = append(p, t4|byte(r>>18))
|
|
p = append(p, tx|byte(r>>12)&maskx)
|
|
p = append(p, tx|byte(r>>6)&maskx)
|
|
p = append(p, tx|byte(r)&maskx)
|
|
return p
|
|
}
|
|
}
|