source: code/trunk/vendor/github.com/valyala/fasthttp/bytesconv.go@ 145

Last change on this file since 145 was 145, checked in by Izuru Yakumo, 22 months ago

Updated the Makefile and vendored depedencies

Signed-off-by: Izuru Yakumo <yakumo.izuru@…>

File size: 8.2 KB
RevLine 
[145]1//go:generate go run bytesconv_table_gen.go
2
3package fasthttp
4
5import (
6 "bufio"
7 "bytes"
8 "errors"
9 "fmt"
10 "io"
11 "math"
12 "net"
13 "reflect"
14 "strings"
15 "sync"
16 "time"
17 "unsafe"
18)
19
20// AppendHTMLEscape appends html-escaped s to dst and returns the extended dst.
21func AppendHTMLEscape(dst []byte, s string) []byte {
22 if strings.IndexByte(s, '<') < 0 &&
23 strings.IndexByte(s, '>') < 0 &&
24 strings.IndexByte(s, '"') < 0 &&
25 strings.IndexByte(s, '\'') < 0 {
26
27 // fast path - nothing to escape
28 return append(dst, s...)
29 }
30
31 // slow path
32 var prev int
33 var sub string
34 for i, n := 0, len(s); i < n; i++ {
35 sub = ""
36 switch s[i] {
37 case '<':
38 sub = "&lt;"
39 case '>':
40 sub = "&gt;"
41 case '"':
42 sub = "&quot;"
43 case '\'':
44 sub = "&#39;"
45 }
46 if len(sub) > 0 {
47 dst = append(dst, s[prev:i]...)
48 dst = append(dst, sub...)
49 prev = i + 1
50 }
51 }
52 return append(dst, s[prev:]...)
53}
54
55// AppendHTMLEscapeBytes appends html-escaped s to dst and returns
56// the extended dst.
57func AppendHTMLEscapeBytes(dst, s []byte) []byte {
58 return AppendHTMLEscape(dst, b2s(s))
59}
60
61// AppendIPv4 appends string representation of the given ip v4 to dst
62// and returns the extended dst.
63func AppendIPv4(dst []byte, ip net.IP) []byte {
64 ip = ip.To4()
65 if ip == nil {
66 return append(dst, "non-v4 ip passed to AppendIPv4"...)
67 }
68
69 dst = AppendUint(dst, int(ip[0]))
70 for i := 1; i < 4; i++ {
71 dst = append(dst, '.')
72 dst = AppendUint(dst, int(ip[i]))
73 }
74 return dst
75}
76
77var errEmptyIPStr = errors.New("empty ip address string")
78
79// ParseIPv4 parses ip address from ipStr into dst and returns the extended dst.
80func ParseIPv4(dst net.IP, ipStr []byte) (net.IP, error) {
81 if len(ipStr) == 0 {
82 return dst, errEmptyIPStr
83 }
84 if len(dst) < net.IPv4len {
85 dst = make([]byte, net.IPv4len)
86 }
87 copy(dst, net.IPv4zero)
88 dst = dst.To4()
89 if dst == nil {
90 panic("BUG: dst must not be nil")
91 }
92
93 b := ipStr
94 for i := 0; i < 3; i++ {
95 n := bytes.IndexByte(b, '.')
96 if n < 0 {
97 return dst, fmt.Errorf("cannot find dot in ipStr %q", ipStr)
98 }
99 v, err := ParseUint(b[:n])
100 if err != nil {
101 return dst, fmt.Errorf("cannot parse ipStr %q: %w", ipStr, err)
102 }
103 if v > 255 {
104 return dst, fmt.Errorf("cannot parse ipStr %q: ip part cannot exceed 255: parsed %d", ipStr, v)
105 }
106 dst[i] = byte(v)
107 b = b[n+1:]
108 }
109 v, err := ParseUint(b)
110 if err != nil {
111 return dst, fmt.Errorf("cannot parse ipStr %q: %w", ipStr, err)
112 }
113 if v > 255 {
114 return dst, fmt.Errorf("cannot parse ipStr %q: ip part cannot exceed 255: parsed %d", ipStr, v)
115 }
116 dst[3] = byte(v)
117
118 return dst, nil
119}
120
121// AppendHTTPDate appends HTTP-compliant (RFC1123) representation of date
122// to dst and returns the extended dst.
123func AppendHTTPDate(dst []byte, date time.Time) []byte {
124 dst = date.In(time.UTC).AppendFormat(dst, time.RFC1123)
125 copy(dst[len(dst)-3:], strGMT)
126 return dst
127}
128
129// ParseHTTPDate parses HTTP-compliant (RFC1123) date.
130func ParseHTTPDate(date []byte) (time.Time, error) {
131 return time.Parse(time.RFC1123, b2s(date))
132}
133
134// AppendUint appends n to dst and returns the extended dst.
135func AppendUint(dst []byte, n int) []byte {
136 if n < 0 {
137 panic("BUG: int must be positive")
138 }
139
140 var b [20]byte
141 buf := b[:]
142 i := len(buf)
143 var q int
144 for n >= 10 {
145 i--
146 q = n / 10
147 buf[i] = '0' + byte(n-q*10)
148 n = q
149 }
150 i--
151 buf[i] = '0' + byte(n)
152
153 dst = append(dst, buf[i:]...)
154 return dst
155}
156
157// ParseUint parses uint from buf.
158func ParseUint(buf []byte) (int, error) {
159 v, n, err := parseUintBuf(buf)
160 if n != len(buf) {
161 return -1, errUnexpectedTrailingChar
162 }
163 return v, err
164}
165
166var (
167 errEmptyInt = errors.New("empty integer")
168 errUnexpectedFirstChar = errors.New("unexpected first char found. Expecting 0-9")
169 errUnexpectedTrailingChar = errors.New("unexpected trailing char found. Expecting 0-9")
170 errTooLongInt = errors.New("too long int")
171)
172
173func parseUintBuf(b []byte) (int, int, error) {
174 n := len(b)
175 if n == 0 {
176 return -1, 0, errEmptyInt
177 }
178 v := 0
179 for i := 0; i < n; i++ {
180 c := b[i]
181 k := c - '0'
182 if k > 9 {
183 if i == 0 {
184 return -1, i, errUnexpectedFirstChar
185 }
186 return v, i, nil
187 }
188 vNew := 10*v + int(k)
189 // Test for overflow.
190 if vNew < v {
191 return -1, i, errTooLongInt
192 }
193 v = vNew
194 }
195 return v, n, nil
196}
197
198var (
199 errEmptyFloat = errors.New("empty float number")
200 errDuplicateFloatPoint = errors.New("duplicate point found in float number")
201 errUnexpectedFloatEnd = errors.New("unexpected end of float number")
202 errInvalidFloatExponent = errors.New("invalid float number exponent")
203 errUnexpectedFloatChar = errors.New("unexpected char found in float number")
204)
205
206// ParseUfloat parses unsigned float from buf.
207func ParseUfloat(buf []byte) (float64, error) {
208 if len(buf) == 0 {
209 return -1, errEmptyFloat
210 }
211 b := buf
212 var v uint64
213 var offset = 1.0
214 var pointFound bool
215 for i, c := range b {
216 if c < '0' || c > '9' {
217 if c == '.' {
218 if pointFound {
219 return -1, errDuplicateFloatPoint
220 }
221 pointFound = true
222 continue
223 }
224 if c == 'e' || c == 'E' {
225 if i+1 >= len(b) {
226 return -1, errUnexpectedFloatEnd
227 }
228 b = b[i+1:]
229 minus := -1
230 switch b[0] {
231 case '+':
232 b = b[1:]
233 minus = 1
234 case '-':
235 b = b[1:]
236 default:
237 minus = 1
238 }
239 vv, err := ParseUint(b)
240 if err != nil {
241 return -1, errInvalidFloatExponent
242 }
243 return float64(v) * offset * math.Pow10(minus*int(vv)), nil
244 }
245 return -1, errUnexpectedFloatChar
246 }
247 v = 10*v + uint64(c-'0')
248 if pointFound {
249 offset /= 10
250 }
251 }
252 return float64(v) * offset, nil
253}
254
255var (
256 errEmptyHexNum = errors.New("empty hex number")
257 errTooLargeHexNum = errors.New("too large hex number")
258)
259
260func readHexInt(r *bufio.Reader) (int, error) {
261 n := 0
262 i := 0
263 var k int
264 for {
265 c, err := r.ReadByte()
266 if err != nil {
267 if err == io.EOF && i > 0 {
268 return n, nil
269 }
270 return -1, err
271 }
272 k = int(hex2intTable[c])
273 if k == 16 {
274 if i == 0 {
275 return -1, errEmptyHexNum
276 }
277 if err := r.UnreadByte(); err != nil {
278 return -1, err
279 }
280 return n, nil
281 }
282 if i >= maxHexIntChars {
283 return -1, errTooLargeHexNum
284 }
285 n = (n << 4) | k
286 i++
287 }
288}
289
290var hexIntBufPool sync.Pool
291
292func writeHexInt(w *bufio.Writer, n int) error {
293 if n < 0 {
294 panic("BUG: int must be positive")
295 }
296
297 v := hexIntBufPool.Get()
298 if v == nil {
299 v = make([]byte, maxHexIntChars+1)
300 }
301 buf := v.([]byte)
302 i := len(buf) - 1
303 for {
304 buf[i] = lowerhex[n&0xf]
305 n >>= 4
306 if n == 0 {
307 break
308 }
309 i--
310 }
311 _, err := w.Write(buf[i:])
312 hexIntBufPool.Put(v)
313 return err
314}
315
316const (
317 upperhex = "0123456789ABCDEF"
318 lowerhex = "0123456789abcdef"
319)
320
321func lowercaseBytes(b []byte) {
322 for i := 0; i < len(b); i++ {
323 p := &b[i]
324 *p = toLowerTable[*p]
325 }
326}
327
328// b2s converts byte slice to a string without memory allocation.
329// See https://groups.google.com/forum/#!msg/Golang-Nuts/ENgbUzYvCuU/90yGx7GUAgAJ .
330//
331// Note it may break if string and/or slice header will change
332// in the future go versions.
333func b2s(b []byte) string {
334 /* #nosec G103 */
335 return *(*string)(unsafe.Pointer(&b))
336}
337
338// s2b converts string to a byte slice without memory allocation.
339//
340// Note it may break if string and/or slice header will change
341// in the future go versions.
342func s2b(s string) (b []byte) {
343 /* #nosec G103 */
344 bh := (*reflect.SliceHeader)(unsafe.Pointer(&b))
345 /* #nosec G103 */
346 sh := (*reflect.StringHeader)(unsafe.Pointer(&s))
347 bh.Data = sh.Data
348 bh.Cap = sh.Len
349 bh.Len = sh.Len
350 return b
351}
352
353// AppendUnquotedArg appends url-decoded src to dst and returns appended dst.
354//
355// dst may point to src. In this case src will be overwritten.
356func AppendUnquotedArg(dst, src []byte) []byte {
357 return decodeArgAppend(dst, src)
358}
359
360// AppendQuotedArg appends url-encoded src to dst and returns appended dst.
361func AppendQuotedArg(dst, src []byte) []byte {
362 for _, c := range src {
363 switch {
364 case c == ' ':
365 dst = append(dst, '+')
366 case quotedArgShouldEscapeTable[int(c)] != 0:
367 dst = append(dst, '%', upperhex[c>>4], upperhex[c&0xf])
368 default:
369 dst = append(dst, c)
370 }
371 }
372 return dst
373}
374
375func appendQuotedPath(dst, src []byte) []byte {
376 // Fix issue in https://github.com/golang/go/issues/11202
377 if len(src) == 1 && src[0] == '*' {
378 return append(dst, '*')
379 }
380
381 for _, c := range src {
382 if quotedPathShouldEscapeTable[int(c)] != 0 {
383 dst = append(dst, '%', upperhex[c>>4], upperhex[c&0xf])
384 } else {
385 dst = append(dst, c)
386 }
387 }
388 return dst
389}
Note: See TracBrowser for help on using the repository browser.