2025-08-27 21:03:25 +02:00
|
|
|
package bind
|
|
|
|
|
|
2025-08-31 01:23:21 +02:00
|
|
|
import (
|
|
|
|
|
"reflect"
|
|
|
|
|
"time"
|
|
|
|
|
)
|
2025-08-27 21:03:25 +02:00
|
|
|
|
|
|
|
|
type unpackFlag int
|
|
|
|
|
|
|
|
|
|
const (
|
|
|
|
|
pointer unpackFlag = 1 << iota
|
|
|
|
|
slice
|
|
|
|
|
anytype
|
|
|
|
|
iface
|
|
|
|
|
)
|
|
|
|
|
|
2025-08-31 00:40:47 +02:00
|
|
|
var typeCirc = make(map[reflect.Type]bool)
|
|
|
|
|
|
2025-08-27 21:03:25 +02:00
|
|
|
func (f unpackFlag) has(v unpackFlag) bool {
|
|
|
|
|
return f&v > 0
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-31 17:11:09 +02:00
|
|
|
func scalarType(t reflect.Type) Scalar {
|
|
|
|
|
switch t.Kind() {
|
2025-08-31 02:56:26 +02:00
|
|
|
case reflect.Bool:
|
|
|
|
|
return Bool
|
|
|
|
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
2025-08-31 17:11:09 +02:00
|
|
|
if isDuration(t) {
|
|
|
|
|
return Duration
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-31 02:56:26 +02:00
|
|
|
return Int
|
|
|
|
|
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
|
|
|
|
return Uint
|
|
|
|
|
case reflect.Float32, reflect.Float64:
|
|
|
|
|
return Float
|
|
|
|
|
case reflect.String:
|
|
|
|
|
return String
|
|
|
|
|
default:
|
2025-08-31 17:11:09 +02:00
|
|
|
if isTime(t) {
|
|
|
|
|
return Time
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-31 02:56:26 +02:00
|
|
|
return Any
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-31 17:11:09 +02:00
|
|
|
func scalarTypeReflect(v any) Scalar {
|
|
|
|
|
return scalarType(reflect.TypeOf(v))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func scalarSize(t reflect.Type) int {
|
|
|
|
|
return int(t.Size()) * 8
|
2025-08-31 02:56:26 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func valueSize(v reflect.Value) int {
|
|
|
|
|
switch v.Kind() {
|
|
|
|
|
case reflect.String:
|
|
|
|
|
return v.Len() * 8
|
|
|
|
|
case reflect.Interface:
|
|
|
|
|
return valueSize(unpackValue(v, pointer|slice|iface|anytype))
|
2025-08-31 17:11:09 +02:00
|
|
|
case reflect.Invalid:
|
|
|
|
|
return 0
|
2025-08-31 02:56:26 +02:00
|
|
|
default:
|
2025-08-31 17:11:09 +02:00
|
|
|
return scalarSize(v.Type())
|
2025-08-31 02:56:26 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-31 17:11:09 +02:00
|
|
|
func valueSizeReflect(v any) int {
|
|
|
|
|
return valueSize(reflect.ValueOf(v))
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-28 05:04:06 +02:00
|
|
|
func setVisited[T comparable](visited map[T]bool, k T) map[T]bool {
|
|
|
|
|
s := make(map[T]bool)
|
|
|
|
|
for v := range visited {
|
|
|
|
|
s[v] = true
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
s[k] = true
|
|
|
|
|
return s
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-31 00:40:47 +02:00
|
|
|
func checkHasCircularType(visited map[reflect.Type]bool, t reflect.Type) bool {
|
2025-08-28 05:04:06 +02:00
|
|
|
if visited[t] {
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch t.Kind() {
|
|
|
|
|
case reflect.Pointer, reflect.Slice:
|
|
|
|
|
visited = setVisited(visited, t)
|
2025-08-31 00:40:47 +02:00
|
|
|
return checkHasCircularType(visited, t.Elem())
|
2025-08-28 05:04:06 +02:00
|
|
|
case reflect.Struct:
|
|
|
|
|
visited = setVisited(visited, t)
|
|
|
|
|
for i := 0; i < t.NumField(); i++ {
|
2025-08-31 00:40:47 +02:00
|
|
|
if checkHasCircularType(visited, t.Field(i).Type) {
|
2025-08-28 05:04:06 +02:00
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false
|
|
|
|
|
default:
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-31 00:40:47 +02:00
|
|
|
func hasCircularType(t reflect.Type) bool {
|
|
|
|
|
if has, cached := typeCirc[t]; cached {
|
|
|
|
|
return has
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
has := checkHasCircularType(nil, t)
|
|
|
|
|
typeCirc[t] = has
|
|
|
|
|
return has
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func checkHasCircularReference(visited map[uintptr]bool, v reflect.Value) bool {
|
|
|
|
|
if !v.IsValid() {
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if hasCircularType(v.Type()) {
|
2025-08-28 05:04:06 +02:00
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch v.Kind() {
|
|
|
|
|
case reflect.Pointer:
|
|
|
|
|
p := v.Pointer()
|
|
|
|
|
if visited[p] {
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
visited = setVisited(visited, v.Pointer())
|
2025-08-31 00:40:47 +02:00
|
|
|
return checkHasCircularReference(visited, v.Elem())
|
2025-08-28 05:04:06 +02:00
|
|
|
case reflect.Slice:
|
|
|
|
|
p := v.Pointer()
|
|
|
|
|
if visited[p] {
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
visited = setVisited(visited, v.Pointer())
|
|
|
|
|
for i := 0; i < v.Len(); i++ {
|
2025-08-31 00:40:47 +02:00
|
|
|
if checkHasCircularReference(visited, v.Index(i)) {
|
2025-08-28 05:04:06 +02:00
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false
|
|
|
|
|
case reflect.Interface:
|
|
|
|
|
if v.IsNil() {
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-31 00:40:47 +02:00
|
|
|
return checkHasCircularReference(visited, v.Elem())
|
2025-08-28 05:04:06 +02:00
|
|
|
case reflect.Struct:
|
|
|
|
|
for i := 0; i < v.NumField(); i++ {
|
2025-08-31 00:40:47 +02:00
|
|
|
if checkHasCircularReference(visited, v.Field(i)) {
|
2025-08-28 05:04:06 +02:00
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-31 00:40:47 +02:00
|
|
|
func hasCircularReference(v reflect.Value) bool {
|
|
|
|
|
return checkHasCircularReference(nil, v)
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-27 21:03:25 +02:00
|
|
|
func isAny(t reflect.Type) bool {
|
|
|
|
|
return t.Kind() == reflect.Interface && t.NumMethod() == 0
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func isInterface(t reflect.Type) bool {
|
|
|
|
|
return t.Kind() == reflect.Interface && t.NumMethod() > 0
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-31 01:23:21 +02:00
|
|
|
func isTime(t reflect.Type) bool {
|
|
|
|
|
return t.ConvertibleTo(reflect.TypeFor[time.Time]())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func isDuration(t reflect.Type) bool {
|
|
|
|
|
return t.ConvertibleTo(reflect.TypeFor[time.Duration]())
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-27 21:03:25 +02:00
|
|
|
func isScalar(t reflect.Type) bool {
|
|
|
|
|
switch t.Kind() {
|
|
|
|
|
case reflect.Bool,
|
|
|
|
|
reflect.Int,
|
|
|
|
|
reflect.Int8,
|
|
|
|
|
reflect.Int16,
|
|
|
|
|
reflect.Int32,
|
|
|
|
|
reflect.Int64,
|
|
|
|
|
reflect.Uint,
|
|
|
|
|
reflect.Uint8,
|
|
|
|
|
reflect.Uint16,
|
|
|
|
|
reflect.Uint32,
|
|
|
|
|
reflect.Uint64,
|
|
|
|
|
reflect.Uintptr,
|
|
|
|
|
reflect.Float32,
|
|
|
|
|
reflect.Float64,
|
|
|
|
|
reflect.String:
|
|
|
|
|
return true
|
|
|
|
|
default:
|
2025-08-31 01:23:21 +02:00
|
|
|
return isTime(t)
|
2025-08-27 21:03:25 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func isScalarMap(t reflect.Type) bool {
|
|
|
|
|
if t.Kind() != reflect.Map {
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
key := unpackType(t.Key(), pointer)
|
|
|
|
|
if key.Kind() != reflect.String {
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
value := unpackType(t.Elem(), pointer|slice)
|
|
|
|
|
return isAny(value) || isScalar(value)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func unpackType(t reflect.Type, unpack unpackFlag) reflect.Type {
|
|
|
|
|
if unpack.has(pointer) && t.Kind() == reflect.Pointer {
|
|
|
|
|
return unpackType(t.Elem(), unpack)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if unpack.has(slice) && t.Kind() == reflect.Slice {
|
|
|
|
|
return unpackType(t.Elem(), unpack)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return t
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func unpackValue(v reflect.Value, unpack unpackFlag) reflect.Value {
|
2025-08-31 17:11:09 +02:00
|
|
|
if !v.IsValid() || v.IsZero() {
|
2025-08-31 00:40:47 +02:00
|
|
|
return v
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-27 21:03:25 +02:00
|
|
|
if unpack.has(pointer) && v.Kind() == reflect.Pointer {
|
|
|
|
|
return unpackValue(v.Elem(), unpack)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if unpack.has(slice) && v.Kind() == reflect.Slice && v.Len() > 0 {
|
|
|
|
|
return unpackValue(v.Index(0), unpack)
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-31 00:40:47 +02:00
|
|
|
if unpack.has(anytype) && isAny(v.Type()) && !v.IsZero() && !v.IsNil() {
|
2025-08-27 21:03:25 +02:00
|
|
|
return unpackValue(v.Elem(), unpack)
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-31 00:40:47 +02:00
|
|
|
if unpack.has(iface) && isInterface(v.Type()) && !v.IsZero() && !v.IsNil() {
|
2025-08-27 21:03:25 +02:00
|
|
|
return unpackValue(v.Elem(), unpack)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return v
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func acceptsScalar(t reflect.Type) bool {
|
|
|
|
|
t = unpackType(t, pointer|slice)
|
|
|
|
|
return isAny(t) || isScalar(t)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func acceptsFields(t reflect.Type) bool {
|
|
|
|
|
t = unpackType(t, pointer|slice)
|
|
|
|
|
return t.Kind() == reflect.Struct || isScalarMap(t)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func acceptsList(t reflect.Type) bool {
|
|
|
|
|
if !bindable(t) {
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
t = unpackType(t, pointer)
|
|
|
|
|
return t.Kind() == reflect.Slice
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func bindable(t reflect.Type) bool {
|
|
|
|
|
return acceptsScalar(t) || acceptsFields(t)
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-28 05:04:06 +02:00
|
|
|
func acceptsScalarChecked(t reflect.Type) bool {
|
2025-08-31 00:40:47 +02:00
|
|
|
if hasCircularType(t) {
|
2025-08-28 05:04:06 +02:00
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return acceptsScalar(t)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func acceptsFieldsChecked(t reflect.Type) bool {
|
2025-08-31 00:40:47 +02:00
|
|
|
if hasCircularType(t) {
|
2025-08-28 05:04:06 +02:00
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return acceptsFields(t)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func acceptsListChecked(t reflect.Type) bool {
|
2025-08-31 00:40:47 +02:00
|
|
|
if hasCircularType(t) {
|
2025-08-28 05:04:06 +02:00
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return acceptsList(t)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func bindableChecked(t reflect.Type) bool {
|
2025-08-31 00:40:47 +02:00
|
|
|
if hasCircularType(t) {
|
2025-08-28 05:04:06 +02:00
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return bindable(t)
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-27 21:03:25 +02:00
|
|
|
func acceptsScalarReflect[T any]() bool {
|
2025-08-28 05:04:06 +02:00
|
|
|
return acceptsScalarChecked(reflect.TypeFor[T]())
|
2025-08-27 21:03:25 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func acceptsFieldsReflect[T any]() bool {
|
2025-08-28 05:04:06 +02:00
|
|
|
return acceptsFieldsChecked(reflect.TypeFor[T]())
|
2025-08-27 21:03:25 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func acceptsListReflect[T any]() bool {
|
2025-08-28 05:04:06 +02:00
|
|
|
return acceptsListChecked(reflect.TypeFor[T]())
|
2025-08-27 21:03:25 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func bindableReflect[T any]() bool {
|
2025-08-28 05:04:06 +02:00
|
|
|
return bindableChecked(reflect.TypeFor[T]())
|
2025-08-27 21:03:25 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// expected to be called with types that can pass the bindable check
|
|
|
|
|
func allocate(t reflect.Type, len int) (reflect.Value, bool) {
|
|
|
|
|
if len == 0 {
|
|
|
|
|
return reflect.Zero(t), false
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-31 00:40:47 +02:00
|
|
|
if isAny(t) || isScalar(t) || t.Kind() == reflect.Struct {
|
2025-08-27 21:03:25 +02:00
|
|
|
p := reflect.New(t)
|
|
|
|
|
return p.Elem(), len == 1
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-31 00:40:47 +02:00
|
|
|
if isScalarMap(t) {
|
|
|
|
|
return reflect.MakeMap(t), len == 1
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-27 21:03:25 +02:00
|
|
|
if t.Kind() == reflect.Slice {
|
|
|
|
|
l := reflect.MakeSlice(t, len, len)
|
|
|
|
|
for i := 0; i < len; i++ {
|
|
|
|
|
e, ok := allocate(t.Elem(), 1)
|
|
|
|
|
if !ok {
|
|
|
|
|
return reflect.Zero(t), false
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
l.Index(i).Set(e)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return l, true
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-31 00:40:47 +02:00
|
|
|
if t.Kind() == reflect.Pointer {
|
|
|
|
|
e, ok := allocate(t.Elem(), len)
|
|
|
|
|
if !ok {
|
|
|
|
|
return reflect.Zero(t), false
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
p := reflect.New(t.Elem())
|
|
|
|
|
p.Elem().Set(e)
|
|
|
|
|
return p, true
|
2025-08-27 21:03:25 +02:00
|
|
|
}
|
|
|
|
|
|
2025-08-31 00:40:47 +02:00
|
|
|
return reflect.Zero(t), false
|
2025-08-27 21:03:25 +02:00
|
|
|
}
|