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