package reflux import ( "reflect" "unsafe" ) // 本文件是整个 reflux 包里唯一使用 unsafe 的地方, 评审内存安全性时只需要看这里 // 加上 value.go 里对这些函数的调用点。 // // 本包 unsafe 路径的核心不变式(invariant), 下面每个函数都依赖它: // // I1. value.ptr 永远是一个"指向某个 value.td.rtype 类型值的合法地址", // 要么来自 reflect.Value.UnsafePointer()/reflect.New(), 要么由 // fieldAt/elemAt 从一个满足 I1 的地址加上该类型真实的字段偏移量算出。 // I2. ptr 是 unsafe.Pointer 类型而不是 uintptr, GC 能正常追踪它, // 被指向的对象不会在 value 存活期间被回收或移动。 // 所有指针运算都用 unsafe.Add 在单个表达式内完成, 不把地址存成 uintptr。 // I3. 只有 Exported == true 的字段才允许构造出 value, 见 rfxFieldDesc.Exported // 的注释: reflect.NewAt 不带只读标记, 语言层面的未导出字段保护在这里失效, // 必须由调用方自己拦住。 // I4. 只有 writable == true 的 value 才允许写入, writable 的传播规则见 // value.go 的 canWrite 说明。 // fieldAt 返回 base 所指 struct 里偏移量为 off 的字段地址。 // 要求: base 满足 I1, off 来自 reflect.StructField.Offset。 func fieldAt(base unsafe.Pointer, off uintptr) unsafe.Pointer { return unsafe.Add(base, off) } // loadPtr 把 p 当作 *T 读出来, 返回它指向的地址。 // 要求: p 指向一个指针类型的字段。返回 nil 表示空指针。 func loadPtr(p unsafe.Pointer) unsafe.Pointer { return *(*unsafe.Pointer)(p) } // sliceHeader 与运行时 slice 的内存布局一致, 用于零拷贝读取切片头。 type sliceHeader struct { Data unsafe.Pointer Len int Cap int } // sliceLen 返回 p 所指切片的长度。要求 p 指向一个 slice 类型的值。 func sliceLen(p unsafe.Pointer) int { return (*sliceHeader)(p).Len } // sliceElemAt 返回 p 所指切片第 i 个元素的地址, 越界返回 nil。 // 要求: elemSize 是该切片元素类型的真实 Size()。 func sliceElemAt(p unsafe.Pointer, i int, elemSize uintptr) unsafe.Pointer { h := (*sliceHeader)(p) if i < 0 || i >= h.Len { return nil } return unsafe.Add(h.Data, uintptr(i)*elemSize) } // arrayElemAt 返回 p 所指数组第 i 个元素的地址, 越界返回 nil。 func arrayElemAt(p unsafe.Pointer, i int, n int, elemSize uintptr) unsafe.Pointer { if i < 0 || i >= n { return nil } return unsafe.Add(p, uintptr(i)*elemSize) } // valueAt 把 (地址, 类型) 还原成一个可寻址的 reflect.Value, 用于回退到 // 标准反射路径(类型转换、JSON、map 操作等)。 // // 安全性: 返回的 Value 是可寻址且可写的, 即使它对应的是未导出字段 —— // 这正是 I3 存在的原因, 调用方必须已经确认过 Exported。 func valueAt(td *rfxTypeDesc, p unsafe.Pointer) reflect.Value { return reflect.NewAt(td.rtype, p).Elem() } // ptrValueAt 把 (地址, 类型) 还原成一个 *T 的 reflect.Value。 func ptrValueAt(td *rfxTypeDesc, p unsafe.Pointer) reflect.Value { return reflect.NewAt(td.rtype, p) } // addrOf 取一个可寻址 reflect.Value 的地址。要求 v.CanAddr()。 func addrOf(v reflect.Value) unsafe.Pointer { return v.Addr().UnsafePointer() } // boxCopy 把一个不可寻址的值(典型来源: map 取值、interface 拆包)拷贝到 // 新分配的堆内存上, 返回该副本的地址。副本与原值不共享内存, 因此对它的写入 // 不会影响原容器 —— 这与 reflect 里 "map 元素不可寻址" 的语义一致。 func boxCopy(v reflect.Value) unsafe.Pointer { p := reflect.New(v.Type()) p.Elem().Set(v) return p.UnsafePointer() } // ---- 基础类型的直接读写 ---- // 这些函数通过 *T 做**有类型**的读写, 编译器会正常插入 GC 写屏障, // 不存在漏掉写屏障导致对象被误回收的问题。unsafe 只体现在"地址是算出来的"。 func loadString(p unsafe.Pointer) string { return *(*string)(p) } func storeString(p unsafe.Pointer, v string) { *(*string)(p) = v } func loadInt(p unsafe.Pointer) int { return *(*int)(p) } func storeInt(p unsafe.Pointer, v int) { *(*int)(p) = v } // 各宽度整数的直读。读取只依赖字段的 Kind(内存宽度), 不要求具名类型完全一致 —— // type Age int 和 int 的内存表示相同, 按 *int 读出来的值是一样的。 // (写入则不同, 见 storeFast: 写要求类型完全一致, 否则可能绕过 cast 的转换语义。) func loadInt8(p unsafe.Pointer) int8 { return *(*int8)(p) } func loadInt16(p unsafe.Pointer) int16 { return *(*int16)(p) } func loadInt32(p unsafe.Pointer) int32 { return *(*int32)(p) } func loadInt64(p unsafe.Pointer) int64 { return *(*int64)(p) } func loadUint(p unsafe.Pointer) uint { return *(*uint)(p) } func loadUint8(p unsafe.Pointer) uint8 { return *(*uint8)(p) } func loadUint16(p unsafe.Pointer) uint16 { return *(*uint16)(p) } func loadUint32(p unsafe.Pointer) uint32 { return *(*uint32)(p) } func loadUint64(p unsafe.Pointer) uint64 { return *(*uint64)(p) } func loadFloat32(p unsafe.Pointer) float32 { return *(*float32)(p) } func loadBool(p unsafe.Pointer) bool { return *(*bool)(p) } func storeBool(p unsafe.Pointer, v bool) { *(*bool)(p) = v } func loadFloat64(p unsafe.Pointer) float64 { return *(*float64)(p) } func storeFloat64(p unsafe.Pointer, v float64) { *(*float64)(p) = v }