Files
Just-DataInspector/Documents/Code_Review_Plan.md
T
DeepSeek V4.1 Flash b58b427f62 review(panels+size): 面板结构收敛收尾、审查清理、新增平行数组护栏
一、审查清理
* `PlotView::FreeMarkerValue` 的 `const Lane&` 形参从未使用(函数体里只有一句
  `(void)lane;`)⇒ 删除形参与该语句,更新 2 个调用点。
* `EdgePanelLayoutFor` 里「选区截取是否就绪(双光标)」算了 3 遍 ⇒ 收敛为函数顶部
  一次 `clipReady`。它同时决定面板高度 footer、条目行可见性、按钮几何,
  三处各算一遍时只要有一处条件写错,就是"底部多一段空白"或"没双光标却能滚动"。

二、新增护栏(离屏检查)
* `panels`:新增「平行数组长度一致」不变式 —— `EdgePanelLayout` 的 13 个按行平行
  vector 必须与行数一致;4 个「重采样」专属数组允许为空、非空则必须对齐。
  这是"某列读到别人矩形"这类错位缺陷的唯一低成本护栏(O(1))。
* `panelscroll`:同样加一份(大文件、566 行时才会暴露的问题)。
* `panels` 输出增加 rows 数,便于定位。

三、文档
* `Documents/Code_Review_Plan.md` 新增 §七(第 35 轮节点与硬约束)。
* `Documents/Code_Review_Progress.md` 新增第 35 轮(面板架构评估 + 三次重构记录)。
* `Documents/Code_Review_Report.md` 新增 §12(评估结论 + gdtoa 最终结论 + 护栏价值)。
* `Documents/agent_mem.md` 新增第 35 轮六条规则。
* `Tools/README.md`:补 `panelscroll`、`integer_replacement_matches_std`,
  断言数更新为 260 / 108,最小集表补充面板相关条目。

验证:
* `make release`(删 .o 全量)零 error / 零 warning,产物 **786,944 B**。
* `make -C Tools` 零 error / 零 warning。
* `UnitTests` → checks=260 failed=0 cases=19。
* `OffscreenChecks -all` → 6/6 检查 PASS,checks=108 failed=0。
* `panels -mode compare` → 7 个面板像素哈希与几何逐项不变。
2026-11-04 14:37:59 +08:00

15 KiB
Raw Blame History

Code Review 计划(分支 Code_Review)

目标:在不引入性能回归、不改变用户可见行为的前提下,对全项目做一次结构 + 性能 + 体积 的查漏补缺,并把「改一个功能要动 40 处」的屎山点收敛成表驱动/单一实现。

基线(本计划开始时):

  • 分支 Code_Review,起点 764e5be(dev 已删除过时的 UI_Refactor_* 三份文档)。
  • make release 产物 890,880 B(.text 753,136 B、.rdata 36,936 B、.rsrc 20,656 B、 .pdata 30,420 B、.xdata 29,496 B、.idata 12,856 B)。
  • 源码约 5.6 万行;最大单文件 PlotViewPaint.cpp 6,561 行、PlotView.hpp 4,654 行。

约束:

  • 每完成一个节点编译验证(release + debug 零警告)并提交,同时更新 Documents/Code_Review_Progress.md。
  • 性能相关节点必须给出同一次运行内的相对量(历史教训:绝对毫秒阈值不可靠)。
  • 不占用用户的鼠标/键盘/屏幕(只用纯控制台探针或源码级核对)。

一、已确认的问题(本计划的事实基础)

A. 体积

项 实测 说明
dynamic_cast(Toolbar.cpp:370) +75,264 B 最小复现程序:39,936 B → 115,200 B。它会链入 RTTI 元数据 + cp-demangle.o(map 里 56,320 B)。而该处 Button::widget 本来就是 UiButtonWidget* ⇒ 转换完全多余。
-fno-rtti 未启用 移除上一条后即可开启,进一步去掉 type_info 表。
.pdata + .xdata 60 KB -fno-exceptions 已开;SEH 展开表仍存在。可尝试 -fno-asynchronous-unwind-tables(历史上链接失败,需复核)。
libmingwex 的 strtodg/gdtoa/mingw_wpformat 27.8 KB 来自 wcstod / wprintf 类调用;可评估用自写解析替代 wcstod 的收益(公式/输入框解析)。
.rsrc 20.7 KB 图标(PNG 帧)+ manifest + 版本信息,已优化过。
重复常量/重复实现 多处 kFormulaEnvelopeBuckets 已清理;仍有 ClampDouble 两份实现等。

