github.com/nikandfor/tlog@v0.21.5-0.20231108111739-3ef89426a96d/tlwire/location.go (about) 1 package tlwire 2 3 import ( 4 "fmt" 5 "sync" 6 7 "github.com/nikandfor/loc" 8 ) 9 10 var ( 11 locmu sync.Mutex 12 loccache = map[loc.PC][]byte{} 13 ) 14 15 func (e Encoder) AppendPC(b []byte, pc loc.PC) []byte { 16 b = append(b, Semantic|Caller) 17 18 if pc == 0 { 19 return append(b, Special|Nil) 20 } 21 22 return e.AppendUint64(b, uint64(pc)) 23 } 24 25 func (e Encoder) AppendPCs(b []byte, pcs loc.PCs) []byte { 26 b = append(b, Semantic|Caller) 27 28 if pcs == nil { 29 return append(b, Special|Nil) 30 } 31 32 b = e.AppendTag(b, Array, len(pcs)) 33 34 for _, pc := range pcs { 35 b = e.AppendUint64(b, uint64(pc)) 36 } 37 38 return b 39 } 40 41 func (e Encoder) AppendCaller(b []byte, pc loc.PC) []byte { 42 b = append(b, Semantic|Caller) 43 44 return e.appendPC(b, pc) 45 } 46 47 func (e Encoder) AppendCallers(b []byte, pcs loc.PCs) []byte { 48 b = append(b, Semantic|Caller) 49 b = e.AppendTag(b, Array, len(pcs)) 50 51 for _, pc := range pcs { 52 b = e.appendPC(b, pc) 53 } 54 55 return b 56 } 57 58 func (e Encoder) appendPC(b []byte, pc loc.PC) []byte { 59 if pc == 0 { 60 return append(b, Special|Nil) 61 } 62 63 locmu.Lock() 64 c, ok := loccache[pc] 65 locmu.Unlock() 66 67 if ok { 68 return append(b, c...) 69 } 70 71 fe := pc.FuncEntry() 72 73 st := len(b) 74 75 l := byte(4) 76 if fe != pc { 77 l++ 78 } 79 80 b = append(b, Map|l) 81 82 b = e.AppendString(b, "p") 83 b = e.AppendUint64(b, uint64(pc)) 84 85 name, file, line := pc.NameFileLine() 86 87 b = e.AppendString(b, "n") 88 b = e.AppendString(b, name) 89 90 b = e.AppendString(b, "f") 91 b = e.AppendString(b, file) 92 93 b = e.AppendString(b, "l") 94 b = e.AppendInt(b, line) 95 96 if fe != pc { 97 b = e.AppendString(b, "e") 98 b = e.AppendUint64(b, uint64(fe)) 99 } 100 101 c = make([]byte, len(b)-st) 102 copy(c, b[st:]) 103 104 locmu.Lock() 105 loccache[pc] = c 106 locmu.Unlock() 107 108 return b 109 } 110 111 func (d Decoder) Caller(p []byte, st int) (pc loc.PC, i int) { 112 if p[st] != Semantic|Caller { 113 panic("not a caller") 114 } 115 116 tag, sub, i := d.Tag(p, st+1) 117 118 if tag == Int || tag == Map { 119 return d.caller(p, st+1) 120 } 121 122 if tag == Special && sub == Nil { 123 return 124 } 125 126 if tag != Array { 127 panic(fmt.Sprintf("unsupported caller tag: %x", tag)) 128 } 129 130 if sub == 0 { 131 return 132 } 133 134 pc, i = d.caller(p, i) 135 136 for el := 1; el < int(sub); el++ { 137 _, i = d.caller(p, i) 138 } 139 140 return 141 } 142 143 func (d Decoder) Callers(p []byte, st int) (pc loc.PC, pcs loc.PCs, i int) { 144 if p[st] != Semantic|Caller { 145 panic("not a caller") 146 } 147 148 tag, sub, i := d.Tag(p, st+1) 149 150 switch { 151 case tag == Int, tag == Map: 152 pc, i = d.caller(p, st+1) 153 return 154 case tag == Array: 155 case tag == Special && sub == Nil: 156 return 157 default: 158 panic(fmt.Sprintf("unsupported caller tag: %x", tag)) 159 } 160 161 if sub == 0 { 162 return 163 } 164 165 pcs = make(loc.PCs, sub) 166 167 for el := 0; el < int(sub); el++ { 168 pcs[el], i = d.caller(p, i) 169 } 170 171 pc = pcs[0] 172 173 return 174 } 175 176 func (d Decoder) caller(p []byte, st int) (pc loc.PC, i int) { 177 i = st 178 179 tag, sub, i := d.Tag(p, i) 180 181 if tag == Int { 182 pc = loc.PC(sub) 183 184 if pc != 0 && !loc.Cached(pc) { 185 loc.SetCache(pc, "_", ".", 0) 186 } 187 188 return 189 } 190 191 var v uint64 192 var k []byte 193 var name, file []byte 194 var line int 195 196 for el := 0; el < int(sub); el++ { 197 k, i = d.Bytes(p, i) 198 199 switch string(k) { 200 case "p": 201 v, i = d.Unsigned(p, i) 202 203 pc = loc.PC(v) 204 case "l": 205 v, i = d.Unsigned(p, i) 206 207 line = int(v) 208 case "n": 209 name, i = d.Bytes(p, i) 210 case "f": 211 file, i = d.Bytes(p, i) 212 default: 213 i = d.Skip(p, i) 214 } 215 } 216 217 if pc == 0 { 218 return 219 } 220 221 loc.SetCacheBytes(pc, name, file, line) 222 223 return 224 }