binary_as_string_codec.go 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238
  1. package extra
  2. import (
  3. "github.com/json-iterator/go"
  4. "github.com/modern-go/reflect2"
  5. "unicode/utf8"
  6. "unsafe"
  7. )
  8. // safeSet holds the value true if the ASCII character with the given array
  9. // position can be represented inside a JSON string without any further
  10. // escaping.
  11. //
  12. // All values are true except for the ASCII control characters (0-31), the
  13. // double quote ("), and the backslash character ("\").
  14. var safeSet = [utf8.RuneSelf]bool{
  15. ' ': true,
  16. '!': true,
  17. '"': false,
  18. '#': true,
  19. '$': true,
  20. '%': true,
  21. '&': true,
  22. '\'': true,
  23. '(': true,
  24. ')': true,
  25. '*': true,
  26. '+': true,
  27. ',': true,
  28. '-': true,
  29. '.': true,
  30. '/': true,
  31. '0': true,
  32. '1': true,
  33. '2': true,
  34. '3': true,
  35. '4': true,
  36. '5': true,
  37. '6': true,
  38. '7': true,
  39. '8': true,
  40. '9': true,
  41. ':': true,
  42. ';': true,
  43. '<': true,
  44. '=': true,
  45. '>': true,
  46. '?': true,
  47. '@': true,
  48. 'A': true,
  49. 'B': true,
  50. 'C': true,
  51. 'D': true,
  52. 'E': true,
  53. 'F': true,
  54. 'G': true,
  55. 'H': true,
  56. 'I': true,
  57. 'J': true,
  58. 'K': true,
  59. 'L': true,
  60. 'M': true,
  61. 'N': true,
  62. 'O': true,
  63. 'P': true,
  64. 'Q': true,
  65. 'R': true,
  66. 'S': true,
  67. 'T': true,
  68. 'U': true,
  69. 'V': true,
  70. 'W': true,
  71. 'X': true,
  72. 'Y': true,
  73. 'Z': true,
  74. '[': true,
  75. '\\': false,
  76. ']': true,
  77. '^': true,
  78. '_': true,
  79. '`': true,
  80. 'a': true,
  81. 'b': true,
  82. 'c': true,
  83. 'd': true,
  84. 'e': true,
  85. 'f': true,
  86. 'g': true,
  87. 'h': true,
  88. 'i': true,
  89. 'j': true,
  90. 'k': true,
  91. 'l': true,
  92. 'm': true,
  93. 'n': true,
  94. 'o': true,
  95. 'p': true,
  96. 'q': true,
  97. 'r': true,
  98. 's': true,
  99. 't': true,
  100. 'u': true,
  101. 'v': true,
  102. 'w': true,
  103. 'x': true,
  104. 'y': true,
  105. 'z': true,
  106. '{': true,
  107. '|': true,
  108. '}': true,
  109. '~': true,
  110. '\u007f': true,
  111. }
  112. var binaryType = reflect2.TypeOfPtr((*[]byte)(nil)).Elem()
  113. type BinaryAsStringExtension struct {
  114. jsoniter.DummyExtension
  115. }
  116. func (extension *BinaryAsStringExtension) CreateEncoder(typ reflect2.Type) jsoniter.ValEncoder {
  117. if typ == binaryType {
  118. return &binaryAsStringCodec{}
  119. }
  120. return nil
  121. }
  122. func (extension *BinaryAsStringExtension) CreateDecoder(typ reflect2.Type) jsoniter.ValDecoder {
  123. if typ == binaryType {
  124. return &binaryAsStringCodec{}
  125. }
  126. return nil
  127. }
  128. type binaryAsStringCodec struct {
  129. }
  130. func (codec *binaryAsStringCodec) Decode(ptr unsafe.Pointer, iter *jsoniter.Iterator) {
  131. rawBytes := iter.ReadStringAsSlice()
  132. bytes := make([]byte, 0, len(rawBytes))
  133. for i := 0; i < len(rawBytes); i++ {
  134. b := rawBytes[i]
  135. if b == '\\' {
  136. b2 := rawBytes[i+1]
  137. if b2 != '\\' {
  138. iter.ReportError("decode binary as string", `\\x is only supported escape`)
  139. return
  140. }
  141. b3 := rawBytes[i+2]
  142. if b3 != 'x' {
  143. iter.ReportError("decode binary as string", `\\x is only supported escape`)
  144. return
  145. }
  146. b4 := rawBytes[i+3]
  147. b5 := rawBytes[i+4]
  148. i += 4
  149. b = readHex(iter, b4, b5)
  150. }
  151. bytes = append(bytes, b)
  152. }
  153. *(*[]byte)(ptr) = bytes
  154. }
  155. func (codec *binaryAsStringCodec) IsEmpty(ptr unsafe.Pointer) bool {
  156. return len(*((*[]byte)(ptr))) == 0
  157. }
  158. func (codec *binaryAsStringCodec) Encode(ptr unsafe.Pointer, stream *jsoniter.Stream) {
  159. newBuffer := writeBytes(stream.Buffer(), *(*[]byte)(ptr))
  160. stream.SetBuffer(newBuffer)
  161. }
  162. func readHex(iter *jsoniter.Iterator, b1, b2 byte) byte {
  163. var ret byte
  164. if b1 >= '0' && b1 <= '9' {
  165. ret = b1 - '0'
  166. } else if b1 >= 'a' && b1 <= 'f' {
  167. ret = b1 - 'a' + 10
  168. } else {
  169. iter.ReportError("read hex", "expects 0~9 or a~f, but found "+string([]byte{b1}))
  170. return 0
  171. }
  172. ret *= 16
  173. if b2 >= '0' && b2 <= '9' {
  174. ret = b2 - '0'
  175. } else if b2 >= 'a' && b2 <= 'f' {
  176. ret = b2 - 'a' + 10
  177. } else {
  178. iter.ReportError("read hex", "expects 0~9 or a~f, but found "+string([]byte{b2}))
  179. return 0
  180. }
  181. return ret
  182. }
  183. var hex = "0123456789abcdef"
  184. func writeBytes(space []byte, s []byte) []byte {
  185. space = append(space, '"')
  186. // write string, the fast path, without utf8 and escape support
  187. var i int
  188. var c byte
  189. for i, c = range s {
  190. if c < utf8.RuneSelf && safeSet[c] {
  191. space = append(space, c)
  192. } else {
  193. break
  194. }
  195. }
  196. if i == len(s)-1 {
  197. space = append(space, '"')
  198. return space
  199. }
  200. return writeBytesSlowPath(space, s[i:])
  201. }
  202. func writeBytesSlowPath(space []byte, s []byte) []byte {
  203. start := 0
  204. // for the remaining parts, we process them char by char
  205. var i int
  206. var b byte
  207. for i, b = range s {
  208. if b >= utf8.RuneSelf {
  209. space = append(space, '\\', '\\', 'x', hex[b>>4], hex[b&0xF])
  210. start = i + 1
  211. continue
  212. }
  213. if safeSet[b] {
  214. continue
  215. }
  216. if start < i {
  217. space = append(space, s[start:i]...)
  218. }
  219. space = append(space, '\\', '\\', 'x', hex[b>>4], hex[b&0xF])
  220. start = i + 1
  221. }
  222. if start < len(s) {
  223. space = append(space, s[start:]...)
  224. }
  225. return append(space, '"')
  226. }