B. 性能(结构性热点)

  1. 面板开关/参数处理是 6 个 EdgePanelKind × ~20–26 处 switch 分支(实测每个 kind 出现 21–26 次,全部由手写 if/switch 维护)。这是新增功能必然漏改 2–3 处的根本原因 (历史已发生 4 次:MARK 漏拖动、RESAMPLE 漏拖动+关闭、CLIP 漏滚轮+缓存键)。
  2. SceneContentHash 里 6 段几乎相同的逐面板混入循环(PlotViewCacheKey.cpp)。
  3. EdgePanelLayoutFor 是巨型分支,EdgePanelLayout 结构体带 20+ 个按面板类型区分的 矩形数组;高度算式与几何算式分开写(历史上多减一个 pad 造成空白空白 BUG)。
  4. PlotView 有 30+ 个缓存,每个都手写「键 + DC + 位图 + 贴图 + 失效」五件套; 失效路径散落在 8 个文件里,m_sceneValid=false 出现 6 处。
  5. ApplicationCommands.cpp 2,165 行混杂:标签页、数据派生、菜单、工具栏、键盘、鼠标。
  6. PlotView.hpp 4,654 行(声明 + 内联实现),单文件认知负担极大。

C. 过时/错误注释与命名(抽样已确认)

  • agent_mem.md §3「目录结构」仍写着已不存在的 Utilities/CsvTable/、Utilities/DataModel/ 等目录;§2 写 -Os 而实际是 -Oz。
  • 源码中留有历史轮次的「本轮」「上一轮」叙述(对当前读者无意义),以及被推翻方案的注释。
  • PlotViewInternal.hpp 里 kFormulaEnvelope* 注释已随实现迁移,需同步。

二、节点计划

优先级:S(安全、收益明确、风险低)→ A(结构调整,中风险,需逐步验证)→ B(深度重构,必须有可验证的前后一致性)。

