123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264 |
- package protocol
- import (
- "bytes"
- "reflect"
- "strconv"
- "strings"
- "unicode/utf8"
- "unsafe"
- )
- const (
- escapes = "\t\n\f\r ,="
- nameEscapes = "\t\n\f\r ,"
- stringFieldEscapes = "\t\n\f\r\\\""
- )
- var (
- stringEscaper = strings.NewReplacer(
- "\t", `\t`,
- "\n", `\n`,
- "\f", `\f`,
- "\r", `\r`,
- `,`, `\,`,
- ` `, `\ `,
- `=`, `\=`,
- )
- nameEscaper = strings.NewReplacer(
- "\t", `\t`,
- "\n", `\n`,
- "\f", `\f`,
- "\r", `\r`,
- `,`, `\,`,
- ` `, `\ `,
- )
- stringFieldEscaper = strings.NewReplacer(
- "\t", `\t`,
- "\n", `\n`,
- "\f", `\f`,
- "\r", `\r`,
- `"`, `\"`,
- `\`, `\\`,
- )
- )
- var (
- unescaper = strings.NewReplacer(
- `\,`, `,`,
- `\"`, `"`, // ???
- `\ `, ` `,
- `\=`, `=`,
- )
- nameUnescaper = strings.NewReplacer(
- `\,`, `,`,
- `\ `, ` `,
- )
- stringFieldUnescaper = strings.NewReplacer(
- `\"`, `"`,
- `\\`, `\`,
- )
- )
- // The various escape functions allocate, I'd like to fix that.
- // TODO: make escape not allocate
- // Escape a tagkey, tagvalue, or fieldkey
- func escape(s string) string {
- if strings.ContainsAny(s, escapes) {
- return stringEscaper.Replace(s)
- }
- return s
- }
- // Escape a measurement name
- func nameEscape(s string) string {
- if strings.ContainsAny(s, nameEscapes) {
- return nameEscaper.Replace(s)
- }
- return s
- }
- // Escape a string field
- func stringFieldEscape(s string) string {
- if strings.ContainsAny(s, stringFieldEscapes) {
- return stringFieldEscaper.Replace(s)
- }
- return s
- }
- const (
- utf8mask = byte(0x3F)
- utf8bytex = byte(0x80) // 1000 0000
- utf8len2 = byte(0xC0) // 1100 0000
- utf8len3 = byte(0xE0) // 1110 0000
- utf8len4 = byte(0xF0) // 1111 0000
- )
- func escapeBytes(dest *[]byte, b []byte) {
- if bytes.ContainsAny(b, escapes) {
- var r rune
- for i, j := 0, 0; i < len(b); i += j {
- r, j = utf8.DecodeRune(b[i:])
- switch {
- case r == '\t':
- *dest = append(*dest, `\t`...)
- case r == '\n':
- *dest = append(*dest, `\n`...)
- case r == '\f':
- *dest = append(*dest, `\f`...)
- case r == '\r':
- *dest = append(*dest, `\r`...)
- case r == ',':
- *dest = append(*dest, `\,`...)
- case r == ' ':
- *dest = append(*dest, `\ `...)
- case r == '=':
- *dest = append(*dest, `\=`...)
- case r <= 1<<7-1:
- *dest = append(*dest, byte(r))
- case r <= 1<<11-1:
- *dest = append(*dest, utf8len2|byte(r>>6), utf8bytex|byte(r)&utf8mask)
- case r <= 1<<16-1:
- *dest = append(*dest, utf8len3|byte(r>>12), utf8bytex|byte(r>>6)&utf8mask, utf8bytex|byte(r)&utf8mask)
- default:
- *dest = append(*dest, utf8len4|byte(r>>18), utf8bytex|byte(r>>12)&utf8mask, utf8bytex|byte(r>>6)&utf8mask, utf8bytex|byte(r)&utf8mask)
- }
- }
- return
- }
- *dest = append(*dest, b...)
- }
- // Escape a measurement name
- func nameEscapeBytes(dest *[]byte, b []byte) {
- if bytes.ContainsAny(b, nameEscapes) {
- var r rune
- for i, j := 0, 0; i < len(b); i += j {
- r, j = utf8.DecodeRune(b[i:])
- switch {
- case r == '\t':
- *dest = append(*dest, `\t`...)
- case r == '\n':
- *dest = append(*dest, `\n`...)
- case r == '\f':
- *dest = append(*dest, `\f`...)
- case r == '\r':
- *dest = append(*dest, `\r`...)
- case r == ',':
- *dest = append(*dest, `\,`...)
- case r == ' ':
- *dest = append(*dest, `\ `...)
- case r == '\\':
- *dest = append(*dest, `\\`...)
- case r <= 1<<7-1:
- *dest = append(*dest, byte(r))
- case r <= 1<<11-1:
- *dest = append(*dest, utf8len2|byte(r>>6), utf8bytex|byte(r)&utf8mask)
- case r <= 1<<16-1:
- *dest = append(*dest, utf8len3|byte(r>>12), utf8bytex|byte(r>>6)&utf8mask, utf8bytex|byte(r)&utf8mask)
- default:
- *dest = append(*dest, utf8len4|byte(r>>18), utf8bytex|byte(r>>12)&utf8mask, utf8bytex|byte(r>>6)&utf8mask, utf8bytex|byte(r)&utf8mask)
- }
- }
- return
- }
- *dest = append(*dest, b...)
- }
- func stringFieldEscapeBytes(dest *[]byte, b []byte) {
- if bytes.ContainsAny(b, stringFieldEscapes) {
- var r rune
- for i, j := 0, 0; i < len(b); i += j {
- r, j = utf8.DecodeRune(b[i:])
- switch {
- case r == '\t':
- *dest = append(*dest, `\t`...)
- case r == '\n':
- *dest = append(*dest, `\n`...)
- case r == '\f':
- *dest = append(*dest, `\f`...)
- case r == '\r':
- *dest = append(*dest, `\r`...)
- case r == ',':
- *dest = append(*dest, `\,`...)
- case r == ' ':
- *dest = append(*dest, `\ `...)
- case r == '\\':
- *dest = append(*dest, `\\`...)
- case r <= 1<<7-1:
- *dest = append(*dest, byte(r))
- case r <= 1<<11-1:
- *dest = append(*dest, utf8len2|byte(r>>6), utf8bytex|byte(r)&utf8mask)
- case r <= 1<<16-1:
- *dest = append(*dest, utf8len3|byte(r>>12), utf8bytex|byte(r>>6)&utf8mask, utf8bytex|byte(r)&utf8mask)
- default:
- *dest = append(*dest, utf8len4|byte(r>>18), utf8bytex|byte(r>>12)&utf8mask, utf8bytex|byte(r>>6)&utf8mask, utf8bytex|byte(r)&utf8mask)
- }
- }
- return
- }
- *dest = append(*dest, b...)
- }
- func unescape(b []byte) string {
- if bytes.ContainsAny(b, escapes) {
- return unescaper.Replace(unsafeBytesToString(b))
- }
- return string(b)
- }
- func nameUnescape(b []byte) string {
- if bytes.ContainsAny(b, nameEscapes) {
- return nameUnescaper.Replace(unsafeBytesToString(b))
- }
- return string(b)
- }
- // unsafeBytesToString converts a []byte to a string without a heap allocation.
- //
- // It is unsafe, and is intended to prepare input to short-lived functions
- // that require strings.
- func unsafeBytesToString(in []byte) string {
- src := *(*reflect.SliceHeader)(unsafe.Pointer(&in))
- dst := reflect.StringHeader{
- Data: src.Data,
- Len: src.Len,
- }
- s := *(*string)(unsafe.Pointer(&dst))
- return s
- }
- // parseIntBytes is a zero-alloc wrapper around strconv.ParseInt.
- func parseIntBytes(b []byte, base int, bitSize int) (i int64, err error) {
- s := unsafeBytesToString(b)
- return strconv.ParseInt(s, base, bitSize)
- }
- // parseUintBytes is a zero-alloc wrapper around strconv.ParseUint.
- func parseUintBytes(b []byte, base int, bitSize int) (i uint64, err error) {
- s := unsafeBytesToString(b)
- return strconv.ParseUint(s, base, bitSize)
- }
- // parseFloatBytes is a zero-alloc wrapper around strconv.ParseFloat.
- func parseFloatBytes(b []byte, bitSize int) (float64, error) {
- s := unsafeBytesToString(b)
- return strconv.ParseFloat(s, bitSize)
- }
- // parseBoolBytes is a zero-alloc wrapper around strconv.ParseBool.
- func parseBoolBytes(b []byte) (bool, error) {
- return strconv.ParseBool(unsafeBytesToString(b))
- }
- func stringFieldUnescape(b []byte) string {
- if bytes.ContainsAny(b, stringFieldEscapes) {
- return stringFieldUnescaper.Replace(unsafeBytesToString(b))
- }
- return string(b)
- }
|