github.com/keakon/golog@v0.0.0-20230330091222-cac71197c18d/utils.go (about)

     1  package golog
     2  
     3  import (
     4  	"bytes"
     5  	"runtime"
     6  	"sync"
     7  	"time"
     8  	_ "unsafe"
     9  )
    10  
    11  const (
    12  	recordBufSize   = 128
    13  	dateTimeBufSize = 10 // length of date string
    14  )
    15  
    16  var (
    17  	recordPool = sync.Pool{
    18  		New: func() interface{} {
    19  			return &Record{}
    20  		},
    21  	}
    22  
    23  	bufPool = sync.Pool{
    24  		New: func() interface{} {
    25  			return bytes.NewBuffer(make([]byte, 0, recordBufSize))
    26  		},
    27  	}
    28  
    29  	uintBytes2 [60][]byte  // 0 - 59
    30  	uintBytes4 [69][]byte  // 1970 - 2038
    31  	uintBytes  [999][]byte // 2 - 1000
    32  
    33  	frameCache sync.Map
    34  
    35  	now = time.Now
    36  
    37  	fastTimer = FastTimer{}
    38  )
    39  
    40  func init() {
    41  	for i := 0; i < 60; i++ { // hour / minute / second is between 0 and 59
    42  		uintBytes2[i] = uint2Bytes(i, 2)
    43  	}
    44  	for i := 0; i < 69; i++ { // year is between 1970 and 2038
    45  		uintBytes4[i] = uint2Bytes(1970+i, 4)
    46  	}
    47  	for i := 0; i < 999; i++ { // source code line number is usually between 2 and 1000
    48  		uintBytes[i] = uint2DynamicBytes(i + 2)
    49  	}
    50  }
    51  
    52  func uint2Bytes(x, size int) []byte {
    53  	// x and size should be uint32
    54  	result := make([]byte, size)
    55  	for i := 0; i < size; i++ {
    56  		r := x % 10
    57  		result[size-i-1] = byte(r) + '0'
    58  		x /= 10
    59  	}
    60  	return result
    61  }
    62  
    63  func uint2DynamicBytes(x int) []byte {
    64  	// x should be uint32
    65  	size := 0
    66  	switch {
    67  	case x < 10:
    68  		return []byte{byte(x) + '0'}
    69  	case x < 100:
    70  		size = 2
    71  	case x < 1000:
    72  		size = 3
    73  	case x < 10000:
    74  		size = 4
    75  	case x < 100000:
    76  		size = 5
    77  	case x < 1000000:
    78  		size = 6
    79  	case x < 10000000:
    80  		size = 7
    81  	case x < 100000000:
    82  		size = 8
    83  	case x < 1000000000:
    84  		size = 9
    85  	default:
    86  		size = 10
    87  	}
    88  	result := make([]byte, size)
    89  	for i := 0; i < size; i++ {
    90  		r := x % 10
    91  		result[size-i-1] = byte(r) + '0'
    92  		x /= 10
    93  	}
    94  	return result
    95  }
    96  
    97  func uint2Bytes2(x int) []byte {
    98  	// x should between 0 and 59
    99  	return uintBytes2[x]
   100  }
   101  
   102  func uint2Bytes4(x int) []byte {
   103  	// x should between 1970 and 2038
   104  	return uintBytes4[x-1970]
   105  }
   106  
   107  func fastUint2DynamicBytes(x int) []byte {
   108  	// x should be uint32
   109  	size := 0
   110  	switch {
   111  	case x < 2:
   112  		return []byte{byte(x) + '0'}
   113  	case x <= 1000:
   114  		return uintBytes[x-2]
   115  	case x < 10000:
   116  		size = 4
   117  	case x < 100000:
   118  		size = 5
   119  	case x < 1000000:
   120  		size = 6
   121  	case x < 10000000:
   122  		size = 7
   123  	case x < 100000000:
   124  		size = 8
   125  	case x < 1000000000:
   126  		size = 9
   127  	default:
   128  		size = 10
   129  	}
   130  	result := make([]byte, size)
   131  	for i := 0; i < size; i++ {
   132  		r := x % 10
   133  		result[size-i-1] = byte(r) + '0'
   134  		x /= 10
   135  	}
   136  	return result
   137  }
   138  
   139  func stopTimer(timer *time.Timer) {
   140  	if !timer.Stop() {
   141  		select {
   142  		case <-timer.C:
   143  		default:
   144  		}
   145  	}
   146  }
   147  
   148  func logError(err error) {
   149  	if internalLogger != nil {
   150  		file, line := Caller(1)
   151  		internalLogger.Log(ErrorLevel, file, line, err.Error())
   152  	}
   153  }
   154  
   155  func setNowFunc(nowFunc func() time.Time) {
   156  	now = nowFunc
   157  }
   158  
   159  //go:noescape
   160  //go:linkname callers runtime.callers
   161  func callers(skip int, pcbuf []uintptr) int
   162  
   163  // Caller caches the result for runtime.Caller().
   164  // Inspired by https://zhuanlan.zhihu.com/p/403417640
   165  func Caller(skip int) (file string, line int) {
   166  	rpc := [1]uintptr{}
   167  	n := callers(skip+1, rpc[:])
   168  	if n < 1 {
   169  		return
   170  	}
   171  
   172  	var frame runtime.Frame
   173  	pc := rpc[0]
   174  	if f, ok := frameCache.Load(pc); ok {
   175  		frame = f.(runtime.Frame)
   176  	} else {
   177  		frame, _ = runtime.CallersFrames([]uintptr{pc}).Next()
   178  		frameCache.Store(pc, frame)
   179  	}
   180  	return frame.File, frame.Line
   181  }
   182  
   183  // FastTimer is not thread-safe for performance reason, but all the threads will notice its changes in a few milliseconds.
   184  type FastTimer struct {
   185  	date      string
   186  	time      string
   187  	stopChan  chan struct{}
   188  	isRunning bool
   189  }
   190  
   191  func (t *FastTimer) update(tm time.Time, buf *bytes.Buffer) {
   192  	buf.Reset()
   193  	year, mon, day := tm.Date()
   194  	buf.Write(uint2Bytes4(year))
   195  	buf.WriteByte('-')
   196  	buf.Write(uint2Bytes2(int(mon)))
   197  	buf.WriteByte('-')
   198  	buf.Write(uint2Bytes2(day))
   199  	t.date = buf.String()
   200  
   201  	buf.Reset()
   202  	hour, min, sec := tm.Clock()
   203  	buf.Write(uint2Bytes2(hour))
   204  	buf.WriteByte(':')
   205  	buf.Write(uint2Bytes2(min))
   206  	buf.WriteByte(':')
   207  	buf.Write(uint2Bytes2(sec))
   208  	t.time = buf.String()
   209  }
   210  
   211  func (t *FastTimer) start() {
   212  	buf := bytes.NewBuffer(make([]byte, 0, dateTimeBufSize))
   213  	t.update(now(), buf)
   214  	t.isRunning = true
   215  
   216  	t.stopChan = make(chan struct{})
   217  
   218  	go func() {
   219  		ticker := time.NewTicker(time.Second)
   220  		defer ticker.Stop()
   221  
   222  		for {
   223  			select {
   224  			case tm := <-ticker.C:
   225  				t.update(tm, buf)
   226  			case <-t.stopChan:
   227  				defer func() { recover() }() // t.stopChan might be closed twice during exiting
   228  				close(t.stopChan)
   229  				return
   230  			}
   231  		}
   232  	}()
   233  }
   234  
   235  func (t *FastTimer) stop() {
   236  	if t.isRunning {
   237  		defer func() { recover() }() // t.stopChan might be closed during exiting
   238  		t.stopChan <- struct{}{}
   239  		t.isRunning = false
   240  	}
   241  }
   242  
   243  // StartFastTimer starts the fastTimer.
   244  func StartFastTimer() {
   245  	fastTimer.start()
   246  }
   247  
   248  // StopFastTimer stops the fastTimer.
   249  func StopFastTimer() {
   250  	fastTimer.stop()
   251  }
   252  
   253  //go:noescape
   254  //go:linkname runtime_procPin runtime.procPin
   255  func runtime_procPin() int
   256  
   257  //go:noescape
   258  //go:linkname runtime_procUnpin runtime.procUnpin
   259  func runtime_procUnpin()