github.com/ezoic/ws@v1.0.4-0.20220713205711-5c1d69e074c5/util.go (about)

     1  package ws
     2  
     3  import (
     4  	"bufio"
     5  	"bytes"
     6  	"fmt"
     7  	"reflect"
     8  	"unsafe"
     9  
    10  	"github.com/ezoic/httphead"
    11  )
    12  
    13  // SelectFromSlice creates accept function that could be used as Protocol/Extension
    14  // select during upgrade.
    15  func SelectFromSlice(accept []string) func(string) bool {
    16  	if len(accept) > 16 {
    17  		mp := make(map[string]struct{}, len(accept))
    18  		for _, p := range accept {
    19  			mp[p] = struct{}{}
    20  		}
    21  		return func(p string) bool {
    22  			_, ok := mp[p]
    23  			return ok
    24  		}
    25  	}
    26  	return func(p string) bool {
    27  		for _, ok := range accept {
    28  			if p == ok {
    29  				return true
    30  			}
    31  		}
    32  		return false
    33  	}
    34  }
    35  
    36  // SelectEqual creates accept function that could be used as Protocol/Extension
    37  // select during upgrade.
    38  func SelectEqual(v string) func(string) bool {
    39  	return func(p string) bool {
    40  		return v == p
    41  	}
    42  }
    43  
    44  func strToBytes(str string) (bts []byte) {
    45  	s := (*reflect.StringHeader)(unsafe.Pointer(&str))
    46  	b := (*reflect.SliceHeader)(unsafe.Pointer(&bts))
    47  	b.Data = s.Data
    48  	b.Len = s.Len
    49  	b.Cap = s.Len
    50  	return
    51  }
    52  
    53  func btsToString(bts []byte) (str string) {
    54  	return *(*string)(unsafe.Pointer(&bts))
    55  }
    56  
    57  // asciiToInt converts bytes to int.
    58  func asciiToInt(bts []byte) (ret int, err error) {
    59  	// ASCII numbers all start with the high-order bits 0011.
    60  	// If you see that, and the next bits are 0-9 (0000 - 1001) you can grab those
    61  	// bits and interpret them directly as an integer.
    62  	var n int
    63  	if n = len(bts); n < 1 {
    64  		return 0, fmt.Errorf("converting empty bytes to int")
    65  	}
    66  	for i := 0; i < n; i++ {
    67  		if bts[i]&0xf0 != 0x30 {
    68  			return 0, fmt.Errorf("%s is not a numeric character", string(bts[i]))
    69  		}
    70  		ret += int(bts[i]&0xf) * pow(10, n-i-1)
    71  	}
    72  	return ret, nil
    73  }
    74  
    75  // pow for integers implementation.
    76  // See Donald Knuth, The Art of Computer Programming, Volume 2, Section 4.6.3
    77  func pow(a, b int) int {
    78  	p := 1
    79  	for b > 0 {
    80  		if b&1 != 0 {
    81  			p *= a
    82  		}
    83  		b >>= 1
    84  		a *= a
    85  	}
    86  	return p
    87  }
    88  
    89  func bsplit3(bts []byte, sep byte) (b1, b2, b3 []byte) {
    90  	a := bytes.IndexByte(bts, sep)
    91  	b := bytes.IndexByte(bts[a+1:], sep)
    92  	if a == -1 || b == -1 {
    93  		return bts, nil, nil
    94  	}
    95  	b += a + 1
    96  	return bts[:a], bts[a+1 : b], bts[b+1:]
    97  }
    98  
    99  func btrim(bts []byte) []byte {
   100  	var i, j int
   101  	for i = 0; i < len(bts) && (bts[i] == ' ' || bts[i] == '\t'); {
   102  		i++
   103  	}
   104  	for j = len(bts); j > i && (bts[j-1] == ' ' || bts[j-1] == '\t'); {
   105  		j--
   106  	}
   107  	return bts[i:j]
   108  }
   109  
   110  func strHasToken(header, token string) (has bool) {
   111  	return btsHasToken(strToBytes(header), strToBytes(token))
   112  }
   113  
   114  func btsHasToken(header, token []byte) (has bool) {
   115  	httphead.ScanTokens(header, func(v []byte) bool {
   116  		has = bytes.EqualFold(v, token)
   117  		return !has
   118  	})
   119  	return
   120  }
   121  
   122  const (
   123  	toLower  = 'a' - 'A'      // for use with OR.
   124  	toUpper  = ^byte(toLower) // for use with AND.
   125  	toLower8 = uint64(toLower) |
   126  		uint64(toLower)<<8 |
   127  		uint64(toLower)<<16 |
   128  		uint64(toLower)<<24 |
   129  		uint64(toLower)<<32 |
   130  		uint64(toLower)<<40 |
   131  		uint64(toLower)<<48 |
   132  		uint64(toLower)<<56
   133  )
   134  
   135  // Algorithm below is like standard textproto/CanonicalMIMEHeaderKey, except
   136  // that it operates with slice of bytes and modifies it inplace without copying.
   137  func canonicalizeHeaderKey(k []byte) {
   138  	upper := true
   139  	for i, c := range k {
   140  		if upper && 'a' <= c && c <= 'z' {
   141  			k[i] &= toUpper
   142  		} else if !upper && 'A' <= c && c <= 'Z' {
   143  			k[i] |= toLower
   144  		}
   145  		upper = c == '-'
   146  	}
   147  }
   148  
   149  // readLine reads line from br. It reads until '\n' and returns bytes without
   150  // '\n' or '\r\n' at the end.
   151  // It returns err if and only if line does not end in '\n'. Note that read
   152  // bytes returned in any case of error.
   153  //
   154  // It is much like the textproto/Reader.ReadLine() except the thing that it
   155  // returns raw bytes, instead of string. That is, it avoids copying bytes read
   156  // from br.
   157  //
   158  // textproto/Reader.ReadLineBytes() is also makes copy of resulting bytes to be
   159  // safe with future I/O operations on br.
   160  //
   161  // We could control I/O operations on br and do not need to make additional
   162  // copy for safety.
   163  //
   164  // NOTE: it may return copied flag to notify that returned buffer is safe to
   165  // use.
   166  func readLine(br *bufio.Reader) ([]byte, error) {
   167  	var line []byte
   168  	for {
   169  		bts, err := br.ReadSlice('\n')
   170  		if err == bufio.ErrBufferFull {
   171  			// Copy bytes because next read will discard them.
   172  			line = append(line, bts...)
   173  			continue
   174  		}
   175  
   176  		// Avoid copy of single read.
   177  		if line == nil {
   178  			line = bts
   179  		} else {
   180  			line = append(line, bts...)
   181  		}
   182  
   183  		if err != nil {
   184  			return line, err
   185  		}
   186  
   187  		// Size of line is at least 1.
   188  		// In other case bufio.ReadSlice() returns error.
   189  		n := len(line)
   190  
   191  		// Cut '\n' or '\r\n'.
   192  		if n > 1 && line[n-2] == '\r' {
   193  			line = line[:n-2]
   194  		} else {
   195  			line = line[:n-1]
   196  		}
   197  
   198  		return line, nil
   199  	}
   200  }
   201  
   202  func min(a, b int) int {
   203  	if a < b {
   204  		return a
   205  	}
   206  	return b
   207  }
   208  
   209  func nonZero(a, b int) int {
   210  	if a != 0 {
   211  		return a
   212  	}
   213  	return b
   214  }