Skip to content

Commit f1fe079

Browse files
committed
Merge branch "feature/http-streaming-parser" into main
2 parents b511893 + 4eedc4c commit f1fe079

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+6113
-4
lines changed

README.md

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -163,17 +163,22 @@ pub fn main() !void {
163163
- **说明**: 创建多个协程,展示时间片抢占调度效果
164164

165165
### 2. 网络服务器 (`nets/`)
166-
- **功能**: 基于 ZCO 的高性能 HTTP 服务器
166+
- **功能**: 基于 ZCO 的高性能 TCP 服务器基础模块
167167
- **运行**: `cd nets && zig build run`
168-
- **说明**: 支持高并发连接,展示 ZCO 在网络编程中的优势
168+
- **说明**: 提供 TCP 连接管理,展示 ZCO 在网络编程中的优势
169169

170-
### 3. WebSocket 服务器 (`websocket/`)
170+
### 3. HTTP 框架 (`http/`)
171+
- **功能**: 完整的 HTTP 框架,支持路由、中间件、JWT、文件上传等
172+
- **运行**: `cd http && zig build run`
173+
- **说明**: 提供完整的 Web 开发功能,包含路由系统、中间件链、JWT认证、静态文件服务、模板引擎等
174+
175+
### 4. WebSocket 服务器 (`websocket/`)
171176
- **功能**: 完整的 WebSocket 服务器实现
172177
- **运行**: `cd websocket && zig build run`
173178
- **测试**: `cd websocket/test && npm install && node client_test.js`
174179
- **说明**: 支持 RFC 6455 标准协议,包含完整的测试套件
175180

176-
### 4. 性能对比测试 (`benchmarks/`)
181+
### 5. 性能对比测试 (`benchmarks/`)
177182
- **功能**: ZCO 与 Go 的性能对比测试套件
178183
- **运行**: `cd benchmarks && ./quick_test.sh`
179184
- **说明**: 提供完整的性能测试和对比分析
@@ -316,6 +321,7 @@ cd benchmarks
316321
- [x] 性能统计和监控
317322
- [x] 批量协程处理优化
318323
- [x] 网络服务器集成
324+
- [x] HTTP 框架模块(路由、中间件、JWT、文件上传、模板引擎)
319325
- [x] WebSocket 服务器模块(v0.4.2)
320326
- [x] 高并发压力测试验证
321327
- [x] 与 Go 的性能对比测试
@@ -325,6 +331,7 @@ cd benchmarks
325331
### 待改进功能
326332
- [x] 优先级感知抢占(已实现环形缓冲区+优先级位图调度器)
327333
- [x] WebSocket 服务器支持(已实现)
334+
- [x] HTTP 框架支持(已实现)
328335
- [ ] 自适应时间片调整
329336
- [ ] 跨平台支持(Windows/macOS)
330337
- [ ] 更详细的性能监控
Lines changed: 228 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,228 @@
1+
# Zig fmt 格式化问题排查指南
2+
3+
## 问题概述
4+
5+
在 Zig 0.14.0 中,使用 `std.fmt` 进行字符串格式化时,如果参数是切片类型(slice),必须使用显式的格式说明符 `{s}``{any}`,而不能使用默认的 `{}`。这会导致编译错误:
6+
7+
```
8+
error: cannot format slice without a specifier (i.e. {s} or {any})
9+
```
10+
11+
## 问题定位过程
12+
13+
### 初始错误
14+
15+
项目在编译时遇到以下错误:
16+
```
17+
/home/winger/zig-linux-x86_64-0.14.0/lib/std/fmt.zig:653:21: error: cannot format slice without a specifier (i.e. {s} or {any})
18+
```
19+
20+
### 排查策略
21+
22+
采用**逐步屏蔽法**定位问题源:
23+
24+
1. **屏蔽 main.zig 中的 fmt 调用**
25+
2. **屏蔽 jwt.zig 中的 fmt 调用**
26+
3. **屏蔽 response.zig 中的 fmt 调用**
27+
4. **屏蔽 router/upload/static 中的 fmt 调用**
28+
5. **定位并修复问题源**
29+
30+
### 问题根源
31+
32+
最终定位到 `response.zig` 第 174 行:
33+
34+
```zig
35+
// ❌ 错误写法
36+
try response_buf.writer().print("HTTP/1.1 {} {}\r\n", .{ self.status, status_text });
37+
38+
// ✅ 正确写法
39+
try response_buf.writer().print("HTTP/1.1 {} {s}\r\n", .{ self.status, status_text });
40+
```
41+
42+
**关键发现**:即使 `status_text` 是字符串字面量(字符串字面量在编译时是已知的),但在格式化时仍需要显式使用 `{s}` 格式说明符。
43+
44+
## Zig 0.14.0 fmt 格式化规则
45+
46+
### 格式说明符
47+
48+
| 类型 | 格式说明符 | 示例 |
49+
|------|-----------|------|
50+
| 整数 | `{}``{d}` | `print("Count: {}", .{count})` |
51+
| 字符串/切片 | `{s}``{any}` | `print("Name: {s}", .{name})` |
52+
| 十六进制 | `{x}` | `print("Hex: {x}", .{value})` |
53+
| 指针 | `{*}` | `print("Ptr: {*}", .{ptr})` |
54+
| 任意类型 | `{any}` | `print("Value: {any}", .{value})` |
55+
56+
### 常见错误场景
57+
58+
#### 1. 字符串字面量格式化
59+
60+
```zig
61+
// ❌ 错误:即使参数是字符串字面量,也需要 {s}
62+
const msg = "Hello";
63+
try writer.print("Message: {}\r\n", .{msg});
64+
65+
// ✅ 正确
66+
const msg = "Hello";
67+
try writer.print("Message: {s}\r\n", .{msg});
68+
```
69+
70+
#### 2. 动态字符串格式化
71+
72+
```zig
73+
// ❌ 错误
74+
const name = try allocator.dupe(u8, "User");
75+
try writer.print("Name: {}", .{name});
76+
77+
// ✅ 正确
78+
const name = try allocator.dupe(u8, "User");
79+
try writer.print("Name: {s}", .{name});
80+
```
81+
82+
#### 3. 混合类型格式化
83+
84+
```zig
85+
// ❌ 错误
86+
try writer.print("Status: {} {}\r\n", .{ status, message });
87+
88+
// ✅ 正确
89+
try writer.print("Status: {} {s}\r\n", .{ status, message });
90+
```
91+
92+
## 解决方案
93+
94+
### 方案 1:使用正确的格式说明符(推荐)
95+
96+
直接修复格式字符串,添加 `{s}` 说明符:
97+
98+
```zig
99+
// 修复前
100+
try response_buf.writer().print("HTTP/1.1 {} {}\r\n", .{ self.status, status_text });
101+
102+
// 修复后
103+
try response_buf.writer().print("HTTP/1.1 {} {s}\r\n", .{ self.status, status_text });
104+
```
105+
106+
### 方案 2:使用 bufPrint(适用于小缓冲区)
107+
108+
对于固定大小的缓冲区,可以使用 `bufPrint`
109+
110+
```zig
111+
var buf: [32]u8 = undefined;
112+
const len_str = try std.fmt.bufPrint(&buf, "{}", .{n});
113+
```
114+
115+
### 方案 3:手动字符串拼接(临时方案)
116+
117+
如果格式化功能暂时有问题,可以使用手动拼接:
118+
119+
```zig
120+
// 临时方案:手动拼接
121+
var message = std.ArrayList(u8).init(allocator);
122+
defer message.deinit();
123+
try message.appendSlice("HTTP/1.1 ");
124+
try message.appendSlice(status_text);
125+
try message.appendSlice("\r\n");
126+
```
127+
128+
## 最佳实践
129+
130+
### 1. 格式化字符串时总是显式指定类型
131+
132+
```zig
133+
// ✅ 推荐:显式类型
134+
try writer.print("Name: {s}, Age: {}, Score: {d}\r\n", .{ name, age, score });
135+
136+
// ❌ 不推荐:依赖自动推断(可能导致错误)
137+
try writer.print("Name: {}, Age: {}, Score: {}\r\n", .{ name, age, score });
138+
```
139+
140+
### 2. 使用 bufPrint 处理固定大小格式化
141+
142+
```zig
143+
// 适用于:数字、小字符串
144+
var buf: [64]u8 = undefined;
145+
const formatted = try std.fmt.bufPrint(&buf, "Value: {}, Hex: {x}", .{ value, value });
146+
```
147+
148+
### 3. 使用 allocPrint 处理动态大小格式化
149+
150+
```zig
151+
// 适用于:长度未知的字符串
152+
const message = try std.fmt.allocPrint(allocator, "User {s} logged in", .{username});
153+
defer allocator.free(message);
154+
```
155+
156+
### 4. 格式化时检查类型
157+
158+
在开发过程中,如果遇到格式化错误,检查:
159+
- 参数是否是切片类型?
160+
- 如果是切片,是否使用了 `{s}``{any}`
161+
- 格式字符串中的占位符数量是否与参数数量匹配?
162+
163+
## 排查清单
164+
165+
遇到 fmt 格式化错误时,按以下步骤排查:
166+
167+
1. ✅ 检查格式字符串中的所有占位符
168+
2. ✅ 确认切片类型的参数使用了 `{s}``{any}`
169+
3. ✅ 验证占位符数量与参数数量匹配
170+
4. ✅ 检查是否有类型推断问题
171+
5. ✅ 尝试使用 `{any}` 进行调试(会显示完整类型信息)
172+
173+
## 常见问题 FAQ
174+
175+
### Q: 为什么字符串字面量也需要 `{s}`
176+
177+
A: 在 Zig 0.14.0 中,字符串字面量在格式化时被视为切片类型,必须显式指定格式说明符以提高类型安全性和代码可读性。
178+
179+
### Q: `{s}``{any}` 有什么区别?
180+
181+
A:
182+
- `{s}`:专门用于字符串/切片,进行字符串格式化
183+
- `{any}`:用于任意类型,会调用类型的 `format` 方法,显示调试信息
184+
185+
### Q: 使用 `bufPrint``allocPrint` 的区别?
186+
187+
A:
188+
- `bufPrint`:使用栈上的固定大小缓冲区,速度快,但大小受限
189+
- `allocPrint`:动态分配内存,大小灵活,但需要手动释放
190+
191+
### Q: 如何避免此类错误?
192+
193+
A:
194+
1. 在格式化字符串时总是显式指定类型
195+
2. 使用类型检查工具或 IDE 插件
196+
3. 建立代码审查规范,检查格式化调用
197+
198+
## 修复记录
199+
200+
### 修复的文件
201+
202+
1. **http/src/response.zig**
203+
- 修复位置:第 174 行
204+
- 问题:`status_text` 未使用 `{s}` 格式说明符
205+
- 修复:`"HTTP/1.1 {} {}\r\n"``"HTTP/1.1 {} {s}\r\n"`
206+
207+
### 其他排查但未修复的位置(使用 bufPrint 处理)
208+
209+
- `http/src/static.zig`: Content-Length 和 ETag 格式化
210+
- `http/src/main.zig`: Upload 消息格式化(用户已手动修复)
211+
212+
## 参考资源
213+
214+
- [Zig std.fmt 文档](https://ziglang.org/documentation/master/std/#std;fmt)
215+
- [Zig 0.14.0 Release Notes](https://ziglang.org/download/0.14.0/release-notes.html)
216+
- [Zig Learn - Formatting](https://ziglearn.org/chapter-1/#formatting)
217+
218+
## 总结
219+
220+
本次排查过程展示了 Zig 0.14.0 中 fmt 格式化的严格性。关键要点:
221+
222+
1. **字符串/切片必须使用 `{s}``{any}`**
223+
2. **即使参数是字符串字面量也不例外**
224+
3. **采用逐步屏蔽法可以有效定位问题源**
225+
4. **显式类型说明符是推荐的编程实践**
226+
227+
遵循这些规则可以避免类似的编译错误,提高代码质量和可维护性。
228+

0 commit comments

Comments
 (0)