escape.go 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264
  1. package protocol
  2. import (
  3. "bytes"
  4. "reflect"
  5. "strconv"
  6. "strings"
  7. "unicode/utf8"
  8. "unsafe"
  9. )
  10. const (
  11. escapes = "\t\n\f\r ,="
  12. nameEscapes = "\t\n\f\r ,"
  13. stringFieldEscapes = "\t\n\f\r\\\""
  14. )
  15. var (
  16. stringEscaper = strings.NewReplacer(
  17. "\t", `\t`,
  18. "\n", `\n`,
  19. "\f", `\f`,
  20. "\r", `\r`,
  21. `,`, `\,`,
  22. ` `, `\ `,
  23. `=`, `\=`,
  24. )
  25. nameEscaper = strings.NewReplacer(
  26. "\t", `\t`,
  27. "\n", `\n`,
  28. "\f", `\f`,
  29. "\r", `\r`,
  30. `,`, `\,`,
  31. ` `, `\ `,
  32. )
  33. stringFieldEscaper = strings.NewReplacer(
  34. "\t", `\t`,
  35. "\n", `\n`,
  36. "\f", `\f`,
  37. "\r", `\r`,
  38. `"`, `\"`,
  39. `\`, `\\`,
  40. )
  41. )
  42. var (
  43. unescaper = strings.NewReplacer(
  44. `\,`, `,`,
  45. `\"`, `"`, // ???
  46. `\ `, ` `,
  47. `\=`, `=`,
  48. )
  49. nameUnescaper = strings.NewReplacer(
  50. `\,`, `,`,
  51. `\ `, ` `,
  52. )
  53. stringFieldUnescaper = strings.NewReplacer(
  54. `\"`, `"`,
  55. `\\`, `\`,
  56. )
  57. )
  58. // The various escape functions allocate, I'd like to fix that.
  59. // TODO: make escape not allocate
  60. // Escape a tagkey, tagvalue, or fieldkey
  61. func escape(s string) string {
  62. if strings.ContainsAny(s, escapes) {
  63. return stringEscaper.Replace(s)
  64. }
  65. return s
  66. }
  67. // Escape a measurement name
  68. func nameEscape(s string) string {
  69. if strings.ContainsAny(s, nameEscapes) {
  70. return nameEscaper.Replace(s)
  71. }
  72. return s
  73. }
  74. // Escape a string field
  75. func stringFieldEscape(s string) string {
  76. if strings.ContainsAny(s, stringFieldEscapes) {
  77. return stringFieldEscaper.Replace(s)
  78. }
  79. return s
  80. }
  81. const (
  82. utf8mask = byte(0x3F)
  83. utf8bytex = byte(0x80) // 1000 0000
  84. utf8len2 = byte(0xC0) // 1100 0000
  85. utf8len3 = byte(0xE0) // 1110 0000
  86. utf8len4 = byte(0xF0) // 1111 0000
  87. )
  88. func escapeBytes(dest *[]byte, b []byte) {
  89. if bytes.ContainsAny(b, escapes) {
  90. var r rune
  91. for i, j := 0, 0; i < len(b); i += j {
  92. r, j = utf8.DecodeRune(b[i:])
  93. switch {
  94. case r == '\t':
  95. *dest = append(*dest, `\t`...)
  96. case r == '\n':
  97. *dest = append(*dest, `\n`...)
  98. case r == '\f':
  99. *dest = append(*dest, `\f`...)
  100. case r == '\r':
  101. *dest = append(*dest, `\r`...)
  102. case r == ',':
  103. *dest = append(*dest, `\,`...)
  104. case r == ' ':
  105. *dest = append(*dest, `\ `...)
  106. case r == '=':
  107. *dest = append(*dest, `\=`...)
  108. case r <= 1<<7-1:
  109. *dest = append(*dest, byte(r))
  110. case r <= 1<<11-1:
  111. *dest = append(*dest, utf8len2|byte(r>>6), utf8bytex|byte(r)&utf8mask)
  112. case r <= 1<<16-1:
  113. *dest = append(*dest, utf8len3|byte(r>>12), utf8bytex|byte(r>>6)&utf8mask, utf8bytex|byte(r)&utf8mask)
  114. default:
  115. *dest = append(*dest, utf8len4|byte(r>>18), utf8bytex|byte(r>>12)&utf8mask, utf8bytex|byte(r>>6)&utf8mask, utf8bytex|byte(r)&utf8mask)
  116. }
  117. }
  118. return
  119. }
  120. *dest = append(*dest, b...)
  121. }
  122. // Escape a measurement name
  123. func nameEscapeBytes(dest *[]byte, b []byte) {
  124. if bytes.ContainsAny(b, nameEscapes) {
  125. var r rune
  126. for i, j := 0, 0; i < len(b); i += j {
  127. r, j = utf8.DecodeRune(b[i:])
  128. switch {
  129. case r == '\t':
  130. *dest = append(*dest, `\t`...)
  131. case r == '\n':
  132. *dest = append(*dest, `\n`...)
  133. case r == '\f':
  134. *dest = append(*dest, `\f`...)
  135. case r == '\r':
  136. *dest = append(*dest, `\r`...)
  137. case r == ',':
  138. *dest = append(*dest, `\,`...)
  139. case r == ' ':
  140. *dest = append(*dest, `\ `...)
  141. case r == '\\':
  142. *dest = append(*dest, `\\`...)
  143. case r <= 1<<7-1:
  144. *dest = append(*dest, byte(r))
  145. case r <= 1<<11-1:
  146. *dest = append(*dest, utf8len2|byte(r>>6), utf8bytex|byte(r)&utf8mask)
  147. case r <= 1<<16-1:
  148. *dest = append(*dest, utf8len3|byte(r>>12), utf8bytex|byte(r>>6)&utf8mask, utf8bytex|byte(r)&utf8mask)
  149. default:
  150. *dest = append(*dest, utf8len4|byte(r>>18), utf8bytex|byte(r>>12)&utf8mask, utf8bytex|byte(r>>6)&utf8mask, utf8bytex|byte(r)&utf8mask)
  151. }
  152. }
  153. return
  154. }
  155. *dest = append(*dest, b...)
  156. }
  157. func stringFieldEscapeBytes(dest *[]byte, b []byte) {
  158. if bytes.ContainsAny(b, stringFieldEscapes) {
  159. var r rune
  160. for i, j := 0, 0; i < len(b); i += j {
  161. r, j = utf8.DecodeRune(b[i:])
  162. switch {
  163. case r == '\t':
  164. *dest = append(*dest, `\t`...)
  165. case r == '\n':
  166. *dest = append(*dest, `\n`...)
  167. case r == '\f':
  168. *dest = append(*dest, `\f`...)
  169. case r == '\r':
  170. *dest = append(*dest, `\r`...)
  171. case r == ',':
  172. *dest = append(*dest, `\,`...)
  173. case r == ' ':
  174. *dest = append(*dest, `\ `...)
  175. case r == '\\':
  176. *dest = append(*dest, `\\`...)
  177. case r <= 1<<7-1:
  178. *dest = append(*dest, byte(r))
  179. case r <= 1<<11-1:
  180. *dest = append(*dest, utf8len2|byte(r>>6), utf8bytex|byte(r)&utf8mask)
  181. case r <= 1<<16-1:
  182. *dest = append(*dest, utf8len3|byte(r>>12), utf8bytex|byte(r>>6)&utf8mask, utf8bytex|byte(r)&utf8mask)
  183. default:
  184. *dest = append(*dest, utf8len4|byte(r>>18), utf8bytex|byte(r>>12)&utf8mask, utf8bytex|byte(r>>6)&utf8mask, utf8bytex|byte(r)&utf8mask)
  185. }
  186. }
  187. return
  188. }
  189. *dest = append(*dest, b...)
  190. }
  191. func unescape(b []byte) string {
  192. if bytes.ContainsAny(b, escapes) {
  193. return unescaper.Replace(unsafeBytesToString(b))
  194. }
  195. return string(b)
  196. }
  197. func nameUnescape(b []byte) string {
  198. if bytes.ContainsAny(b, nameEscapes) {
  199. return nameUnescaper.Replace(unsafeBytesToString(b))
  200. }
  201. return string(b)
  202. }
  203. // unsafeBytesToString converts a []byte to a string without a heap allocation.
  204. //
  205. // It is unsafe, and is intended to prepare input to short-lived functions
  206. // that require strings.
  207. func unsafeBytesToString(in []byte) string {
  208. src := *(*reflect.SliceHeader)(unsafe.Pointer(&in))
  209. dst := reflect.StringHeader{
  210. Data: src.Data,
  211. Len: src.Len,
  212. }
  213. s := *(*string)(unsafe.Pointer(&dst))
  214. return s
  215. }
  216. // parseIntBytes is a zero-alloc wrapper around strconv.ParseInt.
  217. func parseIntBytes(b []byte, base int, bitSize int) (i int64, err error) {
  218. s := unsafeBytesToString(b)
  219. return strconv.ParseInt(s, base, bitSize)
  220. }
  221. // parseUintBytes is a zero-alloc wrapper around strconv.ParseUint.
  222. func parseUintBytes(b []byte, base int, bitSize int) (i uint64, err error) {
  223. s := unsafeBytesToString(b)
  224. return strconv.ParseUint(s, base, bitSize)
  225. }
  226. // parseFloatBytes is a zero-alloc wrapper around strconv.ParseFloat.
  227. func parseFloatBytes(b []byte, bitSize int) (float64, error) {
  228. s := unsafeBytesToString(b)
  229. return strconv.ParseFloat(s, bitSize)
  230. }
  231. // parseBoolBytes is a zero-alloc wrapper around strconv.ParseBool.
  232. func parseBoolBytes(b []byte) (bool, error) {
  233. return strconv.ParseBool(unsafeBytesToString(b))
  234. }
  235. func stringFieldUnescape(b []byte) string {
  236. if bytes.ContainsAny(b, stringFieldEscapes) {
  237. return stringFieldUnescaper.Replace(unsafeBytesToString(b))
  238. }
  239. return string(b)
  240. }