aboutsummaryrefslogtreecommitdiffstats
path: root/vendor/github.com/influxdata/influxdb/pkg/escape/bytes.go
blob: f3b31f42d36841df4afd74bfaa8fe9b52440e235 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
// Package escape contains utilities for escaping parts of InfluxQL
// and InfluxDB line protocol.
package escape // import "github.com/influxdata/influxdb/pkg/escape"

import (
    "bytes"
    "strings"
)

// Codes is a map of bytes to be escaped.
var Codes = map[byte][]byte{
    ',': []byte(`\,`),
    '"': []byte(`\"`),
    ' ': []byte(`\ `),
    '=': []byte(`\=`),
}

// Bytes escapes characters on the input slice, as defined by Codes.
func Bytes(in []byte) []byte {
    for b, esc := range Codes {
        in = bytes.Replace(in, []byte{b}, esc, -1)
    }
    return in
}

const escapeChars = `," =`

// IsEscaped returns whether b has any escaped characters,
// i.e. whether b seems to have been processed by Bytes.
func IsEscaped(b []byte) bool {
    for len(b) > 0 {
        i := bytes.IndexByte(b, '\\')
        if i < 0 {
            return false
        }

        if i+1 < len(b) && strings.IndexByte(escapeChars, b[i+1]) >= 0 {
            return true
        }
        b = b[i+1:]
    }
    return false
}

// AppendUnescaped appends the unescaped version of src to dst
// and returns the resulting slice.
func AppendUnescaped(dst, src []byte) []byte {
    var pos int
    for len(src) > 0 {
        next := bytes.IndexByte(src[pos:], '\\')
        if next < 0 || pos+next+1 >= len(src) {
            return append(dst, src...)
        }

        if pos+next+1 < len(src) && strings.IndexByte(escapeChars, src[pos+next+1]) >= 0 {
            if pos+next > 0 {
                dst = append(dst, src[:pos+next]...)
            }
            src = src[pos+next+1:]
            pos = 0
        } else {
            pos += next + 1
        }
    }

    return dst
}

// Unescape returns a new slice containing the unescaped version of in.
func Unescape(in []byte) []byte {
    if len(in) == 0 {
        return nil
    }

    if bytes.IndexByte(in, '\\') == -1 {
        return in
    }

    i := 0
    inLen := len(in)

    // The output size will be no more than inLen. Preallocating the
    // capacity of the output is faster and uses less memory than
    // letting append() do its own (over)allocation.
    out := make([]byte, 0, inLen)

    for {
        if i >= inLen {
            break
        }
        if in[i] == '\\' && i+1 < inLen {
            switch in[i+1] {
            case ',':
                out = append(out, ',')
                i += 2
                continue
            case '"':
                out = append(out, '"')
                i += 2
                continue
            case ' ':
                out = append(out, ' ')
                i += 2
                continue
            case '=':
                out = append(out, '=')
                i += 2
                continue
            }
        }
        out = append(out, in[i])
        i += 1
    }
    return out
}