github.com/nikandfor/tlog@v0.21.5-0.20231108111739-3ef89426a96d/tlwire/encoder_value.go (about) 1 package tlwire 2 3 import ( 4 "fmt" 5 "net/url" 6 "reflect" 7 "time" 8 "unsafe" 9 10 "github.com/nikandfor/loc" 11 12 "github.com/nikandfor/tlog/low" 13 ) 14 15 type ( 16 TlogAppender interface { 17 TlogAppend(b []byte) []byte 18 } 19 20 ptrSet map[unsafe.Pointer]struct{} 21 22 ValueEncoder func(e *Encoder, b []byte, val interface{}) []byte 23 24 //nolint:structcheck 25 eface struct { 26 typ unsafe.Pointer 27 ptr unsafe.Pointer 28 } 29 30 //nolint:structcheck 31 reflectValue struct { 32 typ unsafe.Pointer 33 ptr unsafe.Pointer 34 flag uintptr 35 } 36 37 encoders map[unsafe.Pointer]ValueEncoder 38 ) 39 40 var defaultEncoders = encoders{} 41 42 func SetEncoder(tp interface{}, encoder ValueEncoder) { 43 defaultEncoders.Set(tp, encoder) 44 } 45 46 func (e *Encoder) SetEncoder(tp interface{}, encoder ValueEncoder) { 47 if e.custom == nil { 48 e.custom = encoders{} 49 } 50 51 e.custom.Set(tp, encoder) 52 } 53 54 func (e encoders) Set(tp interface{}, encoder ValueEncoder) { 55 if tp == nil { 56 panic("nil type") 57 } 58 59 ef := *(*eface)(unsafe.Pointer(&tp)) 60 61 e[ef.typ] = encoder 62 63 if encoder == nil { 64 delete(e, ef.typ) 65 } 66 } 67 68 func init() { 69 SetEncoder(loc.PC(0), func(e *Encoder, b []byte, x interface{}) []byte { 70 return Encoder{}.AppendCaller(b, x.(loc.PC)) 71 }) 72 SetEncoder(loc.PCs(nil), func(e *Encoder, b []byte, x interface{}) []byte { 73 return Encoder{}.AppendCallers(b, x.(loc.PCs)) 74 }) 75 76 SetEncoder(time.Time{}, func(e *Encoder, b []byte, x interface{}) []byte { 77 return Encoder{}.AppendTimeTZ(b, x.(time.Time)) 78 }) 79 SetEncoder((*time.Time)(nil), func(e *Encoder, b []byte, x interface{}) []byte { 80 return Encoder{}.AppendTimeTZ(b, *x.(*time.Time)) 81 }) 82 83 SetEncoder(time.Duration(0), func(e *Encoder, b []byte, x interface{}) []byte { 84 return Encoder{}.AppendDuration(b, x.(time.Duration)) 85 }) 86 SetEncoder((*time.Duration)(nil), func(e *Encoder, b []byte, x interface{}) []byte { 87 return Encoder{}.AppendDuration(b, *x.(*time.Duration)) 88 }) 89 90 SetEncoder((*url.URL)(nil), func(e *Encoder, b []byte, x interface{}) []byte { 91 u := x.(*url.URL) 92 if u == nil { 93 return Encoder{}.AppendNil(b) 94 } 95 96 return Encoder{}.AppendString(b, u.String()) 97 }) 98 } 99 100 func (e *Encoder) AppendKeyValue(b []byte, key string, v interface{}) []byte { 101 b = e.AppendKey(b, key) 102 b = e.AppendValue(b, v) 103 return b 104 } 105 106 //go:linkname appendValue github.com/nikandfor/tlog/tlwire.(*Encoder).appendValue 107 //go:noescape 108 func appendValue(e *Encoder, b []byte, v interface{}) []byte 109 110 func (e *Encoder) AppendValue(b []byte, v interface{}) []byte { 111 return appendValue(e, b, v) 112 } 113 114 func (e *Encoder) AppendValueSafe(b []byte, v interface{}) []byte { 115 return e.appendValue(b, v) 116 } 117 118 // Called through linkname hack as appendValue from (Encoder).AppendValue. 119 func (e *Encoder) appendValue(b []byte, v interface{}) []byte { 120 if v == nil { 121 return append(b, Special|Nil) 122 } 123 124 r := reflect.ValueOf(v) 125 126 return e.appendRaw(b, r, ptrSet{}) 127 } 128 129 func (e *Encoder) appendRaw(b []byte, r reflect.Value, visited ptrSet) []byte { //nolint:gocognit,cyclop 130 if r.CanInterface() { 131 // v := r.Interface() 132 v := valueInterface(r) 133 134 // if r.Type().Comparable() && v != r.Interface() { 135 // panic(fmt.Sprintf("not equal interface %v: %x %v %v", r, value(r), raweface(v), raweface(r.Interface()))) 136 // } 137 138 ef := raweface(v) 139 140 if e != nil { 141 if enc, ok := e.custom[ef.typ]; ok { 142 return enc(e, b, v) 143 } 144 } 145 146 if enc, ok := defaultEncoders[ef.typ]; ok { 147 return enc(e, b, v) 148 } 149 150 switch v := v.(type) { 151 case TlogAppender: 152 return v.TlogAppend(b) 153 case interface { 154 ProtoMessage() 155 }: 156 case error: 157 return e.AppendError(b, v) 158 case fmt.Stringer: 159 return e.AppendString(b, v.String()) 160 } 161 } 162 163 switch r.Kind() { 164 case reflect.String: 165 return e.AppendString(b, r.String()) 166 case reflect.Int, reflect.Int64, reflect.Int32, reflect.Int16, reflect.Int8: 167 return e.AppendInt64(b, r.Int()) 168 case reflect.Uint, reflect.Uint64, reflect.Uint32, reflect.Uint16, reflect.Uint8: 169 return e.AppendUint64(b, r.Uint()) 170 case reflect.Float64, reflect.Float32: 171 return e.AppendFloat(b, r.Float()) 172 case reflect.Ptr, reflect.Interface: 173 if r.IsNil() { 174 return append(b, Special|Nil) 175 } 176 177 if r.Kind() == reflect.Ptr { 178 ptr := unsafe.Pointer(r.Pointer()) 179 180 if visited == nil { 181 visited = make(map[unsafe.Pointer]struct{}) 182 } 183 184 if _, ok := visited[ptr]; ok { 185 return append(b, Special|SelfRef) 186 } 187 188 visited[ptr] = struct{}{} 189 190 defer delete(visited, ptr) 191 } 192 193 r = r.Elem() 194 195 return e.appendRaw(b, r, visited) 196 case reflect.Slice, reflect.Array: 197 if r.Type().Elem().Kind() == reflect.Uint8 { 198 if r.Kind() == reflect.Array { 199 if r.CanAddr() { 200 r = r.Slice(0, r.Len()) 201 } else { 202 return e.AppendTagString(b, Bytes, low.UnsafeString(low.InterfaceData(r.Interface()), r.Len())) 203 } 204 } 205 206 return e.AppendBytes(b, r.Bytes()) 207 } 208 209 l := r.Len() 210 211 b = e.AppendTag(b, Array, l) 212 213 for i := 0; i < l; i++ { 214 b = e.appendRaw(b, r.Index(i), visited) 215 } 216 217 return b 218 case reflect.Map: 219 l := r.Len() 220 221 b = e.AppendTag(b, Map, l) 222 223 it := r.MapRange() 224 225 for it.Next() { 226 b = e.appendRaw(b, it.Key(), visited) 227 b = e.appendRaw(b, it.Value(), visited) 228 } 229 230 return b 231 case reflect.Struct: 232 return e.appendStruct(b, r, visited) 233 case reflect.Bool: 234 if r.Bool() { 235 return append(b, Special|True) 236 } else { //nolint:golint 237 return append(b, Special|False) 238 } 239 case reflect.Func: 240 return append(b, Special|Undefined) 241 case reflect.Uintptr: 242 b = append(b, Semantic|Hex) 243 return e.AppendTag64(b, Int, r.Uint()) 244 case reflect.UnsafePointer: 245 b = append(b, Semantic|Hex) 246 return e.AppendTag64(b, Int, uint64(r.Pointer())) 247 default: 248 panic(r) 249 } 250 } 251 252 func (e *Encoder) appendStruct(b []byte, r reflect.Value, visited ptrSet) []byte { 253 t := r.Type() 254 255 b = append(b, Map|LenBreak) 256 257 b = e.appendStructFields(b, t, r, visited) 258 259 b = append(b, Special|Break) 260 261 return b 262 } 263 264 func (e *Encoder) appendStructFields(b []byte, t reflect.Type, r reflect.Value, visited ptrSet) []byte { 265 // fmt.Fprintf(os.Stderr, "appendStructFields: %v ctx %p %d\n", t, visited, len(visited)) 266 267 s := parseStruct(t) 268 269 for _, fc := range s.fs { 270 fv := r.Field(fc.Idx) 271 272 if fc.OmitEmpty && fv.IsZero() { 273 continue 274 } 275 276 ft := fv.Type() 277 278 if fc.Embed && ft.Kind() == reflect.Struct { 279 b = e.appendStructFields(b, ft, fv, visited) 280 281 continue 282 } 283 284 b = e.AppendString(b, fc.Name) 285 286 if fc.Hex { 287 b = append(b, Semantic|Hex) 288 } 289 290 b = e.appendRaw(b, fv, visited) 291 } 292 293 return b 294 } 295 296 func value(v reflect.Value) reflectValue { 297 return *(*reflectValue)(unsafe.Pointer(&v)) 298 } 299 300 func valueInterface(r reflect.Value) interface{} { 301 v := value(r) 302 303 if r.Kind() == reflect.Interface { 304 // Special case: return the element inside the interface. 305 // Empty interface has one layout, all interfaces with 306 // methods have a second layout. 307 if r.NumMethod() == 0 { 308 return *(*interface{})(v.ptr) 309 } 310 return *(*interface { 311 M() 312 })(v.ptr) 313 } 314 315 const flagAddr = 1 << 8 316 317 v.flag &^= flagAddr 318 319 return reflect_packEface(v) 320 } 321 322 //go:linkname reflect_packEface reflect.packEface 323 func reflect_packEface(reflectValue) interface{} 324 325 func raweface(x interface{}) eface { 326 return *(*eface)(unsafe.Pointer(&x)) 327 }