mirror of
https://github.com/json-iterator/go.git
synced 2025-02-19 19:59:49 +02:00
239 lines
4.9 KiB
Go
239 lines
4.9 KiB
Go
package extra
|
|
|
|
import (
|
|
"github.com/json-iterator/go"
|
|
"github.com/modern-go/reflect2"
|
|
"unicode/utf8"
|
|
"unsafe"
|
|
)
|
|
|
|
// safeSet holds the value true if the ASCII character with the given array
|
|
// position can be represented inside a JSON string without any further
|
|
// escaping.
|
|
//
|
|
// All values are true except for the ASCII control characters (0-31), the
|
|
// double quote ("), and the backslash character ("\").
|
|
var safeSet = [utf8.RuneSelf]bool{
|
|
' ': true,
|
|
'!': true,
|
|
'"': false,
|
|
'#': true,
|
|
'$': true,
|
|
'%': true,
|
|
'&': true,
|
|
'\'': true,
|
|
'(': true,
|
|
')': true,
|
|
'*': true,
|
|
'+': true,
|
|
',': true,
|
|
'-': true,
|
|
'.': true,
|
|
'/': true,
|
|
'0': true,
|
|
'1': true,
|
|
'2': true,
|
|
'3': true,
|
|
'4': true,
|
|
'5': true,
|
|
'6': true,
|
|
'7': true,
|
|
'8': true,
|
|
'9': true,
|
|
':': true,
|
|
';': true,
|
|
'<': true,
|
|
'=': true,
|
|
'>': true,
|
|
'?': true,
|
|
'@': true,
|
|
'A': true,
|
|
'B': true,
|
|
'C': true,
|
|
'D': true,
|
|
'E': true,
|
|
'F': true,
|
|
'G': true,
|
|
'H': true,
|
|
'I': true,
|
|
'J': true,
|
|
'K': true,
|
|
'L': true,
|
|
'M': true,
|
|
'N': true,
|
|
'O': true,
|
|
'P': true,
|
|
'Q': true,
|
|
'R': true,
|
|
'S': true,
|
|
'T': true,
|
|
'U': true,
|
|
'V': true,
|
|
'W': true,
|
|
'X': true,
|
|
'Y': true,
|
|
'Z': true,
|
|
'[': true,
|
|
'\\': false,
|
|
']': true,
|
|
'^': true,
|
|
'_': true,
|
|
'`': true,
|
|
'a': true,
|
|
'b': true,
|
|
'c': true,
|
|
'd': true,
|
|
'e': true,
|
|
'f': true,
|
|
'g': true,
|
|
'h': true,
|
|
'i': true,
|
|
'j': true,
|
|
'k': true,
|
|
'l': true,
|
|
'm': true,
|
|
'n': true,
|
|
'o': true,
|
|
'p': true,
|
|
'q': true,
|
|
'r': true,
|
|
's': true,
|
|
't': true,
|
|
'u': true,
|
|
'v': true,
|
|
'w': true,
|
|
'x': true,
|
|
'y': true,
|
|
'z': true,
|
|
'{': true,
|
|
'|': true,
|
|
'}': true,
|
|
'~': true,
|
|
'\u007f': true,
|
|
}
|
|
|
|
var binaryType = reflect2.TypeOfPtr((*[]byte)(nil)).Elem()
|
|
|
|
type BinaryAsStringExtension struct {
|
|
jsoniter.DummyExtension
|
|
}
|
|
|
|
func (extension *BinaryAsStringExtension) CreateEncoder(typ reflect2.Type) jsoniter.ValEncoder {
|
|
if typ == binaryType {
|
|
return &binaryAsStringCodec{}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (extension *BinaryAsStringExtension) CreateDecoder(typ reflect2.Type) jsoniter.ValDecoder {
|
|
if typ == binaryType {
|
|
return &binaryAsStringCodec{}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
type binaryAsStringCodec struct {
|
|
}
|
|
|
|
func (codec *binaryAsStringCodec) Decode(ptr unsafe.Pointer, iter *jsoniter.Iterator) {
|
|
rawBytes := iter.ReadStringAsSlice()
|
|
bytes := make([]byte, 0, len(rawBytes))
|
|
for i := 0; i < len(rawBytes); i++ {
|
|
b := rawBytes[i]
|
|
if b == '\\' {
|
|
b2 := rawBytes[i+1]
|
|
if b2 != '\\' {
|
|
iter.ReportError("decode binary as string", `\\x is only supported escape`)
|
|
return
|
|
}
|
|
b3 := rawBytes[i+2]
|
|
if b3 != 'x' {
|
|
iter.ReportError("decode binary as string", `\\x is only supported escape`)
|
|
return
|
|
}
|
|
b4 := rawBytes[i+3]
|
|
b5 := rawBytes[i+4]
|
|
i = i + 4
|
|
b = readHex(iter, b4, b5)
|
|
}
|
|
bytes = append(bytes, b)
|
|
}
|
|
*(*[]byte)(ptr) = bytes
|
|
}
|
|
func (codec *binaryAsStringCodec) IsEmpty(ptr unsafe.Pointer) bool {
|
|
return len(*((*[]byte)(ptr))) == 0
|
|
}
|
|
func (codec *binaryAsStringCodec) Encode(ptr unsafe.Pointer, stream *jsoniter.Stream) {
|
|
newBuffer := writeBytes(stream.Buffer(), *(*[]byte)(ptr))
|
|
stream.SetBuffer(newBuffer)
|
|
}
|
|
|
|
func readHex(iter *jsoniter.Iterator, b1, b2 byte) byte {
|
|
var ret byte
|
|
if b1 >= '0' && b1 <= '9' {
|
|
ret = b1 - '0'
|
|
} else if b1 >= 'a' && b1 <= 'f' {
|
|
ret = b1 - 'a' + 10
|
|
} else {
|
|
iter.ReportError("read hex", "expects 0~9 or a~f, but found "+string([]byte{b1}))
|
|
return 0
|
|
}
|
|
ret = ret * 16
|
|
if b2 >= '0' && b2 <= '9' {
|
|
ret = b2 - '0'
|
|
} else if b2 >= 'a' && b2 <= 'f' {
|
|
ret = b2 - 'a' + 10
|
|
} else {
|
|
iter.ReportError("read hex", "expects 0~9 or a~f, but found "+string([]byte{b2}))
|
|
return 0
|
|
}
|
|
return ret
|
|
}
|
|
|
|
var hex = "0123456789abcdef"
|
|
|
|
func writeBytes(space []byte, s []byte) []byte {
|
|
space = append(space, '"')
|
|
// write string, the fast path, without utf8 and escape support
|
|
var i int
|
|
var c byte
|
|
for i, c = range s {
|
|
if c < utf8.RuneSelf && safeSet[c] {
|
|
space = append(space, c)
|
|
} else {
|
|
break
|
|
}
|
|
}
|
|
if i == len(s)-1 {
|
|
space = append(space, '"')
|
|
return space
|
|
}
|
|
return writeBytesSlowPath(space, s[i:])
|
|
}
|
|
|
|
func writeBytesSlowPath(space []byte, s []byte) []byte {
|
|
start := 0
|
|
// for the remaining parts, we process them char by char
|
|
var i int
|
|
var b byte
|
|
for i, b = range s {
|
|
if b >= utf8.RuneSelf {
|
|
space = append(space, '\\', '\\', 'x', hex[b>>4], hex[b&0xF])
|
|
start = i + 1
|
|
continue
|
|
}
|
|
if safeSet[b] {
|
|
continue
|
|
}
|
|
if start < i {
|
|
space = append(space, s[start:i]...)
|
|
}
|
|
space = append(space, '\\', '\\', 'x', hex[b>>4], hex[b&0xF])
|
|
start = i + 1
|
|
}
|
|
if start < len(s) {
|
|
space = append(space, s[start:]...)
|
|
}
|
|
return append(space, '"')
|
|
}
|