阶段 0:建立基线(节点 P0)

  • P0.1 记录基线:release/debug 体积、.text/.rdata 等节表、关键路径的可复跑测量程序 (纯控制台,链接 Build/Release/*.o,不入库)。已完成基线的部分见下表。
  • P0.2 建立 Documents/Code_Review_Progress.md,每节点完成后追加一行结论 + 数字。

阶段 1:体积(节点 P1–P5,安全优先)

  • P1 移除 dynamic_cast + 开启 -fno-rtti
    • 把 Toolbar::BuildBoxes 的 dynamic_cast<const UiButtonWidget*> 改为直接用 Button::widget(已是该类型)或给 UiWidget 加虚函数 ContentBounds()。
    • Makefile CXXFLAGS += -fno-rtti;全仓确认无 typeid/dynamic_cast/type_info。
    • 预期 −70~80 KB。验收:产物下降 ≥60 KB;功能逐项核对(工具栏按钮、布局边界调试)。
  • P2 体积审计第二轮
    • 复核 -fno-asynchronous-unwind-tables(历史上链接失败,试 -fno-unwind-tables 组合)。
    • 评估 wcstod 替身(自写定点/浮点解析,仅在公式与输入框使用)去掉 strtodg/gdtoa/gethex/hexnan(约 20 KB)。
    • 评估 --gc-sections 在 LTO 下是否已生效(读 map 确认无死节)。
    • 验收:逐项给出「测得的字节数 + 是否采纳」的结论,不接受「感觉变小」。
  • P3 重复实现归并
    • ClampDouble 两份(PlotViewInternal.hpp / UiLayout.cpp)→ 统一到 Shared。
    • 扫描全部 static/匿名命名空间重复工具函数,合并为单一实现。
    • 重复字符串/常量字面量、重复的三元式开关表。
  • P4 死代码与未使用成员清理
    • 用非 LTO 对象 + nm 反查「定义但从未被引用」的函数;删除确认无引用的实现。
    • 清理 PlotView.hpp 中已无人读取的成员/字段。
  • P5 资源与启动数据
    • .rsrc 复核(图标帧数、manifest、版本字符串);.idata 导入表复核。

阶段 2:性能(节点 P6–P10)

  • P6 建立可复跑的渲染性能探针(纯控制台)
    • 目标:能在不弹窗口的情况下测量 BuildLaneVisuals / SceneContentHash / EdgePanelLayoutFor / 面板绘制 / 信号面板绘制的耗时。
    • 方式:链接 Build/Release/*.o + 手工构造 DataSet/TabConfig,只调用纯函数与 PlotView 的非窗口方法(避免 Application::Initialize 触发模态框,见历史教训)。
    • 所有断言用同一进程内的相对量。
  • P7 面板元数据表驱动(核心)
    • 新增 EdgePanelSpec(每 kind 一行:命令 id、默认尺寸、是否列表型、footer 类型、 滚动字段偏移、面板状态字段偏移、绘制函数指针、命中函数指针)。
    • 把 6×~20 处 switch (kind) 收敛为「查表 + 3 个通用实现(列表型 / 表格型 / 固定行型)」。
    • 验收:逐像素对比改造前后面板(离屏渲染哈希);新增一个假 kind 只需加一行表项即可 被绘制/命中/拖动/关闭/滚动全覆盖(用编译期断言或探针验证)。
  • P8 SceneContentHash 混入收敛
    • 把 6 段重复循环改为「按面板表遍历 + 每面板一个 MixContent()」。
    • 验收:改造前后哈希值逐位相同(同一状态)。
  • P9 EdgePanelLayoutFor 表驱动 + 高度/几何同源
    • 高度算式与内容几何共用同一份 PanelMetrics 计算(消除重复减 pad 类 BUG)。
    • 验收:探针打印「内容区下边界 vs 按钮上边界」的像素差,与改造前一致。
  • P10 缓存五件套抽象(低风险版)
    • 抽出 OffscreenCache(DC/位图/键/贴图/失效),只替换三处结构完全相同的 (derived / decor / panel),保留 scene 与 lane layer 不动(热路径最复杂)。
    • 验收:逐像素一致 + 帧时间不回退。

阶段 3:架构(节点 P11–P14)

  • P11 ApplicationCommands.cpp 拆分
    • → ApplicationTabs.cpp(标签页)/ ApplicationDerive.cpp(派生/重采样/截取/克隆)/ ApplicationCommands.cpp(命令分发)/ ApplicationMenus.cpp(上下文菜单与设置菜单)。
    • 纯搬移,零行为变更;Makefile 无需改动(按目录通配收集)。
  • P12 PlotView.hpp 拆分
    • 把 EdgePanelLayout、派生曲线缓存条目、图层结构体等纯数据结构搬到 PlotViewTypes.hpp;把 PlotView 的内联小函数按关注点分块放到 PlotViewInline.hpp。
    • 目标:PlotView.hpp ≤ 2,500 行。
  • P13 UI 基础层职责核对
    • 核对 UiWidget / UiComponent / UiLayout / UiBox / UiSurface / UiButtonWidget / UiControls 的边界是否重叠;合并确认冗余的(只保留一套矩形/命中/布局原语)。
    • 明确依赖方向:Components/* → Components/Shared → Utilities(校验无反向)。
  • P14 绘制/功能组件边界
    • PlotViewPaint / Raster / Panels / Features / Interaction 的职责再核对; 把「面板几何」与「面板绘制」的交叉依赖理顺(几何 → 共享 PanelMetrics)。
    • 把散落在 PlotView 里的「颜色/线宽/胶囊样式」常量收敛到 Theme/PaintUtil。

阶段 4:注释与命名清理(节点 P15)

  • P15 注释/命名审计
    • 删除「本轮/上一轮」叙述、被推翻方案的描述、与实现不符的注释。
    • 统一命名:*Dc/*DC、Lane* vs Grid*、Edge* vs Panel* 等不一致处。
    • 确保所有注释与当前实现一致(重点:PlotViewInternal.hpp、PlotViewCacheKey.cpp)。
    • 同步更新 agent_mem.md 的 §2/§3(已经不准确的目录结构与 -Os)。

阶段 5:验证与报告(节点 P16)

  • P16 最终验证与报告
    • release + debug 零警告;产物体积对比;关键路径探针全部通过。
    • 生成 Documents/Code_Review_Report.md:逐节点写「改了什么 / 为什么 / 效果 / 未做的事与原因」,并给出体积与性能的前后对照表。

三、验收标准(硬性)

  1. make release 与 make debug 零 error、零 warning。
  2. 产物体积必须下降(目标:从 890,880 B 降到 ≤ 780 KB;若某项无法达成,在报告里 说明实测字节数与原因)。
  3. 不改变任何用户可见行为(面板几何、绘制像素、交互语义一致)。
  4. 每条性能结论都必须有同进程相对量或逐像素/逐位一致性证据。
  5. 每个节点单独提交,提交信息用中文、说明改动与验证方式。

四、风险与对策

风险 对策
面板表驱动改造引入像素差异 改造前后用离屏渲染哈希对比;先改 CLIP/MARK 两个最简单面板做样板,再推广
-fno-rtti 破坏第三方/库代码 全仓 grep 确认;先单独提交,失败可独立回退
缓存抽象拖慢热路径 只抽象非热路径(derived/decor/panel),scene/lane 不动;每次改完测帧时间
大文件拆分时误改逻辑 纯搬移(不重写函数体),逐文件 diff 核对;先拆 ApplicationCommands
探针触发模态「内存不足」框 绝不在探针里调 Application::Initialize()(历史教训);只构造数据层/纯函数

五、进度索引

进度与每节点的实测数字见 Documents/Code_Review_Progress.md。 最终结论见 Documents/Code_Review_Report.md。


六、第 34 轮追加(2026-11-04):验证基础设施 + 后续建议落地

触发:§三/§10 之后剩下的三项建议(EdgeScanFor 哈希化、EdgePanelLayoutFor 表驱动化、 wcstod 自写替身)都卡在**"没有可自证的验证手段"**上。 用户要求:先把验证工具做成仓库内的正式资产(Tools/ 入库),再动被建议的代码。

6.1 节点与验收标准

节点 内容 验收标准
T1 Tools/ 解除忽略;新增 Tools/Makefile;两个工具目录 + README.md make -C Tools 构建全部、make UnitTests 构建单个、make clean 只清工具产物;改头文件/增删源文件自动重编重链
T2 Tools/UnitTests:数据层语义单元测试 result=PASS,断言数 ≥ 200
T3 Tools/OffscreenChecks:隐藏窗口渲染 + 帧序列 + 面板像素基线 + 边沿统计参考实现 + 性能 零失败;全程不显示窗口、不动真实鼠标/键盘
T4 建议 #2:EdgeScanFor 状态查找哈希化 先量后改;性能收益必须大于体积代价,否则回退
T5 建议 #3:EdgePanelLayoutFor 表驱动化 面板像素基线 A/B 逐像素一致才允许合入
T6 建议 #4:wcstod 自写替身(省 gdtoa 链) 体积收益可测且数值语义逐位一致

6.2 本轮新增的硬约束

  1. 工具不得把产物写进 Tools/:截图 / 基线 / 报告一律写 Temp/tools/(已被忽略); Tools/*/Build/ 同样不入库(其它协作者本地 make -C Tools 重建)。
  2. 工具与本体 ABI 必须一致:-fno-exceptions、-fno-rtti、链接时 -flto -flto-partition=one; 工具源码自身不编成 LTO 字节码(GCC 15.2.0 会在 choose_baseaddr 处 ICE)。
  3. "先量后改":任何以性能 / 体积为名的改动,必须在同一台机器、同一次会话内给出前后数字; 收益低于噪声、或换来体积上涨的,一律回退,并把数字写进 Code_Review_Report.md。
  4. 像素基线只做本地 A/B:哈希与 DPI/字体/数据文件绑定,不入库、不当黄金图。

七、第 35 轮追加(2026-11-04):浮动面板架构评估与收敛 + 整数格式化替换

触发:用户指出「7 个实验性功能面板观感/骨架高度一致,但每新增一种或改一次布局 总会出意料之外的错误」,要求先评估(能否抽公共基类 / 公共组件,浮动面板、 二级菜单、右键菜单是否算公共组件),再动手;同时把 §11.5 的 「替换 std::to_wstring / std::to_string」做掉。

7.1 节点与验收标准

节点 内容 验收标准
P20 面板「特征层」:唯一分类 / 标题 / 滚动几何来源 面板像素基线与几何逐项不变
P21 滚动设置收敛为唯一实现(7 个包装 + 2 条调用链) panelscroll 新检查全绿
P22 全仓替换整数格式化(76 + 2 处) 面板像素不变 + 单元测试逐字符对照
P23 其余审查清理(垃圾形参 / 重复判定) 零 warning、像素不变

7.2 本轮新增硬约束

  1. 新增一种实验性功能面板,只允许改三个地方: LanePanelScrollOf / PanelStyleOf / PanelTitle(都在 PlotViewInternal.hpp / PlotViewPanels.cpp)。三者都是穷举 switch 且不写 default:, 漏登记会被 -Wswitch 编译报警。 禁止再在宽度 / 布局 / 绘制里各写一份 kind == XXX 组合判断。
  2. 底部"有按钮行"与"是构建按钮"是两个概念(PanelStyle::footerButtons() vs buildButton)。混用会让面板高度与内容区高度不一致。
  3. 整数转文本一律用 Utf8Util::FormatInteger*,不要用 std::to_wstring / std::to_string(它们把 MinGW printf 引擎拉进产物,且 libstdc++ 会强制 __USE_MINGW_ANSI_STDIO=1,命令行关不掉 —— 见 Makefile 注释)。
  4. 改面板几何/绘制前后必须跑 OffscreenChecks -run panels -mode record/compare; 本轮它当场抓到了"截取面板少了 37px 高度"的回归。