bytesconv.go 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297
  1. //go:generate go run bytesconv_table_gen.go
  2. package fasthttp
  3. import (
  4. "bufio"
  5. "bytes"
  6. "errors"
  7. "fmt"
  8. "io"
  9. "net"
  10. "strconv"
  11. "sync"
  12. "time"
  13. )
  14. // AppendHTMLEscape appends html-escaped s to dst and returns the extended dst.
  15. func AppendHTMLEscape(dst []byte, s string) []byte {
  16. var (
  17. prev int
  18. sub string
  19. )
  20. for i, n := 0, len(s); i < n; i++ {
  21. sub = ""
  22. switch s[i] {
  23. case '&':
  24. sub = "&amp;"
  25. case '<':
  26. sub = "&lt;"
  27. case '>':
  28. sub = "&gt;"
  29. case '"':
  30. sub = "&#34;" // "&#34;" is shorter than "&quot;".
  31. case '\'':
  32. sub = "&#39;" // "&#39;" is shorter than "&apos;" and apos was not in HTML until HTML5.
  33. }
  34. if sub != "" {
  35. dst = append(dst, s[prev:i]...)
  36. dst = append(dst, sub...)
  37. prev = i + 1
  38. }
  39. }
  40. return append(dst, s[prev:]...)
  41. }
  42. // AppendHTMLEscapeBytes appends html-escaped s to dst and returns
  43. // the extended dst.
  44. func AppendHTMLEscapeBytes(dst, s []byte) []byte {
  45. return AppendHTMLEscape(dst, b2s(s))
  46. }
  47. // AppendIPv4 appends string representation of the given ip v4 to dst
  48. // and returns the extended dst.
  49. func AppendIPv4(dst []byte, ip net.IP) []byte {
  50. ip = ip.To4()
  51. if ip == nil {
  52. return append(dst, "non-v4 ip passed to AppendIPv4"...)
  53. }
  54. dst = AppendUint(dst, int(ip[0]))
  55. for i := 1; i < 4; i++ {
  56. dst = append(dst, '.')
  57. dst = AppendUint(dst, int(ip[i]))
  58. }
  59. return dst
  60. }
  61. var errEmptyIPStr = errors.New("empty ip address string")
  62. // ParseIPv4 parses ip address from ipStr into dst and returns the extended dst.
  63. func ParseIPv4(dst net.IP, ipStr []byte) (net.IP, error) {
  64. if len(ipStr) == 0 {
  65. return dst, errEmptyIPStr
  66. }
  67. if len(dst) < net.IPv4len || len(dst) > net.IPv4len {
  68. dst = make([]byte, net.IPv4len)
  69. }
  70. copy(dst, net.IPv4zero)
  71. dst = dst.To4() // dst is always non-nil here
  72. b := ipStr
  73. for i := 0; i < 3; i++ {
  74. n := bytes.IndexByte(b, '.')
  75. if n < 0 {
  76. return dst, fmt.Errorf("cannot find dot in ipStr %q", ipStr)
  77. }
  78. v, err := ParseUint(b[:n])
  79. if err != nil {
  80. return dst, fmt.Errorf("cannot parse ipStr %q: %w", ipStr, err)
  81. }
  82. if v > 255 {
  83. return dst, fmt.Errorf("cannot parse ipStr %q: ip part cannot exceed 255: parsed %d", ipStr, v)
  84. }
  85. dst[i] = byte(v)
  86. b = b[n+1:]
  87. }
  88. v, err := ParseUint(b)
  89. if err != nil {
  90. return dst, fmt.Errorf("cannot parse ipStr %q: %w", ipStr, err)
  91. }
  92. if v > 255 {
  93. return dst, fmt.Errorf("cannot parse ipStr %q: ip part cannot exceed 255: parsed %d", ipStr, v)
  94. }
  95. dst[3] = byte(v)
  96. return dst, nil
  97. }
  98. // AppendHTTPDate appends HTTP-compliant (RFC1123) representation of date
  99. // to dst and returns the extended dst.
  100. func AppendHTTPDate(dst []byte, date time.Time) []byte {
  101. dst = date.In(time.UTC).AppendFormat(dst, time.RFC1123)
  102. copy(dst[len(dst)-3:], strGMT)
  103. return dst
  104. }
  105. // ParseHTTPDate parses HTTP-compliant (RFC1123) date.
  106. func ParseHTTPDate(date []byte) (time.Time, error) {
  107. return time.Parse(time.RFC1123, b2s(date))
  108. }
  109. // AppendUint appends n to dst and returns the extended dst.
  110. func AppendUint(dst []byte, n int) []byte {
  111. if n < 0 {
  112. // developer sanity-check
  113. panic("BUG: int must be positive")
  114. }
  115. return strconv.AppendUint(dst, uint64(n), 10)
  116. }
  117. // ParseUint parses uint from buf.
  118. func ParseUint(buf []byte) (int, error) {
  119. v, n, err := parseUintBuf(buf)
  120. if n != len(buf) {
  121. return -1, errUnexpectedTrailingChar
  122. }
  123. return v, err
  124. }
  125. var (
  126. errEmptyInt = errors.New("empty integer")
  127. errUnexpectedFirstChar = errors.New("unexpected first char found. Expecting 0-9")
  128. errUnexpectedTrailingChar = errors.New("unexpected trailing char found. Expecting 0-9")
  129. errTooLongInt = errors.New("too long int")
  130. )
  131. func parseUintBuf(b []byte) (int, int, error) {
  132. n := len(b)
  133. if n == 0 {
  134. return -1, 0, errEmptyInt
  135. }
  136. v := 0
  137. for i := 0; i < n; i++ {
  138. c := b[i]
  139. k := c - '0'
  140. if k > 9 {
  141. if i == 0 {
  142. return -1, i, errUnexpectedFirstChar
  143. }
  144. return v, i, nil
  145. }
  146. vNew := 10*v + int(k)
  147. // Test for overflow.
  148. if vNew < v {
  149. return -1, i, errTooLongInt
  150. }
  151. v = vNew
  152. }
  153. return v, n, nil
  154. }
  155. // ParseUfloat parses unsigned float from buf.
  156. func ParseUfloat(buf []byte) (float64, error) {
  157. // The implementation of parsing a float string is not easy.
  158. // We believe that the conservative approach is to call strconv.ParseFloat.
  159. // https://github.com/valyala/fasthttp/pull/1865
  160. res, err := strconv.ParseFloat(b2s(buf), 64)
  161. if res < 0 {
  162. return -1, errors.New("negative input is invalid")
  163. }
  164. if err != nil {
  165. return -1, err
  166. }
  167. return res, err
  168. }
  169. var (
  170. errEmptyHexNum = errors.New("empty hex number")
  171. errTooLargeHexNum = errors.New("too large hex number")
  172. )
  173. func readHexInt(r *bufio.Reader) (int, error) {
  174. var k, i, n int
  175. for {
  176. c, err := r.ReadByte()
  177. if err != nil {
  178. if err == io.EOF && i > 0 {
  179. return n, nil
  180. }
  181. return -1, err
  182. }
  183. k = int(hex2intTable[c])
  184. if k == 16 {
  185. if i == 0 {
  186. return -1, errEmptyHexNum
  187. }
  188. if err := r.UnreadByte(); err != nil {
  189. return -1, err
  190. }
  191. return n, nil
  192. }
  193. if i >= maxHexIntChars {
  194. return -1, errTooLargeHexNum
  195. }
  196. n = (n << 4) | k
  197. i++
  198. }
  199. }
  200. var hexIntBufPool sync.Pool
  201. func writeHexInt(w *bufio.Writer, n int) error {
  202. if n < 0 {
  203. // developer sanity-check
  204. panic("BUG: int must be positive")
  205. }
  206. v := hexIntBufPool.Get()
  207. if v == nil {
  208. v = make([]byte, maxHexIntChars+1)
  209. }
  210. buf := v.([]byte)
  211. i := len(buf) - 1
  212. for {
  213. buf[i] = lowerhex[n&0xf]
  214. n >>= 4
  215. if n == 0 {
  216. break
  217. }
  218. i--
  219. }
  220. _, err := w.Write(buf[i:])
  221. hexIntBufPool.Put(v)
  222. return err
  223. }
  224. const (
  225. upperhex = "0123456789ABCDEF"
  226. lowerhex = "0123456789abcdef"
  227. )
  228. func lowercaseBytes(b []byte) {
  229. for i := 0; i < len(b); i++ {
  230. p := &b[i]
  231. *p = toLowerTable[*p]
  232. }
  233. }
  234. // AppendUnquotedArg appends url-decoded src to dst and returns appended dst.
  235. //
  236. // dst may point to src. In this case src will be overwritten.
  237. func AppendUnquotedArg(dst, src []byte) []byte {
  238. return decodeArgAppend(dst, src)
  239. }
  240. // AppendQuotedArg appends url-encoded src to dst and returns appended dst.
  241. func AppendQuotedArg(dst, src []byte) []byte {
  242. for _, c := range src {
  243. switch {
  244. case c == ' ':
  245. dst = append(dst, '+')
  246. case quotedArgShouldEscapeTable[int(c)] != 0:
  247. dst = append(dst, '%', upperhex[c>>4], upperhex[c&0xf])
  248. default:
  249. dst = append(dst, c)
  250. }
  251. }
  252. return dst
  253. }
  254. func appendQuotedPath(dst, src []byte) []byte {
  255. // Fix issue in https://github.com/golang/go/issues/11202
  256. if len(src) == 1 && src[0] == '*' {
  257. return append(dst, '*')
  258. }
  259. for _, c := range src {
  260. if quotedPathShouldEscapeTable[int(c)] != 0 {
  261. dst = append(dst, '%', upperhex[c>>4], upperhex[c&0xf])
  262. } else {
  263. dst = append(dst, c)
  264. }
  265. }
  266. return dst
  267. }