diff options
Diffstat (limited to 'vendor/codeberg.org/gruf/go-structr/runtime.go')
-rw-r--r-- | vendor/codeberg.org/gruf/go-structr/runtime.go | 134 |
1 files changed, 134 insertions, 0 deletions
diff --git a/vendor/codeberg.org/gruf/go-structr/runtime.go b/vendor/codeberg.org/gruf/go-structr/runtime.go new file mode 100644 index 000000000..cc1bcd86d --- /dev/null +++ b/vendor/codeberg.org/gruf/go-structr/runtime.go @@ -0,0 +1,134 @@ +package structr + +import ( + "fmt" + "reflect" + "unicode" + "unicode/utf8" + "unsafe" + + "github.com/modern-go/reflect2" + "github.com/zeebo/xxh3" +) + +type structfield struct { + // _type is the runtime type pointer + // underlying the struct field type. + // used for repacking our own erfaces. + _type reflect2.Type + + // offset is the offset in memory + // of this struct field from the + // outer-most value ptr location. + offset uintptr + + // hasher is the relevant function + // for hashing value of structfield + // into the supplied hashbuf, where + // return value indicates if zero. + hasher func(*xxh3.Hasher, any) bool +} + +// find_field will search for a struct field with given set of names, +// where names is a len > 0 slice of names account for struct nesting. +func find_field(t reflect.Type, names []string) (sfield structfield) { + var ( + // is_exported returns whether name is exported + // from a package; can be func or struct field. + is_exported = func(name string) bool { + r, _ := utf8.DecodeRuneInString(name) + return unicode.IsUpper(r) + } + + // pop_name pops the next name from + // the provided slice of field names. + pop_name = func() string { + name := names[0] + names = names[1:] + if !is_exported(name) { + panicf("field is not exported: %s", name) + } + return name + } + + // field is the iteratively searched + // struct field value in below loop. + field reflect.StructField + ) + + switch { + // The only 2 types we support are + // structs, and ptrs to a struct. + case t.Kind() == reflect.Struct: + case t.Kind() == reflect.Pointer && + t.Elem().Kind() == reflect.Struct: + t = t.Elem() + default: + panic("index only support struct{} and *struct{}") + } + + for len(names) > 0 { + var ok bool + + // Pop next name. + name := pop_name() + + // Check for valid struct type. + if t.Kind() != reflect.Struct { + panicf("field %s is not struct: %s", t, name) + } + + // Look for next field by name. + field, ok = t.FieldByName(name) + if !ok { + panicf("unknown field: %s", name) + } + + // Increment total field offset. + sfield.offset += field.Offset + + // Set the next type. + t = field.Type + } + + // Get field type as reflect2. + sfield._type = reflect2.Type2(t) + + // Find hasher for type. + sfield.hasher = hasher(t) + + return +} + +// extract_fields extracts given structfields from the provided value type, +// this is done using predetermined struct field memory offset locations. +func extract_fields[T any](value T, fields []structfield) []any { + // Get ptr to raw value data. + ptr := unsafe.Pointer(&value) + + // If this is a pointer type deref the value ptr. + if reflect.TypeOf(value).Kind() == reflect.Pointer { + ptr = *(*unsafe.Pointer)(ptr) + } + + // Prepare slice of field ifaces. + ifaces := make([]any, len(fields)) + + for i := 0; i < len(fields); i++ { + // Manually access field at memory offset and pack eface. + ptr := unsafe.Pointer(uintptr(ptr) + fields[i].offset) + ifaces[i] = fields[i]._type.UnsafeIndirect(ptr) + } + + return ifaces +} + +// data_ptr returns the runtime data ptr associated with value. +func data_ptr(a any) unsafe.Pointer { + return (*struct{ t, v unsafe.Pointer })(unsafe.Pointer(&a)).v +} + +// panicf provides a panic with string formatting. +func panicf(format string, args ...any) { + panic(fmt.Sprintf(format, args...)) +} |