[145] | 1 | //go:generate go run bytesconv_table_gen.go
|
---|
| 2 |
|
---|
| 3 | package fasthttp
|
---|
| 4 |
|
---|
| 5 | import (
|
---|
| 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.
|
---|
| 21 | func 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 = "<"
|
---|
| 39 | case '>':
|
---|
| 40 | sub = ">"
|
---|
| 41 | case '"':
|
---|
| 42 | sub = """
|
---|
| 43 | case '\'':
|
---|
| 44 | sub = "'"
|
---|
| 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.
|
---|
| 57 | func 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.
|
---|
| 63 | func 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 |
|
---|
| 77 | var errEmptyIPStr = errors.New("empty ip address string")
|
---|
| 78 |
|
---|
| 79 | // ParseIPv4 parses ip address from ipStr into dst and returns the extended dst.
|
---|
| 80 | func 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.
|
---|
| 123 | func 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.
|
---|
| 130 | func 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.
|
---|
| 135 | func 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.
|
---|
| 158 | func 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 |
|
---|
| 166 | var (
|
---|
| 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 |
|
---|
| 173 | func 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 |
|
---|
| 198 | var (
|
---|
| 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.
|
---|
| 207 | func 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 |
|
---|
| 255 | var (
|
---|
| 256 | errEmptyHexNum = errors.New("empty hex number")
|
---|
| 257 | errTooLargeHexNum = errors.New("too large hex number")
|
---|
| 258 | )
|
---|
| 259 |
|
---|
| 260 | func 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 |
|
---|
| 290 | var hexIntBufPool sync.Pool
|
---|
| 291 |
|
---|
| 292 | func 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 |
|
---|
| 316 | const (
|
---|
| 317 | upperhex = "0123456789ABCDEF"
|
---|
| 318 | lowerhex = "0123456789abcdef"
|
---|
| 319 | )
|
---|
| 320 |
|
---|
| 321 | func 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.
|
---|
| 333 | func 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.
|
---|
| 342 | func 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.
|
---|
| 356 | func AppendUnquotedArg(dst, src []byte) []byte {
|
---|
| 357 | return decodeArgAppend(dst, src)
|
---|
| 358 | }
|
---|
| 359 |
|
---|
| 360 | // AppendQuotedArg appends url-encoded src to dst and returns appended dst.
|
---|
| 361 | func 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 |
|
---|
| 375 | func 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 | }
|
---|