// 类型布局描述符: 把 struct 的字段偏移量、元素类型大小等信息构建一次并全局缓存, // 热路径上不再走 reflect 的按名字段查找。 package reflux import ( "reflect" "sync" ) // rfxFieldDesc 描述一个字段在其所属 struct 里的位置和类型。 type rfxFieldDesc struct { // Name 字段名(原始声明名) Name string // Offset 字段相对于 struct 起始地址的字节偏移量 Offset uintptr // Type 字段类型的描述符, 递归引用以支持嵌套 struct Type *rfxTypeDesc // Exported 字段是否是导出字段。 // // 这是本原型最关键的安全标记: reflect.NewAt 构造出来的 Value 是可寻址且 // **可写**的, 它不带 reflect 正常路径上的 flagRO 只读标记, 也就是说 Go 语言 // 层面"不能修改未导出字段"这条保护在 unsafe 路径上完全失效。因此必须由我们 // 自己在每一次 Get/Set 前显式拒绝未导出字段。 Exported bool // index 在 reflect.Type 里的字段下标, 回退到 reflect 路径时使用 index int } // rfxTypeDesc 描述一个类型的内存布局, 构建一次之后全局复用。 type rfxTypeDesc struct { rtype reflect.Type Kind reflect.Kind Size uintptr // fields 按声明顺序保存所有字段, 用于线性扫描查找 fields []rfxFieldDesc // fieldMap 按字段名索引, 用于 map 查找。 // 除了原始字段名, 还额外收录了"首字母小写"的别名, 以兼容 reflux 的 // tryStructFieldValue 行为(Get("address") 能命中 Address 字段)。 fieldMap map[string]*rfxFieldDesc // Elem 是 ptr/slice/array 的元素类型描述符 Elem *rfxTypeDesc // elemSize 是元素类型的字节大小, slice/array 下标寻址用 elemSize uintptr // arrayLen 是数组长度(仅 Kind == Array 时有效) arrayLen int // ptrElemKind 缓存指针指向类型的 Kind, 避免热路径上再取一次 ptrElemKind reflect.Kind // fast 标记该类型是否是可以直接按机器类型读写的常见标量类型。 // 热路径上只要比较一个字节, 就能决定走不走零分配快路径。 fast fastKind } // fastKind 标记"类型与 Go 内置标量完全一致"的情况。 // 注意必须是**完全一致**: type MyStr string 的 Kind 也是 String, 但直接按 // string 写入会绕过 reflux 原有的 cast 转换语义, 所以这里只认原生类型。 type fastKind uint8 const ( fkNone fastKind = iota fkString fkInt fkBool fkFloat64 ) var ( typeString = reflect.TypeOf("") typeInt = reflect.TypeOf(int(0)) typeBool = reflect.TypeOf(false) typeFloat64 = reflect.TypeOf(float64(0)) ) func fastKindOf(t reflect.Type) fastKind { switch t { case typeString: return fkString case typeInt: return fkInt case typeBool: return fkBool case typeFloat64: return fkFloat64 } return fkNone } // isScalar 报告该类型是不是可以用 cast 直接转换的标量。 // 只有标量目标才走 fastx 自己的赋值逻辑; 复合类型(指针/切片/结构体/map/interface) // 的赋值语义由 reflux.setValue 定义, 重新实现必然产生偏差, 一律交回它处理。 func (td *rfxTypeDesc) isScalar() bool { switch td.Kind { case reflect.String, 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: return true } return false } // lookupField 用 map 查找字段, 未找到返回 nil。 func (td *rfxTypeDesc) lookupField(name string) *rfxFieldDesc { return td.fieldMap[name] } var ( // descCache 是全局类型描述符缓存。reflect.Type 本身可比较, 直接做 key。 descCache sync.Map // map[reflect.Type]*rfxTypeDesc // descMu 只在缓存未命中(冷路径)时加锁, 保证同一时刻只有一个构建者, // 并且保证带环的类型图整体构建完成后才发布, 读者永远看不到半成品。 descMu sync.Mutex ) // rfxDescriptorOf 返回类型 t 的布局描述符, 第一次遇到时构建, 之后命中缓存。 func rfxDescriptorOf(t reflect.Type) *rfxTypeDesc { if v, ok := descCache.Load(t); ok { return v.(*rfxTypeDesc) } descMu.Lock() defer descMu.Unlock() // 双重检查: 可能在抢锁期间已经被别的 goroutine 构建好了 if v, ok := descCache.Load(t); ok { return v.(*rfxTypeDesc) } // pending 保存本次构建过程中新建的所有描述符。自引用类型 // (type Node struct{ Next *Node }) 靠它终止递归。 pending := make(map[reflect.Type]*rfxTypeDesc, 8) td := buildLocked(t, pending) // 整张类型图都填充完毕之后再统一发布。sync.Map.Store 提供的 // happens-before 保证了读者 Load 到任何一个描述符时, 它引用到的 // 其它描述符也已经写完。 for k, v := range pending { descCache.Store(k, v) } return td } // buildLocked 在持有 descMu 的前提下递归构建描述符。 func buildLocked(t reflect.Type, pending map[reflect.Type]*rfxTypeDesc) *rfxTypeDesc { if v, ok := descCache.Load(t); ok { return v.(*rfxTypeDesc) } if td, ok := pending[t]; ok { return td } td := &rfxTypeDesc{ rtype: t, Kind: t.Kind(), Size: t.Size(), fast: fastKindOf(t), } // 先登记再递归, 否则自引用类型会栈溢出 pending[t] = td switch t.Kind() { case reflect.Struct: n := t.NumField() td.fields = make([]rfxFieldDesc, n) td.fieldMap = make(map[string]*rfxFieldDesc, n*2) for i := 0; i < n; i++ { sf := t.Field(i) td.fields[i] = rfxFieldDesc{ Name: sf.Name, Offset: sf.Offset, Type: buildLocked(sf.Type, pending), Exported: sf.IsExported(), index: i, } } for i := range td.fields { f := &td.fields[i] td.fieldMap[f.Name] = f } // 追加"首字母小写"别名, 兼容 reflux 的大小写宽松查找。 // 只在不与真实字段名冲突时才登记。 for i := range td.fields { f := &td.fields[i] if len(f.Name) > 0 && f.Name[0] >= 'A' && f.Name[0] <= 'Z' { alias := string(f.Name[0]+32) + f.Name[1:] if _, exists := td.fieldMap[alias]; !exists { td.fieldMap[alias] = f } } } case reflect.Ptr: td.Elem = buildLocked(t.Elem(), pending) td.ptrElemKind = t.Elem().Kind() case reflect.Slice: td.Elem = buildLocked(t.Elem(), pending) td.elemSize = t.Elem().Size() case reflect.Array: td.Elem = buildLocked(t.Elem(), pending) td.elemSize = t.Elem().Size() td.arrayLen = t.Len() case reflect.Map: // map 没有稳定的内存布局可以做偏移量运算, 描述符只记录元素类型, // 实际取值走 reflect 回退路径。 td.Elem = buildLocked(t.Elem(), pending) } return td }