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
-```
+
你可以在 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
-```
+
-```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
-```
+
如果你的热路径只处理坐标 `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
-```
+
## 第五步——用函数指针表实现接口
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"]
-```
+
而每个包含虚函数的对象,在内存布局中都会多出一个隐藏的成员——**虚表指针**(vptr),指向该对象所属类的 vtable。
当你写下 `shapes[i]->draw()` 时,编译器生成的代码大致做了这几步:先通过对象找到 `vptr`,定位到对应的 vtable,然后从表中取出 `draw()` 对应的函数指针,最后通过这个指针发起间接调用:
-```mermaid
-graph LR
- A["shapes[1]
(Shape*)"] --> B["Circle 对象
[ vptr ]"]
- B --> C["Circle 的 vtable
[ &Circle::draw ]"]
-```
+
这就是虚函数调用比普通函数调用多出来的全部开销——**一次额外的间接跳转**。在 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
-```
+
代码段(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` 对象本身的大小是 `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
-```
+
我们逐层来看这棵树。
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
-```
+
每一步的顺序都不能调换。时钟没配就调 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
-```
+
芯片 → 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 的阈值是实现定义的——通常在 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."""