github.com/qioalice/ekago/v3@v3.3.2-0.20221202205325-5c262d586ee4/ekasys/stackframe.go (about) 1 // Copyright © 2019. All rights reserved. 2 // Author: Ilya Yuryevich. 3 // Contacts: iyuryevich@pm.me, https://github.com/qioalice 4 // License: https://opensource.org/licenses/MIT 5 6 package ekasys 7 8 import ( 9 "path/filepath" 10 "runtime" 11 12 "github.com/qioalice/ekago/v3/ekastr" 13 ) 14 15 // StackFrame represents one stack level (frame/item). 16 // It general purpose is runtime.Frame type extending. 17 type StackFrame struct { 18 runtime.Frame 19 20 // Format is formatted string representation of the current stack frame: 21 // "<package>/<func> (<short_file>:<file_line>) <full_package_path>". 22 // 23 // Note, stack frame is not formatted by default. 24 // It means that this field is empty in 99% cases. If you need to generate 25 // formatted string use DoFormat method. 26 Format string 27 28 // FormatFileOffset is the index of 29 // "(<short_file>:<file_line>)..." in Format field. 30 FormatFileOffset int 31 32 // FormatFullPathOffset is the index of "<full_package_path>" in Format field. 33 FormatFullPathOffset int 34 } 35 36 // DoFormat generates formatted string representation of the current stack frame, 37 // saves it to Format field and returns it. Output looks like: 38 // "<package>/<func> (<short_file>:<file_line>) <full_package_path>". 39 // 40 // Does not regenerate formatted string if it's already generated (just returns). 41 func (f *StackFrame) DoFormat() string { 42 43 if f.Format == "" { 44 f.Format = f.doFormat() 45 } 46 47 return f.Format 48 } 49 50 // doFormat is a private part of DoFormat() function. 51 func (f *StackFrame) doFormat() string { 52 53 fullPackage, fn := filepath.Split(f.Function) 54 _, file := filepath.Split(f.File) 55 56 // we need last package from the fullPackage 57 lastPackage := filepath.Base(fullPackage) 58 59 // need remove last package from fullPackage 60 if len(lastPackage)+2 <= len(fullPackage) && lastPackage != "." { 61 fullPackage = fullPackage[:len(fullPackage)-len(lastPackage)-2] 62 } 63 64 requiredBufLen := 9 // 2 spaces, '/', '(', ')', ':' + 3 reserved bytes 65 requiredBufLen += len(fullPackage) + len(lastPackage) + len(file) + len(fn) 66 requiredBufLen += ekastr.PItoa32(int32(f.Line)) 67 68 buf := make([]byte, requiredBufLen) 69 offset := 0 70 71 // maybe 'lastPackage' is version of package? 72 if len(lastPackage) > 1 && (lastPackage[0] == 'v' || lastPackage[0] == 'V') { 73 is := true 74 for i, n := 1, len(lastPackage); i < n && is; i++ { 75 if lastPackage[i] < '0' || lastPackage[i] > '9' { 76 is = false 77 } 78 } 79 if is { 80 lastPackage2 := filepath.Base(fullPackage) 81 if len(lastPackage2)+2 <= len(fullPackage) && lastPackage2 != "." { 82 fullPackage = fullPackage[:len(fullPackage)-len(lastPackage2)-1] 83 } 84 copy(buf, lastPackage2) 85 offset += len(lastPackage2) 86 87 buf[offset] = '.' 88 offset++ 89 } 90 } 91 92 copy(buf[offset:], lastPackage) 93 offset += len(lastPackage) 94 95 buf[offset] = '.' 96 offset++ 97 98 copy(buf[offset:], fn) 99 offset += len(fn) 100 101 buf[offset] = ' ' 102 offset++ 103 buf[offset] = '(' 104 offset++ 105 106 f.FormatFileOffset = offset - 1 107 108 copy(buf[offset:], file) 109 offset += len(file) 110 111 buf[offset] = ':' 112 offset++ 113 114 offset += ekastr.BItoa32(buf[offset:], int32(f.Line)) 115 116 buf[offset] = ')' 117 offset++ 118 buf[offset] = ' ' 119 offset++ 120 121 f.FormatFullPathOffset = offset 122 123 copy(buf[offset:], fullPackage) 124 offset += len(fullPackage) 125 126 return string(buf[:offset]) 127 }