-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathrf_misc.go
More file actions
545 lines (476 loc) · 15.1 KB
/
rf_misc.go
File metadata and controls
545 lines (476 loc) · 15.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
package rf
import (
r "reflect"
"runtime"
"strings"
"sync"
)
/*
Tiny shortcut for caching arbitrary structures keyed by `reflect.Type`. Any
reflection-based code that involves walking arbitrary structs should use
`rf.Cache` to "compile" a specialized structure optimized for that particular
traversal, and reuse it for subsequent invocations.
*/
type Cache struct {
sync.Map
Func func(r.Type) any
}
/*
Gets or creates the structure keyed by the type and generated by `self.Func`,
which must be provided at construction time.
*/
func (self *Cache) Get(key r.Type) any {
// Susceptible to "thundering herd". An improvement from no caching, but still
// not ideal.
val, ok := self.Load(key)
if !ok {
if self.Func != nil {
val = self.Func(key)
}
self.Store(key, val)
}
return val
}
/*
Zero-cost integer iterator. Usage:
for range rf.Iter(N) { ... }
for ind := range rf.Iter(N) { ... }
Because `struct{}` is zero-sized, `[]struct{}` is backed by "zerobase"
(see "runtime/malloc.go") and does not allocate. This should perform about as
well as a "normal" counted loop.
This is not reflection related, but exported because it's used internally a
lot.
*/
func Iter(count int) []struct{} {
return make([]struct{}, count)
}
/*
Returns `reflect.Type` of the given type. Differences from `reflect.TypeOf`:
* Avoids spurious heap escape and copying.
* Output is always non-nil.
* When the given type is an interface, including the empty interface `any`,
the output is a non-nil `reflect.Type` describing the given interface.
*/
func Type[A any]() r.Type { return r.TypeOf((*A)(nil)).Elem() }
/*
Same as `reflect.TypeOf`, but dereferences the type, allowing to pass a nil
pointer, which doesn't allocate. The following expressions both produce the
type of `time.Time`, but the second one is cheaper:
rf.DerefType(time.Time{}) // Allocates.
rf.DerefType((*time.Time)(nil)) // Doesn't allocate.
*/
func DerefType(val interface{}) r.Type {
return TypeDeref(r.TypeOf(val))
}
/*
Nil-safe version of `reflect.Type.Kind`. If the input is nil, returns
`reflect.Invalid`.
*/
func TypeKind(val r.Type) r.Kind {
if val == nil {
return r.Invalid
}
return val.Kind()
}
/*
Same as `reflect.TypeOf(val).Kind()`, but returns `reflect.Invalid` for a nil
input, instead of panicking. Doesn't automatically dereference the input.
*/
func Kind(val any) r.Kind {
return TypeKind(r.TypeOf(val))
}
/*
Shortcut for `rf.TypeKind(rf.DerefType(val))`. Returns the kind of the provided
value, automatically dereferencing any outer pointer types until it finds a
non-pointer type. If the input is nil, returns `reflect.Invalid`.
*/
func DerefKind(val any) r.Kind {
return TypeKind(DerefType(val))
}
/*
Returns the element type of the provided type, automatically dereferencing
pointer types. If the input is nil, returns nil.
*/
func TypeDeref(typ r.Type) r.Type {
for typ != nil && typ.Kind() == r.Ptr {
typ = typ.Elem()
}
return typ
}
/*
Shortcut for `rf.ValueDeref(reflect.ValueOf(val))`. Returns the `reflect.Value`
of the input, automatically dereferencing any outer pointers. If any outer
pointers are nil, returns `reflect.Value{}`.
*/
func Deref(val any) r.Value {
return ValueDeref(r.ValueOf(val))
}
/*
Dereferences the provided value until it's no longer a pointer. If the input is
a nil pointer or a pointer to a nil pointer (recursively), this returns
`reflect.Value{}`.
*/
func ValueDeref(val r.Value) r.Value {
for val.Kind() == r.Ptr {
if val.IsNil() {
return r.Value{}
}
val = val.Elem()
}
return val
}
/*
If the value represents a non-pointer and its address can be taken, returns its
address via `reflect.Value.Addr`. Otherwise returns the value as-is. Sometimes
handy when taking an address is an optional optimization, such as before
calling `reflect.Value.Interface`.
*/
func ValueAddr(val r.Value) r.Value {
if val.Kind() != r.Ptr && val.CanAddr() {
return val.Addr()
}
return val
}
/*
Like `reflect.Value.Interface`, but returns nil when value is invalid, and
attempts to take an address by calling `rf.ValueAddr`, which is sometimes more
efficient. Calling `reflect.Value.Interface` on a non-address makes a heap copy
of the underlying value, while indirection can avoid that cost.
*/
func Interface(val r.Value) any {
val = ValueAddr(val)
if val.CanInterface() {
return val.Interface()
}
return nil
}
/*
Shortcut for `rf.TypeElem(reflect.TypeOf(val))`. Returns the element type of the
given value, automatically dereferencing pointer AND slice types, until it
finds the first non-pointer, non-slice type. If the input is nil, returns nil.
*/
func ElemType(val any) r.Type {
return TypeElem(r.TypeOf(val))
}
/*
Returns the element type of the provided type, automatically dereferencing
pointer AND slice types, until it finds the first non-pointer, non-slice type.
If the input is nil, returns nil.
*/
func TypeElem(typ r.Type) r.Type {
for typ != nil && (typ.Kind() == r.Ptr || typ.Kind() == r.Slice) {
typ = typ.Elem()
}
return typ
}
/*
Same as `reflect.Value.Type` but "safe" to call on invalid value, in which case
this will return nil. Should be used only when nil output type is acceptable.
*/
func ValueType(val r.Value) r.Type {
if !val.IsValid() {
return nil
}
return val.Type()
}
// Returns the name of a given function, if possible. Returns "" for nil.
func FuncName(val any) string {
if val == nil {
return ``
}
return runtime.FuncForPC(r.ValueOf(val).Pointer()).Name()
}
/*
True if the input is a nil interface or a non-nil interface containing a nil
value. See `rf.IsValueNil`.
*/
func IsNil(val any) bool {
return val == nil || IsValueNil(r.ValueOf(val))
}
/*
True if the input is invalid or contains a nilable value that is nil.
Automatically dereferences pointers, returning true for a non-nil pointer to a
nil value.
*/
func IsValueNil(val r.Value) bool {
val = ValueDeref(val)
return !val.IsValid() || IsKindNilable(val.Kind()) && val.IsNil()
}
/*
True if the input string is empty. Allows to clarify reflection code that checks
the public/private status of struct fields and type methods. Examples
(oversimplified):
rf.IsPublic(reflect.StructField{}.PkgPath)
rf.IsPublic(reflect.Method{}.PkgPath)
*/
func IsPublic(pkgPath string) bool { return pkgPath == `` }
/*
True if the given struct field is considered public/exported. Shortcut for
`rf.IsPublic(val.PkgPath)`.
*/
func IsFieldPublic(val r.StructField) bool { return IsPublic(val.PkgPath) }
/*
Takes a struct field tag and returns its identifier part, following the
"encoding/json" conventions. Ident "-" is converted to "". Usage:
ident := rf.TagIdent(someField.Tag.Get(`json`))
ident := rf.TagIdent(someField.Tag.Get(`db`))
Rules:
json:"ident" -> "ident"
json:"ident,<extra>" -> "ident"
json:"-" -> ""
json:"-,<extra>" -> ""
*/
func TagIdent(tag string) string {
// TODO: consider validating that the name doesn't contain double quotes.
index := strings.IndexRune(tag, ',')
if index >= 0 {
tag = tag[:index]
}
if tag == `-` {
return ``
}
return tag
}
/*
Zeroes the destination of the given pointer, if possible. The input must be
one of:
* nil interface (nop)
* nil pointer (nop)
* non-nil pointer (gets zeroed)
*/
func Zero(ptr any) {
if ptr == nil {
return
}
val := r.ValueOf(ptr)
ValidateValueKind(val, r.Ptr)
if !val.IsNil() {
val.Elem().Set(r.Zero(val.Type().Elem()))
}
}
/*
Shortcut for `rf.IsValueZero(reflect.ValueOf(val))`. True if the input is nil or
a zero value of its type. Automatically dereferences the input; an
arbitrarily-nested non-nil pointer to a zero value is considered zero.
*/
func IsZero(val any) bool {
return val == nil || IsValueZero(r.ValueOf(val))
}
/*
True if the input is nil (as defined by `rf.IsValueNil`) or a zero value of its
type. Automatically dereferences pointers.
*/
func IsValueZero(val r.Value) bool {
val = ValueDeref(val)
return IsValueNil(val) || val.IsZero()
}
// True for kinds on which it's safe to call `reflect.Value.IsNil`.
func IsKindNilable(kind r.Kind) bool {
switch kind {
case r.Chan, r.Func, r.Interface, r.Map, r.Ptr, r.Slice, r.UnsafePointer:
return true
default:
return false
}
}
/*
True for the kinds "reflect" considers collections: array, chan, map, slice, or
string. These are the kinds for which it's safe to call `reflect.Value.Len`
without panicking.
*/
func IsKindColl(kind r.Kind) bool {
switch kind {
case r.Array, r.Chan, r.Map, r.Slice, r.String:
return true
default:
return false
}
}
/*
True if the kind of the provided value is a collection. See `rf.IsKindColl` for
further details. Note that this follows `reflect.Value.Len` rather than the
built-in `len` in that pointers to arrays are not considered to be
collections.
*/
func IsColl(val any) bool {
return IsKindColl(Kind(val))
}
/*
True if the input belongs to one of the kinds "reflect" considers collections
(see `rf.IsKindColl`), and has the length of zero. If the input is a non-nil
interface that describes a nil collection, such as `([]string)(nil)`, this
returns true. If the input is a nil interface, this returns false. Doesn't
automatically dereference the input.
*/
func IsEmptyColl(val any) bool {
return IsValueEmptyColl(r.ValueOf(val))
}
/*
Variant of `rf.IsEmpty` that takes a `reflect.Value` as input. See `rf.IsEmpty`
for the documentation.
*/
func IsValueEmptyColl(val r.Value) bool {
return IsKindColl(val.Kind()) && val.Len() == 0
}
/*
Short for "normalize nil". If the input is a non-nil interface whose underlying
value is nil, this normalizes it, returning a nil interface. Otherwise this
returns the input as-is.
*/
func NormNil(val any) any {
if IsNil(val) {
return nil
}
return val
}
/*
Shortcut for `rf.ValueLen(rf.Deref(val))`. Returns the length of the given
value, if possible. If not, returns 0. Automatically dereferences the input.
*/
func Len(val any) int {
return ValueLen(Deref(val))
}
/*
If the provided value is a collection via `rf.IsColl`, returns the result of
calling `reflect.Value.Len`. Otherwise returns 0. Doesn't automatically
dereference the input.
*/
func ValueLen(val r.Value) int {
if IsKindColl(val.Kind()) {
return val.Len()
}
return 0
}
/*
Shortcut for `reflect.SliceOf(DerefType(val))`. The following expressions are
all equivalent:
reflect.SliceOf(reflect.TypeOf(``))
reflect.SliceOf(reflect.TypeOf((*string)(nil)).Elem())
rf.SliceType(``)
rf.SliceType((*string)(nil))
*/
func SliceType(val any) r.Type {
return r.SliceOf(DerefType(val))
}
// True if the given field represents an embedded non-pointer struct type. False
// if it's embedded by pointer.
func IsEmbed(val r.StructField) bool {
return val.Anonymous && val.Type != nil && val.Type.Kind() == r.Struct
}
// Shortcut for `rf.TypeFields(rf.DerefType(typ))`.
func Fields(typ any) []r.StructField {
return TypeFields(DerefType(typ))
}
/*
Takes a struct type and returns a shallow slice of its fields, similar to what
you would get by iterating over `reflect.Type.NumField`. For any given type,
caches and reuses the resulting slice for all future calls. The resulting slice
or its elements must not be mutated. Note: because this doesn't support
embedded structs, for most use cases `rf.TypeDeepFields` is more appropriate.
*/
func TypeFields(typ r.Type) []r.StructField {
if typ == nil {
return nil
}
return typeFieldsCache.Get(TypeDeref(typ)).([]r.StructField)
}
// Shortcut for `rf.TypeDeepFields(rf.DerefType(typ))`.
func DeepFields(typ any) []r.StructField {
return TypeDeepFields(DerefType(typ))
}
/*
Takes a struct type and returns a slice of its fields, with support for embedded
structs. Structs embedded by value (not by pointer) are considered parts of the
enclosing struct, rather than fields in their own right, and their fields are
included into this function's output. This is NOT equivalent to the fields you
would get by iterating over `reflect.Type.NumField`. Not only because it
includes the fields of value-embedded structs, but also because it adjusts
`reflect.StructField.Index` and `reflect.StructField.Offset` specifically for
the given ancestor type. In particular, `reflect.StructField.Offset` of
deeply-nested fields is exactly equivalent to the output of `unsafe.Offsetof`
for the same parent type and field, which is NOT what you would normally get
from the "reflect" package.
For comparison. Normally when using `reflect.Type.FieldByIndex`, the returned
fields have both their offset and their index relative to their most immediate
parent, rather than the given ancestor. But it's also inconsistent. When using
`reflect.Type.FieldByName`, the returned fields have their index relative to
the ancestor, but their offset is still relative to their most immediate
parent.
This function fixes ALL of that. It gives you fields where offsets AND indexes
are all relative to the ancestor.
Like `rf.TypeFields`, this caches and reuses the resulting slice for all future
calls for any given type. The resulting slice or its elements must not be
mutated.
*/
func TypeDeepFields(typ r.Type) []r.StructField {
if typ == nil {
return nil
}
return typeDeepFieldsCache.Get(TypeDeref(typ)).([]r.StructField)
}
// Shortcut for `rf.TypeOffsetFields(rf.DerefType(typ))`.
func OffsetFields(typ any) map[uintptr][]r.StructField {
return TypeOffsetFields(DerefType(typ))
}
/*
Takes a struct type and returns a mapping of field offsets to fields. Offsets
correspond to `unsafe.Offsetof`. The map is built from `rf.TypeDeepFields`,
which treats structs embedded by value (not by pointer) as parts of the
enclosing struct, including their fields into the output, and adjusts field
offsets for compatibility with `unsafe.Offsetof`. Offsets may correspond to
multiple fields because fields may be zero-sized. Each included slice has at
least one field. Caches and reuses the resulting map for all future calls for
any given type. The map or anything contained within must not be mutated.
*/
func TypeOffsetFields(typ r.Type) map[uintptr][]r.StructField {
if typ == nil {
return nil
}
return typeOffsetFieldsCache.Get(TypeDeref(typ)).(map[uintptr][]r.StructField)
}
/*
Alias of `[]int` (which is used for struct field paths such as
`reflect.StructField.Index`) with some shortcuts relevant for efficient and
correct walking of struct types.
*/
type Path []int
/*
Returns a copy whose length and capacity are equal to the length of the
original. Such copying is required when walking a struct type to build a
collection of fields for later use (such representations should also be cached
via `rf.Cache`).
*/
func (self Path) Copy() Path {
if self == nil {
return nil
}
out := make(Path, len(self))
copy(out, self)
return out
}
/*
Appends elements to the path, returning a stack-allocatable value that can
revert the path to the previous length by reslicing, keeping any capacity that
was added by the append. Usage:
defer path.Add(field.Index).Reset()
*/
func (self *Path) Add(vals []int) Rev {
len := len(*self)
*self = append(*self, vals...)
return Rev{self, len}
}
/*
Returned by `(*rf.Path).Add`. Syntactic shortcut for appending to a path for the
duration of a function, reverting it back at the end. Should be transient,
stack-allocated, and basically free to use.
*/
type Rev struct {
Path *Path
Len int
}
/*
Reslices `self.Path`, resetting its length to `self.Len` while keeping the
capacity. Usage:
defer path.Add(field.Index).Reset()
*/
func (self Rev) Reset() { *self.Path = (*self.Path)[:self.Len] }