github.com/Aoi-hosizora/ahlib@v1.5.1-0.20230404072829-241b93cf91c7/xreflect/xreflect.go (about) 1 package xreflect 2 3 import ( 4 "math" 5 "reflect" 6 "unsafe" 7 ) 8 9 // ==================== 10 // kind checker related 11 // ==================== 12 13 // IsIntKind checks whether given reflect.Kind represents integers or not. 14 func IsIntKind(kind reflect.Kind) bool { 15 return kind == reflect.Int || kind == reflect.Int8 || kind == reflect.Int16 || kind == reflect.Int32 || kind == reflect.Int64 16 } 17 18 // IsUintKind checks whether given reflect.Kind represents unsigned integers or not. 19 func IsUintKind(kind reflect.Kind) bool { 20 return kind == reflect.Uint || kind == reflect.Uint8 || kind == reflect.Uint16 || kind == reflect.Uint32 || kind == reflect.Uint64 || kind == reflect.Uintptr 21 } 22 23 // IsFloatKind checks whether given reflect.Kind represents floating points or not. 24 func IsFloatKind(kind reflect.Kind) bool { 25 return kind == reflect.Float32 || kind == reflect.Float64 26 } 27 28 // IsComplexKind checks whether given reflect.Kind represents complex numerics or not. 29 func IsComplexKind(kind reflect.Kind) bool { 30 return kind == reflect.Complex64 || kind == reflect.Complex128 31 } 32 33 // IsNumericKind checks whether given reflect.Kind represents numerics or not. 34 // 35 // Numeric types: integers, unsigned integers, floating points, complex numerics. 36 func IsNumericKind(kind reflect.Kind) bool { 37 return kind == reflect.Int || kind == reflect.Int8 || kind == reflect.Int16 || kind == reflect.Int32 || kind == reflect.Int64 || 38 kind == reflect.Uint || kind == reflect.Uint8 || kind == reflect.Uint16 || kind == reflect.Uint32 || kind == reflect.Uint64 || kind == reflect.Uintptr || 39 kind == reflect.Float32 || kind == reflect.Float64 || kind == reflect.Complex64 || kind == reflect.Complex128 40 } 41 42 // IsCollectionKind checks whether given reflect.Kind represents collections or not, whose related reflect.Value's ``Len()'' method is invokable. 43 // 44 // Collection types: string, array, slice, map, chan. 45 func IsCollectionKind(kind reflect.Kind) bool { 46 return kind == reflect.String || kind == reflect.Array || kind == reflect.Slice || kind == reflect.Map || kind == reflect.Chan 47 } 48 49 // IsNillableKind checks whether given reflect.Kind represents nillable types or not, whose related reflect.Value's ``IsNil()'' method is invokable. 50 // 51 // Nillable types: ptr, func, interface, unsafePtr, slice, map, chan. 52 func IsNillableKind(kind reflect.Kind) bool { 53 return kind == reflect.Ptr || kind == reflect.Func || kind == reflect.Interface || kind == reflect.UnsafePointer || 54 kind == reflect.Slice || kind == reflect.Map || kind == reflect.Chan 55 } 56 57 // IsSlicableKind checks whether given reflect.Kind represents slice invokable types or not, whose related reflect.Value's ``Slice()'' and ``Index()'' methods are invokable. 58 // 59 // Slicable types: slice, array, string. 60 func IsSlicableKind(kind reflect.Kind) bool { 61 return kind == reflect.Slice || kind == reflect.Array || kind == reflect.String 62 } 63 64 // IsPointerableKind checks whether given reflect.Kind represents pointer gettable types or not, whose related reflect.Value's ``Pointer()'' and ``UnsafePointer()'' methods are invokable. 65 // Note that these two methods can not be called on string and interface reflect.Value. 66 // 67 // Pointerable types: ptr, func, unsafePtr, slice, map, chan. 68 func IsPointerableKind(kind reflect.Kind) bool { 69 return kind == reflect.Ptr || kind == reflect.Func || kind == reflect.UnsafePointer || 70 kind == reflect.Slice || kind == reflect.Map || kind == reflect.Chan 71 } 72 73 // ===================== 74 // value checker related 75 // ===================== 76 77 // IsNilValue checks whether given value is nil in its type or not. Note that this will also check the wrapped data of given interface{}. 78 func IsNilValue(v interface{}) bool { 79 if v == nil { 80 return true 81 } 82 val := reflect.ValueOf(v) 83 switch val.Kind() { 84 case reflect.Ptr, reflect.Func, reflect.Interface, reflect.UnsafePointer, reflect.Slice, reflect.Map, reflect.Chan: 85 return val.IsNil() 86 } 87 return false 88 } 89 90 // IsZeroValue checks whether given value is the zero value of its type or not. Note that all non-nil nillable collection values (such as empty []int{} 91 // and map[string]int{}) are regarded as not zero. 92 func IsZeroValue(v interface{}) bool { 93 if v == nil { 94 return true 95 } 96 zero := reflect.Zero(reflect.TypeOf(v)).Interface() 97 return reflect.DeepEqual(v, zero) 98 // return reflect.ValueOf(v).IsZero() 99 } 100 101 // IsEmptyCollection checks whether given collection value is empty or not. Note that empty means its value is nil or its length is zero. 102 func IsEmptyCollection(v interface{}) bool { 103 val := reflect.ValueOf(v) 104 switch val.Kind() { 105 case reflect.String, reflect.Array, reflect.Slice, reflect.Map, reflect.Chan: 106 return val.Len() == 0 // call val.Len() directly without unnecessary val.IsNil() 107 } 108 return false // including reflect.Invalid 109 } 110 111 // IsEmptyValue checks whether given value is empty or not. Note that empty means zero value, nil value, zero item and zero field, and this works 112 // almost the same as json.isEmptyValue for "omitempty". 113 func IsEmptyValue(v interface{}) bool { 114 val := reflect.ValueOf(v) 115 switch val.Kind() { 116 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 117 return val.Int() == 0 118 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: 119 return val.Uint() == 0 120 case reflect.Float32, reflect.Float64: 121 f := val.Float() 122 return math.Float64bits(f) == 0 123 case reflect.Complex64, reflect.Complex128: 124 c := val.Complex() 125 return math.Float64bits(real(c)) == 0 && math.Float64bits(imag(c)) == 0 126 case reflect.Bool: 127 return !val.Bool() 128 case reflect.String, reflect.Array, reflect.Slice, reflect.Map, reflect.Chan: 129 return val.Len() == 0 // => val.IsNil() || val.Len() == 0, do not check fields recursively 130 case reflect.Interface, reflect.Ptr, reflect.UnsafePointer, reflect.Func: 131 return val.IsNil() 132 case reflect.Struct: 133 return val.NumField() == 0 // do not check fields recursively (different with val.Zero()) 134 default: 135 // reflect.Invalid, that is untyped "nil" 136 return true 137 } 138 } 139 140 // ===================== 141 // numeric value related 142 // ===================== 143 144 // Float64Value returns the float64 value for given numeric value, returns false if given value is not numeric value. 145 func Float64Value(v interface{}) (float64, bool) { 146 switch vv := v.(type) { 147 case int: 148 return float64(vv), true 149 case int8: 150 return float64(vv), true 151 case int16: 152 return float64(vv), true 153 case int32: 154 return float64(vv), true 155 case int64: 156 return float64(vv), true 157 case uint: 158 return float64(vv), true 159 case uint8: 160 return float64(vv), true 161 case uint16: 162 return float64(vv), true 163 case uint32: 164 return float64(vv), true 165 case uint64: 166 return float64(vv), true 167 case uintptr: 168 return float64(vv), true 169 case float32: 170 return float64(vv), true 171 case float64: 172 return vv, true 173 } 174 return 0, false 175 } 176 177 // Uint64Value returns the uint64 value for given numeric value, returns false if given value is not numeric value. 178 func Uint64Value(v interface{}) (uint64, bool) { 179 switch vv := v.(type) { 180 case int: 181 return uint64(vv), true 182 case int8: 183 return uint64(vv), true 184 case int16: 185 return uint64(vv), true 186 case int32: 187 return uint64(vv), true 188 case int64: 189 return uint64(vv), true 190 case uint: 191 return uint64(vv), true 192 case uint8: 193 return uint64(vv), true 194 case uint16: 195 return uint64(vv), true 196 case uint32: 197 return uint64(vv), true 198 case uint64: 199 return vv, true 200 case uintptr: 201 return uint64(vv), true 202 case float32: 203 return uint64(vv), true 204 case float64: 205 return uint64(vv), true 206 } 207 return 0, false 208 } 209 210 // ======================== 211 // unexported field related 212 // ======================== 213 214 // GetUnexportedField gets the reflect.Value of unexported struct field's. Note that this is an unsafe function. 215 // 216 // Example: 217 // GetUnexportedField(reflect.ValueOf(app).Elem().FieldByName("noMethod")).Interface().(gin.HandlersChain) // (*app).noMethod is a gin.HandlersChain 218 // GetUnexportedField(FieldValueOf(app, "noMethod")).Interface().(gin.HandlersChain) // <- or in this way 219 // GetUnexportedField(reflect.ValueOf(trans).Elem().FieldByName("translations")).MapIndex(reflect.ValueOf("required")) // (*trans).translations is a map[string]xxx 220 // GetUnexportedField(FieldValueOf(trans, "translations")).Interface().(gin.HandlersChain) // <- or in this way 221 func GetUnexportedField(field reflect.Value) reflect.Value { 222 return reflect.NewAt(field.Type(), unsafe.Pointer(field.UnsafeAddr())).Elem() 223 } 224 225 // SetUnexportedField sets reflect.Value to unexported struct field. Note that this is an unsafe function. 226 // 227 // Example: 228 // SetUnexportedField(reflect.ValueOf(ctx).Elem().FieldByName("fullPath"), reflect.ValueOf(newFullPath)) // (*ctx).fullPath and newFullPath is a string 229 // SetUnexportedField(FieldValueOf(ctx, "fullPath"), reflect.ValueOf(newFullPath)) // <- or in this way 230 // SetUnexportedField(reflect.ValueOf(val).Elem().FieldByName("tagNameFunc"), reflect.ValueOf(nilFunc)) // (*val).tagNameFunc and nilFunc is a func 231 // SetUnexportedField(FieldValueOf(val, "tagNameFunc"), reflect.ValueOf(newFullPath)) // <- or in this way 232 func SetUnexportedField(field reflect.Value, value reflect.Value) { 233 reflect.NewAt(field.Type(), unsafe.Pointer(field.UnsafeAddr())).Elem().Set(value) 234 } 235 236 const ( 237 panicNilInterface = "xreflect: nil interface" 238 panicNilPtr = "xreflect: nil pointer" 239 panicNonStructOrPtrOfStruct = "xreflect: not a struct or pointers of struct" 240 panicNonexistentField = "xreflect: nonexistent struct field" 241 ) 242 243 // FieldValueOf returns the reflect.Value of specific struct field from given struct or pointers of struct. 244 // 245 // Example: 246 // FieldValueOf(app, "noMethod") // equals to reflect.ValueOf(app)[.Elem()*].FieldByName("noMethod") 247 // FieldValueOf(trans, "translations") // equals to reflect.ValueOf(trans)[.Elem()*].FieldByName("translations") 248 func FieldValueOf(v interface{}, name string) reflect.Value { 249 if v == nil { 250 panic(panicNilInterface) 251 } 252 val := reflect.ValueOf(v) 253 for val.Kind() == reflect.Ptr && !val.IsNil() { 254 val = val.Elem() 255 } 256 257 if val.Kind() == reflect.Ptr && val.IsNil() { 258 panic(panicNilPtr) 259 } 260 if val.Kind() != reflect.Struct { 261 panic(panicNonStructOrPtrOfStruct) 262 } 263 fval := val.FieldByName(name) 264 if !fval.IsValid() { // not existed 265 panic(panicNonexistentField) 266 } 267 return fval 268 } 269 270 // Attention: 271 // 272 // 1. For searching function by name, please refer to https://github.com/alangpierce/go-forceexport/blob/8f1d6941cd/forceexport.go#L10-L22 273 // and http://www.alangpierce.com/blog/2016/03/17/adventures-in-go-accessing-unexported-functions. 274 // 275 // 2. For calling unexported struct methods, please refer to https://github.com/spance/go-callprivate/blob/d8b9b55236/examples.go#L20-L22. 276 277 // ============== 278 // mass functions 279 // ============== 280 281 // eface keeps the same as runtime.eface, which is the internal representation of interface{}. 282 type eface struct { 283 _type uintptr // *runtime._type 284 data unsafe.Pointer 285 } 286 287 // HasZeroEface checks whether given interface value has no type information or wrapped data (more than `== nil`). Note that this is an unsafe function. 288 func HasZeroEface(v interface{}) bool { 289 e := (*eface)(unsafe.Pointer(&v)) 290 return e._type == 0 || uintptr(e.data) == 0 291 } 292 293 // DeepEqualInValue checks whether given two values are deeply equal without considering their types. Note that the only different between this function and 294 // reflect.DeepEqual is: this function checks by checking type convertable and comparing after type conversion. 295 func DeepEqualInValue(v1, v2 interface{}) bool { 296 if reflect.DeepEqual(v1, v2) { 297 return true 298 } 299 val1, val2 := reflect.ValueOf(v1), reflect.ValueOf(v2) 300 if !val1.IsValid() || !val2.IsValid() { 301 return false 302 } 303 304 // check convertable, and compare after type conversion 305 type1, type2 := val1.Type(), val2.Type() 306 if type1.ConvertibleTo(type2) { 307 return reflect.DeepEqual(val1.Convert(type2).Interface(), v2) 308 } 309 if type2.ConvertibleTo(type1) { 310 return reflect.DeepEqual(v1, val2.Convert(type1).Interface()) 311 } 312 return false // not equal 313 } 314 315 // SameUnderlyingPointer checks whether given two values can get underlying pointer, and whether their underlying pointers point to the same address. 316 func SameUnderlyingPointer(p1, p2 interface{}) bool { 317 val1, val2 := reflect.ValueOf(p1), reflect.ValueOf(p2) 318 if !IsPointerableKind(val1.Kind()) || !IsPointerableKind(val2.Kind()) { 319 return false 320 } 321 322 // compare the underlying pointers of two values 323 return val1.Pointer() == val2.Pointer() 324 } 325 326 // SameUnderlyingPointerWithType checks whether given two values can get underlying pointer, and whether they have the same typ, and their underlying pointers 327 // point to the same address. 328 func SameUnderlyingPointerWithType(p1, p2 interface{}) bool { 329 return SameUnderlyingPointerWithTypeAndKind(p1, p2, 999) 330 } 331 332 // SameUnderlyingPointerWithTypeAndKind checks whether given two values can get underlying pointer, and whether they have the same type and match given kind, 333 // and their underlying pointers point to the same address. 334 func SameUnderlyingPointerWithTypeAndKind(p1, p2 interface{}, kind reflect.Kind) bool { 335 val1, val2 := reflect.ValueOf(p1), reflect.ValueOf(p2) 336 if !IsPointerableKind(val1.Kind()) || !IsPointerableKind(val2.Kind()) { 337 return false 338 } 339 340 if val1.Type() != val2.Type() { 341 return false 342 } 343 if kind <= reflect.UnsafePointer /* `> 26` => DO check kind */ && (val1.Kind() != kind || val2.Kind() != kind) { 344 return false 345 } 346 347 // compare the underlying pointers of two values 348 return val1.Pointer() == val2.Pointer() 349 } 350 351 const ( 352 panicNonNilMap = "xreflect: not a non-nil map" 353 ) 354 355 // hmap keeps the same as runtime.hmap, which represents a header for a Go map. 356 type hmap struct { 357 count int 358 flags uint8 359 B uint8 // log_2 of # of buckets (can hold up to loadFactor * 2^B items) 360 // ... 361 } 362 363 // GetMapB returns the B value from given map value. Note that this is an unsafe function, and returned value may differ in different Go versions. 364 func GetMapB(m interface{}) (b uint8) { 365 if m == nil { 366 panic(panicNonNilMap) 367 } 368 val := reflect.ValueOf(m) 369 if val.Kind() != reflect.Map || val.IsNil() { 370 panic(panicNonNilMap) 371 } 372 373 // https://hackernoon.com/some-insights-on-maps-in-golang-rm5v3ywh 374 e := (*eface)(unsafe.Pointer(&m)) 375 h := (*hmap)(e.data) 376 return h.B 377 } 378 379 // GetMapBuckets returns the B value and the buckets count from given map value. Note that this is an unsafe function, and returned value may 380 // differ in different Go versions. 381 func GetMapBuckets(m interface{}) (b uint8, buckets uint64) { 382 b = GetMapB(m) 383 buckets = uint64(math.Pow(2, float64(b))) // 2^B 384 return b, buckets 385 }