diff --git a/bytesconv.go b/bytesconv.go index d4fba6717c..2766794694 100644 --- a/bytesconv.go +++ b/bytesconv.go @@ -1,3 +1,5 @@ +//go:generate go run bytesconv_table_gen.go + package fasthttp import ( @@ -308,52 +310,11 @@ func writeHexInt(w *bufio.Writer, n int) error { return err } -var hex2intTable = func() []byte { - b := make([]byte, 256) - for i := 0; i < 256; i++ { - c := byte(16) - if i >= '0' && i <= '9' { - c = byte(i) - '0' - } else if i >= 'a' && i <= 'f' { - c = byte(i) - 'a' + 10 - } else if i >= 'A' && i <= 'F' { - c = byte(i) - 'A' + 10 - } - b[i] = c - } - return b -}() - const ( - toLower = 'a' - 'A' upperhex = "0123456789ABCDEF" lowerhex = "0123456789abcdef" ) -var toLowerTable = func() [256]byte { - var a [256]byte - for i := 0; i < 256; i++ { - c := byte(i) - if c >= 'A' && c <= 'Z' { - c += toLower - } - a[i] = c - } - return a -}() - -var toUpperTable = func() [256]byte { - var a [256]byte - for i := 0; i < 256; i++ { - c := byte(i) - if c >= 'a' && c <= 'z' { - c -= toLower - } - a[i] = c - } - return a -}() - func lowercaseBytes(b []byte) { for i := 0; i < len(b); i++ { p := &b[i] @@ -383,58 +344,6 @@ func s2b(s string) (b []byte) { return b } -var quotedArgShouldEscapeTable = func() [256]bool { - // According to RFC 3986 §2.3 - var a [256]bool - for i := 0; i < 256; i++ { - a[i] = true - } - - // ALPHA - for i := int('a'); i <= int('z'); i++ { - a[i] = false - } - for i := int('A'); i <= int('Z'); i++ { - a[i] = false - } - - // DIGIT - for i := int('0'); i <= int('9'); i++ { - a[i] = false - } - - // Unreserved characters - a[int('-')] = false - a[int('_')] = false - a[int('.')] = false - a[int('~')] = false - - return a -}() - -var quotedPathShouldEscapeTable = func() [256]bool { - // The implementation here equal to net/url shouldEscape(s, encodePath) - // - // The RFC allows : @ & = + $ but saves / ; , for assigning - // meaning to individual path segments. This package - // only manipulates the path as a whole, so we allow those - // last three as well. That leaves only ? to escape. - var a = quotedArgShouldEscapeTable - - // '$', '&', '+', ',', '/', ':', ';', '=', '@' - a[int('$')] = false - a[int('&')] = false - a[int('+')] = false - a[int(',')] = false - a[int('/')] = false - a[int(':')] = false - a[int(';')] = false - a[int('=')] = false - a[int('@')] = false - - return a -}() - // AppendUnquotedArg appends url-decoded src to dst and returns appended dst. // // dst may point to src. In this case src will be overwritten. @@ -448,7 +357,7 @@ func AppendQuotedArg(dst, src []byte) []byte { switch { case c == ' ': dst = append(dst, '+') - case quotedArgShouldEscapeTable[int(c)]: + case quotedArgShouldEscapeTable[int(c)] != 0: dst = append(dst, '%', upperhex[c>>4], upperhex[c&0xf]) default: dst = append(dst, c) @@ -464,7 +373,7 @@ func appendQuotedPath(dst, src []byte) []byte { } for _, c := range src { - if quotedPathShouldEscapeTable[int(c)] { + if quotedPathShouldEscapeTable[int(c)] != 0 { dst = append(dst, '%', upperhex[c>>4], upperhex[c&15]) } else { dst = append(dst, c) diff --git a/bytesconv_table.go b/bytesconv_table.go new file mode 100644 index 0000000000..509743f167 --- /dev/null +++ b/bytesconv_table.go @@ -0,0 +1,9 @@ +// Code generated by go run bytesconv_table_gen.go; DO NOT EDIT. +// See bytesconv_table_gen.go for more information about these tables. +package fasthttp + +const hex2intTable = "\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x00\x01\x02\x03\x04\x05\x06\a\b\t\x10\x10\x10\x10\x10\x10\x10\n\v\f\r\x0e\x0f\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\n\v\f\r\x0e\x0f\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10" +const toLowerTable = "\x00\x01\x02\x03\x04\x05\x06\a\b\t\n\v\f\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !\"#$%&'()*+,-./0123456789:;<=>?@abcdefghijklmnopqrstuvwxyz[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\u007f\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff" +const toUpperTable = "\x00\x01\x02\x03\x04\x05\x06\a\b\t\n\v\f\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`ABCDEFGHIJKLMNOPQRSTUVWXYZ{|}~\u007f\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff" +const quotedArgShouldEscapeTable = "\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x01\x01\x01\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x01\x01\x01\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x01\x01\x00\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01" +const quotedPathShouldEscapeTable = "\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x00\x01\x00\x01\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x01\x01\x01\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x01\x01\x00\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01" diff --git a/bytesconv_table_gen.go b/bytesconv_table_gen.go new file mode 100644 index 0000000000..5561d3eac1 --- /dev/null +++ b/bytesconv_table_gen.go @@ -0,0 +1,118 @@ +// +build ignore + +package main + +import ( + "bytes" + "fmt" + "io/ioutil" + "log" +) + +const ( + toLower = 'a' - 'A' +) + +func main() { + hex2intTable := func() [256]byte { + var b [256]byte + for i := 0; i < 256; i++ { + c := byte(16) + if i >= '0' && i <= '9' { + c = byte(i) - '0' + } else if i >= 'a' && i <= 'f' { + c = byte(i) - 'a' + 10 + } else if i >= 'A' && i <= 'F' { + c = byte(i) - 'A' + 10 + } + b[i] = c + } + return b + }() + + toLowerTable := func() [256]byte { + var a [256]byte + for i := 0; i < 256; i++ { + c := byte(i) + if c >= 'A' && c <= 'Z' { + c += toLower + } + a[i] = c + } + return a + }() + + toUpperTable := func() [256]byte { + var a [256]byte + for i := 0; i < 256; i++ { + c := byte(i) + if c >= 'a' && c <= 'z' { + c -= toLower + } + a[i] = c + } + return a + }() + + quotedArgShouldEscapeTable := func() [256]byte { + // According to RFC 3986 §2.3 + var a [256]byte + for i := 0; i < 256; i++ { + a[i] = 1 + } + + // ALPHA + for i := int('a'); i <= int('z'); i++ { + a[i] = 0 + } + for i := int('A'); i <= int('Z'); i++ { + a[i] = 0 + } + + // DIGIT + for i := int('0'); i <= int('9'); i++ { + a[i] = 0 + } + + // Unreserved characters + for _, v := range `-_.~` { + a[v] = 0 + } + + return a + }() + + quotedPathShouldEscapeTable := func() [256]byte { + // The implementation here equal to net/url shouldEscape(s, encodePath) + // + // The RFC allows : @ & = + $ but saves / ; , for assigning + // meaning to individual path segments. This package + // only manipulates the path as a whole, so we allow those + // last three as well. That leaves only ? to escape. + var a = quotedArgShouldEscapeTable + + for _, v := range `$&+,/:;=@` { + a[v] = 0 + } + + return a + }() + + w := new(bytes.Buffer) + w.WriteString(pre) + fmt.Fprintf(w, "const hex2intTable = %q\n", hex2intTable) + fmt.Fprintf(w, "const toLowerTable = %q\n", toLowerTable) + fmt.Fprintf(w, "const toUpperTable = %q\n", toUpperTable) + fmt.Fprintf(w, "const quotedArgShouldEscapeTable = %q\n", quotedArgShouldEscapeTable) + fmt.Fprintf(w, "const quotedPathShouldEscapeTable = %q\n", quotedPathShouldEscapeTable) + + if err := ioutil.WriteFile("bytesconv_table.go", w.Bytes(), 0660); err != nil { + log.Fatal(err) + } +} + +const pre = `// Code generated by go run bytesconv_table_gen.go; DO NOT EDIT. +// See bytesconv_table_gen.go for more information about these tables. +package fasthttp + +`