From 3b38dce583df423fd575ebf945f594c37cacbe40 Mon Sep 17 00:00:00 2001 From: Charliechen114514 <725610365@qq.com> Date: Mon, 25 May 2026 21:41:08 +0800 Subject: [PATCH 1/3] feat: add the drawio render and replace some mermaid code --- .../advanced_feature/02-aos-layout.drawio | 63 ++++++++++ .../02-cache-and-memory-hierarchy.md | 32 +---- .../02-memory-hierarchy.drawio | 56 +++++++++ .../advanced_feature/02-soa-layout.drawio | 66 ++++++++++ .../04-oop-in-c-vtable.drawio | 60 +++++++++ .../advanced_feature/04-oop-in-c.md | 11 +- .../ch08/02-virtual-functions-call.drawio | 76 ++++++++++++ .../ch08/02-virtual-functions-vtable.drawio | 40 ++++++ .../ch08/02-virtual-functions.md | 13 +- .../ch12/01-memory-layout.drawio | 52 ++++++++ .../ch12/01-memory-layout.md | 23 +--- .../03-shared-ptr-structure.drawio | 65 ++++++++++ .../ch01-smart-pointers/03-shared-ptr.md | 16 +-- .../embedded/01-led/04-hal-gpio-clock.drawio | 116 ++++++++++++++++++ .../embedded/01-led/04-hal-gpio-clock.md | 25 +--- ...-command-processor-and-main-walkthrough.md | 43 +------ .../embedded/03-uart/12-main-flow.drawio | 49 ++++++++ .../03-uart/12-system-architecture.drawio | 101 +++++++++++++++ ...pre-05-once-callback-move-only-function.md | 14 +-- .../full/pre-05-sbo-structure.drawio | 53 ++++++++ package.json | 1 + pnpm-lock.yaml | 12 ++ scripts/build.ts | 18 ++- site/.vitepress/config/index.ts | 9 +- site/.vitepress/config/shared.ts | 1 + 25 files changed, 847 insertions(+), 168 deletions(-) create mode 100644 documents/vol1-fundamentals/c_tutorials/advanced_feature/02-aos-layout.drawio create mode 100644 documents/vol1-fundamentals/c_tutorials/advanced_feature/02-memory-hierarchy.drawio create mode 100644 documents/vol1-fundamentals/c_tutorials/advanced_feature/02-soa-layout.drawio create mode 100644 documents/vol1-fundamentals/c_tutorials/advanced_feature/04-oop-in-c-vtable.drawio create mode 100644 documents/vol1-fundamentals/ch08/02-virtual-functions-call.drawio create mode 100644 documents/vol1-fundamentals/ch08/02-virtual-functions-vtable.drawio create mode 100644 documents/vol1-fundamentals/ch12/01-memory-layout.drawio create mode 100644 documents/vol2-modern-features/ch01-smart-pointers/03-shared-ptr-structure.drawio create mode 100644 documents/vol8-domains/embedded/01-led/04-hal-gpio-clock.drawio create mode 100644 documents/vol8-domains/embedded/03-uart/12-main-flow.drawio create mode 100644 documents/vol8-domains/embedded/03-uart/12-system-architecture.drawio create mode 100644 documents/vol9-open-source-project-learn/chrome/01_once_callback/full/pre-05-sbo-structure.drawio diff --git a/documents/vol1-fundamentals/c_tutorials/advanced_feature/02-aos-layout.drawio b/documents/vol1-fundamentals/c_tutorials/advanced_feature/02-aos-layout.drawio new file mode 100644 index 000000000..6c9dbb643 --- /dev/null +++ b/documents/vol1-fundamentals/c_tutorials/advanced_feature/02-aos-layout.drawio @@ -0,0 +1,63 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/documents/vol1-fundamentals/c_tutorials/advanced_feature/02-cache-and-memory-hierarchy.md b/documents/vol1-fundamentals/c_tutorials/advanced_feature/02-cache-and-memory-hierarchy.md index 08f233d63..6bc85c81b 100644 --- a/documents/vol1-fundamentals/c_tutorials/advanced_feature/02-cache-and-memory-hierarchy.md +++ b/documents/vol1-fundamentals/c_tutorials/advanced_feature/02-cache-and-memory-hierarchy.md @@ -59,18 +59,7 @@ Python 和 Java 这类语言把内存管理彻底抽象掉了,程序员基本 这个金字塔结构的核心设计思想叫做**局部性原理**(Principle of Locality)。局部性分两种:**时间局部性**指的是如果一个数据刚被访问过,那它很可能会在不久之后再次被访问;**空间局部性**指的是如果一个数据被访问了,那它附近地址的数据很可能也会被访问。Cache 的所有设计决策——缓存行的大小、预取策略、替换策略——全都是围绕这两个局部性来的。我们可以用一张简图来直观感受这个金字塔: -```mermaid -graph TD - subgraph "越往上越快、越小、越贵;越往下越慢、越大、越便宜" - Reg["寄存器
~1 周期 | 容量: ~数百字节"] - L1["L1 Cache
~3-4 周期 | 容量: 32-64 KB"] - L2["L2 Cache
~10-14 周期 | 容量: 256 KB-1 MB"] - L3["L3 Cache
~30-50 周期 | 容量: 数 MB-数十 MB"] - DRAM["主存 DRAM
~100-300 周期 | 容量: GB 级"] - Disk["SSD/HDD
~微秒/毫秒 | 容量: TB 级"] - end - Reg ~~~ L1 ~~~ L2 ~~~ L3 ~~~ DRAM ~~~ Disk -``` +![存储器层次结构金字塔示意](./02-memory-hierarchy.drawio) 你可以在 Linux 上用 `lscpu` 命令查看自己机器的 Cache 配置,输出的 `L1d cache`、`L2 cache`、`L3 cache` 那几行就是你的 CPU 实际情况。接下来我们一层一层往下拆。 @@ -294,24 +283,9 @@ typedef struct { 对比一下两者在内存中的布局差异: -```mermaid -graph LR - subgraph "AoS 布局:每个元素的 x,y,z,r,g,b 紧挨在一起(24 字节)" - A0["x0 y0 z0 r0 g0 b0"] - A1["x1 y1 z1 r1 g1 b1"] - A2["x2 y2 z2 r2 g2 b2"] - A0 --> A1 --> A2 - end -``` +![AoS 内存布局](./02-aos-layout.drawio) -```mermaid -graph LR - subgraph "SoA 布局:所有 x 连续,所有 y 连续,以此类推" - SX["连续的 x
x0 x1 x2 x3 x4 ..."] - SY["连续的 y
y0 y1 y2 y3 y4 ..."] - SZ["连续的 z
z0 z1 z2 z3 z4 ..."] - end -``` +![SoA 内存布局](./02-soa-layout.drawio) 如果你的热路径只处理坐标 `x`、`y`、`z`,而不碰颜色 `r`、`g`、`b`,那 SoA 的优势就非常明显了——你连续遍历 `x[0]`、`x[1]`、`x[2]`……数据在内存里完全连续,Cache 命中率接近 100%。而 AoS 的情况下,每访问一个 `x` 都会顺带把同一结构体里的 `y`、`z`、`r`、`g`、`b` 也拉进 Cache(因为它们在同一条缓存行上),但我们暂时用不到颜色数据,这些空间就浪费了。 diff --git a/documents/vol1-fundamentals/c_tutorials/advanced_feature/02-memory-hierarchy.drawio b/documents/vol1-fundamentals/c_tutorials/advanced_feature/02-memory-hierarchy.drawio new file mode 100644 index 000000000..9d0cdeed4 --- /dev/null +++ b/documents/vol1-fundamentals/c_tutorials/advanced_feature/02-memory-hierarchy.drawio @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/documents/vol1-fundamentals/c_tutorials/advanced_feature/02-soa-layout.drawio b/documents/vol1-fundamentals/c_tutorials/advanced_feature/02-soa-layout.drawio new file mode 100644 index 000000000..5e7968d38 --- /dev/null +++ b/documents/vol1-fundamentals/c_tutorials/advanced_feature/02-soa-layout.drawio @@ -0,0 +1,66 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/documents/vol1-fundamentals/c_tutorials/advanced_feature/04-oop-in-c-vtable.drawio b/documents/vol1-fundamentals/c_tutorials/advanced_feature/04-oop-in-c-vtable.drawio new file mode 100644 index 000000000..b90cdd3f8 --- /dev/null +++ b/documents/vol1-fundamentals/c_tutorials/advanced_feature/04-oop-in-c-vtable.drawio @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/documents/vol1-fundamentals/c_tutorials/advanced_feature/04-oop-in-c.md b/documents/vol1-fundamentals/c_tutorials/advanced_feature/04-oop-in-c.md index 9e635b11f..06ba83e8b 100644 --- a/documents/vol1-fundamentals/c_tutorials/advanced_feature/04-oop-in-c.md +++ b/documents/vol1-fundamentals/c_tutorials/advanced_feature/04-oop-in-c.md @@ -372,16 +372,7 @@ Circle("Moon", r=2.00) 通过统一的 `shape_area()`、`shape_draw()` 接口调用,每次都走到了正确的具体实现——这就是运行时多态,和 C++ 虚函数的底层机制**完全一样**。内存布局对比如下: -```mermaid -graph LR - subgraph "Circle 对象(每个对象只有 1 个 vptr)" - Obj["vtable
name
radius"] - end - subgraph "kCircleVtable(全局共享)" - VT["circle_area
circle_perimeter
circle_draw"] - end - Obj -->|vptr| VT -``` +![C 语言虚表内存布局](./04-oop-in-c-vtable.drawio) ## 第五步——用函数指针表实现接口 diff --git a/documents/vol1-fundamentals/ch08/02-virtual-functions-call.drawio b/documents/vol1-fundamentals/ch08/02-virtual-functions-call.drawio new file mode 100644 index 000000000..700bbf330 --- /dev/null +++ b/documents/vol1-fundamentals/ch08/02-virtual-functions-call.drawio @@ -0,0 +1,76 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/documents/vol1-fundamentals/ch08/02-virtual-functions-vtable.drawio b/documents/vol1-fundamentals/ch08/02-virtual-functions-vtable.drawio new file mode 100644 index 000000000..b0c94e268 --- /dev/null +++ b/documents/vol1-fundamentals/ch08/02-virtual-functions-vtable.drawio @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/documents/vol1-fundamentals/ch08/02-virtual-functions.md b/documents/vol1-fundamentals/ch08/02-virtual-functions.md index edb478ef9..3b4aba48e 100644 --- a/documents/vol1-fundamentals/ch08/02-virtual-functions.md +++ b/documents/vol1-fundamentals/ch08/02-virtual-functions.md @@ -161,22 +161,13 @@ error: 'void Circle::draw()' marked 'override', but does not override any base c 拿我们的图形类层次来说,编译器大致生成了三张 vtable: -```mermaid -graph LR - ShapeVT["Shape 的 vtable
&Shape::draw"] - CircleVT["Circle 的 vtable
&Circle::draw"] - RectVT["Rectangle 的 vtable
&Rectangle::draw"] -``` +![图形类层次的 vtable 布局](./02-virtual-functions-vtable.drawio) 而每个包含虚函数的对象,在内存布局中都会多出一个隐藏的成员——**虚表指针**(vptr),指向该对象所属类的 vtable。 当你写下 `shapes[i]->draw()` 时,编译器生成的代码大致做了这几步:先通过对象找到 `vptr`,定位到对应的 vtable,然后从表中取出 `draw()` 对应的函数指针,最后通过这个指针发起间接调用: -```mermaid -graph LR - A["shapes[1]
(Shape*)"] --> B["Circle 对象
[ vptr ]"] - B --> C["Circle 的 vtable
[ &Circle::draw ]"] -``` +![虚函数调用过程示意](./02-virtual-functions-call.drawio) 这就是虚函数调用比普通函数调用多出来的全部开销——**一次额外的间接跳转**。在 PC 上,这个开销几乎可以忽略不计。但在资源紧张的嵌入式环境里需要认真对待:每个含虚函数的类多一张 vtable(占用 Flash),每个对象多一个 `vptr`(通常 4 或 8 字节,占用 RAM),每次虚函数调用多一次间接跳转(可能影响流水线和分支预测)。好在绝大多数场景下,这些开销和"解耦带来的架构收益"相比微不足道。 diff --git a/documents/vol1-fundamentals/ch12/01-memory-layout.drawio b/documents/vol1-fundamentals/ch12/01-memory-layout.drawio new file mode 100644 index 000000000..a8fe74179 --- /dev/null +++ b/documents/vol1-fundamentals/ch12/01-memory-layout.drawio @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/documents/vol1-fundamentals/ch12/01-memory-layout.md b/documents/vol1-fundamentals/ch12/01-memory-layout.md index 0eb73cb95..de57d86ce 100644 --- a/documents/vol1-fundamentals/ch12/01-memory-layout.md +++ b/documents/vol1-fundamentals/ch12/01-memory-layout.md @@ -36,28 +36,7 @@ cpp_standard: [11, 14, 17, 20] 一个 C++ 程序运行时,操作系统会为它分配一块虚拟地址空间。这块空间并不是一整块 homogeneous 的区域,而是被划分成了若干个段(segment),每个段有各自的用途和管理方式。对于我们来说,最核心的是以下四个区域: -```mermaid -graph TD - subgraph "高地址" - Stack["栈 (Stack)
局部变量、函数调用帧
↓ 向低地址增长"] - end - subgraph " " - Free["未使用空间"] - end - subgraph " " - Heap["堆 (Heap)
new/malloc 动态分配
↑ 向高地址增长"] - end - subgraph " " - BSS["BSS 段(未初始化数据)
未初始化的全局/static 变量"] - end - subgraph " " - Data["数据段(已初始化数据)
已初始化的全局/static 变量"] - end - subgraph "低地址" - Text["代码段 (Text)
机器指令、只读常量"] - end - Stack ~~~ Free ~~~ Heap ~~~ BSS ~~~ Data ~~~ Text -``` +![进程虚拟地址空间布局](./01-memory-layout.drawio) 代码段(Text segment)存放编译后的机器指令和一些只读数据(比如字符串字面量 `"hello"`),这个区域通常是只读的,尝试修改会直接触发段错误。数据段(Data segment)存放已初始化的全局变量和 `static` 变量,它们的值在程序启动时就已经确定。BSS 段是数据段的一部分,专门放未初始化的全局和 `static` 变量——这些变量会被自动初始化为零,所以可执行文件里不需要存储它们的初始值,只记录大小就够了。堆和栈则是运行时动态使用的区域,前者由程序员手动管理,后者由编译器自动管理。 diff --git a/documents/vol2-modern-features/ch01-smart-pointers/03-shared-ptr-structure.drawio b/documents/vol2-modern-features/ch01-smart-pointers/03-shared-ptr-structure.drawio new file mode 100644 index 000000000..965e4405f --- /dev/null +++ b/documents/vol2-modern-features/ch01-smart-pointers/03-shared-ptr-structure.drawio @@ -0,0 +1,65 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/documents/vol2-modern-features/ch01-smart-pointers/03-shared-ptr.md b/documents/vol2-modern-features/ch01-smart-pointers/03-shared-ptr.md index d4b070b7d..8ff843da6 100644 --- a/documents/vol2-modern-features/ch01-smart-pointers/03-shared-ptr.md +++ b/documents/vol2-modern-features/ch01-smart-pointers/03-shared-ptr.md @@ -86,21 +86,7 @@ Disconnected from 192.168.1.1:8080 我们用一个简化的示意图来理解: -```mermaid -graph LR - subgraph SP["shared_ptr 对象 (栈上)"] - ptr["T* ptr"] - cb["ControlBlock* cb"] - end - ptr --> T["T 对象 (堆上)"] - cb --> CB - subgraph CB["ControlBlock (堆上)"] - sc["strong_count: 2"] - wc["weak_count: 0"] - del["deleter (可选)"] - alloc["allocator (可选)"] - end -``` +![shared_ptr 内部结构示意](./03-shared-ptr-structure.drawio) 所以一个 `shared_ptr` 对象本身的大小是 `2 * sizeof(void*)`——两个指针。在 64 位系统上是 16 字节,比 `unique_ptr`(8 字节)大一倍。控制块本身的大小取决于实现(GNU libstdc++ 在 x86_64 上约为 32 字节)。 diff --git a/documents/vol8-domains/embedded/01-led/04-hal-gpio-clock.drawio b/documents/vol8-domains/embedded/01-led/04-hal-gpio-clock.drawio new file mode 100644 index 000000000..81aa731d2 --- /dev/null +++ b/documents/vol8-domains/embedded/01-led/04-hal-gpio-clock.drawio @@ -0,0 +1,116 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/documents/vol8-domains/embedded/01-led/04-hal-gpio-clock.md b/documents/vol8-domains/embedded/01-led/04-hal-gpio-clock.md index e63d568b2..8cf119566 100644 --- a/documents/vol8-domains/embedded/01-led/04-hal-gpio-clock.md +++ b/documents/vol8-domains/embedded/01-led/04-hal-gpio-clock.md @@ -42,30 +42,7 @@ order: 4 下面是我们项目配置下的简化时钟树。注意,这是**我们实际使用的配置**,而不是STM32参考手册里那张让人看一眼就头疼的完整时钟树。我们先只看与我们相关的部分: -```mermaid -graph TD - HSI["HSI 8MHz\n(内部RC振荡器)"] - DIV["÷2 分频\n4MHz"] - PLL["PLL ×16\n64MHz"] - SYSCLK["SYSCLK\n64MHz"] - AHB1["AHB ÷1\nHCLK = 64MHz"] - APB1["APB1 ÷2\n32MHz"] - APB2["APB2 ÷1\n64MHz"] - DMA["DMA 控制器"] - Flash["Flash 接口"] - APB1_PERIPH["TIM2-4, USART2-3\nI2C1-2, SPI2-3"] - APB2_PERIPH["USART1, SPI1\nTIM1, ..."] - GPIO["GPIOA-E"] - ADC["ADC1-2"] - - HSI --> DIV --> PLL --> SYSCLK --> AHB1 - AHB1 --> APB1 --> APB1_PERIPH - AHB1 --> APB2 --> APB2_PERIPH - APB2 --> GPIO - APB2 --> ADC - AHB1 --> DMA - AHB1 --> Flash -``` +![STM32 时钟树简化示意图](./04-hal-gpio-clock.drawio) 我们逐层来看这棵树。 diff --git a/documents/vol8-domains/embedded/03-uart/12-command-processor-and-main-walkthrough.md b/documents/vol8-domains/embedded/03-uart/12-command-processor-and-main-walkthrough.md index 46e05ebcb..2849aec50 100644 --- a/documents/vol8-domains/embedded/03-uart/12-command-processor-and-main-walkthrough.md +++ b/documents/vol8-domains/embedded/03-uart/12-command-processor-and-main-walkthrough.md @@ -130,20 +130,7 @@ int main() { `main()` 的前半部分是初始化,按严格的顺序执行: -```mermaid -graph TD - A["HAL_Init()\nHAL 库初始化(SysTick 等)"] - B["ClockConfig::instance().setup...\n系统时钟配置(64 MHz HSI)"] - C["LED<Port::C, PIN_13> led\nLED 对象构造(零开销)"] - D["Button<Port::A, PIN_0> button\nButton 对象构造(零开销)"] - E["Logger::driver().set_gpio_init(...)\n注册 GPIO 初始化回调"] - F["Logger::driver().init(UartConfig)\n使能时钟 → GPIO → HAL init"] - G["Logger::driver().enable_interrupt()\nNVIC 使能 USART1 中断"] - H["send_string(\"UART Logger Ready!\")\n阻塞式发送欢迎信息"] - I["uart_start_receive()\n启动中断接收流水线"] - - A --> B --> C --> D --> E --> F --> G --> H --> I -``` +![main() 初始化流程](./12-main-flow.drawio) 每一步的顺序都不能调换。时钟没配就调 HAL 函数会 hard fault。GPIO 没配好 USART 信号到不了引脚。中断没使能就启动接收的话,字节到了也不会触发 ISR。`send_string` 放在 `uart_start_receive` 之前是故意的——先发欢迎信息确认发送链路正常,再启动接收。 @@ -241,33 +228,7 @@ static void handle_command(std::string_view cmd, 把所有数据流画在一起,整个系统的架构是这样的: -```mermaid -graph LR - subgraph STM32["STM32"] - TX["TX (PA9)"] - RX["RX (PA10)"] - LED["PC13 (LED)"] - BTN["PA0 (Button)"] - end - - subgraph ADAPTER["USB-TTL 适配器"] - TTL_TX["TTL TX"] - TTL_RX["TTL RX"] - end - - subgraph PC["PC 终端"] - PC_TX["USB TX"] - PC_RX["USB RX"] - end - - TX -->|"按钮事件 / 命令响应"| TTL_RX - TTL_RX -->|"USB"| PC_RX - PC_TX -->|"USB"| TTL_TX - TTL_TX -->|"命令输入"| RX - - BTN -.->|"poll_events()"| TX - RX -.->|"rx_ring → 行解析\n→ handle_command"| LED -``` +![系统整体数据流架构](./12-system-architecture.drawio) 芯片 → PC 方向:按钮事件和命令响应通过 `send_string()` 发出。这些调用使用阻塞式发送(`HAL_UART_Transmit`),因为发送量小(几十字节),阻塞时间可控(不到 1 毫秒),对系统响应没有影响。 diff --git a/documents/vol8-domains/embedded/03-uart/12-main-flow.drawio b/documents/vol8-domains/embedded/03-uart/12-main-flow.drawio new file mode 100644 index 000000000..a2e244b47 --- /dev/null +++ b/documents/vol8-domains/embedded/03-uart/12-main-flow.drawio @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/documents/vol8-domains/embedded/03-uart/12-system-architecture.drawio b/documents/vol8-domains/embedded/03-uart/12-system-architecture.drawio new file mode 100644 index 000000000..bfc0d9fe2 --- /dev/null +++ b/documents/vol8-domains/embedded/03-uart/12-system-architecture.drawio @@ -0,0 +1,101 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/documents/vol9-open-source-project-learn/chrome/01_once_callback/full/pre-05-once-callback-move-only-function.md b/documents/vol9-open-source-project-learn/chrome/01_once_callback/full/pre-05-once-callback-move-only-function.md index cbcb3e9ff..4f068bb01 100644 --- a/documents/vol9-open-source-project-learn/chrome/01_once_callback/full/pre-05-once-callback-move-only-function.md +++ b/documents/vol9-open-source-project-learn/chrome/01_once_callback/full/pre-05-once-callback-move-only-function.md @@ -160,19 +160,7 @@ f = nullptr; // 清空 f,析构之前持有的可调用对象 `std::move_only_function`(和 `std::function` 一样)内部实现了**小对象优化**(Small Buffer Optimization,SBO)。思路很简单:对象内部预留一块固定大小的缓冲区(通常是几个指针大小),如果可调用对象足够小,就把它直接存到缓冲区里,避免堆分配;如果太大,就在堆上分配内存来存储。 -```mermaid -graph TD - subgraph outer["std::move_only_function"] - direction TB - subgraph sbo_path["SBO 路径:小对象直接内联存储"] - vtable["函数指针 / 虚表指针
用于类型擦除的调用分派"] - sbo_buf["SBO 缓冲区(通常 16-32 字节)
小对象直接存这里"] - end - subgraph heap_path["堆路径:大对象动态分配"] - heap_ptr["堆指针(指向动态分配的对象)
大对象存在堆上"] - end - end -``` +![SBO 小对象优化内部结构](./pre-05-sbo-structure.drawio) SBO 的阈值是实现定义的——通常在 2-3 个指针大小(16-24 字节)左右。捕获少量参数的 lambda(比如 `[x = 42]` 或 `[&ref]`)通常能放进 SBO,不会触发堆分配。但如果 lambda 捕获了大量数据(比如一个 `std::string` + 几个 `int`),超过了 SBO 阈值,构造时就会在堆上分配。 diff --git a/documents/vol9-open-source-project-learn/chrome/01_once_callback/full/pre-05-sbo-structure.drawio b/documents/vol9-open-source-project-learn/chrome/01_once_callback/full/pre-05-sbo-structure.drawio new file mode 100644 index 000000000..b4127b139 --- /dev/null +++ b/documents/vol9-open-source-project-learn/chrome/01_once_callback/full/pre-05-sbo-structure.drawio @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/package.json b/package.json index e6f8aef8c..b29803f93 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,7 @@ "coverage:update": "python3 scripts/coverage.py --update" }, "devDependencies": { + "@dhlx/vitepress-plugin-drawio": "^0.0.10", "@types/node": "^25.6.2", "markdown-it-mathjax3": "^4.3.2", "mermaid": "^10.9.6", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0e38ed945..297962612 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -12,6 +12,9 @@ importers: specifier: ^7.2.0 version: 7.2.0 devDependencies: + '@dhlx/vitepress-plugin-drawio': + specifier: ^0.0.10 + version: 0.0.10 '@types/node': specifier: ^25.6.2 version: 25.6.2 @@ -129,6 +132,9 @@ packages: '@braintree/sanitize-url@6.0.4': resolution: {integrity: sha512-s3jaWicZd0pkP0jf5ysyHUI/RE7MHos6qlToFcGWXVp+ykHOy77OUMrfbgJ9it2C5bow7OIQwYYaHjk9XlBQ2A==} + '@dhlx/vitepress-plugin-drawio@0.0.10': + resolution: {integrity: sha512-xoPA4BMDW8yerhmU7ic1EhKXT8YRcmpHEYLnA525GPaX9dVEzN0hdSHDhUxhUpdNlgu4rMX8Xb05oZoDE404OQ==} + '@docsearch/css@3.8.2': resolution: {integrity: sha512-y05ayQFyUmCXze79+56v/4HpycYF3uFqB78pLPrSV5ZKAlDuIAAJNhaRi8tTdRNXh05yxX/TyNnzD6LwSM89vQ==} @@ -1641,6 +1647,12 @@ snapshots: '@braintree/sanitize-url@6.0.4': {} + '@dhlx/vitepress-plugin-drawio@0.0.10': + optionalDependencies: + vue: 3.5.34 + transitivePeerDependencies: + - typescript + '@docsearch/css@3.8.2': {} '@docsearch/js@3.8.2(@algolia/client-search@5.52.1)(search-insights@2.17.3)': diff --git a/scripts/build.ts b/scripts/build.ts index bd333251c..3815f6f2e 100644 --- a/scripts/build.ts +++ b/scripts/build.ts @@ -148,10 +148,11 @@ function generateVolumeConfig(vol: Volume, lang: 'zh' | 'en', absSiteDir: string const relSidebar = relative(vpDir, join(MAIN_VP, 'config', 'sidebar')).replace(/\\/g, '/') return `import { defineConfig } from 'vitepress' +import withDrawio from '@dhlx/vitepress-plugin-drawio' import { sharedBase, ${lang === 'en' ? 'sharedEnThemeConfig' : 'sharedThemeConfig'} } from '${relShared}' import { volumeSidebar } from '${relSidebar}' -export default defineConfig({ +export default withDrawio(defineConfig({ ...sharedBase, srcDir: '${relSrc.replace(/\\/g, '/')}', outDir: '${relOut.replace(/\\/g, '/')}', @@ -163,6 +164,12 @@ export default defineConfig({ ...${lang === 'en' ? 'sharedEnThemeConfig' : 'sharedThemeConfig'}(), sidebar: { '${prefix}': volumeSidebar('${vol.srcDir}', '${prefix}') }, }, +}), { + width: '100%', + height: '600px', + darkMode: 'auto', + resize: true, + zoom: true, }) ` } @@ -176,11 +183,12 @@ function generateRootConfig(absSiteDir: string, absSrcDir: string): string { const relSidebar = relative(vpDir, join(MAIN_VP, 'config', 'sidebar')).replace(/\\/g, '/') return `import { defineConfig } from 'vitepress' +import withDrawio from '@dhlx/vitepress-plugin-drawio' import { sharedBase, sharedThemeConfig, sharedEnThemeConfig } from '${relShared}' import { navZh, navEn } from '${relNav}' import { buildSidebar } from '${relSidebar}' -export default defineConfig({ +export default withDrawio(defineConfig({ ...sharedBase, srcDir: '${relSrc.replace(/\\/g, '/')}', outDir: '${relOut.replace(/\\/g, '/')}', @@ -199,6 +207,12 @@ export default defineConfig({ footer: { message: '基于 VitePress 构建', copyright: 'Copyright 2025-2026 Charliechen' }, socialLinks: [{ icon: 'github', link: 'https://github.com/Awesome-Embedded-Learning-Studio/Tutorial_AwesomeModernCPP' }], }, +}), { + width: '100%', + height: '600px', + darkMode: 'auto', + resize: true, + zoom: true, }) ` } diff --git a/site/.vitepress/config/index.ts b/site/.vitepress/config/index.ts index 1910bb738..36c9bee26 100644 --- a/site/.vitepress/config/index.ts +++ b/site/.vitepress/config/index.ts @@ -1,11 +1,12 @@ import { defineConfig } from 'vitepress' +import withDrawio from '@dhlx/vitepress-plugin-drawio' import { navZh, navEn } from './nav' import { buildSidebar } from './sidebar' import { kbdPlugin } from '../plugins/kbd-plugin' import { cppTemplateEscapePlugin } from '../plugins/escape-cpp-templates' import { mermaidPlugin } from '../plugins/mermaid-plugin' -export default defineConfig({ +export default withDrawio(defineConfig({ vite: { ssr: { external: ['mermaid'], @@ -92,4 +93,10 @@ export default defineConfig({ { icon: 'github', link: 'https://github.com/Awesome-Embedded-Learning-Studio/Tutorial_AwesomeModernCPP' }, ], }, +}), { + width: '100%', + height: '600px', + darkMode: 'auto', + resize: true, + zoom: true, }) diff --git a/site/.vitepress/config/shared.ts b/site/.vitepress/config/shared.ts index 63e39de27..02e6732d4 100644 --- a/site/.vitepress/config/shared.ts +++ b/site/.vitepress/config/shared.ts @@ -13,6 +13,7 @@ export const sharedBase = { build: { chunkSizeWarningLimit: 5000, }, + assetsInclude: ['**/*.drawio'], }, vue: { From 46236cf2b9d9e9deda7a2c4983ff0f964112a12b Mon Sep 17 00:00:00 2001 From: Charliechen114514 <725610365@qq.com> Date: Mon, 25 May 2026 21:44:14 +0800 Subject: [PATCH 2/3] fix: ci scripts error --- scripts/check_quality.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/check_quality.py b/scripts/check_quality.py index ddc224f06..66140d656 100644 --- a/scripts/check_quality.py +++ b/scripts/check_quality.py @@ -61,7 +61,7 @@ SKIP_FILENAMES = {'index.md', 'tags.md', 'README.md'} SKIP_DIR_PARTS = {'images', 'generated'} -IMAGE_EXTENSIONS = {'.png', '.jpg', '.jpeg', '.gif', '.svg', '.webp'} +IMAGE_EXTENSIONS = {'.png', '.jpg', '.jpeg', '.gif', '.svg', '.webp', '.drawio'} MAX_IMAGE_SIZE_MB = 2.0 From dfdfd130b1f6851b5d9bd8c82ad9453e7ead8a64 Mon Sep 17 00:00:00 2001 From: Charliechen114514 <725610365@qq.com> Date: Mon, 25 May 2026 21:46:02 +0800 Subject: [PATCH 3/3] fix: ci scripts error --- scripts/check_links.py | 164 ++++++++++++++++++++--------------------- 1 file changed, 82 insertions(+), 82 deletions(-) diff --git a/scripts/check_links.py b/scripts/check_links.py index 1db75c5a1..e9a359aba 100755 --- a/scripts/check_links.py +++ b/scripts/check_links.py @@ -23,10 +23,10 @@ class LinkChecker: ] # Image extensions to check against filesystem - IMAGE_EXTENSIONS = {'.png', '.jpg', '.jpeg', '.gif', '.svg', '.bmp', '.webp', '.ico'} + IMAGE_EXTENSIONS = {'.png', '.jpg', '.jpeg', '.gif', '.svg', '.bmp', '.webp', '.ico', '.drawio'} - # Files to skip from checking - SKIP_FILES = {'tags.md'} + # Files to skip from checking + SKIP_FILES = {'tags.md'} def __init__(self, tutorial_dir: Path, fix: bool = False): self.tutorial_dir = tutorial_dir @@ -56,13 +56,13 @@ def extract_links(self, content: str, filepath: Path) -> List[Tuple[int, str, st Returns: List of (line_number, link_text, link_url) """ links = [] - # Match [text](url) and [text]() - markdown_pattern = r'\[([^\]]+)\]\(([^)]+)\)|\[([^\]]+)\]\(<([^>]+)>\)' - # Match Vue components with literal href attributes, such as - # Title. - component_href_pattern = ( - r'<([A-Z][\w.]*)\b[^>]*\bhref=(["\'])([^"\']+)\2' - ) + # Match [text](url) and [text]() + markdown_pattern = r'\[([^\]]+)\]\(([^)]+)\)|\[([^\]]+)\]\(<([^>]+)>\)' + # Match Vue components with literal href attributes, such as + # Title. + component_href_pattern = ( + r'<([A-Z][\w.]*)\b[^>]*\bhref=(["\'])([^"\']+)\2' + ) in_code_block = False for line_num, line in enumerate(content.split('\n'), 1): @@ -77,52 +77,52 @@ def extract_links(self, content: str, filepath: Path) -> List[Tuple[int, str, st # Strip inline code spans to avoid matching C++ syntax like `[&](args)` cleaned = re.sub(r'`[^`]+`', '', line) - for match in re.finditer(markdown_pattern, cleaned): - groups = match.groups() - if groups[1]: # Regular link - link_text, link_url = groups[0], groups[1] - else: # Angle bracket link - link_text, link_url = groups[2], groups[3] - - links.append((line_num, link_text, link_url)) - - for match in re.finditer(component_href_pattern, cleaned): - component_name = match.group(1) - link_url = match.group(3) - links.append((line_num, component_name, link_url)) - - return links - - def candidate_paths(self, link_url: str, source_file: Path) -> List[str]: - """Return possible markdown targets for a link path.""" - # Remove fragments/anchors - link_url = link_url.split('#')[0] - if not link_url: - return [] - - # VitePress treats a trailing slash as an index page. - link_url = link_url.rstrip('/') - - if not link_url: - link_url = 'index' - - if link_url.startswith('/'): - target = self.tutorial_dir / link_url.lstrip('/') - else: - target = source_file.parent / link_url - - try: - resolved = target.resolve() - rel = resolved.relative_to(self.tutorial_dir) - except (ValueError, RuntimeError): - return [link_url] - - candidates = [rel] - if rel.suffix != '.md': - candidates.append(rel.with_suffix('.md')) - candidates.append(rel / 'index.md') - - return [str(candidate) for candidate in candidates] + for match in re.finditer(markdown_pattern, cleaned): + groups = match.groups() + if groups[1]: # Regular link + link_text, link_url = groups[0], groups[1] + else: # Angle bracket link + link_text, link_url = groups[2], groups[3] + + links.append((line_num, link_text, link_url)) + + for match in re.finditer(component_href_pattern, cleaned): + component_name = match.group(1) + link_url = match.group(3) + links.append((line_num, component_name, link_url)) + + return links + + def candidate_paths(self, link_url: str, source_file: Path) -> List[str]: + """Return possible markdown targets for a link path.""" + # Remove fragments/anchors + link_url = link_url.split('#')[0] + if not link_url: + return [] + + # VitePress treats a trailing slash as an index page. + link_url = link_url.rstrip('/') + + if not link_url: + link_url = 'index' + + if link_url.startswith('/'): + target = self.tutorial_dir / link_url.lstrip('/') + else: + target = source_file.parent / link_url + + try: + resolved = target.resolve() + rel = resolved.relative_to(self.tutorial_dir) + except (ValueError, RuntimeError): + return [link_url] + + candidates = [rel] + if rel.suffix != '.md': + candidates.append(rel.with_suffix('.md')) + candidates.append(rel / 'index.md') + + return [str(candidate) for candidate in candidates] def check_file(self, filepath: Path): """Check links in a single file.""" @@ -144,32 +144,32 @@ def check_file(self, filepath: Path): continue # Check image links against filesystem - link_ext = Path(link_url.split('#')[0]).suffix.lower() - if link_ext in self.IMAGE_EXTENSIONS: - candidates = self.candidate_paths(link_url, filepath) - if candidates and not any((self.tutorial_dir / candidate).exists() for candidate in candidates): - self.errors.append( - f"{rel_path}:{line_num} - Broken image: [{link_text}]({link_url})" - ) - continue - - candidates = self.candidate_paths(link_url, filepath) - - if not candidates: - continue - - # Check if file exists - existing = next((candidate for candidate in candidates if candidate in self.all_files), None) - if existing is None: - self.errors.append( - f"{rel_path}:{line_num} - Broken link: [{link_text}]({link_url})" - ) - else: - # Track valid link for reverse index - key = str(existing) - if key not in self.link_map: - self.link_map[key] = [] - self.link_map[key].append((filepath, link_text)) + link_ext = Path(link_url.split('#')[0]).suffix.lower() + if link_ext in self.IMAGE_EXTENSIONS: + candidates = self.candidate_paths(link_url, filepath) + if candidates and not any((self.tutorial_dir / candidate).exists() for candidate in candidates): + self.errors.append( + f"{rel_path}:{line_num} - Broken image: [{link_text}]({link_url})" + ) + continue + + candidates = self.candidate_paths(link_url, filepath) + + if not candidates: + continue + + # Check if file exists + existing = next((candidate for candidate in candidates if candidate in self.all_files), None) + if existing is None: + self.errors.append( + f"{rel_path}:{line_num} - Broken link: [{link_text}]({link_url})" + ) + else: + # Track valid link for reverse index + key = str(existing) + if key not in self.link_map: + self.link_map[key] = [] + self.link_map[key].append((filepath, link_text)) def suggest_fix(self, filepath: Path, line_num: int, old_link: str, new_link: str): """Suggest a fix for a link."""