diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index d1e049d5..60e3a025 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -22,7 +22,7 @@ jobs: strategy: matrix: os: [ubuntu-latest, macos-latest, windows-latest] - version: [0.11.0, 0.12.1, 0.13.0, ""] + version: [0.11.0, 0.12.1, 0.13.0, 0.14.0, ""] fail-fast: false runs-on: ${{ matrix.os }} steps: diff --git a/15_zig_build_fix.txt b/15_zig_build_fix.txt new file mode 100644 index 00000000..5e084769 --- /dev/null +++ b/15_zig_build_fix.txt @@ -0,0 +1,262 @@ + +src\main.zig:10:31: error: root source file struct 'Io' has no member named 'getStdOut' + const stdout_file = std.io.getStdOut().writer(); + ~~~~~~^~~~~~~~~~ +C:\Users\jin\scoop\apps\zig-dev\0.15.0-dev.1283\lib\std\Io.zig:1:1: note: struct declared here +const builtin = @import("builtin"); +^~~~~ +referenced by: + callMain [inlined]: C:\Users\jin\scoop\apps\zig-dev\0.15.0-dev.1283\lib\std\start.zig:627:37 + WinStartup: C:\Users\jin\scoop\apps\zig-dev\0.15.0-dev.1283\lib\std\start.zig:443:53 + 2 reference(s) hidden; use '-freference-trace=4' to see all references +error: the following command failed with 1 compilation errors: +"C:\\Users\\jin\\scoop\\apps\\zig-dev\\0.15.0-dev.1283\\zig.exe" build-exe -ODebug "-Mroot=C:\\Users\\jin\\code\\zig-course\\course\\c +ode\\15\\build_system\\basic\\src\\main.zig" --cache-dir .zig-cache --global-cache-dir "C:\\Users\\jin\\AppData\\Local\\zig" --name zi +g --zig-lib-dir "C:\\Users\\jin\\scoop\\apps\\zig-dev\\0.15.0-dev.1283\\lib\\" --listen=- + +Build Summary: 0/3 steps succeeded; 1 failed +install transitive failure +└─ install zig transitive failure + └─ compile exe zig Debug native 1 errors + +error: the following build command failed with exit code 1: +.zig-cache\o\22df4cf7dbdbf962cf23936c61889573\build.exe C:\Users\jin\scoop\apps\zig-dev\0.15.0-dev.1283\zig.exe C:\Users\jin\scoop\app +s\zig-dev\0.15.0-dev.1283\lib C:\Users\jin\code\zig-course\course\code\15\build_system\basic .zig-cache C:\Users\jin\AppData\Local\zig + --seed 0x625a5d8f -Z22ccef05181dde7e +install +└─ install zig + └─ compile exe zig Debug native 1 errors +src\main.zig:10:31: error: root source file struct 'Io' has no member named 'getStdOut' + const stdout_file = std.io.getStdOut().writer(); + ~~~~~~^~~~~~~~~~ +C:\Users\jin\scoop\apps\zig-dev\0.15.0-dev.1283\lib\std\Io.zig:1:1: note: struct declared here +const builtin = @import("builtin"); +^~~~~ +referenced by: + callMain [inlined]: C:\Users\jin\scoop\apps\zig-dev\0.15.0-dev.1283\lib\std\start.zig:627:37 + WinStartup: C:\Users\jin\scoop\apps\zig-dev\0.15.0-dev.1283\lib\std\start.zig:443:53 + 2 reference(s) hidden; use '-freference-trace=4' to see all references +error: the following command failed with 1 compilation errors: +"C:\\Users\\jin\\scoop\\apps\\zig-dev\\0.15.0-dev.1283\\zig.exe" build-exe -fno-strip -ODebug "-Mroot=C:\\Users\\jin\\code\\zig-course +\\course\\code\\15\\build_system\\cli\\src\\main.zig" --cache-dir .zig-cache --global-cache-dir "C:\\Users\\jin\\AppData\\Local\\zig" +--name zig --zig-lib-dir "C:\\Users\\jin\\scoop\\apps\\zig-dev\\0.15.0-dev.1283\\lib\\" --listen=- + +Build Summary: 0/3 steps succeeded; 1 failed +install transitive failure +└─ install zig transitive failure + └─ compile exe zig Debug native 1 errors + +error: the following build command failed with exit code 1: +.zig-cache\o\5bd10d6c5035afe76329d62536957327\build.exe C:\Users\jin\scoop\apps\zig-dev\0.15.0-dev.1283\zig.exe C:\Users\jin\scoop\app +s\zig-dev\0.15.0-dev.1283\lib C:\Users\jin\code\zig-course\course\code\15\build_system\cli .zig-cache C:\Users\jin\AppData\Local\zig - +-seed 0x59ad36e3 -Z06a0c06bd5c06800 +build.zig:11:18: error: no field or member function named 'addStaticLibrary' in 'Build' + const lib = b.addStaticLibrary(.{ + ~^~~~~~~~~~~~~~~~~ +C:\Users\jin\scoop\apps\zig-dev\0.15.0-dev.1283\lib\std\Build.zig:1:1: note: struct declared here +const std = @import("std.zig"); +^~~~~ +build.zig:11:18: note: method invocation only supports up to one level of implicit pointer dereferencing +build.zig:11:18: note: use '.*' to dereference pointer +referenced by: + runBuild__anon_32108: C:\Users\jin\scoop\apps\zig-dev\0.15.0-dev.1283\lib\std\Build.zig:2211:33 + main: C:\Users\jin\scoop\apps\zig-dev\0.15.0-dev.1283\lib\compiler\build_runner.zig:352:29 + 4 reference(s) hidden; use '-freference-trace=6' to see all references +build.zig:13:10: error: no field named 'root_source_file' in struct 'Build.ExecutableOptions' + .root_source_file = b.path("src/main.zig"), + ^~~~~~~~~~~~~~~~ +C:\Users\jin\scoop\apps\zig-dev\0.15.0-dev.1283\lib\std\Build.zig:768:31: note: struct declared here +pub const ExecutableOptions = struct { + ^~~~~~ +referenced by: + runBuild__anon_32108: C:\Users\jin\scoop\apps\zig-dev\0.15.0-dev.1283\lib\std\Build.zig:2211:33 + main: C:\Users\jin\scoop\apps\zig-dev\0.15.0-dev.1283\lib\compiler\build_runner.zig:352:29 + 4 reference(s) hidden; use '-freference-trace=6' to see all references +install +└─ install hello + └─ compile exe hello Debug native 1 errors +src\main.zig:10:31: error: root source file struct 'Io' has no member named 'getStdOut' + const stdout_file = std.io.getStdOut().writer(); + ~~~~~~^~~~~~~~~~ +C:\Users\jin\scoop\apps\zig-dev\0.15.0-dev.1283\lib\std\Io.zig:1:1: note: struct declared here +const builtin = @import("builtin"); +^~~~~ +referenced by: + callMain [inlined]: C:\Users\jin\scoop\apps\zig-dev\0.15.0-dev.1283\lib\std\start.zig:627:37 + WinStartup: C:\Users\jin\scoop\apps\zig-dev\0.15.0-dev.1283\lib\std\start.zig:443:53 + 2 reference(s) hidden; use '-freference-trace=4' to see all references +error: the following command failed with 1 compilation errors: +"C:\\Users\\jin\\scoop\\apps\\zig-dev\\0.15.0-dev.1283\\zig.exe" build-exe -ODebug "-Mroot=C:\\Users\\jin\\code\\zig-course\\course\\c +ode\\15\\build_system\\step\\src\\main.zig" --cache-dir .zig-cache --global-cache-dir "C:\\Users\\jin\\AppData\\Local\\zig" --name hel +lo --zig-lib-dir "C:\\Users\\jin\\scoop\\apps\\zig-dev\\0.15.0-dev.1283\\lib\\" --listen=- + +Build Summary: 0/3 steps succeeded; 1 failed +install transitive failure +└─ install hello transitive failure + └─ compile exe hello Debug native 1 errors + +error: the following build command failed with exit code 1: +.zig-cache\o\e269fd4c82996e8b6efaa38022c4a79b\build.exe C:\Users\jin\scoop\apps\zig-dev\0.15.0-dev.1283\zig.exe C:\Users\jin\scoop\app +s\zig-dev\0.15.0-dev.1283\lib C:\Users\jin\code\zig-course\course\code\15\build_system\step .zig-cache C:\Users\jin\AppData\Local\zig +--seed 0x1738a8dc -Z7116767a471854aa +install +└─ install zig + └─ compile exe zig Debug native 1 errors +src\main.zig:10:31: error: root source file struct 'Io' has no member named 'getStdOut' + const stdout_file = std.io.getStdOut().writer(); + ~~~~~~^~~~~~~~~~ +C:\Users\jin\scoop\apps\zig-dev\0.15.0-dev.1283\lib\std\Io.zig:1:1: note: struct declared here +const builtin = @import("builtin"); +^~~~~ +referenced by: + callMain [inlined]: C:\Users\jin\scoop\apps\zig-dev\0.15.0-dev.1283\lib\std\start.zig:627:37 + WinStartup: C:\Users\jin\scoop\apps\zig-dev\0.15.0-dev.1283\lib\std\start.zig:443:53 + 2 reference(s) hidden; use '-freference-trace=4' to see all references +error: the following command failed with 1 compilation errors: +"C:\\Users\\jin\\scoop\\apps\\zig-dev\\0.15.0-dev.1283\\zig.exe" build-exe -ODebug "-Mroot=C:\\Users\\jin\\code\\zig-course\\course\\c +ode\\15\\build_system\\test\\src\\main.zig" --cache-dir .zig-cache --global-cache-dir "C:\\Users\\jin\\AppData\\Local\\zig" --name zig + --zig-lib-dir "C:\\Users\\jin\\scoop\\apps\\zig-dev\\0.15.0-dev.1283\\lib\\" --listen=- + +Build Summary: 0/3 steps succeeded; 1 failed +install transitive failure +└─ install zig transitive failure + └─ compile exe zig Debug native 1 errors + +error: the following build command failed with exit code 1: +.zig-cache\o\52bd7ebf7a1e3f67b283bc7a1b1460ca\build.exe C:\Users\jin\scoop\apps\zig-dev\0.15.0-dev.1283\zig.exe C:\Users\jin\scoop\app +s\zig-dev\0.15.0-dev.1283\lib C:\Users\jin\code\zig-course\course\code\15\build_system\test .zig-cache C:\Users\jin\AppData\Local\zig +--seed 0xca605874 -Z5dd6b5163b24fc08 +build.zig:19:10: error: no field named 'target' in struct 'Build.ExecutableOptions' + .target = target, + ^~~~~~ +C:\Users\jin\scoop\apps\zig-dev\0.15.0-dev.1283\lib\std\Build.zig:768:31: note: struct declared here +pub const ExecutableOptions = struct { + ^~~~~~ +referenced by: + runBuild__anon_32108: C:\Users\jin\scoop\apps\zig-dev\0.15.0-dev.1283\lib\std\Build.zig:2211:33 + main: C:\Users\jin\scoop\apps\zig-dev\0.15.0-dev.1283\lib\compiler\build_runner.zig:352:29 + 4 reference(s) hidden; use '-freference-trace=6' to see all references +C:\Users\jin\AppData\Local\zig\p\zig_msgpack-0.0.7-evvueE3MAADy-2EAgCGUYIf1tHC9-z4n2sDIldvTZcY8\build.zig:21:10: error: no field named + 'root_source_file' in struct 'Build.TestOptions' + .root_source_file = b.path(b.pathJoin(&.{ "src", "test.zig" })), + ^~~~~~~~~~~~~~~~ +C:\Users\jin\scoop\apps\zig-dev\0.15.0-dev.1283\lib\std\Build.zig:853:25: note: struct declared here +pub const TestOptions = struct { + ^~~~~~ +referenced by: + runBuild__anon_81738: C:\Users\jin\scoop\apps\zig-dev\0.15.0-dev.1283\lib\std\Build.zig:2211:33 + dependencyInner__anon_78976: C:\Users\jin\scoop\apps\zig-dev\0.15.0-dev.1283\lib\std\Build.zig:2192:29 + 8 reference(s) hidden; use '-freference-trace=10' to see all references +C:\Users\jin\AppData\Local\zig\p\zig_msgpack-0.0.7-evvueE3MAADy-2EAgCGUYIf1tHC9-z4n2sDIldvTZcY8\build.zig:33:10: error: no field named + 'root_source_file' in struct 'Build.ObjectOptions' + .root_source_file = b.path(b.pathJoin(&.{ "src", "msgpack.zig" })), + ^~~~~~~~~~~~~~~~ +C:\Users\jin\scoop\apps\zig-dev\0.15.0-dev.1283\lib\std\Build.zig:800:27: note: struct declared here +pub const ObjectOptions = struct { + ^~~~~~ +install +└─ install assembly_fixed + └─ compile exe assembly_fixed Debug native 1 errors +C:\Users\jin\scoop\apps\zig-dev\0.15.0-dev.1283\lib\std\start.zig:614:46: error: root source file struct 'assembly_fixed' has no membe +r named 'main' + const ReturnType = @typeInfo(@TypeOf(root.main)).@"fn".return_type.?; + ~~~~^~~~~ +course\code\15\assembly_fixed.zig:2:1: note: struct declared here + +^ +C:\Users\jin\scoop\apps\zig-dev\0.15.0-dev.1283\lib\std\start.zig:443:53: note: called inline here + std.os.windows.ntdll.RtlExitUserProcess(callMain()); + ~~~~~~~~^~ +referenced by: + comptime: C:\Users\jin\scoop\apps\zig-dev\0.15.0-dev.1283\lib\std\start.zig:68:30 + start: C:\Users\jin\scoop\apps\zig-dev\0.15.0-dev.1283\lib\std\std.zig:100:27 + 1 reference(s) hidden; use '-freference-trace=3' to see all references +error: the following command failed with 1 compilation errors: +"C:\\Users\\jin\\scoop\\apps\\zig-dev\\0.15.0-dev.1283\\zig.exe" build-exe -ODebug "-Mroot=C:\\Users\\jin\\code\\zig-course\\course\\c +ode\\15\\assembly_fixed.zig" -lc --cache-dir .zig-cache --global-cache-dir "C:\\Users\\jin\\AppData\\Local\\zig" --name assembly_fixed + --zig-lib-dir "C:\\Users\\jin\\scoop\\apps\\zig-dev\\0.15.0-dev.1283\\lib\\" --listen=- +install +└─ install echo_tcp_server + └─ compile exe echo_tcp_server Debug native 12 errors +error: lld-link: undefined symbol: WSAPoll + note: referenced by C:\Users\jin\scoop\apps\zig-dev\0.15.0-dev.1283\lib\std\os\windows.zig:1728 + note: .zig-cache\o\020cf0c8513b3bd5738a335ba568a372\echo_tcp_server_zcu.obj:(os.windows.poll) +error: lld-link: undefined symbol: ioctlsocket + note: referenced by C:\Users\jin\scoop\apps\zig-dev\0.15.0-dev.1283\lib\std\posix.zig:3634 + note: .zig-cache\o\020cf0c8513b3bd5738a335ba568a372\echo_tcp_server_zcu.obj:(posix.socket) + note: referenced by C:\Users\jin\scoop\apps\zig-dev\0.15.0-dev.1283\lib\std\posix.zig:4028 + note: .zig-cache\o\020cf0c8513b3bd5738a335ba568a372\echo_tcp_server_zcu.obj:(posix.setSockFlags) +error: lld-link: undefined symbol: WSAGetLastError + note: referenced by C:\Users\jin\scoop\apps\zig-dev\0.15.0-dev.1283\lib\std\posix.zig:3635 + note: .zig-cache\o\020cf0c8513b3bd5738a335ba568a372\echo_tcp_server_zcu.obj:(posix.socket) + note: referenced by C:\Users\jin\scoop\apps\zig-dev\0.15.0-dev.1283\lib\std\posix.zig:6658 + note: .zig-cache\o\020cf0c8513b3bd5738a335ba568a372\echo_tcp_server_zcu.obj:(posix.setsockopt) + note: referenced by C:\Users\jin\scoop\apps\zig-dev\0.15.0-dev.1283\lib\std\posix.zig:3776 + note: .zig-cache\o\020cf0c8513b3bd5738a335ba568a372\echo_tcp_server_zcu.obj:(posix.bind) + note: referenced 7 more times +error: lld-link: undefined symbol: setsockopt + note: referenced by C:\Users\jin\scoop\apps\zig-dev\0.15.0-dev.1283\lib\std\posix.zig:6656 + note: .zig-cache\o\020cf0c8513b3bd5738a335ba568a372\echo_tcp_server_zcu.obj:(posix.setsockopt) +error: lld-link: undefined symbol: closesocket + note: referenced by C:\Users\jin\scoop\apps\zig-dev\0.15.0-dev.1283\lib\std\os\windows.zig:1671 + note: .zig-cache\o\020cf0c8513b3bd5738a335ba568a372\echo_tcp_server_zcu.obj:(os.windows.closesocket) +error: lld-link: undefined symbol: WSAStartup + note: referenced by C:\Users\jin\scoop\apps\zig-dev\0.15.0-dev.1283\lib\std\os\windows.zig:1566 + note: .zig-cache\o\020cf0c8513b3bd5738a335ba568a372\echo_tcp_server_zcu.obj:(os.windows.WSAStartup) +error: lld-link: undefined symbol: WSASocketW + note: referenced by C:\Users\jin\scoop\apps\zig-dev\0.15.0-dev.1283\lib\std\os\windows.zig:1642 + note: .zig-cache\o\020cf0c8513b3bd5738a335ba568a372\echo_tcp_server_zcu.obj:(os.windows.WSASocketW) +error: lld-link: undefined symbol: bind + note: referenced by C:\Users\jin\scoop\apps\zig-dev\0.15.0-dev.1283\lib\std\os\windows.zig:1663 + note: .zig-cache\o\020cf0c8513b3bd5738a335ba568a372\echo_tcp_server_zcu.obj:(os.windows.bind) +error: lld-link: undefined symbol: listen + note: referenced by C:\Users\jin\scoop\apps\zig-dev\0.15.0-dev.1283\lib\std\os\windows.zig:1667 + note: .zig-cache\o\020cf0c8513b3bd5738a335ba568a372\echo_tcp_server_zcu.obj:(os.windows.listen) +error: lld-link: undefined symbol: getsockname + note: referenced by C:\Users\jin\scoop\apps\zig-dev\0.15.0-dev.1283\lib\std\os\windows.zig:1686 + note: .zig-cache\o\020cf0c8513b3bd5738a335ba568a372\echo_tcp_server_zcu.obj:(os.windows.getsockname) +error: lld-link: undefined symbol: accept + note: referenced by C:\Users\jin\scoop\apps\zig-dev\0.15.0-dev.1283\lib\std\os\windows.zig:1682 + note: .zig-cache\o\020cf0c8513b3bd5738a335ba568a372\echo_tcp_server_zcu.obj:(os.windows.accept) +error: lld-link: undefined symbol: WSASend + note: referenced by C:\Users\jin\scoop\apps\zig-dev\0.15.0-dev.1283\lib\std\net.zig:2083 + note: .zig-cache\o\020cf0c8513b3bd5738a335ba568a372\echo_tcp_server_zcu.obj:(net.Stream.Writer__struct_31094.sendBuf +s) +error: the following command failed with 12 compilation errors: +"C:\\Users\\jin\\scoop\\apps\\zig-dev\\0.15.0-dev.1283\\zig.exe" build-exe -ODebug "-Mroot=C:\\Users\\jin\\code\\zig-course\\course\\c +ode\\15\\echo_tcp_server.zig" -lc --cache-dir .zig-cache --global-cache-dir "C:\\Users\\jin\\AppData\\Local\\zig" --name echo_tcp_serv +er --zig-lib-dir "C:\\Users\\jin\\scoop\\apps\\zig-dev\\0.15.0-dev.1283\\lib\\" --listen=- +install +└─ install hello_world + └─ compile exe hello_world Debug native 1 errors +course\code\15\hello_world.zig:22:30: error: root source file struct 'Io' has no member named 'getStdOut' + const stdout = std.io.getStdOut().writer(); + ~~~~~~^~~~~~~~~~ +C:\Users\jin\scoop\apps\zig-dev\0.15.0-dev.1283\lib\std\Io.zig:1:1: note: struct declared here +const builtin = @import("builtin"); +^~~~~ +referenced by: + main: course\code\15\hello_world.zig:3:17 + callMain [inlined]: C:\Users\jin\scoop\apps\zig-dev\0.15.0-dev.1283\lib\std\start.zig:627:37 + callMainWithArgs [inlined]: C:\Users\jin\scoop\apps\zig-dev\0.15.0-dev.1283\lib\std\start.zig:587:20 + main: C:\Users\jin\scoop\apps\zig-dev\0.15.0-dev.1283\lib\std\start.zig:602:28 + 1 reference(s) hidden; use '-freference-trace=5' to see all references +error: the following command failed with 1 compilation errors: +"C:\\Users\\jin\\scoop\\apps\\zig-dev\\0.15.0-dev.1283\\zig.exe" build-exe -ODebug "-Mroot=C:\\Users\\jin\\code\\zig-course\\course\\c +ode\\15\\hello_world.zig" -lc --cache-dir .zig-cache --global-cache-dir "C:\\Users\\jin\\AppData\\Local\\zig" --name hello_world --zig +-lib-dir "C:\\Users\\jin\\scoop\\apps\\zig-dev\\0.15.0-dev.1283\\lib\\" --listen=- + +Build Summary: 118/125 steps succeeded; 3 failed; 10/10 tests passed +install transitive failure +├─ install assembly_fixed transitive failure +│ └─ compile exe assembly_fixed Debug native 1 errors +├─ install echo_tcp_server transitive failure +│ └─ compile exe echo_tcp_server Debug native 12 errors +└─ install hello_world transitive failure + └─ compile exe hello_world Debug native 1 errors + +error: the following build command failed with exit code 1: +.zig-cache\o\48912e7b0e7a403214ce8e205d9457a3\build.exe C:\Users\jin\scoop\apps\zig-dev\0.15.0-dev.1283\zig.exe C:\Users\jin\scoop\app +s\zig-dev\0.15.0-dev.1283\lib C:\Users\jin\code\zig-course .zig-cache C:\Users\jin\AppData\Local\zig --seed 0x9311a7fb -Z19e9ce87436cf +a8c + diff --git a/README.md b/README.md index b2c4ca20..dbf81f60 100644 --- a/README.md +++ b/README.md @@ -14,51 +14,229 @@ **Zig 语言圣经** 是一份开源的 Zig 语言综合教程,旨在为中文 Zig 爱好者提供一份高质量的学习资源,内容涵盖从基础语法到高级特性的方方面面。 +## 📖 在线阅读 + +- **官方网站**: [https://course.ziglang.cc/](https://course.ziglang.cc/) +- **GitHub Pages**: [https://zigcc.github.io/zig-course/](https://zigcc.github.io/zig-course/) + ## ✨ 内容特色 本教程覆盖了 Zig 学习和实践中的多个重要领域: -- **环境配置**: 指导如何安装和配置 Zig 开发环境。 -- **基础入门**: 包括变量、类型、流程控制、错误处理等基础知识。 -- **高级主题**: 深入探讨 `comptime`、异步、内存管理、C 语言交互等高级特性。 -- **工程实践**: 涵盖构建系统、包管理、单元测试和代码风格指南。 -- **版本示例**: 提供与 Zig 不同版本相对应的代码示例。 +- **环境配置**: 指导如何安装和配置 Zig 开发环境,支持多种编辑器配置 +- **基础入门**: 包括变量、类型、流程控制、错误处理等基础知识 +- **高级主题**: 深入探讨 `comptime`、异步、内存管理、C 语言交互等高级特性 +- **工程实践**: 涵盖构建系统、包管理、单元测试和代码风格指南 +- **版本兼容**: 提供与 Zig 0.11-0.15 版本相对应的代码示例 +- **实战案例**: 包含 TCP 服务器等实际项目示例 + +## 📁 项目结构 + +``` +zig-course/ +├── .github/ # GitHub Actions 工作流 +│ ├── workflows/ # CI/CD 配置 +│ └── dependabot.yml # 依赖更新配置 +├── build/ # 不同 Zig 版本的构建脚本 +│ ├── 0.11.zig # Zig 0.11 构建配置 +│ ├── 0.12.zig # Zig 0.12 构建配置 +│ └── ... # 其他版本 +├── course/ # 教程主要内容 +│ ├── .vitepress/ # VitePress 配置 +│ │ ├── config.mts # 站点配置 +│ │ ├── theme/ # 主题定制 +│ │ └── ... +│ ├── basic/ # 基础教程 +│ │ ├── basic_type/ # 基本类型 +│ │ ├── advanced_type/ # 高级类型 +│ │ ├── process_control/ # 流程控制 +│ │ └── ... +│ ├── advanced/ # 高级教程 +│ │ ├── comptime.md # 编译期计算 +│ │ ├── async.md # 异步编程 +│ │ ├── memory_manage.md # 内存管理 +│ │ └── ... +│ ├── engineering/ # 工程实践 +│ │ ├── build-system.md # 构建系统 +│ │ ├── package_management.md # 包管理 +│ │ └── ... +│ ├── environment/ # 环境配置 +│ ├── examples/ # 示例项目 +│ ├── code/ # 代码示例(按版本分类) +│ │ ├── 11/ # Zig 0.11 示例 +│ │ ├── 12/ # Zig 0.12 示例 +│ │ └── ... +│ ├── picture/ # 图片资源 +│ ├── public/ # 静态资源 +│ └── update/ # 版本更新说明 +├── draw/ # 绘图源文件 +├── build.zig # 主构建文件 +├── package.json # Node.js 依赖配置 +├── CONTRIBUTING.md # 贡献指南 +├── CODE_OF_CONDUCT.md # 行为准则 +└── README.md # 项目说明 +``` + +## 🚀 本地开发 + +### 环境要求 + +- **Node.js**: 推荐使用 [Bun](https://bun.sh/) 作为包管理器 +- **Zig**: 支持 0.11-0.15 版本 +- **autocorrect**: 用于中英文排版优化(可选) + +### 快速开始 + +```sh +# 克隆仓库 +git clone https://github.com/zigcc/zig-course.git +cd zig-course + +# 安装依赖 +bun install + +# 启动开发服务器 +bun dev -## 🚀 如何阅读 +# 在浏览器中访问 http://localhost:5173 +``` -本项目使用 VitePress 构建。您可以直接在本地启动开发服务器以阅读最新内容: +### 可用命令 ```sh -bun i # 安装依赖 -bun dev # 启动热更开发服务 +bun dev # 启动开发服务器(热重载) +bun build # 构建生产版本 +bun preview # 预览构建结果 +bun format # 格式化代码(prettier + zig fmt + autocorrect) +bun check # 检查代码格式 +bun export-pdf # 导出 PDF 版本 ``` ## 🤝 参与贡献 -欢迎各位志同道合的“道友”参与贡献本文档,并一起壮大 zig 中文社区! +我们热烈欢迎各位"道友"参与贡献,一起壮大 Zig 中文社区! -贡献方法: +### 贡献方式 -- Fork 本文档仓库 -- 创建一个新的分支,请勿直接使用主分支进行修改 -- 发起 Pull Request -- 等待 Review -- 合并到上游仓库,并由 GitHub Action 自动构建 +1. **内容贡献** + - 修正错误和改进现有内容 + - 添加新的章节或示例 + - 翻译和本地化改进 + - 添加代码示例和实战案例 -**开发命令:** +2. **技术贡献** + - 改进网站功能和用户体验 + - 优化构建流程和 CI/CD + - 修复 bug 和性能问题 -```sh -bun i # 安装依赖 -bun dev # 启动热更开发服务 -bun format # 运行 prettier, zig fmt 和 autocorrect 格式化程序 -bun run build # 构建产物 -bun run preview # 运行预览 -``` +3. **社区贡献** + - 参与讨论和问题解答 + - 推广和分享项目 + - 提供反馈和建议 + +### 贡献流程 + +1. **Fork 仓库** + + ```sh + # 在 GitHub 上 Fork 本仓库 + git clone https://github.com/YOUR_USERNAME/zig-course.git + cd zig-course + ``` + +2. **创建功能分支** + + ```sh + git checkout -b feature/your-feature-name + # 或 + git checkout -b fix/your-fix-name + ``` + +3. **进行修改** + - 遵循现有的代码风格和文档格式 + - 确保所有代码示例都能正常运行 + - 运行 `bun format` 格式化代码 + +4. **测试修改** + + ```sh + bun dev # 本地测试 + bun build # 确保构建成功 + ``` + +5. **提交更改** + + ```sh + git add . + git commit -m "feat: 添加新功能描述" + # 或 + git commit -m "fix: 修复问题描述" + ``` + +6. **推送并创建 PR** + ```sh + git push origin feature/your-feature-name + # 在 GitHub 上创建 Pull Request + ``` + +### 贡献规范 + +- **提交信息**: 使用 [约定式提交](https://www.conventionalcommits.org/zh-hans/) 格式 +- **代码风格**: 运行 `bun format` 确保代码格式一致 +- **文档规范**: + - 中英文之间添加空格 + - 使用中文标点符号 + - 代码块指定语言类型 +- **分支命名**: + - 功能:`feature/功能描述` + - 修复:`fix/问题描述` + - 文档:`docs/文档更新` + +### 内容编写指南 + +1. **Markdown 格式** + - 使用标准 Markdown 语法 + - 代码块指定语言 `zig` + - 适当使用表格和列表 + +2. **代码示例** + - 确保代码能在对应 Zig 版本下运行 + - 添加必要的注释说明 + - 提供完整的可运行示例 + +3. **图片和资源** + - 图片放在 `course/picture/` 目录下 + - 使用相对路径引用 + - 提供 alt 文本描述 + +### 版本兼容性 + +本项目支持多个 Zig 版本,在贡献代码时请注意: + +- 在 `course/code/` 目录下按版本分类存放示例代码 +- 确保代码示例在对应版本下能正常编译运行 +- 如有版本差异,请在文档中明确说明 + +## 📋 开发注意事项 + +- **包管理器**: 本项目使用 [Bun](https://bun.sh/),请勿提交其他包管理器的配置文件 +- **依赖更新**: 更新依赖前请参考 [Bun Lockfile 文档](https://bun.sh/docs/install/lockfile) +- **格式化**: 提交前务必运行 `bun format` 进行代码格式化 +- **构建测试**: 确保 `bun build` 能成功构建 + +## 📄 许可证 + +本项目采用 [MIT 许可证](LICENSE),欢迎自由使用和分发。 + +## 🙏 致谢 + +感谢所有为本项目做出贡献的开发者和 Zig 中文社区的支持! + +## 📞 联系我们 -> [!NOTE] -> 请自行安装 `bun` (建议也安装 `autocorrect`,并且在提交前运行 `bun format`) +- **GitHub Issues**: [提交问题和建议](https://github.com/zigcc/zig-course/issues) +- **GitHub Discussions**: [参与社区讨论](https://github.com/zigcc/zig-course/discussions) -> [!NOTE] -> 本文档所使用的构建工具为 [bunjs](https://bun.sh/),在提交时请勿将其他 nodejs 的包管理工具的额外配置文件添加到仓库中。 +--- -> 如需要更新依赖,请参照此处 [Lockfile](https://bun.sh/docs/install/lockfile) 先设置 git 使用 bun 来 diff 文件! +如果这个项目对你有帮助,请给我们一个 ⭐️ Star! diff --git a/build/0.15.zig b/build/0.15.zig index 2e8f27a6..0d3fcfe3 100644 --- a/build/0.15.zig +++ b/build/0.15.zig @@ -2,7 +2,7 @@ const std = @import("std"); const Build = std.Build; const ChildProcess = std.process.Child; -const log = std.log.scoped(.For_0_14_0); +const log = std.log.scoped(.For_0_15_0); const args = [_][]const u8{ "zig", "build" }; @@ -21,7 +21,7 @@ pub fn build(b: *Build) void { // open dir var dir = std.fs.openDirAbsolute(full_path, .{ .iterate = true }) catch |err| { - log.err("open 14 path failed, err is {}", .{err}); + log.err("open 15 path failed, err is {}", .{err}); std.process.exit(1); }; defer dir.close(); @@ -44,20 +44,32 @@ pub fn build(b: *Build) void { // build exe const exe = b.addExecutable(.{ .name = output_name, - .root_source_file = b.path(path), - .target = target, - .optimize = optimize, + .root_module = b.addModule(output_name, .{ + .root_source_file = b.path(path), + .target = target, + .optimize = optimize, + }), }); exe.linkLibC(); + if (exe.root_module.resolved_target.?.result.os.tag == .windows and std.mem.eql(u8, "echo_tcp_server.zig", entry.name)) { + std.log.info("link ws2_32 for {s}", .{entry.name}); + exe.linkSystemLibrary("ws2_32"); + } // add to default install b.installArtifact(exe); // build test + const test_name = std.fmt.allocPrint(b.allocator, "{s}_test", .{output_name}) catch |err| { + log.err("fmt test name failed, err is {}", .{err}); + std.process.exit(1); + }; const unit_tests = b.addTest(.{ - .root_source_file = b.path(path), - .target = target, - .optimize = optimize, + .root_module = b.addModule(test_name, .{ + .root_source_file = b.path(path), + .target = target, + .optimize = optimize, + }), }); // add to default install diff --git a/course/code/14/hello_world.zig b/course/code/14/hello_world.zig index 96e7b2b8..2df2c318 100644 --- a/course/code/14/hello_world.zig +++ b/course/code/14/hello_world.zig @@ -1,5 +1,5 @@ pub fn main() !void { - One.main(); + try One.main(); try Two.main(); try Three.main(); } diff --git a/course/code/15 b/course/code/15 deleted file mode 120000 index 719486ac..00000000 --- a/course/code/15 +++ /dev/null @@ -1 +0,0 @@ -./14 \ No newline at end of file diff --git a/course/code/15/array.zig b/course/code/15/array.zig new file mode 100644 index 00000000..06c48ccb --- /dev/null +++ b/course/code/15/array.zig @@ -0,0 +1,136 @@ +pub fn main() !void { + CreateArray.main(); + Deconstruct.main(); + Matrix.main(); + TerminatedArray.main(); + Multiply.main(); + Connect.main(); + FuncInitArray.main(); + ComptimeInitArray.main(); +} +const CreateArray = struct { + // #region create_array + const print = @import("std").debug.print; + + pub fn main() void { + const message = [5]u8{ 'h', 'e', 'l', 'l', 'o' }; + // const message = [_]u8{ 'h', 'e', 'l', 'l', 'o' }; + print("{s}\n", .{message}); // hello + print("{c}\n", .{message[0]}); // h + } + // #endregion create_array +}; + +const Deconstruct = struct { + // #region deconstruct + const print = @import("std").debug.print; + + fn swizzleRgbaToBgra(rgba: [4]u8) [4]u8 { + // 解构 + const r, const g, const b, const a = rgba; + return .{ b, g, r, a }; + } + + pub fn main() void { + const pos = [_]i32{ 1, 2 }; + // 解构 + const x, const y = pos; + print("x = {}, y = {}\n", .{ x, y }); + + const orange: [4]u8 = .{ 255, 165, 0, 255 }; + print("{any}\n", .{swizzleRgbaToBgra(orange)}); + } + // #endregion deconstruct +}; + +const Matrix = struct { + // #region matrix + const print = @import("std").debug.print; + + pub fn main() void { + const matrix_4x4 = [4][4]f32{ + [_]f32{ 1.0, 0.0, 0.0, 0.0 }, + [_]f32{ 0.0, 1.0, 0.0, 1.0 }, + [_]f32{ 0.0, 0.0, 1.0, 0.0 }, + [_]f32{ 0.0, 0.0, 0.0, 1.0 }, + }; + + for (matrix_4x4, 0..) |arr_val, arr_index| { + for (arr_val, 0..) |val, index| { + print("元素{}-{}是: {}\n", .{ arr_index, index, val }); + } + } + } + // #endregion matrix +}; + +const TerminatedArray = struct { + // #region terminated_array + const print = @import("std").debug.print; + + pub fn main() void { + const array = [_:0]u8{ 1, 2, 3, 4 }; + print("数组长度为: {}\n", .{array.len}); // 4 + print("数组最后一个元素值: {}\n", .{array[array.len - 1]}); // 4 + print("哨兵值为: {}\n", .{array[array.len]}); // 0 + } + // #endregion terminated_array +}; + +const Multiply = struct { + // #region multiply + const print = @import("std").debug.print; + + pub fn main() void { + const small = [3]i8{ 1, 2, 3 }; + const big: [9]i8 = small ** 3; + print("{any}\n", .{big}); // [9]i8{ 1, 2, 3, 1, 2, 3, 1, 2, 3 } + } + // #endregion multiply +}; + +const Connect = struct { + // #region connect + const print = @import("std").debug.print; + + pub fn main() void { + const part_one = [_]i32{ 1, 2, 3, 4 }; + const part_two = [_]i32{ 5, 6, 7, 8 }; + const all_of_it = part_one ++ part_two; // [_]i32{ 1, 2, 3, 4, 5, 6, 7, 8 } + + _ = all_of_it; + } + // #endregion connect +}; + +const FuncInitArray = struct { + // #region func_init_array + const print = @import("std").debug.print; + + pub fn main() void { + const array = [_]i32{make(3)} ** 10; + print("{any}\n", .{array}); + } + + fn make(x: i32) i32 { + return x + 1; + } + // #endregion func_init_array +}; + +const ComptimeInitArray = struct { + // #region comptime_init_array + const print = @import("std").debug.print; + + pub fn main() void { + const fancy_array = init: { + var initial_value: [10]usize = undefined; + for (&initial_value, 0..) |*pt, i| { + pt.* = i; + } + break :init initial_value; + }; + print("{any}\n", .{fancy_array}); + } + // #endregion comptime_init_array +}; diff --git a/course/code/15/assembly.zig b/course/code/15/assembly.zig new file mode 100644 index 00000000..d6efe2ef --- /dev/null +++ b/course/code/15/assembly.zig @@ -0,0 +1,69 @@ +pub fn main() !void {} + +const external_assembly = struct { + // #region external_assembly + const std = @import("std"); + + comptime { + asm ( + \\.global my_func; + \\.type my_func, @function; + \\my_func: + \\ lea (%rdi,%rsi,1),%eax + \\ retq + ); + } + + extern fn my_func(a: i32, b: i32) i32; + + pub fn main() void { + std.debug.print("{}\n", .{my_func(2, 5)}); + } + // #endregion external_assembly +}; + +const inline_assembly = struct { + // #region inline_assembly + const std = @import("std"); + + pub fn main() noreturn { + // Temporarily disabled due to Zig 0.15 syntax changes + // const msg = "hello world\n"; + // _ = syscall3(SYS_write, STDOUT_FILENO, @intFromPtr(msg), msg.len); + // _ = syscall1(SYS_exit, 0); + std.process.exit(0); + } + + pub const SYS_write = 1; + pub const SYS_exit = 60; + + pub const STDOUT_FILENO = 1; + + // Temporarily disabled due to Zig 0.15 inline assembly syntax changes + // TODO: Update to new Zig 0.15 inline assembly syntax + + // pub fn syscall1(number: usize, arg1: usize) usize { + // var result: usize = undefined; + // asm volatile ("syscall" + // : [ret] "={rax}" (result) + // : [number] "{rax}" (number), + // [arg1] "{rdi}" (arg1) + // : "rcx", "r11" + // ); + // return result; + // } + + // pub fn syscall3(number: usize, arg1: usize, arg2: usize, arg3: usize) usize { + // var result: usize = undefined; + // asm volatile ("syscall" + // : [ret] "={rax}" (result) + // : [number] "{rax}" (number), + // [arg1] "{rdi}" (arg1), + // [arg2] "{rsi}" (arg2), + // [arg3] "{rdx}" (arg3) + // : "rcx", "r11" + // ); + // return result; + // } + // #endregion inline_assembly +}; diff --git a/course/code/15/atomic.zig b/course/code/15/atomic.zig new file mode 100644 index 00000000..acdc0712 --- /dev/null +++ b/course/code/15/atomic.zig @@ -0,0 +1,50 @@ +pub fn main() !void { + // #region atomic_value + const std = @import("std"); + const RefCount = struct { + count: std.atomic.Value(usize), + dropFn: *const fn (*RefCount) void, + + const RefCount = @This(); + + fn ref(rc: *RefCount) void { + // no synchronization necessary; just updating a counter. + _ = rc.count.fetchAdd(1, .monotonic); + } + + fn unref(rc: *RefCount) void { + // release ensures code before unref() happens-before the + // count is decremented as dropFn could be called by then. + if (rc.count.fetchSub(1, .release) == 1) { + // seeing 1 in the counter means that other unref()s have happened, + // but it doesn't mean that uses before each unref() are visible. + // The load acquires the release-sequence created by previous unref()s + // in order to ensure visibility of uses before dropping. + _ = rc.count.load(.acquire); + (rc.dropFn)(rc); + } + } + + fn noop(rc: *RefCount) void { + _ = rc; + } + }; + + var ref_count: RefCount = .{ + .count = std.atomic.Value(usize).init(0), + .dropFn = RefCount.noop, + }; + ref_count.ref(); + ref_count.unref(); + // #endregion atomic_value + +} + +test "spinLoopHint" { + const std = @import("std"); + // #region spinLoopHint + for (0..10) |_| { + std.atomic.spinLoopHint(); + } + // #endregion spinLoopHint +} diff --git a/course/code/15/build_system/README.md b/course/code/15/build_system/README.md new file mode 100644 index 00000000..4aaf9c5f --- /dev/null +++ b/course/code/15/build_system/README.md @@ -0,0 +1 @@ +该文件夹是构建系统的示例文件! diff --git a/course/code/15/build_system/basic/build.zig b/course/code/15/build_system/basic/build.zig new file mode 100644 index 00000000..69ae8244 --- /dev/null +++ b/course/code/15/build_system/basic/build.zig @@ -0,0 +1,17 @@ +const std = @import("std"); + +pub fn build(b: *std.Build) void { + const target = b.standardTargetOptions(.{}); + const optimize = b.standardOptimizeOption(.{}); + + const exe = b.addExecutable(.{ + .name = "zig", + .root_module = b.createModule(.{ + .root_source_file = b.path("src/main.zig"), + .target = target, + .optimize = optimize, + }), + }); + + b.installArtifact(exe); +} diff --git a/course/code/15/build_system/basic/build.zig.zon b/course/code/15/build_system/basic/build.zig.zon new file mode 100644 index 00000000..8553e004 --- /dev/null +++ b/course/code/15/build_system/basic/build.zig.zon @@ -0,0 +1,73 @@ +.{ + // This is the default name used by packages depending on this one. For + // example, when a user runs `zig fetch --save `, this field is used + // as the key in the `dependencies` table. Although the user can choose a + // different name, most users will stick with this provided value. + // + // It is redundant to include "zig" in this name because it is already + // within the Zig package namespace. + .name = .basic, + + // This is a [Semantic Version](https://semver.org/). + // In a future version of Zig it will be used for package deduplication. + .version = "0.0.0", + .fingerprint = 0x907975534fe79435, + + // This field is optional. + // This is currently advisory only; Zig does not yet do anything + // with this value. + //.minimum_zig_version = "0.11.0", + + // This field is optional. + // Each dependency must either provide a `url` and `hash`, or a `path`. + // `zig build --fetch` can be used to fetch all dependencies of a package, recursively. + // Once all dependencies are fetched, `zig build` no longer requires + // internet connectivity. + .dependencies = .{ + // See `zig fetch --save ` for a command-line interface for adding dependencies. + //.example = .{ + // // When updating this field to a new URL, be sure to delete the corresponding + // // `hash`, otherwise you are communicating that you expect to find the old hash at + // // the new URL. + // .url = "https://example.com/foo.tar.gz", + // + // // This is computed from the file contents of the directory of files that is + // // obtained after fetching `url` and applying the inclusion rules given by + // // `paths`. + // // + // // This field is the source of truth; packages do not come from a `url`; they + // // come from a `hash`. `url` is just one of many possible mirrors for how to + // // obtain a package matching this `hash`. + // // + // // Uses the [multihash](https://multiformats.io/multihash/) format. + // .hash = "...", + // + // // When this is provided, the package is found in a directory relative to the + // // build root. In this case the package's hash is irrelevant and therefore not + // // computed. This field and `url` are mutually exclusive. + // .path = "foo", + + // // When this is set to `true`, a package is declared to be lazily + // // fetched. This makes the dependency only get fetched if it is + // // actually used. + // .lazy = false, + //}, + }, + + // Specifies the set of files and directories that are included in this package. + // Only files and directories listed here are included in the `hash` that + // is computed for this package. Only files listed here will remain on disk + // when using the zig package manager. As a rule of thumb, one should list + // files required for compilation plus any license(s). + // Paths are relative to the build root. Use the empty string (`""`) to refer to + // the build root itself. + // A directory listed here means that all files within, recursively, are included. + .paths = .{ + "build.zig", + "build.zig.zon", + "src", + // For example... + //"LICENSE", + //"README.md", + }, +} diff --git a/course/code/15/build_system/basic/src/main.zig b/course/code/15/build_system/basic/src/main.zig new file mode 100644 index 00000000..96d9a362 --- /dev/null +++ b/course/code/15/build_system/basic/src/main.zig @@ -0,0 +1,24 @@ +const std = @import("std"); + +pub fn main() !void { + // Prints to stderr (it's a shortcut based on `std.io.getStdErr()`) + std.debug.print("All your {s} are belong to us.\n", .{"codebase"}); + + // stdout is for the actual output of your application, for example if you + // are implementing gzip, then only the compressed bytes should be sent to + // stdout, not any debugging messages. + var stdout_buffer: [1024]u8 = undefined; + var stdout_writer = std.fs.File.stdout().writer(&stdout_buffer); + const stdout = &stdout_writer.interface; + + try stdout.print("Run `zig build test` to run the tests.\n", .{}); + + try stdout.flush(); +} + +test "simple test" { + var list = std.ArrayList(i32).init(std.testing.allocator); + defer list.deinit(); // try commenting this out and see if zig detects the memory leak! + try list.append(42); + try std.testing.expectEqual(@as(i32, 42), list.pop()); +} diff --git a/course/code/15/build_system/build.zig b/course/code/15/build_system/build.zig new file mode 100644 index 00000000..5909232b --- /dev/null +++ b/course/code/15/build_system/build.zig @@ -0,0 +1,95 @@ +const std = @import("std"); +const ChildProcess = std.process.Child; + +const args = [_][]const u8{ "zig", "build" }; + +pub fn build(b: *std.Build) !void { + const optimize = b.standardOptimizeOption(.{}); + // #region crossTarget + // 构建一个target + const target_query = std.Target.Query{ + .cpu_arch = .x86_64, + .os_tag = .windows, + .abi = .gnu, + }; + + const ResolvedTarget = std.Build.ResolvedTarget; + + // 解析的target + const resolved_target: ResolvedTarget = b.resolveTargetQuery(target_query); + + // 解析结果 + const target: std.Target = resolved_target.result; + _ = target; + + // 构建 exe + const exe = b.addExecutable(.{ + .name = "zig", + .root_module = b.addModule("zig", .{ + .root_source_file = b.path("main.zig"), + // 实际使用的是resolved_target + .target = resolved_target, + .optimize = optimize, + }), + }); + // #endregion crossTarget + + b.installArtifact(exe); + + const full_path = try std.process.getCwdAlloc(b.allocator); + + var dir = std.fs.openDirAbsolute(full_path, .{ .iterate = true }) catch |err| { + std.log.err("open path failed {s}, err is {}", .{ full_path, err }); + std.process.exit(1); + }; + defer dir.close(); + + var iterate = dir.iterate(); + + while (iterate.next()) |val| { + if (val) |entry| { + // get the entry name, entry can be file or directory + const name = entry.name; + if (entry.kind == .directory) { + if (eqlu8(name, ".zig-cache") or eqlu8(name, "zig-out") or eqlu8(name, "zig-cache")) + continue; + + // build child process + var child = ChildProcess.init(&args, b.allocator); + + // build cwd + const cwd = std.fs.path.join(b.allocator, &[_][]const u8{ + full_path, + name, + }) catch |err| { + std.log.err("fmt path failed, err is {}", .{err}); + std.process.exit(1); + }; + + // open entry dir + const entry_dir = std.fs.openDirAbsolute(cwd, .{}) catch unreachable; + entry_dir.access("build.zig", .{}) catch { + std.log.err("not found build.zig in path {s}", .{cwd}); + std.process.exit(1); + }; + + // set child cwd + // this api maybe changed in the future + child.cwd = cwd; + + // spawn and wait child process + _ = child.spawnAndWait() catch unreachable; + } + } else { + // Stop endless loop + break; + } + } else |err| { + std.log.err("iterate examples_path failed, err is {}", .{err}); + std.process.exit(1); + } +} + +fn eqlu8(a: []const u8, b: []const u8) bool { + return std.mem.eql(u8, a, b); +} diff --git a/course/code/15/build_system/cli/build.zig b/course/code/15/build_system/cli/build.zig new file mode 100644 index 00000000..a1036af5 --- /dev/null +++ b/course/code/15/build_system/cli/build.zig @@ -0,0 +1,21 @@ +const std = @import("std"); + +pub fn build(b: *std.Build) void { + const target = b.standardTargetOptions(.{}); + const optimize = b.standardOptimizeOption(.{}); + const is_strip = + b.option(bool, "is_strip", "whether strip executable") orelse + false; + + const exe = b.addExecutable(.{ + .name = "zig", + .root_module = b.createModule(.{ + .root_source_file = b.path("src/main.zig"), + .target = target, + .optimize = optimize, + .strip = is_strip, + }), + }); + + b.installArtifact(exe); +} diff --git a/course/code/15/build_system/cli/build.zig.zon b/course/code/15/build_system/cli/build.zig.zon new file mode 100644 index 00000000..09eef71d --- /dev/null +++ b/course/code/15/build_system/cli/build.zig.zon @@ -0,0 +1,73 @@ +.{ + // This is the default name used by packages depending on this one. For + // example, when a user runs `zig fetch --save `, this field is used + // as the key in the `dependencies` table. Although the user can choose a + // different name, most users will stick with this provided value. + // + // It is redundant to include "zig" in this name because it is already + // within the Zig package namespace. + .name = .cli, + + // This is a [Semantic Version](https://semver.org/). + // In a future version of Zig it will be used for package deduplication. + .version = "0.0.0", + .fingerprint = 0x48f6513c15de5e44, + + // This field is optional. + // This is currently advisory only; Zig does not yet do anything + // with this value. + //.minimum_zig_version = "0.11.0", + + // This field is optional. + // Each dependency must either provide a `url` and `hash`, or a `path`. + // `zig build --fetch` can be used to fetch all dependencies of a package, recursively. + // Once all dependencies are fetched, `zig build` no longer requires + // internet connectivity. + .dependencies = .{ + // See `zig fetch --save ` for a command-line interface for adding dependencies. + //.example = .{ + // // When updating this field to a new URL, be sure to delete the corresponding + // // `hash`, otherwise you are communicating that you expect to find the old hash at + // // the new URL. + // .url = "https://example.com/foo.tar.gz", + // + // // This is computed from the file contents of the directory of files that is + // // obtained after fetching `url` and applying the inclusion rules given by + // // `paths`. + // // + // // This field is the source of truth; packages do not come from a `url`; they + // // come from a `hash`. `url` is just one of many possible mirrors for how to + // // obtain a package matching this `hash`. + // // + // // Uses the [multihash](https://multiformats.io/multihash/) format. + // .hash = "...", + // + // // When this is provided, the package is found in a directory relative to the + // // build root. In this case the package's hash is irrelevant and therefore not + // // computed. This field and `url` are mutually exclusive. + // .path = "foo", + + // // When this is set to `true`, a package is declared to be lazily + // // fetched. This makes the dependency only get fetched if it is + // // actually used. + // .lazy = false, + //}, + }, + + // Specifies the set of files and directories that are included in this package. + // Only files and directories listed here are included in the `hash` that + // is computed for this package. Only files listed here will remain on disk + // when using the zig package manager. As a rule of thumb, one should list + // files required for compilation plus any license(s). + // Paths are relative to the build root. Use the empty string (`""`) to refer to + // the build root itself. + // A directory listed here means that all files within, recursively, are included. + .paths = .{ + "build.zig", + "build.zig.zon", + "src", + // For example... + //"LICENSE", + //"README.md", + }, +} diff --git a/course/code/15/build_system/cli/src/main.zig b/course/code/15/build_system/cli/src/main.zig new file mode 100644 index 00000000..2e81a719 --- /dev/null +++ b/course/code/15/build_system/cli/src/main.zig @@ -0,0 +1,24 @@ +const std = @import("std"); + +pub fn main() !void { + // Prints to stderr (it's a shortcut based on `std.io.getStdErr()`) + std.debug.print("All your {s} are belong to us.\n", .{"codebase"}); + + // stdout is for the actual output of your application, for example if you + // are implementing gzip, then only the compressed bytes should be sent to + // stdout, not any debugging messages. + var stdout_buffer: [1024]u8 = undefined; + var stdout_writer = std.fs.File.stdout().writer(&stdout_buffer); + const stdout = &stdout_writer.interface; + + try stdout.print("Run `zig build test` to run the tests.\n", .{}); + + try stdout.flush(); // don't forget to flush! +} + +test "simple test" { + var list = std.ArrayList(i32).init(std.testing.allocator); + defer list.deinit(); // try commenting this out and see if zig detects the memory leak! + try list.append(42); + try std.testing.expectEqual(@as(i32, 42), list.pop()); +} diff --git a/course/code/15/build_system/docs/build.zig b/course/code/15/build_system/docs/build.zig new file mode 100644 index 00000000..3ab94831 --- /dev/null +++ b/course/code/15/build_system/docs/build.zig @@ -0,0 +1,32 @@ +const std = @import("std"); + +pub fn build(b: *std.Build) void { + // 标准构建目标 + const target = b.standardTargetOptions(.{}); + // 标准构建模式 + const optimize = b.standardOptimizeOption(.{}); + + // 构建一个 object,用于生成文档 + const object = b.addObject(.{ + .name = "object", + .root_module = b.addModule("object", .{ + .root_source_file = b.path("src/root.zig"), + .target = target, + .optimize = optimize, + }), + }); + // 创建一个 step + const docs_step = b.step("docs", "Generate docs"); + + // 生成文档 + const docs_install = b.addInstallDirectory(.{ + // 指定文档来源 + .source_dir = object.getEmittedDocs(), + // 指定安装目录 + .install_dir = .prefix, + // 指定文档子文件夹 + .install_subdir = "docs", + }); + + docs_step.dependOn(&docs_install.step); +} diff --git a/course/code/15/build_system/docs/build.zig.zon b/course/code/15/build_system/docs/build.zig.zon new file mode 100644 index 00000000..a40db6b3 --- /dev/null +++ b/course/code/15/build_system/docs/build.zig.zon @@ -0,0 +1,73 @@ +.{ + // This is the default name used by packages depending on this one. For + // example, when a user runs `zig fetch --save `, this field is used + // as the key in the `dependencies` table. Although the user can choose a + // different name, most users will stick with this provided value. + // + // It is redundant to include "zig" in this name because it is already + // within the Zig package namespace. + .name = .docs, + + // This is a [Semantic Version](https://semver.org/). + // In a future version of Zig it will be used for package deduplication. + .version = "0.0.0", + .fingerprint = 0x51572bb73db54779, + + // This field is optional. + // This is currently advisory only; Zig does not yet do anything + // with this value. + //.minimum_zig_version = "0.11.0", + + // This field is optional. + // Each dependency must either provide a `url` and `hash`, or a `path`. + // `zig build --fetch` can be used to fetch all dependencies of a package, recursively. + // Once all dependencies are fetched, `zig build` no longer requires + // internet connectivity. + .dependencies = .{ + // See `zig fetch --save ` for a command-line interface for adding dependencies. + //.example = .{ + // // When updating this field to a new URL, be sure to delete the corresponding + // // `hash`, otherwise you are communicating that you expect to find the old hash at + // // the new URL. + // .url = "https://example.com/foo.tar.gz", + // + // // This is computed from the file contents of the directory of files that is + // // obtained after fetching `url` and applying the inclusion rules given by + // // `paths`. + // // + // // This field is the source of truth; packages do not come from a `url`; they + // // come from a `hash`. `url` is just one of many possible mirrors for how to + // // obtain a package matching this `hash`. + // // + // // Uses the [multihash](https://multiformats.io/multihash/) format. + // .hash = "...", + // + // // When this is provided, the package is found in a directory relative to the + // // build root. In this case the package's hash is irrelevant and therefore not + // // computed. This field and `url` are mutually exclusive. + // .path = "foo", + + // // When this is set to `true`, a package is declared to be lazily + // // fetched. This makes the dependency only get fetched if it is + // // actually used. + // .lazy = false, + //}, + }, + + // Specifies the set of files and directories that are included in this package. + // Only files and directories listed here are included in the `hash` that + // is computed for this package. Only files listed here will remain on disk + // when using the zig package manager. As a rule of thumb, one should list + // files required for compilation plus any license(s). + // Paths are relative to the build root. Use the empty string (`""`) to refer to + // the build root itself. + // A directory listed here means that all files within, recursively, are included. + .paths = .{ + "build.zig", + "build.zig.zon", + "src", + // For example... + //"LICENSE", + //"README.md", + }, +} diff --git a/course/code/15/build_system/docs/src/root.zig b/course/code/15/build_system/docs/src/root.zig new file mode 100644 index 00000000..ecfeade1 --- /dev/null +++ b/course/code/15/build_system/docs/src/root.zig @@ -0,0 +1,10 @@ +const std = @import("std"); +const testing = std.testing; + +export fn add(a: i32, b: i32) i32 { + return a + b; +} + +test "basic add functionality" { + try testing.expect(add(3, 7) == 10); +} diff --git a/course/code/15/build_system/embedfile/build.zig b/course/code/15/build_system/embedfile/build.zig new file mode 100644 index 00000000..44ba9a52 --- /dev/null +++ b/course/code/15/build_system/embedfile/build.zig @@ -0,0 +1,44 @@ +const std = @import("std"); + +pub fn build(b: *std.Build) void { + // 标准构建目标 + const target = b.standardTargetOptions(.{}); + + // 标准构建模式 + const optimize = b.standardOptimizeOption(.{}); + + // 添加一个二进制可执行程序构建 + const exe = b.addExecutable(.{ + .name = "zig", + .root_module = b.addModule("zig", .{ + .root_source_file = b.path("src/main.zig"), + .target = target, + .optimize = optimize, + }), + }); + + exe.root_module.addAnonymousImport( + "hello", + .{ .root_source_file = b.path("src/hello.txt") }, + ); + + // 添加到顶级 install step 中作为依赖 + b.installArtifact(exe); + + // zig 提供了一个方便的函数允许我们直接运行构建结果 + const run_cmd = b.addRunArtifact(exe); + + // 指定依赖 + run_cmd.step.dependOn(b.getInstallStep()); + + // 传递参数 + if (b.args) |args| { + run_cmd.addArgs(args); + } + + // 指定一个 step 为 run + const run_step = b.step("run", "Run the app"); + + // 指定该 step 依赖于 run_exe,即实际的运行 + run_step.dependOn(&run_cmd.step); +} diff --git a/course/code/15/build_system/embedfile/build.zig.zon b/course/code/15/build_system/embedfile/build.zig.zon new file mode 100644 index 00000000..9fab9793 --- /dev/null +++ b/course/code/15/build_system/embedfile/build.zig.zon @@ -0,0 +1,73 @@ +.{ + // This is the default name used by packages depending on this one. For + // example, when a user runs `zig fetch --save `, this field is used + // as the key in the `dependencies` table. Although the user can choose a + // different name, most users will stick with this provided value. + // + // It is redundant to include "zig" in this name because it is already + // within the Zig package namespace. + .name = .embedfile, + + // This is a [Semantic Version](https://semver.org/). + // In a future version of Zig it will be used for package deduplication. + .version = "0.0.0", + .fingerprint = 0x3e6a9e8b250bb664, + + // This field is optional. + // This is currently advisory only; Zig does not yet do anything + // with this value. + //.minimum_zig_version = "0.11.0", + + // This field is optional. + // Each dependency must either provide a `url` and `hash`, or a `path`. + // `zig build --fetch` can be used to fetch all dependencies of a package, recursively. + // Once all dependencies are fetched, `zig build` no longer requires + // internet connectivity. + .dependencies = .{ + // See `zig fetch --save ` for a command-line interface for adding dependencies. + //.example = .{ + // // When updating this field to a new URL, be sure to delete the corresponding + // // `hash`, otherwise you are communicating that you expect to find the old hash at + // // the new URL. + // .url = "https://example.com/foo.tar.gz", + // + // // This is computed from the file contents of the directory of files that is + // // obtained after fetching `url` and applying the inclusion rules given by + // // `paths`. + // // + // // This field is the source of truth; packages do not come from a `url`; they + // // come from a `hash`. `url` is just one of many possible mirrors for how to + // // obtain a package matching this `hash`. + // // + // // Uses the [multihash](https://multiformats.io/multihash/) format. + // .hash = "...", + // + // // When this is provided, the package is found in a directory relative to the + // // build root. In this case the package's hash is irrelevant and therefore not + // // computed. This field and `url` are mutually exclusive. + // .path = "foo", + + // // When this is set to `true`, a package is declared to be lazily + // // fetched. This makes the dependency only get fetched if it is + // // actually used. + // .lazy = false, + //}, + }, + + // Specifies the set of files and directories that are included in this package. + // Only files and directories listed here are included in the `hash` that + // is computed for this package. Only files listed here will remain on disk + // when using the zig package manager. As a rule of thumb, one should list + // files required for compilation plus any license(s). + // Paths are relative to the build root. Use the empty string (`""`) to refer to + // the build root itself. + // A directory listed here means that all files within, recursively, are included. + .paths = .{ + "build.zig", + "build.zig.zon", + "src", + // For example... + //"LICENSE", + //"README.md", + }, +} diff --git a/course/code/15/build_system/embedfile/src/hello.txt b/course/code/15/build_system/embedfile/src/hello.txt new file mode 100644 index 00000000..b45ef6fe --- /dev/null +++ b/course/code/15/build_system/embedfile/src/hello.txt @@ -0,0 +1 @@ +Hello, World! \ No newline at end of file diff --git a/course/code/15/build_system/embedfile/src/main.zig b/course/code/15/build_system/embedfile/src/main.zig new file mode 100644 index 00000000..467b20ed --- /dev/null +++ b/course/code/15/build_system/embedfile/src/main.zig @@ -0,0 +1,7 @@ +const std = @import("std"); +const hello = @embedFile("hello"); +// const hello = @embedFile("hello.txt"); 均可以 + +pub fn main() !void { + std.debug.print("{s}\n", .{hello}); +} diff --git a/course/code/15/build_system/externalfile/build.zig b/course/code/15/build_system/externalfile/build.zig new file mode 100644 index 00000000..dc370645 --- /dev/null +++ b/course/code/15/build_system/externalfile/build.zig @@ -0,0 +1,63 @@ +const std = @import("std"); + +pub fn build(b: *std.Build) !void { + // 标准构建目标 + const target = b.standardTargetOptions(.{}); + + // 标准构建模式 + const optimize = b.standardOptimizeOption(.{}); + + // 在 windows 平台无法使用 bash,故我们直接返回 + if (target.result.os.tag == .windows) { + return; + } + + // 添加一个二进制可执行程序构建 + const exe = b.addExecutable(.{ + .name = "zig", + .root_module = b.addModule("zig", .{ + .root_source_file = b.path("src/main.zig"), + .target = target, + .optimize = optimize, + }), + }); + + // 构建一个运行命令 + const run_sys_cmd = b.addSystemCommand(&.{ + "/bin/sh", + "-c", + }); + + // 添加参数,此方法允许添加多个参数 + // 也可以使用 addArg 来添加单个参数 + run_sys_cmd.addArgs(&.{ + "echo hello", + }); + + // 尝试运行命令并捕获标准输出 + // 也可以使用 captureStdErr 来捕获标准错误输出 + const output = run_sys_cmd.captureStdOut(); + + // 添加一个匿名的依赖 + exe.root_module.addAnonymousImport("hello", .{ .root_source_file = output }); + + // 添加到顶级 install step 中作为依赖 + b.installArtifact(exe); + + // zig 提供了一个方便的函数允许我们直接运行构建结果 + const run_cmd = b.addRunArtifact(exe); + + // 指定依赖 + run_cmd.step.dependOn(b.getInstallStep()); + + // 传递参数 + if (b.args) |args| { + run_cmd.addArgs(args); + } + + // 指定一个 step 为 run + const run_step = b.step("run", "Run the app"); + + // 指定该 step 依赖于 run_exe,即实际的运行 + run_step.dependOn(&run_cmd.step); +} diff --git a/course/code/15/build_system/externalfile/build.zig.zon b/course/code/15/build_system/externalfile/build.zig.zon new file mode 100644 index 00000000..bded66c1 --- /dev/null +++ b/course/code/15/build_system/externalfile/build.zig.zon @@ -0,0 +1,73 @@ +.{ + // This is the default name used by packages depending on this one. For + // example, when a user runs `zig fetch --save `, this field is used + // as the key in the `dependencies` table. Although the user can choose a + // different name, most users will stick with this provided value. + // + // It is redundant to include "zig" in this name because it is already + // within the Zig package namespace. + .name = .externalfile, + + // This is a [Semantic Version](https://semver.org/). + // In a future version of Zig it will be used for package deduplication. + .version = "0.0.0", + .fingerprint = 0x61de81227ca0d23c, + + // This field is optional. + // This is currently advisory only; Zig does not yet do anything + // with this value. + //.minimum_zig_version = "0.11.0", + + // This field is optional. + // Each dependency must either provide a `url` and `hash`, or a `path`. + // `zig build --fetch` can be used to fetch all dependencies of a package, recursively. + // Once all dependencies are fetched, `zig build` no longer requires + // internet connectivity. + .dependencies = .{ + // See `zig fetch --save ` for a command-line interface for adding dependencies. + //.example = .{ + // // When updating this field to a new URL, be sure to delete the corresponding + // // `hash`, otherwise you are communicating that you expect to find the old hash at + // // the new URL. + // .url = "https://example.com/foo.tar.gz", + // + // // This is computed from the file contents of the directory of files that is + // // obtained after fetching `url` and applying the inclusion rules given by + // // `paths`. + // // + // // This field is the source of truth; packages do not come from a `url`; they + // // come from a `hash`. `url` is just one of many possible mirrors for how to + // // obtain a package matching this `hash`. + // // + // // Uses the [multihash](https://multiformats.io/multihash/) format. + // .hash = "...", + // + // // When this is provided, the package is found in a directory relative to the + // // build root. In this case the package's hash is irrelevant and therefore not + // // computed. This field and `url` are mutually exclusive. + // .path = "foo", + + // // When this is set to `true`, a package is declared to be lazily + // // fetched. This makes the dependency only get fetched if it is + // // actually used. + // .lazy = false, + //}, + }, + + // Specifies the set of files and directories that are included in this package. + // Only files and directories listed here are included in the `hash` that + // is computed for this package. Only files listed here will remain on disk + // when using the zig package manager. As a rule of thumb, one should list + // files required for compilation plus any license(s). + // Paths are relative to the build root. Use the empty string (`""`) to refer to + // the build root itself. + // A directory listed here means that all files within, recursively, are included. + .paths = .{ + "build.zig", + "build.zig.zon", + "src", + // For example... + //"LICENSE", + //"README.md", + }, +} diff --git a/course/code/15/build_system/externalfile/src/main.zig b/course/code/15/build_system/externalfile/src/main.zig new file mode 100644 index 00000000..4cab998e --- /dev/null +++ b/course/code/15/build_system/externalfile/src/main.zig @@ -0,0 +1,6 @@ +const std = @import("std"); +const hello = @embedFile("hello"); + +pub fn main() !void { + std.debug.print("{s}", .{hello}); +} diff --git a/course/code/15/build_system/lib/build.zig b/course/code/15/build_system/lib/build.zig new file mode 100644 index 00000000..e5edc582 --- /dev/null +++ b/course/code/15/build_system/lib/build.zig @@ -0,0 +1,31 @@ +const std = @import("std"); + +pub fn build(b: *std.Build) void { + // 使用默认提供的构建目标,支持我们从命令行构建时指定构建目标(架构、系统、abi等等) + const target = b.standardTargetOptions(.{}); + + // 使用默认提供的优化方案,支持我们从命令行构建时指定构建模式 + const optimize = b.standardOptimizeOption(.{}); + + // 尝试添加一个静态库 + const lib = b.addLibrary(.{ .name = "example", .root_module = b.createModule(.{ + .root_source_file = b.path("src/root.zig"), + .target = target, + .optimize = optimize, + }) }); + + b.installArtifact(lib); + + const exe = b.addExecutable(.{ + .name = "zig", + .root_module = b.createModule(.{ + .root_source_file = b.path("src/main.zig"), + .target = target, + .optimize = optimize, + }), + }); + + exe.linkLibrary(lib); + + b.installArtifact(exe); +} diff --git a/course/code/15/build_system/lib/build.zig.zon b/course/code/15/build_system/lib/build.zig.zon new file mode 100644 index 00000000..6ca3a749 --- /dev/null +++ b/course/code/15/build_system/lib/build.zig.zon @@ -0,0 +1,73 @@ +.{ + // This is the default name used by packages depending on this one. For + // example, when a user runs `zig fetch --save `, this field is used + // as the key in the `dependencies` table. Although the user can choose a + // different name, most users will stick with this provided value. + // + // It is redundant to include "zig" in this name because it is already + // within the Zig package namespace. + .name = .library, + + // This is a [Semantic Version](https://semver.org/). + // In a future version of Zig it will be used for package deduplication. + .version = "0.0.0", + .fingerprint = 0xa18098bc77aad8e8, + + // This field is optional. + // This is currently advisory only; Zig does not yet do anything + // with this value. + //.minimum_zig_version = "0.11.0", + + // This field is optional. + // Each dependency must either provide a `url` and `hash`, or a `path`. + // `zig build --fetch` can be used to fetch all dependencies of a package, recursively. + // Once all dependencies are fetched, `zig build` no longer requires + // internet connectivity. + .dependencies = .{ + // See `zig fetch --save ` for a command-line interface for adding dependencies. + //.example = .{ + // // When updating this field to a new URL, be sure to delete the corresponding + // // `hash`, otherwise you are communicating that you expect to find the old hash at + // // the new URL. + // .url = "https://example.com/foo.tar.gz", + // + // // This is computed from the file contents of the directory of files that is + // // obtained after fetching `url` and applying the inclusion rules given by + // // `paths`. + // // + // // This field is the source of truth; packages do not come from a `url`; they + // // come from a `hash`. `url` is just one of many possible mirrors for how to + // // obtain a package matching this `hash`. + // // + // // Uses the [multihash](https://multiformats.io/multihash/) format. + // .hash = "...", + // + // // When this is provided, the package is found in a directory relative to the + // // build root. In this case the package's hash is irrelevant and therefore not + // // computed. This field and `url` are mutually exclusive. + // .path = "foo", + + // // When this is set to `true`, a package is declared to be lazily + // // fetched. This makes the dependency only get fetched if it is + // // actually used. + // .lazy = false, + //}, + }, + + // Specifies the set of files and directories that are included in this package. + // Only files and directories listed here are included in the `hash` that + // is computed for this package. Only files listed here will remain on disk + // when using the zig package manager. As a rule of thumb, one should list + // files required for compilation plus any license(s). + // Paths are relative to the build root. Use the empty string (`""`) to refer to + // the build root itself. + // A directory listed here means that all files within, recursively, are included. + .paths = .{ + "build.zig", + "build.zig.zon", + "src", + // For example... + //"LICENSE", + //"README.md", + }, +} diff --git a/course/code/15/build_system/lib/src/main.zig b/course/code/15/build_system/lib/src/main.zig new file mode 100644 index 00000000..734464bc --- /dev/null +++ b/course/code/15/build_system/lib/src/main.zig @@ -0,0 +1,17 @@ +const std = @import("std"); + +pub fn main() !void { + // Prints to stderr (it's a shortcut based on `std.io.getStdErr()`) + std.debug.print("All your {s} are belong to us.\n", .{"codebase"}); + + // stdout is for the actual output of your application, for example if you + // are implementing gzip, then only the compressed bytes should be sent to + // stdout, not any debugging messages. + var stdout_buffer: [1024]u8 = undefined; + var stdout_writer = std.fs.File.stdout().writer(&stdout_buffer); + const stdout = &stdout_writer.interface; + + try stdout.print("Run `zig build test` to run the tests.\n", .{}); + + try stdout.flush(); +} diff --git a/course/code/15/build_system/lib/src/root.zig b/course/code/15/build_system/lib/src/root.zig new file mode 100644 index 00000000..ecfeade1 --- /dev/null +++ b/course/code/15/build_system/lib/src/root.zig @@ -0,0 +1,10 @@ +const std = @import("std"); +const testing = std.testing; + +export fn add(a: i32, b: i32) i32 { + return a + b; +} + +test "basic add functionality" { + try testing.expect(add(3, 7) == 10); +} diff --git a/course/code/15/build_system/main.zig b/course/code/15/build_system/main.zig new file mode 100644 index 00000000..b16fb0fd --- /dev/null +++ b/course/code/15/build_system/main.zig @@ -0,0 +1,5 @@ +const std = @import("std"); + +pub fn main() !void { + std.debug.print("Hello, World!", .{}); +} diff --git a/course/code/15/build_system/options/build.zig b/course/code/15/build_system/options/build.zig new file mode 100644 index 00000000..23d90abc --- /dev/null +++ b/course/code/15/build_system/options/build.zig @@ -0,0 +1,34 @@ +const std = @import("std"); + +pub fn build(b: *std.Build) void { + // 标准构建目标 + const target = b.standardTargetOptions(.{}); + + // 标准构建模式 + const optimize = b.standardOptimizeOption(.{}); + + // 添加一个二进制可执行程序构建 + const exe = b.addExecutable(.{ + .name = "zig", + .root_module = b.createModule(.{ + .root_source_file = b.path("src/main.zig"), + .target = target, + .optimize = optimize, + }), + }); + + // 通过标准库获取时间戳 + const timestamp = std.time.timestamp(); + + // 创建一个 options + const options = b.addOptions(); + + // 向 options 添加 option, 变量名是time_stamp + options.addOption(i64, "time_stamp", timestamp); + + // 向 exe 中添加 options + exe.root_module.addOptions("timestamp", options); + + // 添加到顶级 install step 中作为依赖 + b.installArtifact(exe); +} diff --git a/course/code/15/build_system/options/build.zig.zon b/course/code/15/build_system/options/build.zig.zon new file mode 100644 index 00000000..b3a7f357 --- /dev/null +++ b/course/code/15/build_system/options/build.zig.zon @@ -0,0 +1,73 @@ +.{ + // This is the default name used by packages depending on this one. For + // example, when a user runs `zig fetch --save `, this field is used + // as the key in the `dependencies` table. Although the user can choose a + // different name, most users will stick with this provided value. + // + // It is redundant to include "zig" in this name because it is already + // within the Zig package namespace. + .name = .options, + + // This is a [Semantic Version](https://semver.org/). + // In a future version of Zig it will be used for package deduplication. + .version = "0.0.0", + .fingerprint = 0xd035fa8769f41b1a, + + // This field is optional. + // This is currently advisory only; Zig does not yet do anything + // with this value. + //.minimum_zig_version = "0.11.0", + + // This field is optional. + // Each dependency must either provide a `url` and `hash`, or a `path`. + // `zig build --fetch` can be used to fetch all dependencies of a package, recursively. + // Once all dependencies are fetched, `zig build` no longer requires + // internet connectivity. + .dependencies = .{ + // See `zig fetch --save ` for a command-line interface for adding dependencies. + //.example = .{ + // // When updating this field to a new URL, be sure to delete the corresponding + // // `hash`, otherwise you are communicating that you expect to find the old hash at + // // the new URL. + // .url = "https://example.com/foo.tar.gz", + // + // // This is computed from the file contents of the directory of files that is + // // obtained after fetching `url` and applying the inclusion rules given by + // // `paths`. + // // + // // This field is the source of truth; packages do not come from a `url`; they + // // come from a `hash`. `url` is just one of many possible mirrors for how to + // // obtain a package matching this `hash`. + // // + // // Uses the [multihash](https://multiformats.io/multihash/) format. + // .hash = "...", + // + // // When this is provided, the package is found in a directory relative to the + // // build root. In this case the package's hash is irrelevant and therefore not + // // computed. This field and `url` are mutually exclusive. + // .path = "foo", + + // // When this is set to `true`, a package is declared to be lazily + // // fetched. This makes the dependency only get fetched if it is + // // actually used. + // .lazy = false, + //}, + }, + + // Specifies the set of files and directories that are included in this package. + // Only files and directories listed here are included in the `hash` that + // is computed for this package. Only files listed here will remain on disk + // when using the zig package manager. As a rule of thumb, one should list + // files required for compilation plus any license(s). + // Paths are relative to the build root. Use the empty string (`""`) to refer to + // the build root itself. + // A directory listed here means that all files within, recursively, are included. + .paths = .{ + "build.zig", + "build.zig.zon", + "src", + // For example... + //"LICENSE", + //"README.md", + }, +} diff --git a/course/code/15/build_system/options/src/main.zig b/course/code/15/build_system/options/src/main.zig new file mode 100644 index 00000000..635449e3 --- /dev/null +++ b/course/code/15/build_system/options/src/main.zig @@ -0,0 +1,7 @@ +const std = @import("std"); +// timestamp 这个包是通过 build.zig 添加的 +const timestamp = @import("timestamp"); + +pub fn main() !void { + std.debug.print("build time stamp is {}\n", .{timestamp.time_stamp}); +} diff --git a/course/code/15/build_system/step/build.zig b/course/code/15/build_system/step/build.zig new file mode 100644 index 00000000..6e40f3b4 --- /dev/null +++ b/course/code/15/build_system/step/build.zig @@ -0,0 +1,44 @@ +const std = @import("std"); + +pub fn build(b: *std.Build) void { + // 标准构建目标 + const target = b.standardTargetOptions(.{}); + + // 标准构建模式 + const optimize = b.standardOptimizeOption(.{}); + + // 添加一个二进制可执行程序构建 + const exe = b.addExecutable(.{ + .name = "hello", + .root_module = b.addModule("hello", .{ + .root_source_file = b.path("src/main.zig"), + .target = target, + .optimize = optimize, + }), + }); + + // 添加到顶级 install step 中作为依赖 + b.installArtifact(exe); + + // zig 提供了一个方便的函数允许我们直接运行构建结果 + const run_exe = b.addRunArtifact(exe); + + // 注意:该步骤可选,显式声明运行依赖于构建 + // 这会使运行是从构建输出目录(默认为 zig-out/bin )运行而不是构建缓存中运行 + // 不过,如果应用程序运行依赖于其他已存在的文件(例如某些 ini 配置文件) + // 这可以确保它们正确的运行 + run_exe.step.dependOn(b.getInstallStep()); + + // 注意:此步骤可选 + // 此操作允许用户通过构建系统的命令传递参数,例如 zig build -- arg1 arg2 + // 当前是将参数传递给运行构建结果 + if (b.args) |args| { + run_exe.addArgs(args); + } + + // 指定一个 step 为 run + const run_step = b.step("run", "Run the application"); + + // 指定该 step 依赖于 run_exe,即实际的运行 + run_step.dependOn(&run_exe.step); +} diff --git a/course/code/15/build_system/step/build.zig.zon b/course/code/15/build_system/step/build.zig.zon new file mode 100644 index 00000000..a2461217 --- /dev/null +++ b/course/code/15/build_system/step/build.zig.zon @@ -0,0 +1,73 @@ +.{ + // This is the default name used by packages depending on this one. For + // example, when a user runs `zig fetch --save `, this field is used + // as the key in the `dependencies` table. Although the user can choose a + // different name, most users will stick with this provided value. + // + // It is redundant to include "zig" in this name because it is already + // within the Zig package namespace. + .name = .step, + + // This is a [Semantic Version](https://semver.org/). + // In a future version of Zig it will be used for package deduplication. + .version = "0.0.0", + .fingerprint = 0x43b9fe3c8067cab6, + + // This field is optional. + // This is currently advisory only; Zig does not yet do anything + // with this value. + //.minimum_zig_version = "0.11.0", + + // This field is optional. + // Each dependency must either provide a `url` and `hash`, or a `path`. + // `zig build --fetch` can be used to fetch all dependencies of a package, recursively. + // Once all dependencies are fetched, `zig build` no longer requires + // internet connectivity. + .dependencies = .{ + // See `zig fetch --save ` for a command-line interface for adding dependencies. + //.example = .{ + // // When updating this field to a new URL, be sure to delete the corresponding + // // `hash`, otherwise you are communicating that you expect to find the old hash at + // // the new URL. + // .url = "https://example.com/foo.tar.gz", + // + // // This is computed from the file contents of the directory of files that is + // // obtained after fetching `url` and applying the inclusion rules given by + // // `paths`. + // // + // // This field is the source of truth; packages do not come from a `url`; they + // // come from a `hash`. `url` is just one of many possible mirrors for how to + // // obtain a package matching this `hash`. + // // + // // Uses the [multihash](https://multiformats.io/multihash/) format. + // .hash = "...", + // + // // When this is provided, the package is found in a directory relative to the + // // build root. In this case the package's hash is irrelevant and therefore not + // // computed. This field and `url` are mutually exclusive. + // .path = "foo", + + // // When this is set to `true`, a package is declared to be lazily + // // fetched. This makes the dependency only get fetched if it is + // // actually used. + // .lazy = false, + //}, + }, + + // Specifies the set of files and directories that are included in this package. + // Only files and directories listed here are included in the `hash` that + // is computed for this package. Only files listed here will remain on disk + // when using the zig package manager. As a rule of thumb, one should list + // files required for compilation plus any license(s). + // Paths are relative to the build root. Use the empty string (`""`) to refer to + // the build root itself. + // A directory listed here means that all files within, recursively, are included. + .paths = .{ + "build.zig", + "build.zig.zon", + "src", + // For example... + //"LICENSE", + //"README.md", + }, +} diff --git a/course/code/15/build_system/step/src/main.zig b/course/code/15/build_system/step/src/main.zig new file mode 100644 index 00000000..734464bc --- /dev/null +++ b/course/code/15/build_system/step/src/main.zig @@ -0,0 +1,17 @@ +const std = @import("std"); + +pub fn main() !void { + // Prints to stderr (it's a shortcut based on `std.io.getStdErr()`) + std.debug.print("All your {s} are belong to us.\n", .{"codebase"}); + + // stdout is for the actual output of your application, for example if you + // are implementing gzip, then only the compressed bytes should be sent to + // stdout, not any debugging messages. + var stdout_buffer: [1024]u8 = undefined; + var stdout_writer = std.fs.File.stdout().writer(&stdout_buffer); + const stdout = &stdout_writer.interface; + + try stdout.print("Run `zig build test` to run the tests.\n", .{}); + + try stdout.flush(); +} diff --git a/course/code/15/build_system/system_lib/build.zig b/course/code/15/build_system/system_lib/build.zig new file mode 100644 index 00000000..bf986458 --- /dev/null +++ b/course/code/15/build_system/system_lib/build.zig @@ -0,0 +1,32 @@ +const std = @import("std"); + +pub fn build(b: *std.Build) void { + // 使用默认提供的构建目标,支持我们从命令行构建时指定构建目标(架构、系统、abi等等) + const target = b.standardTargetOptions(.{}); + + // 使用默认提供的优化方案,支持我们从命令行构建时指定构建模式 + const optimize = b.standardOptimizeOption(.{}); + + const exe = b.addExecutable(.{ + .name = "zip", + .root_module = b.addModule("zip", .{ + .root_source_file = b.path("src/main.zig"), + // 构建目标 + .target = target, + // 构建模式 + .optimize = optimize, + }), + }); + + if (target.result.os.tag == .windows) + // 连接到系统的 ole32 + exe.linkSystemLibrary("ole32") + else + // 链接到系统的 libz + exe.linkSystemLibrary("z"); + + // 链接到 libc + exe.linkLibC(); + + b.installArtifact(exe); +} diff --git a/course/code/15/build_system/system_lib/build.zig.zon b/course/code/15/build_system/system_lib/build.zig.zon new file mode 100644 index 00000000..6f9b6047 --- /dev/null +++ b/course/code/15/build_system/system_lib/build.zig.zon @@ -0,0 +1,73 @@ +.{ + // This is the default name used by packages depending on this one. For + // example, when a user runs `zig fetch --save `, this field is used + // as the key in the `dependencies` table. Although the user can choose a + // different name, most users will stick with this provided value. + // + // It is redundant to include "zig" in this name because it is already + // within the Zig package namespace. + .name = .system_lib, + + // This is a [Semantic Version](https://semver.org/). + // In a future version of Zig it will be used for package deduplication. + .version = "0.0.0", + .fingerprint = 0x64b791172db5c549, + + // This field is optional. + // This is currently advisory only; Zig does not yet do anything + // with this value. + //.minimum_zig_version = "0.11.0", + + // This field is optional. + // Each dependency must either provide a `url` and `hash`, or a `path`. + // `zig build --fetch` can be used to fetch all dependencies of a package, recursively. + // Once all dependencies are fetched, `zig build` no longer requires + // internet connectivity. + .dependencies = .{ + // See `zig fetch --save ` for a command-line interface for adding dependencies. + //.example = .{ + // // When updating this field to a new URL, be sure to delete the corresponding + // // `hash`, otherwise you are communicating that you expect to find the old hash at + // // the new URL. + // .url = "https://example.com/foo.tar.gz", + // + // // This is computed from the file contents of the directory of files that is + // // obtained after fetching `url` and applying the inclusion rules given by + // // `paths`. + // // + // // This field is the source of truth; packages do not come from a `url`; they + // // come from a `hash`. `url` is just one of many possible mirrors for how to + // // obtain a package matching this `hash`. + // // + // // Uses the [multihash](https://multiformats.io/multihash/) format. + // .hash = "...", + // + // // When this is provided, the package is found in a directory relative to the + // // build root. In this case the package's hash is irrelevant and therefore not + // // computed. This field and `url` are mutually exclusive. + // .path = "foo", + + // // When this is set to `true`, a package is declared to be lazily + // // fetched. This makes the dependency only get fetched if it is + // // actually used. + // .lazy = false, + //}, + }, + + // Specifies the set of files and directories that are included in this package. + // Only files and directories listed here are included in the `hash` that + // is computed for this package. Only files listed here will remain on disk + // when using the zig package manager. As a rule of thumb, one should list + // files required for compilation plus any license(s). + // Paths are relative to the build root. Use the empty string (`""`) to refer to + // the build root itself. + // A directory listed here means that all files within, recursively, are included. + .paths = .{ + "build.zig", + "build.zig.zon", + "src", + // For example... + //"LICENSE", + //"README.md", + }, +} diff --git a/course/code/15/build_system/system_lib/src/main.zig b/course/code/15/build_system/system_lib/src/main.zig new file mode 100644 index 00000000..1df86419 --- /dev/null +++ b/course/code/15/build_system/system_lib/src/main.zig @@ -0,0 +1,5 @@ +const std = @import("std"); + +pub fn main() !void { + std.log.info("Hello, world!", .{}); +} diff --git a/course/code/15/build_system/test/build.zig b/course/code/15/build_system/test/build.zig new file mode 100644 index 00000000..48ca138a --- /dev/null +++ b/course/code/15/build_system/test/build.zig @@ -0,0 +1,46 @@ +const std = @import("std"); + +pub fn build(b: *std.Build) void { + // 标准构建目标 + const target = b.standardTargetOptions(.{}); + + // 标准构建模式 + const optimize = b.standardOptimizeOption(.{}); + + // 添加一个二进制可执行程序构建 + const exe = b.addExecutable(.{ + .name = "zig", + .root_module = b.addModule("zig", .{ + .root_source_file = b.path("src/main.zig"), + .target = target, + .optimize = optimize, + }), + }); + + // 添加到顶级 install step 中作为依赖 + b.installArtifact(exe); + + // 此处开始构建单元测试 + + // 构建一个单元测试的 Compile + const exe_unit_tests = b.addTest(.{ + .root_module = b.addModule("zig_unit_tests", .{ + .root_source_file = b.path("src/main.zig"), + .target = target, + .optimize = optimize, + }), + }); + + // 执行单元测试 + const run_exe_unit_tests = b.addRunArtifact(exe_unit_tests); + + // 如果想要跳过外部来自于其他包的单元测试(例如依赖中的包) + // 可以使用 skip_foreign_checks + run_exe_unit_tests.skip_foreign_checks = true; + + // 构建一个 step,用于执行测试 + const test_step = b.step("test", "Run unit tests"); + + // 测试 step 依赖上方构建的 run_exe_unit_tests + test_step.dependOn(&run_exe_unit_tests.step); +} diff --git a/course/code/15/build_system/test/build.zig.zon b/course/code/15/build_system/test/build.zig.zon new file mode 100644 index 00000000..92b35441 --- /dev/null +++ b/course/code/15/build_system/test/build.zig.zon @@ -0,0 +1,73 @@ +.{ + // This is the default name used by packages depending on this one. For + // example, when a user runs `zig fetch --save `, this field is used + // as the key in the `dependencies` table. Although the user can choose a + // different name, most users will stick with this provided value. + // + // It is redundant to include "zig" in this name because it is already + // within the Zig package namespace. + .name = .ttest, + + // This is a [Semantic Version](https://semver.org/). + // In a future version of Zig it will be used for package deduplication. + .version = "0.0.0", + .fingerprint = 0x334b1002be4b456c, + + // This field is optional. + // This is currently advisory only; Zig does not yet do anything + // with this value. + //.minimum_zig_version = "0.11.0", + + // This field is optional. + // Each dependency must either provide a `url` and `hash`, or a `path`. + // `zig build --fetch` can be used to fetch all dependencies of a package, recursively. + // Once all dependencies are fetched, `zig build` no longer requires + // internet connectivity. + .dependencies = .{ + // See `zig fetch --save ` for a command-line interface for adding dependencies. + //.example = .{ + // // When updating this field to a new URL, be sure to delete the corresponding + // // `hash`, otherwise you are communicating that you expect to find the old hash at + // // the new URL. + // .url = "https://example.com/foo.tar.gz", + // + // // This is computed from the file contents of the directory of files that is + // // obtained after fetching `url` and applying the inclusion rules given by + // // `paths`. + // // + // // This field is the source of truth; packages do not come from a `url`; they + // // come from a `hash`. `url` is just one of many possible mirrors for how to + // // obtain a package matching this `hash`. + // // + // // Uses the [multihash](https://multiformats.io/multihash/) format. + // .hash = "...", + // + // // When this is provided, the package is found in a directory relative to the + // // build root. In this case the package's hash is irrelevant and therefore not + // // computed. This field and `url` are mutually exclusive. + // .path = "foo", + + // // When this is set to `true`, a package is declared to be lazily + // // fetched. This makes the dependency only get fetched if it is + // // actually used. + // .lazy = false, + //}, + }, + + // Specifies the set of files and directories that are included in this package. + // Only files and directories listed here are included in the `hash` that + // is computed for this package. Only files listed here will remain on disk + // when using the zig package manager. As a rule of thumb, one should list + // files required for compilation plus any license(s). + // Paths are relative to the build root. Use the empty string (`""`) to refer to + // the build root itself. + // A directory listed here means that all files within, recursively, are included. + .paths = .{ + "build.zig", + "build.zig.zon", + "src", + // For example... + //"LICENSE", + //"README.md", + }, +} diff --git a/course/code/15/build_system/test/src/main.zig b/course/code/15/build_system/test/src/main.zig new file mode 100644 index 00000000..96d9a362 --- /dev/null +++ b/course/code/15/build_system/test/src/main.zig @@ -0,0 +1,24 @@ +const std = @import("std"); + +pub fn main() !void { + // Prints to stderr (it's a shortcut based on `std.io.getStdErr()`) + std.debug.print("All your {s} are belong to us.\n", .{"codebase"}); + + // stdout is for the actual output of your application, for example if you + // are implementing gzip, then only the compressed bytes should be sent to + // stdout, not any debugging messages. + var stdout_buffer: [1024]u8 = undefined; + var stdout_writer = std.fs.File.stdout().writer(&stdout_buffer); + const stdout = &stdout_writer.interface; + + try stdout.print("Run `zig build test` to run the tests.\n", .{}); + + try stdout.flush(); +} + +test "simple test" { + var list = std.ArrayList(i32).init(std.testing.allocator); + defer list.deinit(); // try commenting this out and see if zig detects the memory leak! + try list.append(42); + try std.testing.expectEqual(@as(i32, 42), list.pop()); +} diff --git a/course/code/15/build_system/test/src/root.zig b/course/code/15/build_system/test/src/root.zig new file mode 100644 index 00000000..ecfeade1 --- /dev/null +++ b/course/code/15/build_system/test/src/root.zig @@ -0,0 +1,10 @@ +const std = @import("std"); +const testing = std.testing; + +export fn add(a: i32, b: i32) i32 { + return a + b; +} + +test "basic add functionality" { + try testing.expect(add(3, 7) == 10); +} diff --git a/course/code/15/build_system/tinytetris/build.zig b/course/code/15/build_system/tinytetris/build.zig new file mode 100644 index 00000000..ba101e52 --- /dev/null +++ b/course/code/15/build_system/tinytetris/build.zig @@ -0,0 +1,59 @@ +const std = @import("std"); + +pub fn build(b: *std.Build) void { + // 构建目标 + const target = b.standardTargetOptions(.{}); + + // 构建优化模式 + const optimize = b.standardOptimizeOption(.{}); + + if (target.result.os.tag == .windows) { + return; + } + + // 添加一个二进制可执行程序构建 + // 注意:我们在这里并没有使用 root_source_file 字段 + // 该字段是为 zig 源文件准备的 + const exe = b.addExecutable(.{ + .name = "tinytetris", + .root_module = b.createModule(.{ + .target = target, + .optimize = optimize, + }), + }); + + // 添加 C 源代码文件,两个参数: + // 源代码路径(相对于build.zig) + // 传递的 flags + // 多个 C 源代码文件可以使用 addCSourceFiles + exe.addCSourceFile(.{ + .file = b.path("src/main.cc"), + .flags = &.{}, + }); + + // 链接C++ 标准库 + // 同理对于 C 标准库可以使用 linkLibC + exe.linkLibCpp(); + + // 链接系统库 ncurses + exe.linkSystemLibrary("ncurses"); + + // 添加到顶级 install step 中作为依赖 + b.installArtifact(exe); + + // 创建一个运行 + const run_cmd = b.addRunArtifact(exe); + + // 依赖于构建 + run_cmd.step.dependOn(b.getInstallStep()); + + // 运行时参数传递 + if (b.args) |args| { + run_cmd.addArgs(args); + } + + // 运行的 step + const run_step = b.step("run", "Run the app"); + // 依赖于前面的运行 + run_step.dependOn(&run_cmd.step); +} diff --git a/course/code/15/build_system/tinytetris/build.zig.zon b/course/code/15/build_system/tinytetris/build.zig.zon new file mode 100644 index 00000000..a8f670af --- /dev/null +++ b/course/code/15/build_system/tinytetris/build.zig.zon @@ -0,0 +1,73 @@ +.{ + // This is the default name used by packages depending on this one. For + // example, when a user runs `zig fetch --save `, this field is used + // as the key in the `dependencies` table. Although the user can choose a + // different name, most users will stick with this provided value. + // + // It is redundant to include "zig" in this name because it is already + // within the Zig package namespace. + .name = .tinytetris, + + // This is a [Semantic Version](https://semver.org/). + // In a future version of Zig it will be used for package deduplication. + .version = "0.0.0", + .fingerprint = 0x5f03916d4d995c27, + + // This field is optional. + // This is currently advisory only; Zig does not yet do anything + // with this value. + //.minimum_zig_version = "0.11.0", + + // This field is optional. + // Each dependency must either provide a `url` and `hash`, or a `path`. + // `zig build --fetch` can be used to fetch all dependencies of a package, recursively. + // Once all dependencies are fetched, `zig build` no longer requires + // internet connectivity. + .dependencies = .{ + // See `zig fetch --save ` for a command-line interface for adding dependencies. + //.example = .{ + // // When updating this field to a new URL, be sure to delete the corresponding + // // `hash`, otherwise you are communicating that you expect to find the old hash at + // // the new URL. + // .url = "https://example.com/foo.tar.gz", + // + // // This is computed from the file contents of the directory of files that is + // // obtained after fetching `url` and applying the inclusion rules given by + // // `paths`. + // // + // // This field is the source of truth; packages do not come from a `url`; they + // // come from a `hash`. `url` is just one of many possible mirrors for how to + // // obtain a package matching this `hash`. + // // + // // Uses the [multihash](https://multiformats.io/multihash/) format. + // .hash = "...", + // + // // When this is provided, the package is found in a directory relative to the + // // build root. In this case the package's hash is irrelevant and therefore not + // // computed. This field and `url` are mutually exclusive. + // .path = "foo", + + // // When this is set to `true`, a package is declared to be lazily + // // fetched. This makes the dependency only get fetched if it is + // // actually used. + // .lazy = false, + //}, + }, + + // Specifies the set of files and directories that are included in this package. + // Only files and directories listed here are included in the `hash` that + // is computed for this package. Only files listed here will remain on disk + // when using the zig package manager. As a rule of thumb, one should list + // files required for compilation plus any license(s). + // Paths are relative to the build root. Use the empty string (`""`) to refer to + // the build root itself. + // A directory listed here means that all files within, recursively, are included. + .paths = .{ + "build.zig", + "build.zig.zon", + "src", + // For example... + //"LICENSE", + //"README.md", + }, +} diff --git a/course/code/15/build_system/tinytetris/src/main.cc b/course/code/15/build_system/tinytetris/src/main.cc new file mode 100644 index 00000000..3018b6a4 --- /dev/null +++ b/course/code/15/build_system/tinytetris/src/main.cc @@ -0,0 +1,161 @@ +#include +#include +#include +#include +#include + +// block layout is: {w-1,h-1}{x0,y0}{x1,y1}{x2,y2}{x3,y3} (two bits each) +int x = 431424, y = 598356, r = 427089, px = 247872, py = 799248, pr, + c = 348480, p = 615696, tick, board[20][10], + block[7][4] = {{x, y, x, y}, + {r, p, r, p}, + {c, c, c, c}, + {599636, 431376, 598336, 432192}, + {411985, 610832, 415808, 595540}, + {px, py, px, py}, + {614928, 399424, 615744, 428369}}, + score = 0; + +// extract a 2-bit number from a block entry +int NUM(int x, int y) { return 3 & block[p][x] >> y; } + +// create a new piece, don't remove old one (it has landed and should stick) +void new_piece() { + y = py = 0; + p = rand() % 7; + r = pr = rand() % 4; + x = px = rand() % (10 - NUM(r, 16)); +} + +// draw the board and score +void frame() { + for (int i = 0; i < 20; i++) { + move(1 + i, 1); // otherwise the box won't draw + for (int j = 0; j < 10; j++) { + board[i][j] && attron(262176 | board[i][j] << 8); + printw(" "); + attroff(262176 | board[i][j] << 8); + } + } + move(21, 1); + printw("Score: %d", score); + refresh(); +} + +// set the value of the board for a particular (x,y,r) piece +void set_piece(int x, int y, int r, int v) { + for (int i = 0; i < 8; i += 2) { + board[NUM(r, i * 2) + y][NUM(r, (i * 2) + 2) + x] = v; + } +} + +// move a piece from old (p*) coords to new +void update_piece() { + set_piece(px, py, pr, 0); + set_piece(px = x, py = y, pr = r, p + 1); +} + +// remove line(s) from the board if they're full +void remove_line() { + for (int row = y; row <= y + NUM(r, 18); row++) { + c = 1; + for (int i = 0; i < 10; i++) { + c *= board[row][i]; + } + if (!c) { + continue; + } + for (int i = row - 1; i > 0; i--) { + memcpy(&board[i + 1][0], &board[i][0], 40); + } + memset(&board[0][0], 0, 10); + score++; + } +} + +// check if placing p at (x,y,r) will be a collision +int check_hit(int x, int y, int r) { + if (y + NUM(r, 18) > 19) { + return 1; + } + set_piece(px, py, pr, 0); + c = 0; + for (int i = 0; i < 8; i += 2) { + board[y + NUM(r, i * 2)][x + NUM(r, (i * 2) + 2)] && c++; + } + set_piece(px, py, pr, p + 1); + return c; +} + +// slowly tick the piece y position down so the piece falls +int do_tick() { + if (++tick > 30) { + tick = 0; + if (check_hit(x, y + 1, r)) { + if (!y) { + return 0; + } + remove_line(); + new_piece(); + } else { + y++; + update_piece(); + } + } + return 1; +} + +// main game loop with wasd input checking +void runloop() { + while (do_tick()) { + usleep(10000); + if ((c = getch()) == 'a' && x > 0 && !check_hit(x - 1, y, r)) { + x--; + } + if (c == 'd' && x + NUM(r, 16) < 9 && !check_hit(x + 1, y, r)) { + x++; + } + if (c == 's') { + while (!check_hit(x, y + 1, r)) { + y++; + update_piece(); + } + remove_line(); + new_piece(); + } + if (c == 'w') { + ++r %= 4; + while (x + NUM(r, 16) > 9) { + x--; + } + if (check_hit(x, y, r)) { + x = px; + r = pr; + } + } + if (c == 'q') { + return; + } + update_piece(); + frame(); + } +} + +// init curses and start runloop +int main() { + srand(time(0)); + initscr(); + start_color(); + // colours indexed by their position in the block + for (int i = 1; i < 8; i++) { + init_pair(i, i, 0); + } + new_piece(); + resizeterm(22, 22); + noecho(); + timeout(0); + curs_set(0); + box(stdscr, 0, 0); + runloop(); + endwin(); +} \ No newline at end of file diff --git a/course/code/15/char-and-boolean.zig b/course/code/15/char-and-boolean.zig new file mode 100644 index 00000000..1c88251b --- /dev/null +++ b/course/code/15/char-and-boolean.zig @@ -0,0 +1,61 @@ +pub fn main() !void { + try CHAR.main(); +} + +const CHAR = struct { + const print = @import("std").debug.print; + const expect = @import("std").testing.expect; + + pub fn main() !void { + // #region char + // 格式化时,可以使用 u 输出对应的字符 + const me_zh = '我'; + print("{0u} = {0x}\n", .{me_zh}); // 我 = 6211 + + // 如果是 ASCII 字符,还可以使用 c 进行格式化 + const me_en = 'I'; + print("{0u} = {0c} = {0x}\n", .{me_en}); // I = I = 49 + + // 下面的写法会报错,因为这些 emoji 虽然看上去只有一个字,但其实需要由多个码位组合而成 + // const hand = '🖐🏽'; + // const flag = '🇨🇳'; + // #endregion char + + // #region string-literal + // 存储的是 UTF-8 编码序列 + const bytes = "Hello, 世界!"; + + print("{}\n", .{@TypeOf(bytes)}); // *const [16:0]u8 + print("{}\n", .{bytes.len}); // 16 + + // 通过索引访问到的是 UTF-8 编码序列中的字节 + // 由于 UTF-8 兼容 ASCII,所以可以直接打印 ASCII 字符 + print("{c}\n", .{bytes[1]}); // 'e' + + // “世”字的 UTF-8 编码为 E4 B8 96 + try expect(bytes[7] == 0xE4); + try expect(bytes[8] == 0xB8); + try expect(bytes[9] == 0x96); + + // 以 NUL 结尾 + print("{d}\n", .{bytes[16]}); // 0 + + // #endregion string-literal + + // #region multiline-string-literal + // “我”字的 UTF-8 编码为 E6 88 91 + const string = + \\I + \\我 + ; + try expect(string[0] == 'I'); + try expect(string[1] == '\n'); + try expect(string[2] == 0xE6); + try expect(string[3] == 0x88); + try expect(string[4] == 0x91); + try expect(string[5] == 0); + try expect(string.len == 5); + + // #endregion multiline-string-literal + } +}; diff --git a/course/code/15/comptime.zig b/course/code/15/comptime.zig new file mode 100644 index 00000000..3c14639a --- /dev/null +++ b/course/code/15/comptime.zig @@ -0,0 +1,170 @@ +pub fn main() !void { + try comptimeVariable.main(); + try comptimeExpression.main(); +} + +const DuckType = struct { + // #region DuckType_max + fn max(comptime T: type, a: T, b: T) T { + return if (a > b) a else b; + } + // #endregion DuckType_max + + // #region DuckType_maxPlus + fn maxPlus(comptime T: type, a: T, b: T) T { + if (T == bool) { + return a or b; + } else if (a > b) { + return a; + } else { + return b; + } + } + // #endregion DuckType_maxPlus + + // #region DuckType_max_actual + fn max_actual(a: bool, b: bool) bool { + { + return a or b; + } + } + // #endregion DuckType_max_actual +}; + +const comptimeVariable = struct { + // #region comptimeVariable + const expect = @import("std").testing.expect; + + const CmdFn = struct { + name: []const u8, + func: fn (i32) i32, + }; + + // 这里的 cmd_fns 是一个常量,所以它是编译期可知的 + const cmd_fns = [_]CmdFn{ + CmdFn{ .name = "one", .func = one }, + CmdFn{ .name = "two", .func = two }, + CmdFn{ .name = "three", .func = three }, + }; + + fn one(value: i32) i32 { + return value + 1; + } + fn two(value: i32) i32 { + return value + 2; + } + fn three(value: i32) i32 { + return value + 3; + } + + // #region comptimeVariable_default + fn performFn(comptime prefix_char: u8, start_value: i32) i32 { + var result: i32 = start_value; + // 以下的变量 i 被标记为编译期已知的 + comptime var i = 0; + // 这里将会被内联,实际编译出来的代码将不包含循环 + // 原因是cmd_fns是一个常量,那么代表它是编译期可知的 + // 也就是说整个循环的执行结果在编译期就可以确定 + inline while (i < cmd_fns.len) : (i += 1) { + if (cmd_fns[i].name[0] == prefix_char) { + result = cmd_fns[i].func(result); + } + } + return result; + } + // #endregion comptimeVariable_default + + pub fn main() !void { + try expect(performFn('t', 1) == 6); + try expect(performFn('o', 0) == 1); + try expect(performFn('w', 99) == 99); + } + // #endregion comptimeVariable + + // #region comptimeVariable_t + fn performFn_for_t(start_value: i32) i32 { + var result: i32 = start_value; + result = two(result); + result = three(result); + return result; + } + // #endregion comptimeVariable_t + + // #region comptimeVariable_o + fn performFn_for_o(start_value: i32) i32 { + var result: i32 = start_value; + result = one(result); + return result; + } + // #endregion comptimeVariable_o + + // #region comptimeVariable_w + fn performFn_for_w(start_value: i32) i32 { + var result: i32 = start_value; + _ = &result; + return result; + } + // #endregion comptimeVariable_w +}; + +const comptimeExpression = struct { + // #region comptimeExpression + fn fibonacci(index: u32) u32 { + if (index < 2) return index; + return fibonacci(index - 1) + fibonacci(index - 2); + } + + pub fn main() !void { + const expect = @import("std").testing.expect; + + // 运行时测试 + try expect(fibonacci(7) == 13); + + // 编译期测试 + try comptime expect(fibonacci(7) == 13); + } + // #endregion comptimeExpression + + // #region comptimeExpression_container + const c = add_comptime(1, 2); + + fn add_comptime(comptime a: usize, comptime b: usize) usize { + return a + b; + } + // #endregion comptimeExpression_container +}; + +const GenericDataStruct = struct { + // #region GenericDataStruct + fn List(comptime T: type) type { + return struct { + items: []T, + len: usize, + }; + } + + var buffer: [10]i32 = undefined; + + var list = List(i32){ + .items = &buffer, + .len = 0, + }; + // #endregion GenericDataStruct + + // #region GenericDataStruct_node + const Node = struct { + next: ?*Node, + name: []const u8, + }; + + var node_a = Node{ + .next = null, + .name = "Node A", + }; + + var node_b = Node{ + .next = &node_a, + .name = "Node B", + }; + // #endregion GenericDataStruct_node +}; diff --git a/course/code/15/decision.zig b/course/code/15/decision.zig new file mode 100644 index 00000000..3b47914e --- /dev/null +++ b/course/code/15/decision.zig @@ -0,0 +1,164 @@ +pub fn main() !void { + try Basic.main(); + try MatchEnum.main(); + try TernayExpress.main(); + try DestructOptional.main(); + try DestructErrorUnion.main(); + try DestructErrorOptionalUnion.main(); +} + +const Basic = struct { + // #region more_if + const print = @import("std").debug.print; + + pub fn main() !void { + // #region default_if + const num: u8 = 1; + if (num == 1) { + print("num is 1\n", .{}); + } else if (num == 2) { + print("num is 2\n", .{}); + } else { + print("num is other\n", .{}); + } + // #endregion default_if + } + // #endregion more_if +}; + +const MatchEnum = struct { + // #region more_match_enum + const std = @import("std"); + + pub fn main() !void { + // #region default_match_enum + const Small = enum { + one, + two, + three, + four, + }; + + const demo = Small.one; + if (demo == Small.one) { + std.debug.print("{}\n", .{demo}); + } + // #endregion default_match_enum + } + // #endregion more_match_enum +}; + +const TernayExpress = struct { + // #region more_ternary + const print = @import("std").debug.print; + + pub fn main() !void { + // #region default_ternary + const a: u32 = 5; + const b: u32 = 4; + // 下方 result 的值应该是47 + const result = if (a != b) 47 else 3089; + + print("result is {}\n", .{result}); + // #endregion default_ternary + } + // #endregion more_ternary +}; + +const DestructOptional = struct { + const std = @import("std"); + const expect = std.testing.expect; + + fn a() !void { + // #region destruct_optional + const val: ?u32 = null; + if (val) |real_b| { + _ = real_b; + } else { + try expect(true); + } + // #endregion destruct_optional + } + + fn b() !void { + // #region capture_optional_pointer + var c: ?u32 = 3; + if (c) |*value| { + value.* = 2; + } + // #endregion capture_optional_pointer + } + + pub fn main() !void { + try a(); + try b(); + } +}; + +const DestructErrorUnion = struct { + const std = @import("std"); + const expect = std.testing.expect; + + fn a() !void { + // #region destruct_error_union + const val: anyerror!u32 = 0; + if (val) |value| { + try expect(value == 0); + } else |err| { + _ = err; + unreachable; + } + // #endregion destruct_error_union + } + + fn b() !void { + const val: anyerror!u32 = error.BadValue; + // #region only_catch_error + if (val) |_| {} else |err| { + try expect(err == error.BadValue); + } + // #endregion only_catch_error + } + + fn c() !void { + // #region catch_pointer + var val: anyerror!u32 = 3; + if (val) |*value| { + value.* = 9; + } else |_| { + unreachable; + } + // #endregion catch_pointer + } + + pub fn main() !void { + try a(); + try b(); + try c(); + } +}; + +const DestructErrorOptionalUnion = struct { + const std = @import("std"); + const expect = std.testing.expect; + pub fn main() !void { + // #region destruct_error_optional_union + const a: anyerror!?u32 = 0; + if (a) |optional_value| { + try expect(optional_value.? == 0); + } else |err| { + _ = err; + } + // #endregion destruct_error_optional_union + // #region destruct_error_optional_union_pointer + var d: anyerror!?u32 = 3; + if (d) |*optional_value| { + if (optional_value.*) |*value| { + value.* = 9; + } + } else |_| { + // nothing + } + // #endregion destruct_error_optional_union_pointer + } +}; diff --git a/course/code/15/defer.zig b/course/code/15/defer.zig new file mode 100644 index 00000000..7fda8827 --- /dev/null +++ b/course/code/15/defer.zig @@ -0,0 +1,19 @@ +// #region Defer +const std = @import("std"); +const print = std.debug.print; + +pub fn main() !void { + defer print("exec third\n", .{}); + + if (false) { + defer print("will not exec\n", .{}); + } + + defer { + print("exec second\n", .{}); + } + defer { + print("exec first\n", .{}); + } +} +// #endregion Defer diff --git a/course/code/15/define_variable.zig b/course/code/15/define_variable.zig new file mode 100644 index 00000000..8db019f5 --- /dev/null +++ b/course/code/15/define_variable.zig @@ -0,0 +1,211 @@ +// #region top-level +//! 顶层文档注释 +//! 顶层文档注释 + +const S = struct { + //! 顶层文档注释 +}; +// #endregion top-level + +pub fn main() !void { + _ = Timestamp{ + .seconds = 0, + .nanos = 0, + }; + DefineVar.main(); + Const.main(); + Undefined.main(); + UseUndefined.main(); + Block.main(); +} + +// #region doc-comment +/// 存储时间戳的结构体,精度为纳秒 +/// (像这里就是多行文档注释) +const Timestamp = struct { + /// 自纪元开始后的秒数 (此处也是一个文档注释). + seconds: i64, // 我们可以以此代表1970年前 (此处是普通注释) + + /// 纳秒数 (文档注释). + nanos: u32, + + /// 返回一个 Timestamp 结构体代表 unix 纪元; + /// 1970年 1月1日 00:00:00 UTC (文档注释). + pub fn unixEpoch() Timestamp { + return Timestamp{ + .seconds = 0, + .nanos = 0, + }; + } +}; +// #endregion doc-comment + +const DefineVar = struct { + // #region define + const std = @import("std"); + + pub fn main() void { + // 声明变量 variable 类型为u16, 并指定值为 666 + var variable: u16 = 0; + variable = 666; + + std.debug.print("变量 variable 是{}\n", .{variable}); + } + // #endregion define +}; + +const Const = struct { + // #region const + const std = @import("std"); + + pub fn main() void { + const constant: u16 = 666; + + std.debug.print("常量 constant 是{}\n", .{constant}); + } + // #endregion const +}; + +const Undefined = struct { + // #region undefined + const std = @import("std"); + + pub fn main() void { + var variable: u16 = undefined; + + variable = 666; + + std.debug.print("变量 variable 是{}\n", .{variable}); + } + // #endregion undefined +}; + +const UseUndefined = struct { + // #region use-undefined + const std = @import("std"); + + // 填充连续递增的数字 + // 注意该函数中并没有对 output 进行读操作,所以 output 的初始值不重要 + fn iota(init: u8, output: []u8) void { + for (output, init..) |*e, v| { + e.* = @intCast(v); + } + } + + pub fn main() void { + // buffer 定义时不需要初始化 + var buffer: [8]u8 = undefined; + + // 因为 iota() 会为 buffer 里的元素赋值 + iota(7, &buffer); + + // 输出 { 7, 8, 9, 10, 11, 12, 13, 14 } + std.debug.print("{any}\n", .{buffer}); + } + // #endregion use-undefined +}; + +// #region identifier +const @"identifier with spaces in it" = 0xff; +const @"1SmallStep4Man" = 112358; + +const c = @import("std").c; +pub extern "c" fn @"error"() void; +pub extern "c" fn @"fstat$INODE64"(fd: c.fd_t, buf: *c.Stat) c_int; + +const Color = enum { + red, + @"really red", +}; +const color: Color = .@"really red"; +// #endregion identifier + +const Block = struct { + pub fn main() void { + // #region block + var y: i32 = 123; + + const x = blk: { + y += 1; + break :blk y; + }; + // #endregion block + _ = x; + } +}; + +const Deconstruct = struct { + fn main() void { + // #region deconstruct + const print = @import("std").debug.print; + var x: u32 = undefined; + var y: u32 = undefined; + var z: u32 = undefined; + // 元组 + const tuple = .{ 1, 2, 3 }; + // 解构元组 + x, y, z = tuple; + + print("tuple: x = {}, y = {}, z = {}\n", .{ x, y, z }); + // 数组 + const array = [_]u32{ 4, 5, 6 }; + // 解构数组 + x, y, z = array; + + print("array: x = {}, y = {}, z = {}\n", .{ x, y, z }); + // 向量定义 + const vector: @Vector(3, u32) = .{ 7, 8, 9 }; + // 解构向量 + x, y, z = vector; + + print("vector: x = {}, y = {}, z = {}\n", .{ x, y, z }); + // #endregion deconstruct + + } +}; + +const Deconstruct_2 = struct { + pub fn main() !void { + // #region deconstruct_2 + const print = @import("std").debug.print; + var x: u32 = undefined; + + const tuple = .{ 1, 2, 3 }; + + x, var y: u32, const z = tuple; + + print("x = {}, y = {}, z = {}\n", .{ x, y, z }); + + // y 可变 + y = 100; + + // 可以用 _ 丢弃不想要的值 + _, x, _ = tuple; + + print("x = {}", .{x}); + // #endregion deconstruct_2 + } +}; + +const ThreadLocal = struct { + // #region threadlocal + const std = @import("std"); + threadlocal var x: i32 = 1234; + + fn main() !void { + const thread1 = try std.Thread.spawn(.{}, testTls, .{}); + const thread2 = try std.Thread.spawn(.{}, testTls, .{}); + testTls(); + thread1.join(); + thread2.join(); + } + + fn testTls() void { + // 1234 + std.debug.print("x is {}\n", .{x}); + x += 1; + // 1235 + std.debug.print("x is {}\n", .{x}); + } + // #endregion threadlocal +}; diff --git a/course/code/15/echo_tcp_server.zig b/course/code/15/echo_tcp_server.zig new file mode 100644 index 00000000..caa7283c --- /dev/null +++ b/course/code/15/echo_tcp_server.zig @@ -0,0 +1,180 @@ +const std = @import("std"); +const builtin = @import("builtin"); +const net = std.net; +const windows = std.os.windows; +const linux = std.os.linux; + +// POLLIN, POLLERR, POLLHUP, POLLNVAL 均是 poll 的事件 + +/// windows context 定义 +const windows_context = struct { + const POLLIN: i16 = 0x0100; + const POLLERR: i16 = 0x0001; + const POLLHUP: i16 = 0x0002; + const POLLNVAL: i16 = 0x0004; + const INVALID_SOCKET = windows.ws2_32.INVALID_SOCKET; +}; + +/// linux context 定义 +const linux_context = struct { + const POLLIN: i16 = 0x0001; + const POLLERR: i16 = 0x0008; + const POLLHUP: i16 = 0x0010; + const POLLNVAL: i16 = 0x0020; + const INVALID_SOCKET = -1; +}; + +/// macOS context 定义 +const macos_context = struct { + const POLLIN: i16 = 0x0001; + const POLLERR: i16 = 0x0008; + const POLLHUP: i16 = 0x0010; + const POLLNVAL: i16 = 0x0020; + const INVALID_SOCKET = -1; +}; + +const context = switch (builtin.os.tag) { + .windows => windows_context, + .linux => linux_context, + .macos => macos_context, + else => @compileError("unsupported os"), +}; + +pub fn main() !void { + // #region listen + // 解析地址 + const port = 8080; + const address = try net.Address.parseIp4("127.0.0.1", port); + // 初始化一个server,这里就包含了 socket() 和 bind() 两个过程 + var server = try address.listen(.{}); + defer server.deinit(); + // #endregion listen + + // #region data + // 定义最大连接数 + const max_sockets = 1000; + // buffer 用于存储 client 发过来的数据 + var buf: [1024]u8 = std.mem.zeroes([1024]u8); + // 存储 accept 拿到的 connections + var connections: [max_sockets]?net.Server.Connection = undefined; + // sockfds 用于存储 pollfd, 用于传递给 poll 函数 + var sockfds: [max_sockets]if (builtin.os.tag == .windows) + windows.ws2_32.pollfd + else + std.posix.pollfd = undefined; + // #endregion data + for (0..max_sockets) |i| { + sockfds[i].fd = context.INVALID_SOCKET; + sockfds[i].events = context.POLLIN; + connections[i] = null; + } + sockfds[0].fd = server.stream.handle; + + std.log.info("start listening at {d}...", .{port}); + + // 无限循环,等待客户端连接或者已连接的客户端发送数据 + while (true) { + // 调用 poll,nums 是返回的事件数量 + var nums = if (builtin.os.tag == .windows) windows.poll(&sockfds, max_sockets, -1) else try std.posix.poll(&sockfds, -1); + if (nums == 0) { + continue; + } + // 如果返回的事件数量小于0,说明出错了 + // 仅仅在 windows 下会出现这种情况 + if (nums < 0) { + @panic("An error occurred in poll"); + } + + // NOTE: 值得注意的是,我们使用的模型是先处理已连接的客户端,再处理新连接的客户端 + + // #region exist-connections + // 遍历所有的连接,处理事件 + for (1..max_sockets) |i| { + // 这里的 nums 是 poll 返回的事件数量 + // 在windows下,WSApoll允许返回0,未超时且没有套接字处于指定的状态 + if (nums == 0) { + break; + } + const sockfd = sockfds[i]; + + // 检查是否是无效的 socket + if (sockfd.fd == context.INVALID_SOCKET) { + continue; + } + + // 由于 windows 针对无效的socket也会触发POLLNVAL + // 当前 sock 有 IO 事件时,处理完后将 nums 减一 + defer if (sockfd.revents != 0) { + nums -= 1; + }; + + // 检查是否是 POLLIN 事件,即是否有数据可读 + if (sockfd.revents & (context.POLLIN) != 0) { + const c = connections[i]; + if (c) |connection| { + const len = try connection.stream.read(&buf); + // 如果连接已经断开,那么关闭连接 + // 这是因为如果已经 close 的连接,读取的时候会返回0 + if (len == 0) { + // 但为了保险起见,我们还是调用 close + // 因为有可能是连接没有断开,但是出现了错误 + connection.stream.close(); + // 将 pollfd 和 connection 置为无效 + sockfds[i].fd = context.INVALID_SOCKET; + std.log.info("client from {any} close!", .{ + connection.address, + }); + connections[i] = null; + } else { + // 如果读取到了数据,那么将数据写回去 + // 但仅仅这样写一次并不安全 + // 最优解应该是使用for循环检测写入的数据大小是否等于buf长度 + // 如果不等于就继续写入 + // 这是因为 TCP 是一个面向流的协议 + // 它并不保证一次 write 调用能够发送所有的数据 + // 作为示例,我们不检查是否全部写入 + _ = try connection.stream.write(buf[0..len]); + } + } + } + // 检查是否是 POLLNVAL | POLLERR | POLLHUP 事件,即是否有错误发生,或者连接断开 + else if ((sockfd.revents & + (context.POLLNVAL | context.POLLERR | context.POLLHUP)) != 0) + { + // 将 pollfd 和 connection 置为无效 + sockfds[i].fd = context.INVALID_SOCKET; + connections[i] = null; + std.log.info("client {} close", .{i}); + } + } + // #endregion exist-connections + + // #region new-connection + // 检查是否有新的连接 + // 这里的 sockfds[0] 是 server 的 pollfd + // 这里的 nums 检查可有可无,因为我们只关心是否有新的连接,POLLIN 就足够了 + if (sockfds[0].revents & context.POLLIN != 0 and nums > 0) { + std.log.info("new client", .{}); + // 如果有新的连接,那么调用 accept + const client = try server.accept(); + for (1..max_sockets) |i| { + // 找到一个空的 pollfd,将新的连接放进去 + if (sockfds[i].fd == context.INVALID_SOCKET) { + sockfds[i].fd = client.stream.handle; + connections[i] = client; + std.log.info("new client {} comes", .{i}); + break; + } + // 如果没有找到空的 pollfd,那么说明连接数已经达到了最大值 + if (i == max_sockets - 1) { + @panic("too many clients"); + } + } + } + // #endregion new-connection + } + + if (builtin.os.tag == .windows) { + try windows.ws2_32.WSACleanup(); + } +} diff --git a/course/code/15/enum.zig b/course/code/15/enum.zig new file mode 100644 index 00000000..18afdc71 --- /dev/null +++ b/course/code/15/enum.zig @@ -0,0 +1,161 @@ +pub fn main() !void { + try EnumSize.main(); + try EnumReference.main(); +} + +// #region basic_enum +const Type = enum { + ok, + not_ok, +}; + +const c = Type.ok; +// #endregion basic_enum + +// #region enum_with_value +// 指定枚举的标记类型 +// 现在我们可以在 u2 和 Value 这个枚举类型之中任意切换了 +const Value = enum(u2) { + zero, + one, + two, +}; +// #endregion enum_with_value + +// #region enum_with_value2 +const Value2 = enum(u32) { + hundred = 100, + thousand = 1000, + million = 1000000, +}; + +// 覆盖部分值 +const Value3 = enum(u4) { + a, + b = 8, + c, + d = 4, + e, +}; +// #endregion enum_with_value2 + +// #region enum_with_method +const Suit = enum { + clubs, + spades, + diamonds, + hearts, + + pub fn isClubs(self: Suit) bool { + return self == Suit.clubs; + } +}; +// #endregion enum_with_method + +const EnumSize = struct { + + // #region enum_size + const std = @import("std"); + const expect = std.testing.expect; + const mem = std.mem; + + const Small = enum { + one, + two, + three, + four, + }; + + pub fn main() !void { + try expect(@typeInfo(Small).@"enum".tag_type == u2); + try expect(@typeInfo(Small).@"enum".fields.len == 4); + try expect(mem.eql(u8, @typeInfo(Small).@"enum".fields[1].name, "two")); + try expect(mem.eql(u8, @tagName(Small.three), "three")); + } + // #endregion enum_size +}; + +const EnumReference = struct { + // #region enum_reference + const Color = enum { + auto, + off, + on, + }; + + pub fn main() !void { + const color1: Color = .auto; // 此处枚举进行了自动推断 + const color2 = Color.auto; + _ = (color1 == color2); // 这里比较的结果是 true + } + // #endregion enum_reference +}; + +const Non_exhaustiveEnum = struct { + // #region non_exhaustive_enum + const Number = enum(u8) { + one, + two, + three, + _, + }; + + const number = Number.one; + const result = switch (number) { + .one => true, + .two, .three => false, + _ => false, + }; + // result 是 true + + const is_one = switch (number) { + .one => true, + else => false, + }; + // is_one 也是true + // #endregion non_exhaustive_enum + + const std = @import("std"); + const expect = std.testing.expect; + + pub fn main() !void { + // #region enum_from_int + const Color = enum(u4) { + red, + green, + blue, + _, + }; + + // 明确列出的枚举值 + const blue: Color = @enumFromInt(2); + try expect(blue == .blue); + + // 未列出的枚举值:8 在 u4 的范围内(0~15) + const yellow: Color = @enumFromInt(8); + try expect(@TypeOf(yellow) == Color); + try expect(@intFromEnum(yellow) == 8); + + // 42 超出了 u4 的范围,会触发未定义行为 + // const ub: Color = @enumFromInt(42); + + // #endregion enum_from_int + } +}; + +const EnumLiteral_ = struct { + const std = @import("std"); + pub fn main() !void { + // #region enum_literal + // 使用内建函数 @Type 构造出一个 EnumLiteral 类型 + // 这是目前官方文档中的使用方案 + const EnumLiteral: type = @Type(.enum_literal); + + // 定义一个常量 enum_literal,它的类型为 EnumLiteral,并赋值为 “.kkk” + const enum_literal: EnumLiteral = .kkk; + + // 使用内建函数 @tagName 获取 enum_literal 的 tag name,并进行打印 + std.debug.print("enum_literal is {s}", .{@tagName(enum_literal)}); + // #endregion enum_literal + } +}; diff --git a/course/code/15/error_handle.zig b/course/code/15/error_handle.zig new file mode 100644 index 00000000..c65439e9 --- /dev/null +++ b/course/code/15/error_handle.zig @@ -0,0 +1,263 @@ +pub fn main() !void { + BasicUse.main(); + JustOneError.main(); +} + +const BasicUse = struct { + // #region BasicUse + const std = @import("std"); + + // 定义一个错误集合类型 + const FileOpenError = error{ + AccessDenied, + OutOfMemory, + FileNotFound, + }; + + // 定义另一个错误集合类型 + const AllocationError = error{ + OutOfMemory, + }; + + pub fn main() void { + const err = foo(AllocationError.OutOfMemory); + if (err == FileOpenError.OutOfMemory) { + std.debug.print("error is OutOfMemory\n", .{}); + } + } + + fn foo(err: AllocationError) FileOpenError { + return err; + } + // #endregion BasicUse +}; + +const JustOneError = struct { + pub fn main() void { + { + // #region JustOneError1 + const err = error.FileNotFound; + // #endregion JustOneError1 + if (err != anyerror.OutOfMemory) {} + } + + { + // #region JustOneError2 + const err = (error{FileNotFound}).FileNotFound; + // #endregion JustOneError2 + if (err != anyerror.OutOfMemory) {} + } + } +}; + +const ConvertEnglishToInteger = struct { + // #region ConvertEnglishToInteger + const std = @import("std"); + const maxInt = std.math.maxInt; + + pub fn parseU64(buf: []const u8, radix: u8) !u64 { + var x: u64 = 0; + + for (buf) |c| { + const digit = charToDigit(c); + + if (digit >= radix) { + return error.InvalidChar; + } + + // x *= radix + var ov = @mulWithOverflow(x, radix); + if (ov[1] != 0) return error.OverFlow; + + // x += digit + ov = @addWithOverflow(ov[0], digit); + if (ov[1] != 0) return error.OverFlow; + x = ov[0]; + } + + return x; + } + + fn charToDigit(c: u8) u8 { + return switch (c) { + '0'...'9' => c - '0', + 'A'...'Z' => c - 'A' + 10, + 'a'...'z' => c - 'a' + 10, + else => maxInt(u8), + }; + } + // #endregion ConvertEnglishToInteger +}; + +test "parse u64" { + const result = try ConvertEnglishToInteger.parseU64("1234", 10); + try @import("std").testing.expect(result == 1234); +} + +const CatchBasic = struct { + const parseU64 = ConvertEnglishToInteger.parseU64; + // #region CatchBasic + fn doAThing(str: []u8) void { + const number = parseU64(str, 10) catch 13; + _ = number; // ... + } + // #endregion CatchBasic +}; + +const CatchAdvanced = struct { + const parseU64 = ConvertEnglishToInteger.parseU64; + // #region CatchAdvanced + fn doAThing(str: []u8) void { + const number = parseU64(str, 10) catch blk: { + // 指定某些复杂逻辑处理 + break :blk 13; + }; + _ = number; // 这里的 number 已经被初始化 + } + // #endregion CatchAdvanced +}; + +const TryBasic = struct { + const parseU64 = ConvertEnglishToInteger.parseU64; + // #region TryBasic1 + fn doAThing1(str: []u8) !void { + const number = try parseU64(str, 10); + _ = number; + } + // #endregion TryBasic1 + + // #region TryBasic2 + fn doAThing2(str: []u8) !void { + const number = parseU64(str, 10) catch |err| return err; + _ = number; + } + // #endregion TryBasic2 +}; + +const AssertNoError = struct { + const parseU64 = ConvertEnglishToInteger.parseU64; + // #region AssertNoError + const number = parseU64("1234", 10) catch unreachable; + // #endregion AssertNoError +}; + +const PreciseErrorHandle = struct { + const parseU64 = ConvertEnglishToInteger.parseU64; + fn doSomethingWithNumber(_: u64) void {} + + // #region PreciseErrorHandle + fn doAThing(str: []u8) void { + if (parseU64(str, 10)) |number| { + doSomethingWithNumber(number); + } else |err| switch (err) { + error.Overflow => { + // 处理溢出 + }, + // 此处假定这个错误不会发生 + error.InvalidChar => unreachable, + // 这里你也可以使用 else 来捕获额外的错误 + else => |leftover_err| return leftover_err, + } + } + // #endregion PreciseErrorHandle +}; + +const NotHandleError = struct { + const parseU64 = ConvertEnglishToInteger.parseU64; + fn doSomethingWithNumber(_: u64) void {} + + // #region NotHandleError + fn doADifferentThing(str: []u8) void { + if (parseU64(str, 10)) |number| { + doSomethingWithNumber(number); + } else |_| { + // 你也可以在这里做点额外的事情 + } + // 或者你也可以这样: + parseU64(str, 10) catch {}; + } + // #endregion NotHandleError +}; + +const ErrDefer = struct { + const std = @import("std"); + + // #region DeferErrorCapture + fn deferErrorCaptureExample() !void { + // 捕获错误 + errdefer |err| { + std.debug.print("the error is {s}\n", .{@errorName(err)}); + } + + return error.DeferError; + } + // #endregion DeferErrorCapture +}; + +const DeferErrDefer = struct { + // #region DeferErrDefer + const std = @import("std"); + const Allocator = std.mem.Allocator; + + const Foo = struct { + data: u32, + }; + + fn tryToAllocateFoo(allocator: Allocator) !*Foo { + return allocator.create(Foo); + } + + fn deallocateFoo(allocator: Allocator, foo: *Foo) void { + allocator.destroy(foo); + } + + fn getFooData() !u32 { + return 666; + } + + fn createFoo(allocator: Allocator, param: i32) !*Foo { + const foo = getFoo: { + var foo = try tryToAllocateFoo(allocator); + errdefer deallocateFoo(allocator, foo); + + foo.data = try getFooData(); + + break :getFoo foo; + }; + // This lasts for the rest of the function + errdefer deallocateFoo(allocator, foo); + + // Error is now properly handled by errdefer + if (param > 1337) return error.InvalidParam; + + return foo; + } + // #endregion DeferErrDefer +}; + +test "createFoo" { + try @import("std").testing.expectError(error.InvalidParam, DeferErrDefer.createFoo(@import("std").testing.allocator, 2468)); +} + +const ReferError = struct { + + // #region ReferError + // 由编译器推导而出的错误集 + pub fn add_inferred(comptime T: type, a: T, b: T) !T { + const ov = @addWithOverflow(a, b); + if (ov[1] != 0) return error.Overflow; + return ov[0]; + } + + // 明确声明的错误集 + pub fn add_explicit(comptime T: type, a: T, b: T) Error!T { + const ov = @addWithOverflow(a, b); + if (ov[1] != 0) return error.Overflow; + return ov[0]; + } + + const Error = error{ + Overflow, + }; + // #endregion ReferError +}; diff --git a/course/code/15/function.zig b/course/code/15/function.zig new file mode 100644 index 00000000..fa5e81de --- /dev/null +++ b/course/code/15/function.zig @@ -0,0 +1,80 @@ +//! 该文件有一部分函数没有进行测试,仅定义 +//! ExitProcess 和 atan2 函数是外部函数,不会进行测试。 +pub fn main() !void { + _ = add(1, 2); + _ = max(u8, 1, 2); + { + const num: u8 = 1; + _ = addFortyTwo(num); + } + _ = sub(2, 1); + + // 需要注意这是个死循环函数,不会返回。 + abort(); + + _ = shiftLeftOne(1); +} + +// #region add +pub fn add(a: u8, b: u8) u8 { + return a + b; +} +// #endregion add + +// #region max +fn max(comptime T: type, a: T, b: T) T { + return if (a > b) a else b; +} +// #endregion max + +// #region addFortyTwo +fn addFortyTwo(x: anytype) @TypeOf(x) { + return x + 42; +} +// #endregion addFortyTwo + +// #region ExitProcess +const WINAPI = @import("std").os.windows.WINAPI; +extern "kernel32" fn ExitProcess(exit_code: c_uint) callconv(WINAPI) noreturn; +// #endregion ExitProcess + +// #region sub +export fn sub(a: i8, b: i8) i8 { + return a - b; +} +// #endregion sub + +// #region atan2 +extern "c" fn atan2(a: f64, b: f64) f64; +// #endregion atan2 + +// #region abort +fn abort() noreturn { + @branchHint(.cold); + while (true) {} +} +// #endregion abort + +// #region shiftLeftOne +// 强制该函数在所有被调用位置内联,否则失败。 +inline fn shiftLeftOne(a: u32) u32 { + return a << 1; +} +// #endregion shiftLeftOne + +// #region closure +fn bar(comptime x: i32) fn (i32) i32 { + return struct { + pub fn foo(y: i32) i32 { + var counter = 0; + for (x..y) |i| { + if ((i % 2) == 0) { + counter += i * i; + } + } + return counter; + } + }.foo; +} + +// #endregion closure diff --git a/course/code/15/hello_world.zig b/course/code/15/hello_world.zig new file mode 100644 index 00000000..19eec76e --- /dev/null +++ b/course/code/15/hello_world.zig @@ -0,0 +1,63 @@ +const std = @import("std"); + +pub fn main() !void { + try One.main(); + try Two.main(); + try Three.main(); +} + +const One = struct { + // #region one + pub fn main() !void { + std.debug.print("Hello, World!\n", .{}); + } + // #endregion one +}; + +const Two = struct { + // #region two + pub fn main() !void { + var stdout_buffer: [1024]u8 = undefined; + var stdout_writer = std.fs.File.stdout().writer(&stdout_buffer); + const stdout = &stdout_writer.interface; + + var stderr_buffer: [1024]u8 = undefined; + var stderr_writer = std.fs.File.stderr().writer(&stderr_buffer); + const stderr = &stderr_writer.interface; + + try stdout.print("Hello {s}!\n", .{"out"}); + try stderr.print("Hello {s}!\n", .{"err"}); + + try stdout.flush(); + try stderr.flush(); + } // #endregion two +}; + +const Three = struct { + // #region three + pub fn main() !void { + // 定义两个缓冲区 + var stdout_buffer: [1024]u8 = undefined; // [!code focus] + var stderr_buffer: [1024]u8 = undefined; // [!code focus] + + // 获取writer句柄// [!code focus] + var stdout_writer = std.fs.File.stdout().writer(&stdout_buffer); + const stdout = &stdout_writer.interface; + + // 获取writer句柄// [!code focus] + var stderr_writer = std.fs.File.stderr().writer(&stderr_buffer); + const stderr = &stderr_writer.interface; + + // 通过句柄写入buffer// [!code focus] + try stdout.print("Hello {s}!\n", .{"out"}); // [!code focus] + try stderr.print("Hello {s}!\n", .{"err"}); // [!code focus] + + try stdout.flush(); + try stderr.flush(); + + // 尝试刷新buffer// [!code focus] + try stdout.flush(); // [!code focus] + try stderr.flush(); // [!code focus] + } + // #endregion three +}; diff --git a/course/code/15/import_dependency_build/build.zig b/course/code/15/import_dependency_build/build.zig new file mode 100644 index 00000000..2e1e3913 --- /dev/null +++ b/course/code/15/import_dependency_build/build.zig @@ -0,0 +1,63 @@ +const std = @import("std"); +const ChildProcess = std.process.Child; + +const args = [_][]const u8{ "zig", "build" }; + +pub fn build(b: *std.Build) !void { + const full_path = try std.process.getCwdAlloc(b.allocator); + + var dir = std.fs.openDirAbsolute(full_path, .{ .iterate = true }) catch |err| { + std.log.err("open path failed {s}, err is {}", .{ full_path, err }); + std.process.exit(1); + }; + defer dir.close(); + + var iterate = dir.iterate(); + + while (iterate.next()) |val| { + if (val) |entry| { + // get the entry name, entry can be file or directory + const name = entry.name; + if (entry.kind == .directory) { + if (eqlu8(name, ".zig-cache") or eqlu8(name, "zig-out") or eqlu8(name, "zig-cache")) + continue; + + // build child process + var child = ChildProcess.init(&args, b.allocator); + + // build cwd + const cwd = std.fs.path.join(b.allocator, &[_][]const u8{ + full_path, + name, + }) catch |err| { + std.log.err("fmt path failed, err is {}", .{err}); + std.process.exit(1); + }; + + // open entry dir + const entry_dir = std.fs.openDirAbsolute(cwd, .{}) catch unreachable; + entry_dir.access("build.zig", .{}) catch { + std.log.err("not found build.zig in path {s}", .{cwd}); + std.process.exit(1); + }; + + // set child cwd + // this api maybe changed in the future + child.cwd = cwd; + + // spawn and wait child process + _ = child.spawnAndWait() catch unreachable; + } + } else { + // Stop endless loop + break; + } + } else |err| { + std.log.err("iterate examples_path failed, err is {}", .{err}); + std.process.exit(1); + } +} + +fn eqlu8(a: []const u8, b: []const u8) bool { + return std.mem.eql(u8, a, b); +} diff --git a/course/code/15/import_dependency_build/pkg1/build.zig b/course/code/15/import_dependency_build/pkg1/build.zig new file mode 100644 index 00000000..71478915 --- /dev/null +++ b/course/code/15/import_dependency_build/pkg1/build.zig @@ -0,0 +1,5 @@ +const std = @import("std"); + +pub fn build(b: *std.Build) void { + @import("pkg2").helperFunction(b); +} diff --git a/course/code/15/import_dependency_build/pkg1/build.zig.zon b/course/code/15/import_dependency_build/pkg1/build.zig.zon new file mode 100644 index 00000000..6eceed4b --- /dev/null +++ b/course/code/15/import_dependency_build/pkg1/build.zig.zon @@ -0,0 +1,15 @@ +.{ + .name = .pkg1, + .version = "0.0.0", + .fingerprint = 0x759ba61b105d16f9, + .dependencies = .{ + .pkg2 = .{ + // path 为本地包的路径 + .path = "../pkg2", + }, + }, + .paths = .{ + "build.zig", + "build.zig.zon", + }, +} diff --git a/course/code/15/import_dependency_build/pkg2/build.zig b/course/code/15/import_dependency_build/pkg2/build.zig new file mode 100644 index 00000000..020bb13d --- /dev/null +++ b/course/code/15/import_dependency_build/pkg2/build.zig @@ -0,0 +1,9 @@ +const std = @import("std"); + +pub fn build(b: *std.Build) void { + _ = b; +} + +pub fn helperFunction(artifact: *std.Build) void { + _ = artifact; +} diff --git a/course/code/15/import_dependency_build/pkg2/build.zig.zon b/course/code/15/import_dependency_build/pkg2/build.zig.zon new file mode 100644 index 00000000..35893f62 --- /dev/null +++ b/course/code/15/import_dependency_build/pkg2/build.zig.zon @@ -0,0 +1,10 @@ +.{ + .name = .pkg2, + .version = "0.0.0", + .fingerprint = 0xec92f7a1a7362798, + .dependencies = .{}, + .paths = .{ + "build.zig", + "build.zig.zon", + }, +} diff --git a/course/code/15/import_vcpkg/build.zig b/course/code/15/import_vcpkg/build.zig new file mode 100644 index 00000000..bb4a649c --- /dev/null +++ b/course/code/15/import_vcpkg/build.zig @@ -0,0 +1,30 @@ +const std = @import("std"); +pub fn build(_: *std.Build) void {} + +const Build = struct { + pub fn build(b: *std.Build) void { + const target = b.standardTargetOptions(.{}); + const optimize = b.standardOptimizeOption(.{}); + + const exe = b.addExecutable(.{ + .name = "c_lib_import_gsl_windows-x64", + .root_module = b.addModule("c_lib_import_gsl_windows-x64", .{ + .root_source_file = b.path("src/main.zig"), + .target = target, + .optimize = optimize, + }), + }); + // #region c_import + // 增加 include 搜索目录 + exe.addIncludePath(.{ .cwd_relative = "D:\\vcpkg\\installed\\windows-x64\\include" }); + // 增加 lib 搜索目录 + exe.addLibraryPath(.{ .cwd_relative = "D:\\vcpkg\\installed\\windows-x64\\lib" }); + // 链接标准c库 + exe.linkLibC(); + // 链接第三方库gsl + exe.linkSystemLibrary("gsl"); + // #endregion c_import + + b.installArtifact(exe); + } +}; diff --git a/course/code/15/import_vcpkg/build.zig.zon b/course/code/15/import_vcpkg/build.zig.zon new file mode 100644 index 00000000..ec0454bf --- /dev/null +++ b/course/code/15/import_vcpkg/build.zig.zon @@ -0,0 +1,73 @@ +.{ + // This is the default name used by packages depending on this one. For + // example, when a user runs `zig fetch --save `, this field is used + // as the key in the `dependencies` table. Although the user can choose a + // different name, most users will stick with this provided value. + // + // It is redundant to include "zig" in this name because it is already + // within the Zig package namespace. + .name = .import_vcpkg, + + // This is a [Semantic Version](https://semver.org/). + // In a future version of Zig it will be used for package deduplication. + .version = "0.0.0", + .fingerprint = 0x75583b9623cebdcb, + + // This field is optional. + // This is currently advisory only; Zig does not yet do anything + // with this value. + //.minimum_zig_version = "0.11.0", + + // This field is optional. + // Each dependency must either provide a `url` and `hash`, or a `path`. + // `zig build --fetch` can be used to fetch all dependencies of a package, recursively. + // Once all dependencies are fetched, `zig build` no longer requires + // internet connectivity. + .dependencies = .{ + // See `zig fetch --save ` for a command-line interface for adding dependencies. + //.example = .{ + // // When updating this field to a new URL, be sure to delete the corresponding + // // `hash`, otherwise you are communicating that you expect to find the old hash at + // // the new URL. + // .url = "https://example.com/foo.tar.gz", + // + // // This is computed from the file contents of the directory of files that is + // // obtained after fetching `url` and applying the inclusion rules given by + // // `paths`. + // // + // // This field is the source of truth; packages do not come from a `url`; they + // // come from a `hash`. `url` is just one of many possible mirrors for how to + // // obtain a package matching this `hash`. + // // + // // Uses the [multihash](https://multiformats.io/multihash/) format. + // .hash = "...", + // + // // When this is provided, the package is found in a directory relative to the + // // build root. In this case the package's hash is irrelevant and therefore not + // // computed. This field and `url` are mutually exclusive. + // .path = "foo", + + // // When this is set to `true`, a package is declared to be lazily + // // fetched. This makes the dependency only get fetched if it is + // // actually used. + // .lazy = false, + //}, + }, + + // Specifies the set of files and directories that are included in this package. + // Only files and directories listed here are included in the `hash` that + // is computed for this package. Only files listed here will remain on disk + // when using the zig package manager. As a rule of thumb, one should list + // files required for compilation plus any license(s). + // Paths are relative to the build root. Use the empty string (`""`) to refer to + // the build root itself. + // A directory listed here means that all files within, recursively, are included. + .paths = .{ + "build.zig", + "build.zig.zon", + "src", + // For example... + //"LICENSE", + //"README.md", + }, +} diff --git a/course/code/15/import_vcpkg/src/main.zig b/course/code/15/import_vcpkg/src/main.zig new file mode 100644 index 00000000..533f922b --- /dev/null +++ b/course/code/15/import_vcpkg/src/main.zig @@ -0,0 +1,25 @@ +const std = @import("std"); + +// #region import_gsl +const gsl = @cImport({ + @cInclude("gsl/gsl_fft_complex.h"); +}); +// #endregion import_gsl + +pub fn main() !void { + const n = 8; + + var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); + defer arena.deinit(); + const allocator = arena.allocator(); + // #region use_gsl_fft + // [实数0,虚数0,实数1,虚数1,实数2,虚数2,...] + var data: []f64 = try allocator.alloc(f64, n * 2); + // 虚数恒为0,实数为0,1,2,... + for (0..n) |i| data[i * 2] = @floatFromInt(i); + // 快速离散傅里叶变换 + _ = gsl.gsl_fft_complex_radix2_forward(data.ptr, 1, n); + // 输出结果 + try std.io.stdout.writer().print("\n{any}\n", .{data}); + // #endregion use_gsl_fft +} diff --git a/course/code/15/interact_with_c.zig b/course/code/15/interact_with_c.zig new file mode 100644 index 00000000..e45f817e --- /dev/null +++ b/course/code/15/interact_with_c.zig @@ -0,0 +1,52 @@ +pub fn main() !void { + cHeaderImport.main(); +} + +const cHeaderImport = struct { + // #region cHeaderImport + const c = @cImport({ + @cDefine("_NO_CRT_STDIO_INLINE", "1"); + @cInclude("stdio.h"); + }); + pub fn main() void { + _ = c.printf("hello\n"); + } + // #endregion cHeaderImport +}; + +const cTranslate = struct { + // #region cTranslate + // 示例文件 + const c = @cImport({ + @cDefine("_NO_CRT_STDIO_INLINE", "1"); + @cInclude("stdio.h"); + }); + pub fn main() void { + _ = c; + } + // #endregion cTranslate +}; + +const external = struct { + // #region external_func + // 这是对应 C printf 的声明 + pub extern "c" fn printf(format: [*:0]const u8, ...) c_int; + // #endregion external_func + + // #region external + // 使用 callconv 声明函数调用约定为 C + fn add(count: c_int, ...) callconv(.C) c_int { + // 对应 C 的宏 va_start + var ap = @cVaStart(); + // 对应 C 的宏 va_end + defer @cVaEnd(&ap); + var i: usize = 0; + var sum: c_int = 0; + while (i < count) : (i += 1) { + // 对应 C 的宏 va_arg + sum += @cVaArg(&ap, c_int); + } + return sum; + } + // #endregion external +}; diff --git a/course/code/15/loop.zig b/course/code/15/loop.zig new file mode 100644 index 00000000..2f0c520f --- /dev/null +++ b/course/code/15/loop.zig @@ -0,0 +1,309 @@ +pub fn main() !void { + ForArray.main(); + ForHandleArray.main(); + IndexFor.main(); + MultiFor.main(); + ForAsExpression.main(); + LabelFor.main(); + try InlineFor.main(); + WhileBasic.main(); + WhileContinue.main(); + LabelWhile.main(); + try InlineWhile.main(); + WhileOptional.main(); + + WhileErrorUnion.main(); +} + +const ForArray = struct { + pub fn main() void { + // #region for_array + const items = [_]i32{ 4, 5, 3, 4, 0 }; + var sum: i32 = 0; + + for (items) |value| { + if (value == 0) { + continue; + } + sum += value; + } + // #endregion for_array + + // #region for_integer + for (0..5) |i| { + _ = i; + // do something + } + // #endregion for_integer + } +}; + +const ForHandleArray = struct { + pub fn main() void { + // #region for_handle_array + var items = [_]i32{ 3, 4, 2 }; + + for (&items) |*value| { + value.* += 1; + } + // #endregion for_handle_array + } +}; + +const IndexFor = struct { + pub fn main() void { + // #region index_for + const items = [_]i32{ 4, 5, 3, 4, 0 }; + for (items, 0..) |value, i| { + _ = value; + _ = i; + // do something + } + // #endregion index_for + } +}; + +const MultiFor = struct { + pub fn main() void { + // #region multi_for + const items = [_]usize{ 1, 2, 3 }; + const items2 = [_]usize{ 4, 5, 6 }; + + for (items, items2) |i, j| { + _ = i; + _ = j; + // do something + } + // #endregion multi_for + } +}; + +const ForAsExpression = struct { + pub fn main() void { + // #region for_as_expression + const items = [_]?i32{ 3, 4, null, 5 }; + + const result = for (items) |value| { + if (value == 5) { + break value; + } + } else 0; + // #endregion for_as_expression + + _ = result; + } +}; + +const LabelFor = struct { + pub fn main() void { + { + // #region label_for_1 + var count: usize = 0; + outer: for (1..6) |_| { + for (1..6) |_| { + count += 1; + break :outer; + } + } + // #endregion label_for_1 + } + + { + // #region label_for_2 + var count: usize = 0; + outer: for (1..9) |_| { + for (1..6) |_| { + count += 1; + continue :outer; + } + } + // #endregion label_for_2 + } + } +}; + +const InlineFor = struct { + // #region inline_for_more + const std = @import("std"); + const expect = std.testing.expect; + + // #region inline_for + pub fn main() !void { + const nums = [_]i32{ 2, 4, 6 }; + var sum: usize = 0; + inline for (nums) |i| { + const T = switch (i) { + 2 => f32, + 4 => i8, + 6 => bool, + else => unreachable, + }; + sum += typeNameLength(T); + } + try expect(sum == 9); + } + + fn typeNameLength(comptime T: type) usize { + return @typeName(T).len; + } + + // #endregion inline_for + // #endregion inline_for_more +}; + +const WhileBasic = struct { + // #region while_more + const std = @import("std"); + + pub fn main() void { + // #region while_basic + var i: usize = 0; + while (i < 10) { + if (i == 5) { + continue; + } + std.debug.print("i is {}\n", .{i}); + i += 1; + } + // #endregion while_basic + // #endregion while_more + } +}; + +const WhileContinue = struct { + pub fn main() void { + { + // #region while_continue_1 + var i: usize = 0; + while (i < 10) : (i += 1) {} + // #endregion while_continue_1 + } + + { + // #region while_continue_2 + var i: usize = 1; + var j: usize = 1; + while (i * j < 2000) : ({ + i *= 2; + j *= 3; + }) {} + // #endregion while_continue_2 + } + } +}; + +// #region while_as_expression +fn rangeHasNumber(begin: usize, end: usize, number: usize) bool { + var i = begin; + return while (i < end) : (i += 1) { + if (i == number) { + break true; + } + } else false; +} +// #endregion while_as_expression + +const LabelWhile = struct { + pub fn main() void { + { + // #region label_while_continue + var i: usize = 0; + outer: while (i < 10) : (i += 1) { + while (true) { + continue :outer; + } + } + // #endregion label_while_continue + } + { + // #region label_while_break + outer: while (true) { + while (true) { + break :outer; + } + } + // #endregion label_while_break + } + } +}; + +const InlineWhile = struct { + // #region inline_while_more + const std = @import("std"); + const expect = std.testing.expect; + + // #region inline_while + pub fn main() !void { + comptime var i = 0; + var sum: usize = 0; + inline while (i < 3) : (i += 1) { + const T = switch (i) { + 0 => f32, + 1 => i8, + 2 => bool, + else => unreachable, + }; + sum += typeNameLength(T); + } + try expect(sum == 9); + } + + fn typeNameLength(comptime T: type) usize { + return @typeName(T).len; + } + // #endregion inline_while + // #endregion inline_while_more +}; + +const WhileOptional = struct { + // #region while_optional_more + const std = @import("std"); + + var numbers_left: u32 = undefined; + fn eventuallyNullSequence() ?u32 { + return if (numbers_left == 0) null else blk: { + numbers_left -= 1; + break :blk numbers_left; + }; + } + + pub fn main() void { + var sum2: u32 = 0; + numbers_left = 3; + // #region while_optional + while (eventuallyNullSequence()) |value| { + sum2 += value; + } else { + std.debug.print("meet a null\n", .{}); + } + // 还可以使用else分支,碰到第一个 null 时触发并退出循环 + // #endregion while_optional + } + // #endregion while_optional_more +}; + +const WhileErrorUnion = struct { + // #region while_error_union_more + const std = @import("std"); + var numbers_left: u32 = undefined; + + fn eventuallyErrorSequence() anyerror!u32 { + return if (numbers_left == 0) error.ReachedZero else blk: { + numbers_left -= 1; + break :blk numbers_left; + }; + } + + pub fn main() void { + var sum1: u32 = 0; + numbers_left = 3; + // #region while_error_union + while (eventuallyErrorSequence()) |value| { + sum1 += value; + } else |err| { + std.debug.print("meet a err: {}\n", .{err}); + } + // #endregion while_error_union + } + // #endregion while_error_union_more +}; diff --git a/course/code/15/memory_manager.zig b/course/code/15/memory_manager.zig new file mode 100644 index 00000000..4f450a54 --- /dev/null +++ b/course/code/15/memory_manager.zig @@ -0,0 +1,230 @@ +pub fn main() !void { + try DebugAllocator.main(); + try SmpAllocator.main(); + try BestAllocator.main(); + try FixedBufferAllocator.main(); + try ThreadSafeFixedBufferAllocator.main(); + try ArenaAllocator.main(); + try c_allocator.main(); + try page_allocator.main(); + try StackFallbackAllocator.main(); + try MemoryPool.main(); +} + +const DebugAllocator = struct { + // #region DebugAllocator + const std = @import("std"); + + pub fn main() !void { + // 使用模型,一定要是变量,不能是常量 + var gpa = std.heap.DebugAllocator(.{}){}; + // 拿到一个allocator + const allocator = gpa.allocator(); + + // defer 用于执行debug_allocator善后工作 + defer { + // 尝试进行 deinit 操作 + const deinit_status = gpa.deinit(); + + // 检测是否发生内存泄漏 + if (deinit_status == .leak) @panic("TEST FAIL"); + } + + //申请内存 + const bytes = try allocator.alloc(u8, 100); + // 延后释放内存 + defer allocator.free(bytes); + } + // #endregion DebugAllocator +}; + +const SmpAllocator = struct { + // #region SmpAllocator + const std = @import("std"); + + pub fn main() !void { + // 无需任何初始化,拿来就可以使用 + const allocator = std.heap.smp_allocator; + + //申请内存 + const bytes = try allocator.alloc(u8, 100); + // 延后释放内存 + defer allocator.free(bytes); + } + // #endregion SmpAllocator +}; + +const FixedBufferAllocator = struct { + // #region FixedBufferAllocator + const std = @import("std"); + + pub fn main() !void { + var buffer: [1000]u8 = undefined; + // 一块内存区域,传入到fixed buffer中 + var fba = std.heap.FixedBufferAllocator.init(&buffer); + + // 获取内存allocator + const allocator = fba.allocator(); + + // 申请内存 + const memory = try allocator.alloc(u8, 100); + // 释放内存 + defer allocator.free(memory); + } + // #endregion FixedBufferAllocator +}; + +const ThreadSafeFixedBufferAllocator = struct { + // #region ThreadSafeFixedBufferAllocator + const std = @import("std"); + + pub fn main() !void { + var buffer: [1000]u8 = undefined; + // 一块内存区域,传入到fixed buffer中 + var fba = std.heap.FixedBufferAllocator.init(&buffer); + + // 获取内存allocator + const allocator = fba.allocator(); + + // 使用 ThreadSafeAllocator 包裹, 你需要设置使用的内存分配器,还可以配置使用的mutex + var thread_safe_fba = std.heap.ThreadSafeAllocator{ .child_allocator = allocator }; + + // 获取线程安全的内存allocator + const thread_safe_allocator = thread_safe_fba.allocator(); + + // 申请内存 + const memory = try thread_safe_allocator.alloc(u8, 100); + // 释放内存 + defer thread_safe_allocator.free(memory); + } + // #endregion ThreadSafeFixedBufferAllocator +}; + +const BestAllocator = struct { + const std = @import("std"); + const builtin = @import("builtin"); + var debug_allocator: std.heap.DebugAllocator(.{}) = .{}; + + pub fn main() !void { + const allocator, const is_debug = allocator: { + if (builtin.os.tag == .wasi) break :allocator .{ std.heap.wasm_allocator, false }; + break :allocator switch (builtin.mode) { + .Debug, .ReleaseSafe => .{ debug_allocator.allocator(), true }, + .ReleaseFast, .ReleaseSmall => .{ std.heap.smp_allocator, false }, + }; + }; + defer if (is_debug) { + _ = debug_allocator.deinit(); + }; + //申请内存 + const bytes = try allocator.alloc(u8, 100); + // 延后释放内存 + defer allocator.free(bytes); + } +}; + +const ArenaAllocator = struct { + // #region ArenaAllocator + const std = @import("std"); + + pub fn main() !void { + // 使用模型,一定要是变量,不能是常量 + var gpa = std.heap.GeneralPurposeAllocator(.{}){}; + // 拿到一个allocator + const allocator = gpa.allocator(); + + // defer 用于执行general_purpose_allocator善后工作 + defer { + const deinit_status = gpa.deinit(); + + if (deinit_status == .leak) @panic("TEST FAIL"); + } + + // 对通用内存分配器进行一层包裹 + var arena = std.heap.ArenaAllocator.init(allocator); + + // defer 最后释放内存 + defer arena.deinit(); + + // 获取分配器 + const arena_allocator = arena.allocator(); + + _ = try arena_allocator.alloc(u8, 1); + _ = try arena_allocator.alloc(u8, 10); + _ = try arena_allocator.alloc(u8, 100); + } + // #endregion ArenaAllocator +}; + +const c_allocator = struct { + // #region c_allocator + const std = @import("std"); + + pub fn main() !void { + // 用起来和 C 一样纯粹 + const allocator = std.heap.c_allocator; + const num = try allocator.alloc(u8, 1); + defer allocator.free(num); + } + // #endregion c_allocator +}; + +const page_allocator = struct { + // #region page_allocator + const std = @import("std"); + + pub fn main() !void { + const allocator = std.heap.page_allocator; + const memory = try allocator.alloc(u8, 100); + defer allocator.free(memory); + } + // #endregion page_allocator +}; + +const StackFallbackAllocator = struct { + // #region stack_fallback_allocator + const std = @import("std"); + + pub fn main() !void { + // 初始化一个优先使用栈区的分配器 + // 栈区大小为256个字节,如果栈区不够用,就会使用page allocator + var stack_alloc = std.heap.stackFallback( + 256 * @sizeOf(u8), + std.heap.page_allocator, + ); + // 获取分配器 + const stack_allocator = stack_alloc.get(); + // 申请内存 + const memory = try stack_allocator.alloc(u8, 100); + // 释放内存 + defer stack_allocator.free(memory); + } + // #endregion stack_fallback_allocator +}; + +const MemoryPool = struct { + // #region MemoryPool + const std = @import("std"); + + pub fn main() !void { + // 此处为了演示,直接使用page allocator + var pool = std.heap.MemoryPool(u32).init(std.heap.page_allocator); + defer pool.deinit(); + + // 连续申请三个对象 + const p1 = try pool.create(); + const p2 = try pool.create(); + const p3 = try pool.create(); + + // 回收p2 + pool.destroy(p2); + // 再申请一个新的对象 + const p4 = try pool.create(); + + // 注意,此时p2和p4指向同一块内存 + _ = p1; + _ = p3; + _ = p4; + } + // #endregion MemoryPool +}; diff --git a/course/code/15/number.zig b/course/code/15/number.zig new file mode 100644 index 00000000..59241a30 --- /dev/null +++ b/course/code/15/number.zig @@ -0,0 +1,58 @@ +pub fn main() void { + // #region type + // 下划线可以放在数字之间作为视觉分隔符 + const one_billion = 1_000_000_000; + const binary_mask = 0b1_1111_1111; + const permissions = 0o7_5_5; + const big_address = 0xFF80_0000_0000_0000; + // #endregion type + + _ = one_billion; + _ = binary_mask; + _ = permissions; + _ = big_address; + + { + // #region float + const std = @import("std"); + + const inf = std.math.inf(f32); + const negative_inf = -std.math.inf(f64); + const nan = std.math.nan(f128); + // #endregion float + + _ = inf; + _ = negative_inf; + _ = nan; + } + + { + const std = @import("std"); + const print = std.debug.print; + + // #region complex + const Complex = std.math.Complex(f64); + const i = Complex.init(0, 1); + + // 虚数单位的平方 + const z1 = i.mul(i); + print("i * i = ({d:.1},{d:.1})\n", .{ z1.re, z1.im }); + // i * i = (-1.0,0.0) + + // 使用常见函数 + const z2 = std.math.complex.pow(i, Complex.init(2, 0)); + print("pow(i, 2) = ({d:.1},{d:.1})\n", .{ z2.re, z2.im }); + // pow(i, 2) = (-1.0,0.0) + + // 欧拉公式 + const z3 = std.math.complex.exp(i.mul(Complex.init(std.math.pi, 0))); + print("exp(i, pi) = ({d:.1},{d:.1})\n", .{ z3.re, z3.im }); + // exp(i, pi) = (-1.0,0.0) + + // 共轭复数 + const z4 = Complex.init(1, 2).mul(Complex.init(1, -2)); + print("(1 + 2i) * (1 - 2i) = ({d:.1},{d:.1})\n", .{ z4.re, z4.im }); + // (1 + 2i) * (1 - 2i) = (5.0,0.0) + // #endregion complex + } +} diff --git a/course/code/15/opaque.zig b/course/code/15/opaque.zig new file mode 100644 index 00000000..ad2313b6 --- /dev/null +++ b/course/code/15/opaque.zig @@ -0,0 +1,11 @@ +// #region opaque +const Derp = opaque {}; +const Wat = opaque {}; + +extern fn bar(d: *Derp) void; +fn foo(w: *Wat) callconv(.C) void { + bar(w); +} +// #endregion opaque + +pub fn main() !void {} diff --git a/course/code/15/optional_type.zig b/course/code/15/optional_type.zig new file mode 100644 index 00000000..ab242483 --- /dev/null +++ b/course/code/15/optional_type.zig @@ -0,0 +1,59 @@ +pub fn main() !void { + try ComptimeAccessOptionalType.main(); +} + +// #region basic_type +// 一个普通的i32整数 +const normal_int: i32 = 1234; + +// i32的可选类型,现在它的值可以是 i32 或者 null +const optional_int: ?i32 = 5678; +// #endregion basic_type + +const Malloc = struct { + const Foo = struct {}; + + // #region malloc + // extern 用于连接标准 libc 的 malloc 函数,它是 posix 标准之一 + extern fn malloc(size: usize) ?*u8; + + fn doAThing() ?*Foo { + // 尝试调用 malloc 申请内存,如果失败则返回null + const ptr = malloc(1234) orelse return null; + _ = ptr; // ... + } + // #endregion malloc +}; + +const CheckNull = struct { + const Foo = struct {}; + // #region check_null + fn doSomethingWithFoo(foo: *Foo) void { + _ = foo; + } + + fn doAThing(optional_foo: ?*Foo) void { + // 干点什么。。。 + if (optional_foo) |foo| { + doSomethingWithFoo(foo); + } + // 干点什么。。。 + } + // #endregion check_null +}; + +const ComptimeAccessOptionalType = struct { + const expect = @import("std").testing.expect; + pub fn main() !void { + // #region comptime_access_optional_type + // 声明一个可选类型,并赋值为 null + var foo: ?i32 = null; + + // 重新赋值为子类型的值,这里是 i32 + foo = 1234; + + // 使用编译期反射来获取 foo 的类型信息 + try comptime expect(@typeInfo(@TypeOf(foo)).optional.child == i32); + // #endregion comptime_access_optional_type + } +}; diff --git a/course/code/15/package_management_exporter/Readme.md b/course/code/15/package_management_exporter/Readme.md new file mode 100644 index 00000000..44e8e91d --- /dev/null +++ b/course/code/15/package_management_exporter/Readme.md @@ -0,0 +1 @@ +该目录仅仅是作为演示用的空项目! diff --git a/course/code/15/package_management_exporter/build.zig b/course/code/15/package_management_exporter/build.zig new file mode 100644 index 00000000..ac1784a0 --- /dev/null +++ b/course/code/15/package_management_exporter/build.zig @@ -0,0 +1,14 @@ +const std = @import("std"); + +pub fn build(b: *std.Build) void { + const target = b.standardTargetOptions(.{}); + const optimize = b.standardOptimizeOption(.{}); + + // #region create_module + _ = b.addModule("exporter", .{ + .root_source_file = b.path("src/root.zig"), + .target = target, + .optimize = optimize, + }); + // #endregion create_module +} diff --git a/course/code/15/package_management_exporter/build.zig.zon b/course/code/15/package_management_exporter/build.zig.zon new file mode 100644 index 00000000..3f2fcacd --- /dev/null +++ b/course/code/15/package_management_exporter/build.zig.zon @@ -0,0 +1,15 @@ +// #region package_management +.{ + // 包名字 + .name = .exporter, + // 包版本 + .version = "0.0.0", + .fingerprint = 0x6a125ce7eaa53f2, + // 包所包含的源文件,一般用于在对外提供包时才使用,还是建议养成写清楚paths的习惯 + .paths = .{ + "build.zig", + "build.zig.zon", + "src", + }, +} +// #endregion package_management diff --git a/course/code/15/package_management_exporter/src/root.zig b/course/code/15/package_management_exporter/src/root.zig new file mode 100644 index 00000000..19ab525b --- /dev/null +++ b/course/code/15/package_management_exporter/src/root.zig @@ -0,0 +1,3 @@ +pub fn add(a: i32, b: i32) i32 { + return a + b; +} diff --git a/course/code/15/package_management_importer/Readme.md b/course/code/15/package_management_importer/Readme.md new file mode 100644 index 00000000..44e8e91d --- /dev/null +++ b/course/code/15/package_management_importer/Readme.md @@ -0,0 +1 @@ +该目录仅仅是作为演示用的空项目! diff --git a/course/code/15/package_management_importer/build.zig b/course/code/15/package_management_importer/build.zig new file mode 100644 index 00000000..62723df4 --- /dev/null +++ b/course/code/15/package_management_importer/build.zig @@ -0,0 +1,32 @@ +const std = @import("std"); + +pub fn build(b: *std.Build) void { + const target = b.standardTargetOptions(.{}); + const optimize = b.standardOptimizeOption(.{}); + + const exe = b.addExecutable(.{ + .name = "importer", + .root_module = b.addModule("importer", .{ + .root_source_file = b.path("src/main.zig"), + .target = target, + .optimize = optimize, + }), + }); + + // #region import_module + // 通过 dependency 函数获取到依赖 + const pe = b.dependency("path-exporter", .{ + .target = target, + .optimize = optimize, + }); + const te = b.dependency("tarball-exporter", .{ + .target = target, + .optimize = optimize, + }); + // 将 module 添加到 exe 的 root module 中 + exe.root_module.addImport("path_exporter", pe.module("exporter")); + exe.root_module.addImport("tarball_exporter", te.module("msgpack")); + // #endregion import_module + + b.installArtifact(exe); +} diff --git a/course/code/15/package_management_importer/build.zig.zon b/course/code/15/package_management_importer/build.zig.zon new file mode 100644 index 00000000..f74a0521 --- /dev/null +++ b/course/code/15/package_management_importer/build.zig.zon @@ -0,0 +1,27 @@ +// #region package_management +.{ + // 包名字 + .name = .importer, + // 包版本 + .version = "0.0.0", + .fingerprint = 0x64e883e88dde22e2, + // 包依赖 + .dependencies = .{ + // 包依赖项的名字 + .@"tarball-exporter" = .{ + .url = "https://github.com/zigcc/zig-msgpack/archive/33771261cc6bba98cee380392f6e95fbca30d956.tar.gz", + .hash = "zig_msgpack-0.0.8-evvueB_ZAQBNRm7kdh1FslBxMvpu5WKvU2RrYhUY_Dne", + }, + .@"path-exporter" = .{ + // path 为本地包的路径 + .path = "../package_management_exporter", + }, + }, + // 包所包含的源文件,一般用于在对外提供包时才使用,还是建议养成写清楚paths的习惯 + .paths = .{ + "src", + "build.zig", + "build.zig.zon", + }, +} +// #endregion package_management diff --git a/course/code/15/package_management_importer/src/main.zig b/course/code/15/package_management_importer/src/main.zig new file mode 100644 index 00000000..ef0882e1 --- /dev/null +++ b/course/code/15/package_management_importer/src/main.zig @@ -0,0 +1,21 @@ +const std = @import("std"); +const pe = @import("path_exporter"); +const te = @import("tarball_exporter"); + +pub fn main() !void { + var stdout_buffer: [1024]u8 = undefined; + var stdout_writer = std.fs.File.stdout().writer(&stdout_buffer); + const stdout = &stdout_writer.interface; + + const str2: te.Str = .{ .str = "2" }; + + try stdout.print( + \\Result of 1 + 1 + \\Path-Exporter: {} + \\Tarball-Expoter: {s} + , .{ + pe.add(1, 1), + str2.value(), + }); + try stdout.flush(); +} diff --git a/course/code/15/pointer.zig b/course/code/15/pointer.zig new file mode 100644 index 00000000..6af33843 --- /dev/null +++ b/course/code/15/pointer.zig @@ -0,0 +1,266 @@ +pub fn main() !void { + try SinglePointer.main(); + try MultiPointer.main(); + try ArrayAndSlice.main(); + try Slice.main(); + try STPointer.main(); + try Volatile.main(); + try Align.main(); + try AlignCast.main(); + try ZeroPointer.main(); + ComptimePointer.main(); + ptr2int.main(); + try compPointer.main(); +} + +const SinglePointer = struct { + // #region single_pointer + const print = @import("std").debug.print; + + pub fn main() !void { + var integer: i16 = 666; + const ptr = &integer; + ptr.* = ptr.* + 1; + + print("{}\n", .{integer}); + } + // #endregion single_pointer +}; + +const fnPointer = struct { + // #region fn_pointer + const Call2Op = *const fn (a: i8, b: i8) i8; + // Call20p 是一个函数指针类型,指向一个接受两个 i8 类型参数并返回 i8 类型的函数 + // #endregion fn_pointer +}; + +const ptr2int = struct { + pub fn main() void { + // #region ptr2int + const std = @import("std"); + + // ptrFromInt 将整数转换为指针 + const ptr: *i32 = @ptrFromInt(0xdeadbee0); + // intFromPtr 将指针转换为整数 + const addr = @intFromPtr(ptr); + + if (@TypeOf(addr) == usize) { + std.debug.print("success\n", .{}); + } + if (addr == 0xdeadbee0) { + std.debug.print("success\n", .{}); + } + // #endregion ptr2int + } +}; + +const MultiPointer = struct { + // #region multi_pointer + const print = @import("std").debug.print; + + pub fn main() !void { + const array = [_]i32{ 1, 2, 3, 4 }; + const ptr: [*]const i32 = &array; + + print("第一个元素:{}\n", .{ptr[0]}); + } + // #endregion multi_pointer +}; +const ArrayAndSlice = struct { + // #region array_and_slice + const expect = @import("std").testing.expect; + + pub fn main() !void { + var array: [5]u8 = "hello".*; + + const array_pointer = &array; + try expect(array_pointer.len == 5); + + const slice: []u8 = array[1..3]; + try expect(slice.len == 2); + } + // #endregion array_and_slice +}; + +const Slice = struct { + // #region slice + const print = @import("std").debug.print; + + pub fn main() !void { + var array = [_]i32{ 1, 2, 3, 4 }; + const arr_ptr: *const [4]i32 = &array; + + print("数组第一个元素为:{}\n", .{arr_ptr[0]}); + print("数组长度为:{}\n", .{arr_ptr.len}); + + const slice = array[1 .. array.len - 1]; + const slice_ptr: []i32 = slice; + + print("切片第一个元素为:{}\n", .{slice_ptr[0]}); + print("切片长度为:{}\n", .{slice_ptr.len}); + } + // #endregion slice +}; + +const STPointer = struct { + // #region st_pointer + const std = @import("std"); + + // 我们也可以用 std.c.printf 代替 + pub extern "c" fn printf(format: [*:0]const u8, ...) c_int; + + pub fn main() anyerror!void { + _ = printf("Hello, world!\n"); // OK + } + // #endregion st_pointer +}; + +const Volatile = struct { + // #region volatile + // expect 是单元测试的断言函数 + const expect = @import("std").testing.expect; + + pub fn main() !void { + const mmio_ptr: *volatile u8 = @ptrFromInt(0x12345678); + try expect(@TypeOf(mmio_ptr) == *volatile u8); + } + // #endregion volatile +}; + +const Align = struct { + // #region align + const std = @import("std"); + const builtin = @import("builtin"); + const expect = std.testing.expect; + + pub fn main() !void { + var x: i32 = 1234; + // 获取内存对齐信息 + const align_of_i32 = @alignOf(@TypeOf(x)); + // 尝试比较类型 + try expect(@TypeOf(&x) == *i32); + // 尝试在设置内存对齐后再进行类型比较 + try expect(*i32 == *align(align_of_i32) i32); + + if (builtin.target.cpu.arch == .x86_64) { + // 获取了 x86_64 架构的指针对齐大小 + try expect(@typeInfo(*i32).pointer.alignment == 4); + } + } + // #endregion align +}; + +const AlignCast = struct { + // #region align_cast + const expect = @import("std").testing.expect; + + // 全局变量 + var foo: u8 align(4) = 100; + + fn derp() align(@sizeOf(usize) * 2) i32 { + return 1234; + } + + // 以下是两个函数 + fn noop1() align(1) void {} + fn noop4() align(4) void {} + + pub fn main() !void { + // 全局变量对齐 + try expect(@typeInfo(@TypeOf(&foo)).pointer.alignment == 4); + try expect(@TypeOf(&foo) == *align(4) u8); + const as_pointer_to_array: *align(4) [1]u8 = &foo; + const as_slice: []align(4) u8 = as_pointer_to_array; + const as_unaligned_slice: []u8 = as_slice; + try expect(as_unaligned_slice[0] == 100); + + // 函数对齐 + try expect(derp() == 1234); + try expect(@TypeOf(derp) == fn () i32); + try expect(@TypeOf(&derp) == *align(@sizeOf(usize) * 2) const fn () i32); + + noop1(); + try expect(@TypeOf(noop1) == fn () void); + try expect(@TypeOf(&noop1) == *align(1) const fn () void); + + noop4(); + try expect(@TypeOf(noop4) == fn () void); + try expect(@TypeOf(&noop4) == *align(4) const fn () void); + } + // #endregion align_cast +}; + +const ZeroPointer = struct { + // #region zero_pointer + // 本示例中仅仅是构建了一个零指针 + // 并未使用,故可以在所有平台运行 + const std = @import("std"); + const expect = std.testing.expect; + + pub fn main() !void { + const zero: usize = 0; + const ptr: *allowzero i32 = @ptrFromInt(zero); + try expect(@intFromPtr(ptr) == 0); + } + // #endregion zero_pointer +}; + +const ComptimePointer = struct { + // #region comptime_pointer + const expect = @import("std").testing.expect; + + pub fn main() void { + comptime { + // 在这个 comptime 块中,可以正常使用pointer + // 不依赖于编译结果的内存布局,即在编译期时不依赖于未定义的内存布局 + var x: i32 = 1; + const ptr = &x; + ptr.* += 1; + x += 1; + try expect(ptr.* == 3); + } + } + // #endregion comptime_pointer +}; + +const compPointer = struct { + pub fn main() !void { + // #region comp_pointer + comptime { + const expect = @import("std").testing.expect; + // 只要指针不被解引用,那么就可以这么做 + const ptr: *i32 = @ptrFromInt(0xdeadbee0); + const addr = @intFromPtr(ptr); + try expect(@TypeOf(addr) == usize); + try expect(addr == 0xdeadbee0); + } + // #endregion comp_pointer + } +}; + +const ptrCast = struct { + const std = @import("std"); + pub fn main() !void { + // #region ptr_cast + const bytes align(@alignOf(u32)) = [_]u8{ 0x12, 0x12, 0x12, 0x12 }; + // 将 u8数组指针 转换为 u32 类型的指针 + const u32_ptr: *const u32 = @ptrCast(&bytes); + + if (u32_ptr.* == 0x12121212) { + std.debug.print("success\n", .{}); + } + + // 通过标准库转为 u32 + const u32_value = std.mem.bytesAsSlice(u32, bytes[0..])[0]; + + if (u32_value == 0x12121212) { + std.debug.print("success\n", .{}); + } + + // 通过内置函数转换 + if (@as(u32, @bitCast(bytes)) == 0x12121212) { + std.debug.print("success\n", .{}); + } + // #endregion ptr_cast + } +}; diff --git a/course/code/15/reflection.zig b/course/code/15/reflection.zig new file mode 100644 index 00000000..17526051 --- /dev/null +++ b/course/code/15/reflection.zig @@ -0,0 +1,272 @@ +pub fn main() !void { + typeName.main(); + typeInfo.main(); + hasDecl.main(); + hasField.main(); + + Field.main(); + fieldParentPtr.main(); + call.main(); + Type.main(); +} + +test "all" { + _ = NoEffects; + _ = TypeInfo2; + _ = TypeInfo3; +} + +const NoEffects = struct { + // #region no_effects + const std = @import("std"); + const expect = std.testing.expect; + + test "no runtime side effects" { + var data: i32 = 0; + const T = @TypeOf(foo(i32, &data)); + try comptime expect(T == i32); + try expect(data == 0); + } + + fn foo(comptime T: type, ptr: *T) T { + ptr.* += 1; + return ptr.*; + } + // #endregion no_effects +}; + +const typeName = struct { + // #region typeName + const std = @import("std"); + + const T = struct { + const Y = struct {}; + }; + + pub fn main() void { + std.debug.print("{s}\n", .{@typeName(T)}); + std.debug.print("{s}\n", .{@typeName(T.Y)}); + } + // #endregion typeName +}; + +const typeInfo = struct { + // #region typeInfo + const std = @import("std"); + + const T = struct { + a: u8, + b: u8, + }; + + pub fn main() void { + // 通过 @typeInfo 获取类型信息 + const type_info = @typeInfo(T); + // 断言它为 struct + const struct_info = type_info.@"struct"; + + // inline for 打印该结构体内部字段的信息 + inline for (struct_info.fields) |field| { + std.debug.print("field name is {s}, field type is {}\n", .{ + field.name, + field.type, + }); + } + } + // #endregion typeInfo +}; + +const TypeInfo2 = struct { + // #region TypeInfo2 + const std = @import("std"); + + fn IntToArray(comptime T: type) type { + // 获得类型信息,并断言为Int + const int_info = @typeInfo(T).int; + // 获得Int位数 + const bits = int_info.bits; + // 检查位数是否被8整除 + if (bits % 8 != 0) @compileError("bit count not a multiple of 8"); + // 生成新类型 + return [bits / 8]u8; + } + + test { + try std.testing.expectEqual([1]u8, IntToArray(u8)); + try std.testing.expectEqual([2]u8, IntToArray(u16)); + try std.testing.expectEqual([3]u8, IntToArray(u24)); + try std.testing.expectEqual([4]u8, IntToArray(u32)); + } + // #endregion TypeInfo2 +}; + +const TypeInfo3 = struct { + // #region TypeInfo3 + const std = @import("std"); + + fn ExternAlignOne(comptime T: type) type { + // 获得类型信息,并断言为Struct. + comptime var struct_info = @typeInfo(T).@"struct"; + // 将内存布局改为 extern + struct_info.layout = .@"extern"; + // 复制字段信息(原为只读切片,故需复制) + comptime var new_fields = struct_info.fields[0..struct_info.fields.len].*; + // 修改每个字段对齐为1 + inline for (&new_fields) |*f| f.alignment = 1; + // 替换字段定义 + struct_info.fields = &new_fields; + // 重新构造类型 + return @Type(.{ .@"struct" = struct_info }); + } + + const MyStruct = struct { + a: u32, + b: u32, + }; + + test { + const NewType = ExternAlignOne(MyStruct); + try std.testing.expectEqual(4, @alignOf(MyStruct)); + try std.testing.expectEqual(1, @alignOf(NewType)); + } + // #endregion TypeInfo3 +}; + +const hasDecl = struct { + // #region hasDecl + const std = @import("std"); + + const Foo = struct { + nope: i32, + + pub var blah = "xxx"; + const hi = 1; + }; + + pub fn main() void { + // true + std.debug.print("blah:{}\n", .{@hasDecl(Foo, "blah")}); + // true + // hi 此声明可以被检测到是因为类型和代码处于同一个文件中,这导致他们之间可以互相访问 + // 换另一个文件就不行了 + std.debug.print("hi:{}\n", .{@hasDecl(Foo, "hi")}); + // false 不检查字段 + std.debug.print("nope:{}\n", .{@hasDecl(Foo, "nope")}); + // false 没有对应的声明 + std.debug.print("nope1234:{}\n", .{@hasDecl(Foo, "nope1234")}); + } + // #endregion hasDecl +}; + +const hasField = struct { + // #region hasField + const std = @import("std"); + + const Foo = struct { + nope: i32, + + pub var blah = "xxx"; + const hi = 1; + }; + + pub fn main() void { + // false + std.debug.print("blah:{}\n", .{@hasField(Foo, "blah")}); + // false + std.debug.print("hi:{}\n", .{@hasField(Foo, "hi")}); + // true + std.debug.print("nope:{}\n", .{@hasField(Foo, "nope")}); + // false + std.debug.print("nope1234:{}\n", .{@hasField(Foo, "nope1234")}); + } + // #endregion hasField +}; + +const Field = struct { + // #region Field + const std = @import("std"); + + const Point = struct { + x: u32, + y: u32, + + pub var z: u32 = 1; + }; + + pub fn main() void { + var p = Point{ .x = 0, .y = 0 }; + + @field(p, "x") = 4; + @field(p, "y") = @field(p, "x") + 1; + // x is 4, y is 5 + std.debug.print("x is {}, y is {}\n", .{ p.x, p.y }); + + // Point's z is 1 + std.debug.print("Point's z is {}\n", .{@field(Point, "z")}); + } + // #endregion Field +}; + +const fieldParentPtr = struct { + // #region fieldParentPtr + const std = @import("std"); + + const Point = struct { + x: u32, + }; + + pub fn main() void { + var p = Point{ .x = 0 }; + + const res = &p == @as(*Point, @fieldParentPtr("x", &p.x)); + + // test is true + std.debug.print("test is {}\n", .{res}); + } + // #endregion fieldParentPtr +}; + +const call = struct { + // #region call + const std = @import("std"); + + fn add(a: i32, b: i32) i32 { + return a + b; + } + + pub fn main() void { + std.debug.print("call function add, the result is {}\n", .{@call(.auto, add, .{ 1, 2 })}); + } + // #endregion call +}; + +const Type = struct { + // #region Type + const std = @import("std"); + + const T = @Type(.{ + .@"struct" = .{ + .layout = .auto, + .fields = &.{ + .{ + .alignment = 8, + .name = "b", + .type = u32, + .is_comptime = false, + .default_value_ptr = null, + }, + }, + .decls = &.{}, + .is_tuple = false, + }, + }); + + pub fn main() void { + const D = T{ + .b = 666, + }; + + std.debug.print("{}\n", .{D.b}); + } + // #endregion Type +}; diff --git a/course/code/15/slice.zig b/course/code/15/slice.zig new file mode 100644 index 00000000..ff75aa21 --- /dev/null +++ b/course/code/15/slice.zig @@ -0,0 +1,69 @@ +pub fn main() !void { + Basic.main(); + PointerSlice.main(); + TerminatedSlice.main(); +} + +const Basic = struct { + // #region basic_more + const print = @import("std").debug.print; + + pub fn main() void { + // #region basic + var array = [_]i32{ 1, 2, 3, 4 }; + + const len: usize = 3; + const slice: []i32 = array[0..len]; + + for (slice, 0..) |ele, index| { + print("第{}个元素为:{}\n", .{ index + 1, ele }); + } + print("slice 类型为{}\n", .{@TypeOf(slice)}); + + const slice_2: []i32 = array[0..array.len]; + print("slice_2 类型为{}\n", .{@TypeOf(slice_2)}); + // #endregion basic + } + // #endregion basic_more +}; + +const PointerSlice = struct { + // #region pointer_slice_more + const print = @import("std").debug.print; + + pub fn main() void { + // #region pointer_slice + var array = [_]i32{ 1, 2, 3, 4 }; + + // 边界使用变量,保证切片不会被优化为数组指针 + var len: usize = 3; + _ = &len; + + var slice = array[0..len]; + + print("slice 类型为{}\n", .{@TypeOf(slice)}); + print("slice.ptr 类型为{}\n", .{@TypeOf(slice.ptr)}); + print("slice 的索引 0 取地址,得到指针类型为{}\n", .{@TypeOf(&slice[0])}); + // #endregion pointer_slice + } + // #endregion pointer_slice_more +}; + +const TerminatedSlice = struct { + // #region terminated_slice_more + const print = @import("std").debug.print; + + pub fn main() void { + // #region terminated_slice + // 显式声明切片类型 + const str_slice: [:0]const u8 = "hello"; + print("str_slice类型:{}\n", .{@TypeOf(str_slice)}); + + var array = [_]u8{ 3, 2, 1, 0, 3, 2, 1, 0 }; + const runtime_length: usize = 3; + const slice: [:0]u8 = array[0..runtime_length :0]; + print("slice类型:{}\n", .{@TypeOf(slice)}); + // #endregion terminated_slice + } + // #endregion terminated_slice_more +}; diff --git a/course/code/15/string.zig b/course/code/15/string.zig new file mode 100644 index 00000000..ad347839 --- /dev/null +++ b/course/code/15/string.zig @@ -0,0 +1,80 @@ +pub fn main() !void { + StringType.main(); + String.main(); + MultilineString.main(); +} + +const StringType = struct { + // #region string_type + const print = @import("std").debug.print; + pub fn main() void { + const foo = "banana"; + print("{}\n", .{@TypeOf(foo)}); + } + // #endregion string_type +}; + +const String = struct { + // #region string + const print = @import("std").debug.print; + const mem = @import("std").mem; // 用于比较字节 + + pub fn main() void { + const bytes = "hello"; + print("{}\n", .{@TypeOf(bytes)}); // *const [5:0]u8 + print("{d}\n", .{bytes.len}); // 5 + print("{c}\n", .{bytes[1]}); // 'e' + print("{d}\n", .{bytes[5]}); // 0 + print("{}\n", .{'e' == '\x65'}); // true + print("{d}\n", .{'\u{1f4a9}'}); // 128169 + print("{d}\n", .{'💯'}); // 128175 + print("{u}\n", .{'⚡'}); + print("{}\n", .{mem.eql(u8, "hello", "h\x65llo")}); // true + print("{}\n", .{mem.eql(u8, "💯", "\xf0\x9f\x92\xaf")}); // true + const invalid_utf8 = "\xff\xfe"; // 非UTF-8 字符串可以使用\xNN. + print("0x{x}\n", .{invalid_utf8[1]}); // 索引它们会返回独立的字节 + print("0x{x}\n", .{"💯"[1]}); + } + // #endregion string +}; + +const MultilineString = struct { + // #region multiline_string + const print = @import("std").debug.print; + + pub fn main() void { + const hello_world_in_c = + \\#include + \\ + \\int main(int argc, char **argv) { + \\ printf("hello world\n"); + \\ return 0; + \\} + ; + print("{s}\n", .{hello_world_in_c}); + } + // #endregion multiline_string +}; + +const PrintString = struct { + // 注意:这个不用测试,因为它本来就是错误示例 + // #region print_string_err + const std = @import("std"); + + pub fn main() void { + funnyPrint("banana"); + } + + fn funnyPrint(msg: []u8) void { + std.debug.print("*farts*, {s}", .{msg}); + } + // #endregion print_string_err +}; + +const DefineString = struct { + // #region define_string + const message_1 = "hello"; + const message_2 = [_]u8{ 'h', 'e', 'l', 'l', 'o' }; + const message_3: []const u8 = &.{ 'h', 'e', 'l', 'l', 'o' }; + // #endregion define_string +}; diff --git a/course/code/15/struct.zig b/course/code/15/struct.zig new file mode 100644 index 00000000..03bf5808 --- /dev/null +++ b/course/code/15/struct.zig @@ -0,0 +1,444 @@ +pub fn main() !void { + Struct.main(); + SelfReference1.main(); + SelfReference2.main(); + try SelfReference3.main(); + AutoReference.main(); + DefaultField.main(); + EmptyStruct.main(); + Tuple_.main(); + NamePrinciple.main(); + try PackedBitOffset.main(); + try PackedCast.main(); + DestructTuple.main(); +} + +const StructAllDefault = struct { + // #region all_default + const Threshold = struct { + minimum: f32, + maximum: f32, + + // 选择声明一个默认值 + const default: Threshold = .{ + .minimum = 0.25, + .maximum = 0.75, + }; + }; + + pub fn main() !void { + const std = @import("std"); + // 初始化时直接使用默认值 + const threshold: Threshold = .default; + std.debug.print("minimum is %d, maximum is %d", .{ threshold.minimum, threshold.maximum }); + } + + // #endregion all_default +}; + +const Struct = struct { + // #region more_struct + const std = @import("std"); + + // #region default_struct + const Circle = struct { + radius: u8, + + const PI: f16 = 3.14; + + pub fn init(radius: u8) Circle { + return Circle{ .radius = radius }; + } + + fn area(self: *Circle) f16 { + return @as(f16, @floatFromInt(self.radius * self.radius)) * PI; + } + }; + // #endregion default_struct + + pub fn main() void { + const radius: u8 = 5; + var circle = Circle.init(radius); + std.debug.print("The area of a circle with radius {} is {d:.2}\n", .{ radius, circle.area() }); + } + // #endregion more_struct +}; + +const SelfReference1 = struct { + // #region more_self_reference1 + const std = @import("std"); + + // #region deault_self_reference1 + const TT = struct { + pub fn print(self: *TT) void { + _ = self; // _ 表示不使用变量 + std.debug.print("Hello, world!\n", .{}); + } + }; + // #endregion deault_self_reference1 + + pub fn main() void { + var tmp: TT = .{}; + tmp.print(); + } + // #endregion more_self_reference1 +}; + +const SelfReference2 = struct { + // #region more_self_reference2 + const std = @import("std"); + + // #region deault_self_reference2 + fn List(comptime T: type) type { + return struct { + const Self = @This(); + + items: []T, + + fn length(self: Self) usize { + return self.items.len; + } + }; + } + // #endregion deault_self_reference2 + + pub fn main() void { + const int_list = List(u8); + var arr: [5]u8 = .{ + 1, 2, 3, 4, 5, + }; + + var list: int_list = .{ + .items = &arr, + }; + + std.debug.print("list len is {}\n", .{list.length()}); + } + // #endregion more_self_reference2 +}; + +const DestructTuple = struct { + pub fn main() void { + // #region destruct_tuple + const print = @import("std").debug.print; + + var x: u32 = undefined; + var y: u32 = undefined; + var z: u32 = undefined; + + const tuple = .{ 1, 2, 3 }; + + x, y, z = tuple; + + print("tuple: x = {}, y = {}, z = {}\n", .{ x, y, z }); + // #endregion destruct_tuple + } +}; + +const SelfReference3 = struct { + // #region more_self_reference3 + const std = @import("std"); + + var gpa = std.heap.GeneralPurposeAllocator(.{}){}; + + // #region deault_self_reference3 + const User = struct { + userName: []u8, + password: []u8, + email: []u8, + active: bool, + + pub const writer = "zig-course"; + + pub fn init(userName: []u8, password: []u8, email: []u8, active: bool) User { + return User{ + .userName = userName, + .password = password, + .email = email, + .active = active, + }; + } + + pub fn print(self: *User) void { + std.debug.print( + \\username: {s} + \\password: {s} + \\email: {s} + \\active: {} + \\ + , .{ + self.userName, + self.password, + self.email, + self.active, + }); + } + }; + // #endregion deault_self_reference3 + + const name = "xiaoming"; + const passwd = "123456"; + const mail = "123456@qq.com"; + + pub fn main() !void { + // 我们在这里使用了内存分配器的知识,如果你需要的话,可以提前跳到内存管理进行学习! + const allocator = gpa.allocator(); + defer { + const deinit_status = gpa.deinit(); + if (deinit_status == .leak) std.testing.expect(false) catch @panic("TEST FAIL"); + } + + const username = try allocator.alloc(u8, 20); + defer allocator.free(username); + + // @memset 是一个内存初始化函数,它会将一段内存初始化为 0 + @memset(username, 0); + // @memcpy 是一个内存拷贝函数,它会将一个内存区域的内容拷贝到另一个内存区域 + @memcpy(username[0..name.len], name); + + const password = try allocator.alloc(u8, 20); + defer allocator.free(password); + + @memset(password, 0); + @memcpy(password[0..passwd.len], passwd); + + const email = try allocator.alloc(u8, 20); + defer allocator.free(email); + + @memset(email, 0); + @memcpy(email[0..mail.len], mail); + + var user = User.init(username, password, email, true); + user.print(); + } + // #endregion more_self_reference3 +}; + +const AutoReference = struct { + pub fn main() void { + // #region auto_reference + const Point = struct { x: i32, y: i32 }; + + const pt: Point = .{ + .x = 13, + .y = 67, + }; + // #endregion auto_reference + + _ = pt; + } +}; + +// #region linked_list +fn LinkedList(comptime T: type) type { + return struct { + pub const Node = struct { + // 这里我们提前使用了可选类型,如有需要可以提前跳到可选类型部分学习! + prev: ?*Node, + next: ?*Node, + data: T, + }; + + first: ?*Node, + last: ?*Node, + len: usize, + }; +} +// #endregion linked_list + +const DefaultField = struct { + pub fn main() void { + // #region default_field + const Foo = struct { + a: i32 = 1234, + b: i32, + }; + + const x = Foo{ + .b = 5, + }; + // #endregion default_field + _ = x; + } +}; + +const EmptyStruct = struct { + // #region more_empty_struct + const std = @import("std"); + + // #region default_empty_struct + const Empty = struct {}; + // #endregion default_empty_struct + + pub fn main() void { + std.debug.print("{}\n", .{@sizeOf(Empty)}); + } + // #endregion more_empty_struct +}; + +const BasePtr = struct { + // #region base_ptr + const Point = struct { + x: f32, + y: f32, + }; + + fn setYBasedOnX(x: *f32, y: f32) void { + const point: Point = @fieldParentPtr("x", x); + point.y = y; + } + // #endregion base_ptr +}; + +const Tuple_ = struct { + pub fn main() void { + // #region tuple + // 我们定义了一个元组类型 + const Tuple = struct { u8, u8 }; + + // 直接使用字面量来定义一个元组 + const values = .{ + @as(u32, 1234), + @as(f64, 12.34), + true, + "hi", + }; + // 值得注意的是,values的类型和Tuple仅仅是结构相似,但不是同一类型! + // 因为values的类型是由编译器在编译期间自行推导出来的。 + + const hi = values.@"3"; // "hi" + // #endregion tuple + _ = hi; + _ = Tuple; + } +}; + +const NamePrinciple = struct { + // #region name_principle + const std = @import("std"); + + pub fn main() void { + const Foo = struct {}; + std.debug.print("variable: {s}\n", .{@typeName(Foo)}); + std.debug.print("anonymous: {s}\n", .{@typeName(struct {})}); + std.debug.print("function: {s}\n", .{@typeName(List(i32))}); + } + + fn List(comptime T: type) type { + return struct { + x: T, + }; + } + // #endregion name_principle +}; + +const PackedBitOffset = struct { + // #region packed_bit_offset + const std = @import("std"); + const expect = std.testing.expect; + + const BitField = packed struct { + a: u3, + b: u3, + c: u2, + }; + + pub fn main() !void { + // @bitOffsetOf 用于获取位域的偏移量(即偏移几位) + try expect(@bitOffsetOf(BitField, "a") == 0); + try expect(@bitOffsetOf(BitField, "b") == 3); + try expect(@bitOffsetOf(BitField, "c") == 6); + + // @offsetOf 用于获取字段的偏移量(即偏移几个字节) + try expect(@offsetOf(BitField, "a") == 0); + try expect(@offsetOf(BitField, "b") == 0); + try expect(@offsetOf(BitField, "c") == 0); + } + // #endregion packed_bit_offset +}; + +const PackedCast = struct { + // #region packed_cast + const std = @import("std"); + // 这里获取目标架构是字节排序方式,大端和小端 + const native_endian = @import("builtin").target.cpu.arch.endian(); + const expect = std.testing.expect; + + const Full = packed struct { + number: u16, + }; + + const Divided = packed struct { + half1: u8, + quarter3: u4, + quarter4: u4, + }; + + fn doTheTest() !void { + try expect(@sizeOf(Full) == 2); + try expect(@sizeOf(Divided) == 2); + + const full = Full{ .number = 0x1234 }; + const divided: Divided = @bitCast(full); + + try expect(divided.half1 == 0x34); + try expect(divided.quarter3 == 0x2); + try expect(divided.quarter4 == 0x1); + + const ordered: [2]u8 = @bitCast(full); + + switch (native_endian) { + .big => { + try expect(ordered[0] == 0x12); + try expect(ordered[1] == 0x34); + }, + .little => { + try expect(ordered[0] == 0x34); + try expect(ordered[1] == 0x12); + }, + } + } + + pub fn main() !void { + try doTheTest(); + try comptime doTheTest(); + } + // #endregion packed_cast +}; + +const aligned_struct = struct { + // #region aligned_struct + const std = @import("std"); + const expect = std.testing.expect; + + const S = packed struct { + a: u32, + b: u32, + }; + test "overaligned pointer to packed struct" { + var foo: S align(4) = .{ .a = 1, .b = 2 }; + const ptr: *align(4) S = &foo; + const ptr_to_b: *u32 = &ptr.b; + try expect(ptr_to_b.* == 2); + } + // #endregion aligned_struct +}; + +const reorder_struct = struct { + // #region reorder_struct + const std = @import("std"); + + const Foo = packed struct { + x: i32, + y: [*]i32, // 一个多项指针 + }; + + pub fn main() !void { + std.debug.print("{any}\n", .{@sizeOf(Foo)}); + std.debug.print("{any}\n", .{@bitSizeOf(Foo) / 8}); + + std.debug.print("{any}\n", .{@bitOffsetOf(Foo, "x") / 8}); + std.debug.print("{any}\n", .{@bitOffsetOf(Foo, "y") / 8}); + } + // #endregion reorder_struct +}; diff --git a/course/code/15/switch.zig b/course/code/15/switch.zig new file mode 100644 index 00000000..98bad14b --- /dev/null +++ b/course/code/15/switch.zig @@ -0,0 +1,283 @@ +pub fn main() !void { + Basic.main(); + try Advanced.main(); + Expression.main(); + Catch_tagUnion.main(); + AutoRefer.main(); + try LabeledSwitch1.main(); + try LabeledSwitch2.main(); +} + +const Basic = struct { + // #region basic_more + const std = @import("std"); + const print = std.debug.print; + + pub fn main() void { + // #region basic + const num: u8 = 5; + switch (num) { + 5 => { + print("this is 5\n", .{}); + }, + else => { + print("this is not 5\n", .{}); + }, + } + // #endregion basic + } + // #endregion basic_more +}; + +const Advanced = struct { + const std = @import("std"); + const expect = std.testing.expect; + pub fn main() !void { + // #region advanced + const a: u64 = 10; + const zz: u64 = 103; + + // 作为表达式使用 + const b = switch (a) { + // 多匹配项 + 1, 2, 3 => 0, + + // 范围匹配 + 5...100 => 1, + + // tag形式的分配匹配,可以任意复杂 + 101 => blk: { + const c: u64 = 5; + // 下一行代表返回到blk这个tag处 + break :blk c * 2 + 1; + }, + + zz => zz, + // 支持编译期运算 + blk: { + const d: u32 = 5; + const e: u32 = 100; + break :blk d + e; + } => 107, + + // else 匹配剩余的分支 + else => 9, + }; + + try expect(b == 1); + // #endregion advanced + } +}; + +const Expression = struct { + // #region expression_more + const builtin = @import("builtin"); + + pub fn main() void { + // #region expression + const os_msg = switch (builtin.target.os.tag) { + .linux => "we found a linux user", + else => "not a linux user", + }; + // #endregion expression + _ = os_msg; + } + // #endregion expression_more +}; + +const Catch_tagUnion = struct { + const std = @import("std"); + pub fn main() void { + // #region catch_tag_union + // 定义两个结构体 + const Point = struct { + x: u8, + y: u8, + }; + const Item = union(enum) { + a: u32, + c: Point, + d, + e: u32, + }; + + var a = Item{ .c = Point{ .x = 1, .y = 2 } }; + + const b = switch (a) { + // 多个匹配 + Item.a, Item.e => |item| item, + + // 可以使用 * 语法来捕获对应的指针进行修改操作 + Item.c => |*item| blk: { + item.*.x += 1; + break :blk 6; + }, + + // 这里最后一个联合类型,匹配已经穷尽了,我们就不需要使用else了 + Item.d => 8, + }; + + std.debug.print("{any}\n", .{b}); + // #endregion catch_tag_union + } +}; + +const AutoRefer = struct { + pub fn main() void { + // #region auto_refer + const Color = enum { + auto, + off, + on, + }; + const color = Color.off; + // 编译器会帮我们完成其余的工作 + const result = switch (color) { + .auto => false, + .on => false, + .off => true, + }; + // #endregion auto_refer + + _ = result; + } +}; + +// #region isFieldOptional +// 这段函数用来判断一个结构体的字段是否是 optional,同时它也是 comptime 的 +// 故我们可以在下面使用inline 来要求编译器帮我们展开这个switch +fn isFieldOptional(comptime T: type, field_index: usize) !bool { + const fields = @typeInfo(T).Struct.fields; + return switch (field_index) { + // 这里每次都是不同的值 + inline 0...fields.len - 1 => |idx| { + return @typeInfo(fields[idx].type) == .Optional; + }, + else => return error.IndexOutOfBounds, + }; +} +// #endregion isFieldOptional + +// #region withSwitch +const AnySlice = union(enum) { + a: u8, + b: i8, + c: bool, + d: []u8, +}; + +fn withSwitch(any: AnySlice) usize { + return switch (any) { + // 这里的 slice 可以匹配所有的 Anyslice 类型 + inline else => |slice| _ = slice, + }; +} +// #endregion withSwitch + +// #region catch_tag_union_value +const U = union(enum) { + a: u32, + b: f32, +}; + +fn getNum(u: U) u32 { + switch (u) { + // 这里 num 是一个运行时可知的值 + // 而 tag 则是对应的标签名,这是编译期可知的 + inline else => |num, tag| { + if (tag == .b) { + return @intFromFloat(num); + } + return num; + }, + } +} +// #endregion catch_tag_union_value + +const LabeledSwitch1 = struct { + pub fn main() !void { + // #region labeled_switch_1 + sw: switch (@as(i32, 5)) { + 5 => continue :sw 4, + + // `continue` can occur multiple times within a single switch prong. + 2...4 => |v| { + if (v > 3) { + continue :sw 2; + } else if (v == 3) { + + // `break` can target labeled loops. + break :sw; + } + + continue :sw 1; + }, + + 1 => return, + + else => unreachable, + } + // #endregion labeled_switch_1 + } +}; + +const LabeledSwitch2 = struct { + pub fn main() !void { + // #region labeled_switch_2 + var sw: i32 = 5; + while (true) { + switch (sw) { + 5 => { + sw = 4; + continue; + }, + 2...4 => |v| { + if (v > 3) { + sw = 2; + continue; + } else if (v == 3) { + break; + } + + sw = 1; + continue; + }, + 1 => return, + else => unreachable, + } + } + // #endregion labeled_switch_2 + } +}; + +// #region vm +const Instruction = enum { + add, + mul, + end, +}; + +fn evaluate(initial_stack: []const i32, code: []const Instruction) !i32 { + const std = @import("std"); + var stack = try std.BoundedArray(i32, 8).fromSlice(initial_stack); + var ip: usize = 0; + + return vm: switch (code[ip]) { + // Because all code after `continue` is unreachable, this branch does + // not provide a result. + .add => { + try stack.append(stack.pop().? + stack.pop().?); + + ip += 1; + continue :vm code[ip]; + }, + .mul => { + try stack.append(stack.pop().? * stack.pop().?); + + ip += 1; + continue :vm code[ip]; + }, + .end => stack.pop().?, + }; +} +// #endregion vm diff --git a/course/code/15/type-cast.zig b/course/code/15/type-cast.zig new file mode 100644 index 00000000..a400d10d --- /dev/null +++ b/course/code/15/type-cast.zig @@ -0,0 +1,276 @@ +pub fn main() !void { + try tag_union_enum.main(); + try peer_resolution_2.main(); + try peer_resolution_3.main(); + try peer_resolution_4.main(); + try peer_resolution_5.main(); + try peer_resolution_7.main(); +} + +const widen = struct { + // #region widen + const a: u8 = 250; + const b: u16 = a; + const c: u32 = b; + const d: u64 = c; + const e: u64 = d; + const f: u128 = e; + // f 和 a 是相等的 + + const g: u8 = 250; + const h: i16 = h; + // g 和 h 相等 + + const i: f16 = 12.34; + const j: f32 = i; + const k: f64 = j; + const l: f128 = k; + // i 和 l 相等 + // #endregion widen +}; + +const pointer_arr_slice_1 = struct { + // #region pointer_arr_slice_1 + const x1: []const u8 = "hello"; + const x2: []const u8 = &[5]u8{ 'h', 'e', 'l', 'l', 111 }; + // x1 和 x2 相等 + + const y1: anyerror![]const u8 = "hello"; + const y2: anyerror![]const u8 = &[5]u8{ 'h', 'e', 'l', 'l', 111 }; + // 是错误联合类型时,也有效 + + const z1: ?[]const u8 = "hello"; + const z2: ?[]const u8 = &[5]u8{ 'h', 'e', 'l', 'l', 111 }; + // 可选类型也有效果 + + const a1: anyerror!?[]const u8 = "hello"; + const a2: anyerror!?[]const u8 = &[5]u8{ 'h', 'e', 'l', 'l', 111 }; + // 错误联合可选类型也有效 + // #endregion pointer_arr_slice_1 +}; + +const pointer_arr_slice_2 = struct { + // #region pointer_arr_slice_2 + var buf: [5]u8 = "hello".*; + const x: []u8 = &buf; + + const buf2 = [2]f32{ 1.2, 3.4 }; + const x2: []const f32 = &buf2; + // #endregion pointer_arr_slice_2 +}; + +const pointer_arr_slice_3 = struct { + // #region pointer_arr_slice_3 + var buf: [5]u8 = "hello".*; + const x: [*]u8 = &buf; + + var buf2: [5]u8 = "hello".*; + const x2: ?[*]u8 = &buf2; + // 可选类型也有效 + + var buf3: [5]u8 = "hello".*; + const x3: anyerror![*]u8 = &buf3; + // 联合错误类型也有效 + + var buf4: [5]u8 = "hello".*; + const x4: anyerror!?[*]u8 = &buf4; + // 联合错误可选类型也有效 + // #endregion pointer_arr_slice_3 +}; + +const pointer_arr_slice_4 = struct { + // #region pointer_arr_slice_4 + var x: i32 = 1234; + const y: *[1]i32 = &x; + const z: [*]i32 = y; + // 先转为长度为 1 的数组指针,再转换为多项指针。 + // 如果 x 直接赋值给 z,则编译器会报错 + // #endregion pointer_arr_slice_4 +}; + +const optional_payload = struct { + // #region optional_payload + const y: ?i32 = null; + const y1: anyerror!?i32 = null; + // 错误联合可选类型也可以 + // #endregion optional_payload + + // #region error_union + const z: anyerror!i32 = error.Failure; + // #endregion error_union +}; + +const comptime_integer = struct { + // #region comptime_integer + const x: u64 = 255; + const y: u8 = x; + // 自动转换到 u8 + // #endregion comptime_integer +}; + +const tag_union_enum = struct { + // #region tag_union_enum + const std = @import("std"); + const expect = std.testing.expect; + + const E = enum { + one, + two, + three, + }; + + const U = union(E) { + one: i32, + two: f32, + three, + }; + + const U2 = union(enum) { + a: void, + b: f32, + + fn tag(self: U2) usize { + switch (self) { + .a => return 1, + .b => return 2, + } + } + }; + + pub fn main() !void { + const u = U{ .two = 12.34 }; + const e: E = u; // 将联合类型转换为枚举 + try expect(e == E.two); + + const three = E.three; + // 将枚举转换为联合类型,注意这里 three 并没有对应的类型,故可以直接转换 + const u_2: U = three; + try expect(u_2 == E.three); + + const u_3: U = .three; // 字面量供 zig 编译器来自动推导 + try expect(u_3 == E.three); + + const u_4: U2 = .a; // 字面量供 zig 编译器来推导,a 也是没有对应的类型(void) + try expect(u_4.tag() == 1); + + // 下面的 b 字面量推导是错误的,因为它有对应的类型 f32 + //var u_5: U2 = .b; + //try expect(u_5.tag() == 2); + } + // #endregion tag_union_enum +}; + +const tuple_arr = struct { + // #region tuple_arr + const Tuple = struct { u8, u8 }; + + const tuple: Tuple = .{ 5, 6 }; + // 一切都是自动完成的 + const array: [2]u8 = tuple; + // #endregion tuple_arr +}; + +const peer_resolution_1 = struct { + // #region peer_resolution_1 + const a: i8 = 12; + const b: i16 = 34; + const c = a + b; + // c 的类型是 u16 + // #endregion peer_resolution_1 +}; + +const peer_resolution_2 = struct { + // #region peer_resolution_2 + const std = @import("std"); + const expect = std.testing.expect; + const mem = std.mem; + + pub fn main() !void { + // mem.eql 执行检查内存是否相等 + try expect(mem.eql(u8, boolToStr(true), "true")); + try expect(mem.eql(u8, boolToStr(false), "false")); + try comptime expect(mem.eql(u8, boolToStr(true), "true")); + try comptime expect(mem.eql(u8, boolToStr(false), "false")); + } + + fn boolToStr(b: bool) []const u8 { + return if (b) "true" else "false"; + } + // #endregion peer_resolution_2 +}; + +const peer_resolution_3 = struct { + // #region peer_resolution_3 + const std = @import("std"); + const expect = std.testing.expect; + const mem = std.mem; + + pub fn main() !void { + try testPeerResolveArrayConstSlice(true); + // 上面这个语句执行会成功 + } + + fn testPeerResolveArrayConstSlice(b: bool) !void { + const value1 = if (b) "aoeu" else @as([]const u8, "zz"); + const value2 = if (b) @as([]const u8, "zz") else "aoeu"; + try expect(mem.eql(u8, value1, "aoeu")); + try expect(mem.eql(u8, value2, "zz")); + } + // #endregion peer_resolution_3 +}; + +const peer_resolution_4 = struct { + // #region peer_resolution_4 + pub fn main() !void { + // 下面语句执行为 true + _ = peerTypeTAndOptionalT(true, false).? == 0; + } + fn peerTypeTAndOptionalT(c: bool, b: bool) ?usize { + if (c) { + return if (b) null else @as(usize, 0); + } + + return @as(usize, 3); + } + // #endregion peer_resolution_4 +}; + +const peer_resolution_5 = struct { + // #region peer_resolution_5 + fn peerTypeEmptyArrayAndSlice(a: bool, slice: []const u8) []const u8 { + if (a) { + return &[_]u8{}; + } + + return slice[0..1]; + } + + pub fn main() !void { + // 以下两句均为true + _ = peerTypeEmptyArrayAndSlice(true, "hi").len == 0; + _ = peerTypeEmptyArrayAndSlice(false, "hi").len == 1; + } + // #endregion peer_resolution_5 +}; + +const peer_resolution_6 = struct { + // #region peer_resolution_6 + fn peerTypeEmptyArrayAndSliceAndError(a: bool, slice: []u8) anyerror![]u8 { + if (a) { + return &[_]u8{}; + } + + return slice[0..1]; + } + // #endregion peer_resolution_6 +}; + +const peer_resolution_7 = struct { + pub fn main() !void { + // #region peer_resolution_7 + const a: *const usize = @ptrFromInt(0x123456780); + const b: ?*usize = @ptrFromInt(0x123456780); + _ = a == b; // 这个表达式的值为 true + // #endregion peer_resolution_7 + } +}; diff --git a/course/code/15/union.zig b/course/code/15/union.zig new file mode 100644 index 00000000..be251e64 --- /dev/null +++ b/course/code/15/union.zig @@ -0,0 +1,136 @@ +pub fn main() !void { + try Basic.main(); + try Tag.main(); + try CapturePayload.main(); + TagName.main(); +} + +const Basic = struct { + // #region more_basic + const print = @import("std").debug.print; + + // #region default_basic + const Payload = union { + int: i64, + float: f64, + boolean: bool, + }; + + pub fn main() !void { + var payload = Payload{ .int = 1234 }; + payload = Payload{ .int = 9 }; + // var payload_1: Payload = .{ .int = 1234 }; + + print("{}\n", .{payload.int}); + } + // #endregion default_basic + // #endregion more_basic +}; + +const UnionInit = struct { + // #region union_init + const Payload = union { + int: i64, + float: f64, + boolean: bool, + }; + // 通过 @unionInit 初始化一个联合类型 + const payload = @unionInit(Payload, "int", 666); + // #endregion union_init +}; + +const Tag = struct { + // #region more_tag + const std = @import("std"); + const expect = std.testing.expect; + + pub fn main() !void { + // #region default_tag + // 一个枚举,用于给联合类型挂上标记 + const ComplexTypeTag = enum { + ok, + not_ok, + }; + + // 带标记的联合类型 + const ComplexType = union(ComplexTypeTag) { + ok: u8, + not_ok: void, + }; + + const c = ComplexType{ .ok = 42 }; + // 可以直接将标记联合类型作为枚举来使用,这是合法的 + try expect(@as(ComplexTypeTag, c) == ComplexTypeTag.ok); + + // 使用 switch 进行匹配 + switch (c) { + ComplexTypeTag.ok => |value| try expect(value == 42), + ComplexTypeTag.not_ok => unreachable, + } + + // 使用 zig 的 meta 库获取对应的 tag + try expect(std.meta.Tag(ComplexType) == ComplexTypeTag); + // #endregion default_tag + } + // #endregion more_tag +}; + +const CapturePayload = struct { + // #region more_capture_payload + const std = @import("std"); + const expect = std.testing.expect; + + pub fn main() !void { + // #region default_capture_payload + // 枚举,用于给联合类型打上标记 + const ComplexTypeTag = enum { + ok, + not_ok, + }; + + // 带标记的联合类型 + const ComplexType = union(ComplexTypeTag) { + ok: u8, + not_ok: void, + }; + + var c = ComplexType{ .ok = 42 }; + + // 使用 switch 进行匹配 + switch (c) { + // 捕获了标记联合值的指针,用于修改值 + ComplexTypeTag.ok => |*value| value.* += 1, + ComplexTypeTag.not_ok => unreachable, + } + + try expect(c.ok == 43); + // #endregion default_capture_payload + } + // #endregion more_capture_payload +}; + +const TagName = struct { + pub fn main() void { + // #region tag_name + const Small2 = union(enum) { + a: i32, + b: bool, + c: u8, + }; + + const name = @tagName(Small2.a); + // 这个返回值将会是 a + // #endregion tag_name + _ = name; + } +}; + +// #region auto_infer +const Number = union { + int: i32, + float: f64, +}; + +// 自动推断 +const i: Number = .{ .int = 42 }; +// #endregion auto_infer diff --git a/course/code/15/unit_test.zig b/course/code/15/unit_test.zig new file mode 100644 index 00000000..acbe65a2 --- /dev/null +++ b/course/code/15/unit_test.zig @@ -0,0 +1,82 @@ +pub fn main() !void {} + +const Basic = struct { + const std = @import("std"); + + test "expect addOne adds one to 41" { + + // 标准库提供了不少有用的函数 + // testing 下的函数均是测试使用的 + // expect 会假定其参数为 true,如果不通过则报告错误 + // try 用于当 expect 返回错误时,直接返回,并通知测试运行器测试结果未通过 + try std.testing.expect(addOne(41) == 42); + } + + test addOne { + // test 的名字也可以使用标识符,例如我们在这里使用的就是函数名字 addOne + try std.testing.expect(addOne(41) == 42); + } + + /// 定义一个函数效果是给传入的参数执行加一操作 + fn addOne(number: i32) i32 { + return number + 1; + } +}; + +const Nestd = struct { + const std = @import("std"); + const expect = std.testing.expect; + + test { + std.testing.refAllDecls(S); + _ = S; + _ = U; + } + + const S = struct { + test "S demo test" { + try expect(true); + } + + const SE = enum { + V, + + // 此处测试由于未被引用,将不会执行. + test "This Test Won't Run" { + try expect(false); + } + }; + }; + + const U = union { // U 被顶层测试块引用了 + s: US, // 并且US在此处被引用,则US容器中的测试块也会被执行测试 + + const US = struct { + test "U.US demo test" { + // This test is a top-level test declaration for the struct. + // The struct is nested (declared) inside of a union. + try expect(true); + } + }; + + test "U demo test" { + try expect(true); + } + }; +}; + +test "all" { + _ = Basic; +} + +const allDecl = struct { + const std = @import("std"); + const builtin = @import("builtin"); + + pub fn refAllDecls(comptime T: type) void { + if (!builtin.is_test) return; + inline for (comptime std.meta.declarations(T)) |decl| { + _ = &@field(T, decl.name); + } + } +}; diff --git a/course/code/15/unreachable.zig b/course/code/15/unreachable.zig new file mode 100644 index 00000000..95ccf4e4 --- /dev/null +++ b/course/code/15/unreachable.zig @@ -0,0 +1,9 @@ +pub fn main() !void { + // #region unreachable + const x = 1; + const y = 2; + if (x + y != 3) { + unreachable; + } + // #endregion unreachable +} diff --git a/course/code/15/vector.zig b/course/code/15/vector.zig new file mode 100644 index 00000000..993ab895 --- /dev/null +++ b/course/code/15/vector.zig @@ -0,0 +1,134 @@ +pub fn main() !void { + Basic.main(); + Splat.main(); + Reduce.main(); + Shuffle.main(); + Select.main(); +} + +const Basic = struct { + // #region basic + const std = @import("std"); + const print = std.debug.print; + + pub fn main() void { + const ele_4 = @Vector(4, i32); + + // 向量必须拥有编译期已知的长度和类型 + const a = ele_4{ 1, 2, 3, 4 }; + const b = ele_4{ 5, 6, 7, 8 }; + + // 执行相加的操作 + const c = a + b; + + print("Vector c is {any}\n", .{c}); + // 以数组索引的语法来访问向量的元素 + print("the third element of Vector c is {}\n", .{c[2]}); + + // 定义一个数组,注意我们这里使用的是浮点类型 + var arr1: [4]f32 = [_]f32{ 1.1, 3.2, 4.5, 5.6 }; + // 直接转换成为一个向量 + const vec: @Vector(4, f32) = arr1; + + print("Vector vec is {any}\n", .{vec}); + + // 将一个切片转换为向量 + const vec2: @Vector(2, f32) = arr1[1..3].*; + print("Vector vec2 is {any}\n", .{vec2}); + } + // #endregion basic +}; + +const Splat = struct { + pub fn main() void { + // #region splat + const scalar: u32 = 5; + const result: @Vector(4, u32) = @splat(scalar); + // #endregion splat + _ = result; + } +}; + +const Deconstruct = struct { + // #region deconstruct + const print = @import("std").debug.print; + + pub fn unpack(x: @Vector(4, f32), y: @Vector(4, f32)) @Vector(4, f32) { + const a, const c, _, _ = x; + const b, const d, _, _ = y; + return .{ a, b, c, d }; + } + + pub fn main() void { + const x: @Vector(4, f32) = .{ 1.0, 2.0, 3.0, 4.0 }; + const y: @Vector(4, f32) = .{ 5.0, 6.0, 7.0, 8.0 }; + print("{}", .{unpack(x, y)}); + } + // #endregion deconstruct +}; + +const Reduce = struct { + const std = @import("std"); + const print = std.debug.print; + + pub fn main() void { + // #region reduce + const V = @Vector(4, i32); + const value = V{ 1, -1, 1, -1 }; + + const result = value > @as(V, @splat(0)); + // result 是 { true, false, true, false }; + + const is_all_true = @reduce(.And, result); + // is_all_true 是 false + // #endregion reduce + print("is_all_true is {}\n", .{is_all_true}); + } +}; +const Shuffle = struct { + const std = @import("std"); + const print = std.debug.print; + + pub fn main() void { + //#region shuffle + const a = @Vector(7, u8){ 'o', 'l', 'h', 'e', 'r', 'z', 'w' }; + const b = @Vector(4, u8){ 'w', 'd', '!', 'x' }; + + const mask1 = @Vector(5, i32){ 2, 3, 1, 1, 0 }; + const res1: @Vector(5, u8) = @shuffle(u8, a, undefined, mask1); + // res1 的值是 hello + + // Combining two vectors + const mask2 = @Vector(6, i32){ -1, 0, 4, 1, -2, -3 }; + const res2: @Vector(6, u8) = @shuffle(u8, a, b, mask2); + // res2 的值是 world! + //#endregion shuffle + _ = res1; + _ = res2; + } +}; + +const Select = struct { + pub fn main() void { + + //#region select + const ele_4 = @Vector(4, i32); + + // 向量必须拥有编译期已知的长度和类型 + const a = ele_4{ 1, 2, 3, 4 }; + const b = ele_4{ 5, 6, 7, 8 }; + + const pred = @Vector(4, bool){ + true, + false, + false, + true, + }; + + const c = @select(i32, pred, a, b); + // c 是 { 1, 6, 7, 4 } + //#endregion select + + _ = c; + } +};