github.com/bingoohuang/gg@v0.0.0-20240325092523-45da7dee9335/pkg/hack/curgoroutineid.go (about)

     1  package hack
     2  
     3  import (
     4  	"bytes"
     5  	"errors"
     6  	"fmt"
     7  	"runtime"
     8  	"strconv"
     9  	"sync"
    10  )
    11  
    12  // CurGoroutineID get current goroutine id by walk stack.
    13  // from https://github.com/golang/net/blob/master/http2/gotrack.go
    14  // This func get goroutine id by parse current stack string, the performance is not so good.
    15  // asm version, https://github.com/petermattis/goid
    16  func CurGoroutineID() uint64 {
    17  	bp := littleBuf.Get().(*[]byte)
    18  	defer littleBuf.Put(bp)
    19  
    20  	b := *bp
    21  	b = b[:runtime.Stack(b, false)]
    22  	// Parse the 4707 out of "goroutine 4707 ["
    23  	b = bytes.TrimPrefix(b, goroutineSpace)
    24  	i := bytes.IndexByte(b, ' ')
    25  	if i < 0 {
    26  		panic(fmt.Sprintf("No space found in %q", b))
    27  	}
    28  	b = b[:i]
    29  	n, err := ParseUintBytes(b, 10, 64)
    30  	if err != nil {
    31  		panic(fmt.Sprintf("Failed to parse goroutine ID out of %q: %v", b, err))
    32  	}
    33  	return n
    34  }
    35  
    36  var (
    37  	goroutineSpace = []byte("goroutine ")
    38  	littleBuf      = sync.Pool{
    39  		New: func() interface{} {
    40  			buf := make([]byte, 64)
    41  			return &buf
    42  		},
    43  	}
    44  )
    45  
    46  // ParseUintBytes is like strconv.ParseUint, but using a []byte.
    47  func ParseUintBytes(s []byte, base int, bitSize int) (n uint64, err error) {
    48  	var cutoff, maxVal uint64
    49  
    50  	if bitSize == 0 {
    51  		bitSize = strconv.IntSize
    52  	}
    53  
    54  	s0 := s
    55  	switch {
    56  	case len(s) < 1:
    57  		err = strconv.ErrSyntax
    58  		goto Error
    59  
    60  	case 2 <= base && base <= 36:
    61  		// valid base; nothing to do
    62  
    63  	case base == 0:
    64  		// Look for octal, hex prefix.
    65  		switch {
    66  		case s[0] == '0' && len(s) > 1 && (s[1] == 'x' || s[1] == 'X'):
    67  			base = 16
    68  			s = s[2:]
    69  			if len(s) < 1 {
    70  				err = strconv.ErrSyntax
    71  				goto Error
    72  			}
    73  		case s[0] == '0':
    74  			base = 8
    75  		default:
    76  			base = 10
    77  		}
    78  
    79  	default:
    80  		err = errors.New("invalid base " + strconv.Itoa(base))
    81  		goto Error
    82  	}
    83  
    84  	n = 0
    85  	cutoff = cutoff64(base)
    86  	maxVal = 1<<uint(bitSize) - 1
    87  
    88  	for i := 0; i < len(s); i++ {
    89  		var v byte
    90  		d := s[i]
    91  		switch {
    92  		case '0' <= d && d <= '9':
    93  			v = d - '0'
    94  		case 'a' <= d && d <= 'z':
    95  			v = d - 'a' + 10
    96  		case 'A' <= d && d <= 'Z':
    97  			v = d - 'A' + 10
    98  		default:
    99  			n = 0
   100  			err = strconv.ErrSyntax
   101  			goto Error
   102  		}
   103  		if int(v) >= base {
   104  			n = 0
   105  			err = strconv.ErrSyntax
   106  			goto Error
   107  		}
   108  
   109  		if n >= cutoff {
   110  			// n*base overflows
   111  			n = 1<<64 - 1
   112  			err = strconv.ErrRange
   113  			goto Error
   114  		}
   115  		n *= uint64(base)
   116  
   117  		n1 := n + uint64(v)
   118  		if n1 < n || n1 > maxVal {
   119  			// n+v overflows
   120  			n = 1<<64 - 1
   121  			err = strconv.ErrRange
   122  			goto Error
   123  		}
   124  		n = n1
   125  	}
   126  
   127  	return n, nil
   128  
   129  Error:
   130  	return n, &strconv.NumError{Func: "ParseUint", Num: string(s0), Err: err}
   131  }
   132  
   133  // Return the first number n such that n*base >= 1<<64.
   134  func cutoff64(base int) uint64 {
   135  	if base < 2 {
   136  		return 0
   137  	}
   138  	return (1<<64-1)/uint64(base) + 1
   139  }