bytesconv.go 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353
  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. "math"
  10. "net"
  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. var b [20]byte
  116. buf := b[:]
  117. i := len(buf)
  118. var q int
  119. for n >= 10 {
  120. i--
  121. q = n / 10
  122. buf[i] = '0' + byte(n-q*10)
  123. n = q
  124. }
  125. i--
  126. buf[i] = '0' + byte(n)
  127. dst = append(dst, buf[i:]...)
  128. return dst
  129. }
  130. // ParseUint parses uint from buf.
  131. func ParseUint(buf []byte) (int, error) {
  132. v, n, err := parseUintBuf(buf)
  133. if n != len(buf) {
  134. return -1, errUnexpectedTrailingChar
  135. }
  136. return v, err
  137. }
  138. var (
  139. errEmptyInt = errors.New("empty integer")
  140. errUnexpectedFirstChar = errors.New("unexpected first char found. Expecting 0-9")
  141. errUnexpectedTrailingChar = errors.New("unexpected trailing char found. Expecting 0-9")
  142. errTooLongInt = errors.New("too long int")
  143. )
  144. func parseUintBuf(b []byte) (int, int, error) {
  145. n := len(b)
  146. if n == 0 {
  147. return -1, 0, errEmptyInt
  148. }
  149. v := 0
  150. for i := 0; i < n; i++ {
  151. c := b[i]
  152. k := c - '0'
  153. if k > 9 {
  154. if i == 0 {
  155. return -1, i, errUnexpectedFirstChar
  156. }
  157. return v, i, nil
  158. }
  159. vNew := 10*v + int(k)
  160. // Test for overflow.
  161. if vNew < v {
  162. return -1, i, errTooLongInt
  163. }
  164. v = vNew
  165. }
  166. return v, n, nil
  167. }
  168. var (
  169. errEmptyFloat = errors.New("empty float number")
  170. errDuplicateFloatPoint = errors.New("duplicate point found in float number")
  171. errUnexpectedFloatEnd = errors.New("unexpected end of float number")
  172. errInvalidFloatExponent = errors.New("invalid float number exponent")
  173. errUnexpectedFloatChar = errors.New("unexpected char found in float number")
  174. )
  175. // ParseUfloat parses unsigned float from buf.
  176. func ParseUfloat(buf []byte) (float64, error) {
  177. if len(buf) == 0 {
  178. return -1, errEmptyFloat
  179. }
  180. b := buf
  181. var v uint64
  182. offset := 1.0
  183. var pointFound bool
  184. for i, c := range b {
  185. if c < '0' || c > '9' {
  186. if c == '.' {
  187. if pointFound {
  188. return -1, errDuplicateFloatPoint
  189. }
  190. pointFound = true
  191. continue
  192. }
  193. if c == 'e' || c == 'E' {
  194. if i+1 >= len(b) {
  195. return -1, errUnexpectedFloatEnd
  196. }
  197. b = b[i+1:]
  198. minus := -1
  199. switch b[0] {
  200. case '+':
  201. b = b[1:]
  202. minus = 1
  203. case '-':
  204. b = b[1:]
  205. default:
  206. minus = 1
  207. }
  208. vv, err := ParseUint(b)
  209. if err != nil {
  210. return -1, errInvalidFloatExponent
  211. }
  212. return float64(v) * offset * math.Pow10(minus*vv), nil
  213. }
  214. return -1, errUnexpectedFloatChar
  215. }
  216. v = 10*v + uint64(c-'0')
  217. if pointFound {
  218. offset /= 10
  219. }
  220. }
  221. return float64(v) * offset, nil
  222. }
  223. var (
  224. errEmptyHexNum = errors.New("empty hex number")
  225. errTooLargeHexNum = errors.New("too large hex number")
  226. )
  227. func readHexInt(r *bufio.Reader) (int, error) {
  228. var k, i, n int
  229. for {
  230. c, err := r.ReadByte()
  231. if err != nil {
  232. if err == io.EOF && i > 0 {
  233. return n, nil
  234. }
  235. return -1, err
  236. }
  237. k = int(hex2intTable[c])
  238. if k == 16 {
  239. if i == 0 {
  240. return -1, errEmptyHexNum
  241. }
  242. if err := r.UnreadByte(); err != nil {
  243. return -1, err
  244. }
  245. return n, nil
  246. }
  247. if i >= maxHexIntChars {
  248. return -1, errTooLargeHexNum
  249. }
  250. n = (n << 4) | k
  251. i++
  252. }
  253. }
  254. var hexIntBufPool sync.Pool
  255. func writeHexInt(w *bufio.Writer, n int) error {
  256. if n < 0 {
  257. // developer sanity-check
  258. panic("BUG: int must be positive")
  259. }
  260. v := hexIntBufPool.Get()
  261. if v == nil {
  262. v = make([]byte, maxHexIntChars+1)
  263. }
  264. buf := v.([]byte)
  265. i := len(buf) - 1
  266. for {
  267. buf[i] = lowerhex[n&0xf]
  268. n >>= 4
  269. if n == 0 {
  270. break
  271. }
  272. i--
  273. }
  274. _, err := w.Write(buf[i:])
  275. hexIntBufPool.Put(v)
  276. return err
  277. }
  278. const (
  279. upperhex = "0123456789ABCDEF"
  280. lowerhex = "0123456789abcdef"
  281. )
  282. func lowercaseBytes(b []byte) {
  283. for i := 0; i < len(b); i++ {
  284. p := &b[i]
  285. *p = toLowerTable[*p]
  286. }
  287. }
  288. // AppendUnquotedArg appends url-decoded src to dst and returns appended dst.
  289. //
  290. // dst may point to src. In this case src will be overwritten.
  291. func AppendUnquotedArg(dst, src []byte) []byte {
  292. return decodeArgAppend(dst, src)
  293. }
  294. // AppendQuotedArg appends url-encoded src to dst and returns appended dst.
  295. func AppendQuotedArg(dst, src []byte) []byte {
  296. for _, c := range src {
  297. switch {
  298. case c == ' ':
  299. dst = append(dst, '+')
  300. case quotedArgShouldEscapeTable[int(c)] != 0:
  301. dst = append(dst, '%', upperhex[c>>4], upperhex[c&0xf])
  302. default:
  303. dst = append(dst, c)
  304. }
  305. }
  306. return dst
  307. }
  308. func appendQuotedPath(dst, src []byte) []byte {
  309. // Fix issue in https://github.com/golang/go/issues/11202
  310. if len(src) == 1 && src[0] == '*' {
  311. return append(dst, '*')
  312. }
  313. for _, c := range src {
  314. if quotedPathShouldEscapeTable[int(c)] != 0 {
  315. dst = append(dst, '%', upperhex[c>>4], upperhex[c&0xf])
  316. } else {
  317. dst = append(dst, c)
  318. }
  319. }
  320. return dst
  321. }