You've already forked json-iterator
mirror of
https://github.com/json-iterator/go.git
synced 2025-06-12 22:47:42 +02:00
fix #244 use BinaryAsStringExtension to make []byte pretty, while the output is valid json, but it can not be decoded by other json codec, as \x01 is decoded as \x01 by them, which is not original input
This commit is contained in:
238
extra/binary_as_string_codec.go
Normal file
238
extra/binary_as_string_codec.go
Normal file
@ -0,0 +1,238 @@
|
||||
package extra
|
||||
|
||||
import (
|
||||
"github.com/json-iterator/go"
|
||||
"unsafe"
|
||||
"unicode/utf8"
|
||||
"github.com/v2pro/plz/reflect2"
|
||||
)
|
||||
|
||||
// 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, '"')
|
||||
}
|
Reference in New Issue
Block a user