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()