原来的流程文档只画了 Get 和 Set, New 在图里就是一个方框。但 New 的分支其实不少, 而且"指针模式 vs 值模式"是对外承诺的语义, 值得单独说清楚。 新增 docs/flow-new.svg 与对应章节, 覆盖四步: 快速分支 -> 解包 -> 解引用与校验 -> 按 isPtr 分岔。其中三处是使用者真正需要知道的: 1. 快速分支里 []R 不深拷贝也不包指针。这样 Raw() 才是 Slice kind、 Array() 才能取到里面的 R。 2. 传值比传指针贵约 60 倍(约 1518ns/44allocs vs 约 25ns), 因为要 DeepClone 递归复制整个对象图, 代价随对象规模增长。不需要副本语义就一律传指针。 顺带记下 DeepClone 的已知限制: 循环引用会栈溢出(CloneValue 没有已访问 集合), 这是旧实现就有的行为, 当前实现复用了它所以两边一致。 3. 为什么两条路最后都标 ptrRoot。漏掉它会连锁出问题: Raw() 返回值类型而不是 指针时, normalizeAccessorSlice 会把 []R 解成 []map 而不是 []*map —— 这个 bug 只在 []R 相关用例上才暴露得出来。 另外补 rfx_size_test.go: 文档里"24 字节"这个数字此前没有测试守着, 而 rfx 在 walk 循环里全程按值传递, 加字段会直接拖慢热路径。加了 ptrRoot 之后仍是 24 字节 (落在原有的对齐填充里), 用测试钉住, 以后加字段时会先失败。 同时修正: 源码对照表补上 New/newRfx 与 normalizeInputValue/DeepClone 的位置, Get 章节的 rfx 字段列表补上 ptrRoot, 开头改成三个入口。
17 lines
489 B
Go
17 lines
489 B
Go
package reflux
|
|
|
|
import (
|
|
"testing"
|
|
"unsafe"
|
|
)
|
|
|
|
// rfx 必须保持在 24 字节: 它在 walk 循环里全程按值传递, 变大会直接拖慢热路径,
|
|
// 也会让 boxed() 的那次堆分配跨到更大的 size class。
|
|
// docs/flow.md 里的说明依赖这个数字。
|
|
func TestRfxStaysSmall(t *testing.T) {
|
|
const want = 24
|
|
if got := unsafe.Sizeof(rfx{}); got != want {
|
|
t.Fatalf("rfx 大小 = %d 字节, 期望 %d —— 加字段前请确认是否真的值得", got, want)
|
|
}
|
|
}
|