From 1a811538cb9966444072986d67e290aebddb707e Mon Sep 17 00:00:00 2001 From: jiacai2050 Date: Fri, 17 Apr 2026 17:11:37 +0800 Subject: [PATCH 1/7] refactor(zig-cookbook): adopt Zig 0.16 I/O system Refactor main entry points to accept std.process.Init. Migrate file I/O, networking, and crypto operations to use the new std.Io interface. Removes zigcli dependency and bumps minimum Zig version to 0.16.0. --- assets/src/01-01.zig | 11 +++++---- assets/src/01-02.zig | 15 ++++++------ assets/src/01-03.zig | 25 +++++++++----------- assets/src/01-04.zig | 6 ++--- assets/src/01-05.zig | 16 ++++++------- assets/src/02-01.zig | 23 +++++++++--------- assets/src/02-03.zig | 18 +++++++-------- assets/src/03-01.zig | 29 ++++++++++++----------- assets/src/04-01.zig | 39 ++++++++++++++++--------------- assets/src/04-02.zig | 18 ++++++++------- assets/src/04-03.zig | 55 +++++++++++--------------------------------- assets/src/05-01.zig | 13 +++++------ assets/src/05-02.zig | 13 +++++------ assets/src/05-03.zig | 31 ++++++++++++++----------- assets/src/06-01.zig | 7 ++++-- assets/src/07-02.zig | 15 +++++++----- assets/src/07-03.zig | 28 +++++++++------------- assets/src/07-04.zig | 19 +++++++++++---- assets/src/08-02.zig | 39 +++++++++++-------------------- assets/src/10-01.zig | 6 ++--- assets/src/10-02.zig | 19 ++++----------- assets/src/10-03.zig | 6 ++--- assets/src/12-02.zig | 6 ++--- assets/src/12-03.zig | 7 ++---- assets/src/14-03.zig | 6 ++--- build.zig | 54 ++++++++++++++++++++++--------------------- build.zig.zon | 9 ++------ 27 files changed, 240 insertions(+), 293 deletions(-) diff --git a/assets/src/01-01.zig b/assets/src/01-01.zig index a7c2d49..3d5103b 100644 --- a/assets/src/01-01.zig +++ b/assets/src/01-01.zig @@ -1,13 +1,14 @@ const std = @import("std"); -const fs = std.fs; const print = std.debug.print; -pub fn main() !void { - const file = try fs.cwd().openFile("tests/zig-zen.txt", .{}); - defer file.close(); +pub fn main(init: std.process.Init) !void { + const io = init.io; + + const file = try std.Io.Dir.cwd().openFile(io, "tests/zig-zen.txt", .{}); + defer file.close(io); var file_buffer: [4096]u8 = undefined; - var reader = file.reader(&file_buffer); + var reader = file.reader(io, &file_buffer); var line_no: usize = 0; while (try reader.interface.takeDelimiter('\n')) |line| { line_no += 1; diff --git a/assets/src/01-02.zig b/assets/src/01-02.zig index d3a3c9c..27faef9 100644 --- a/assets/src/01-02.zig +++ b/assets/src/01-02.zig @@ -1,33 +1,32 @@ const std = @import("std"); -const fs = std.fs; -const print = std.debug.print; const filename = "/tmp/zig-cookbook-01-02.txt"; -pub fn main() !void { +pub fn main(init: std.process.Init) !void { if (.windows == @import("builtin").os.tag) { std.debug.print("MMap is not supported in Windows\n", .{}); return; } - const file = try fs.cwd().createFile(filename, .{ + const io = init.io; + const file = try std.Io.Dir.cwd().createFile(io, filename, .{ .read = true, .truncate = true, .exclusive = false, // Set to true will ensure this file is created by us }); - defer file.close(); + defer file.close(io); const content_to_write = "hello zig cookbook"; // Before mmap, we need to ensure file isn't empty - try file.setEndPos(content_to_write.len); + try file.setLength(io, content_to_write.len); - const md = try file.stat(); + const md = try file.stat(io); try std.testing.expectEqual(md.size, content_to_write.len); const ptr = try std.posix.mmap( null, content_to_write.len, - std.posix.PROT.READ | std.posix.PROT.WRITE, + .{ .READ = true, .WRITE = true }, .{ .TYPE = .SHARED }, file.handle, 0, diff --git a/assets/src/01-03.zig b/assets/src/01-03.zig index 5ad3df6..3179a0a 100644 --- a/assets/src/01-03.zig +++ b/assets/src/01-03.zig @@ -1,30 +1,27 @@ //! Find files that have been modified in the last 24 hours const std = @import("std"); -const builtin = @import("builtin"); -const fs = std.fs; const print = std.debug.print; -pub fn main() !void { - var gpa = std.heap.GeneralPurposeAllocator(.{}){}; - defer _ = gpa.deinit(); - const allocator = gpa.allocator(); +pub fn main(init: std.process.Init) !void { + const gpa = init.gpa; + const io = init.io; - var iter_dir = try fs.cwd().openDir("src", .{ .iterate = true }); - defer iter_dir.close(); + var iter_dir = try std.Io.Dir.cwd().openDir(io, "src", .{ .iterate = true }); + defer iter_dir.close(io); - var walker = try iter_dir.walk(allocator); + var walker = try iter_dir.walk(gpa); defer walker.deinit(); - const now = std.time.nanoTimestamp(); - while (try walker.next()) |entry| { + const now_ns = std.Io.Clock.real.now(io).nanoseconds; + while (try walker.next(io)) |entry| { if (entry.kind != .file) { continue; } - const stat = try iter_dir.statFile(entry.path); - const last_modified = stat.mtime; - const duration = now - last_modified; + const stat = try iter_dir.statFile(io, entry.path, .{}); + const last_modified = stat.mtime.nanoseconds; + const duration = now_ns - last_modified; if (duration < std.time.ns_per_hour * 24) { print("Last modified: {d} seconds ago, size:{d} bytes, filename: {s}\n", .{ @divTrunc(duration, std.time.ns_per_s), diff --git a/assets/src/01-04.zig b/assets/src/01-04.zig index 4792b08..f23e3b3 100644 --- a/assets/src/01-04.zig +++ b/assets/src/01-04.zig @@ -1,12 +1,12 @@ //! Test file/directory existence const std = @import("std"); -const fs = std.fs; -pub fn main() !void { +pub fn main(init: std.process.Init) !void { + const io = init.io; const filename = "build.zig"; var found = true; - fs.cwd().access(filename, .{}) catch |e| switch (e) { + std.Io.Dir.cwd().access(io, filename, .{}) catch |e| switch (e) { error.FileNotFound => found = false, else => return e, }; diff --git a/assets/src/01-05.zig b/assets/src/01-05.zig index d9bc153..e1d3b03 100644 --- a/assets/src/01-05.zig +++ b/assets/src/01-05.zig @@ -1,20 +1,18 @@ const std = @import("std"); -const fs = std.fs; const print = std.debug.print; -pub fn main() !void { - var gpa = std.heap.GeneralPurposeAllocator(.{}){}; - defer if (gpa.deinit() != .ok) @panic("leak"); - const allocator = gpa.allocator(); +pub fn main(init: std.process.Init) !void { + const gpa = init.gpa; + const io = init.io; // In order to walk the directry, `iterate` must be set to true. - var dir = try fs.cwd().openDir("zig-out", .{ .iterate = true }); - defer dir.close(); + var dir = try std.Io.Dir.cwd().openDir(io, "zig-out", .{ .iterate = true }); + defer dir.close(io); - var walker = try dir.walk(allocator); + var walker = try dir.walk(gpa); defer walker.deinit(); - while (try walker.next()) |entry| { + while (try walker.next(io)) |entry| { print("path: {s}, basename:{s}, type:{s}\n", .{ entry.path, entry.basename, diff --git a/assets/src/02-01.zig b/assets/src/02-01.zig index 69937e8..3a2f808 100644 --- a/assets/src/02-01.zig +++ b/assets/src/02-01.zig @@ -1,16 +1,16 @@ const std = @import("std"); -const fs = std.fs; const Sha256 = std.crypto.hash.sha2.Sha256; // In real world, this may set to page_size, usually it's 4096. const BUF_SIZE = 16; fn sha256_digest( - file: fs.File, + file: std.Io.File, + io: std.Io, ) ![Sha256.digest_length]u8 { var sha256 = Sha256.init(.{}); var file_buf: [BUF_SIZE]u8 = undefined; - var reader = file.reader(&file_buf); + var reader = file.reader(io, &file_buf); var read_buf: [BUF_SIZE]u8 = undefined; var n = try reader.interface.readSliceShort(&read_buf); while (n != 0) { @@ -21,21 +21,20 @@ fn sha256_digest( return sha256.finalResult(); } -pub fn main() !void { - var gpa = std.heap.GeneralPurposeAllocator(.{}){}; - defer _ = gpa.deinit(); - const allocator = gpa.allocator(); +pub fn main(init: std.process.Init) !void { + const gpa = init.gpa; + const io = init.io; - const file = try fs.cwd().openFile("tests/zig-zen.txt", .{}); - defer file.close(); + const file = try std.Io.Dir.cwd().openFile(io, "tests/zig-zen.txt", .{}); + defer file.close(io); - const digest = try sha256_digest(file); + const digest = try sha256_digest(file, io); const hex_digest = try std.fmt.allocPrint( - allocator, + gpa, "{x}", .{&digest}, ); - defer allocator.free(hex_digest); + defer gpa.free(hex_digest); try std.testing.expectEqualStrings( "2210e9263ece534df0beff39ec06850d127dc60aa17bbc7769c5dc2ea5f3e342", diff --git a/assets/src/02-03.zig b/assets/src/02-03.zig index eb58cf2..33f1a24 100644 --- a/assets/src/02-03.zig +++ b/assets/src/02-03.zig @@ -1,17 +1,16 @@ const std = @import("std"); -pub fn main() !void { - var dbg = std.heap.DebugAllocator(.{}){}; - defer _ = dbg.deinit(); - const allocator = dbg.allocator(); +pub fn main(init: std.process.Init) !void { + const gpa = init.gpa; + const io = init.io; const password = "happy"; //Random salt (Must be at least 8 bytes, recommended 16+) var raw: [8]u8 = undefined; - std.crypto.random.bytes(&raw); - const salt = try std.fmt.allocPrint(allocator, "{s}", .{std.fmt.bytesToHex(raw, .lower)}); - defer allocator.free(salt); + try std.Io.randomSecure(io, &raw); + const salt = try std.fmt.allocPrint(gpa, "{s}", .{std.fmt.bytesToHex(raw, .lower)}); + defer gpa.free(salt); //Parameters for Argon2id const params = std.crypto.pwhash.argon2.Params{ @@ -24,13 +23,14 @@ pub fn main() !void { var derived: [dk_len]u8 = undefined; try std.crypto.pwhash.argon2.kdf( - allocator, + gpa, &derived, password, salt, params, .argon2id, //argon2i, argon2d and argon2id + io, ); std.debug.print("Argon2id derived key: {s}\n", .{std.fmt.bytesToHex(derived, .lower)}); -} \ No newline at end of file +} diff --git a/assets/src/03-01.zig b/assets/src/03-01.zig index 207ae16..af3457c 100644 --- a/assets/src/03-01.zig +++ b/assets/src/03-01.zig @@ -1,28 +1,29 @@ const std = @import("std"); const time = std.time; -const Instant = time.Instant; -const Timer = time.Timer; +const Io = std.Io; const print = std.debug.print; -fn expensive_function() void { +fn expensiveFunction(io: Io) !void { // sleep 500ms - std.Thread.sleep(time.ns_per_ms * 500); + try Io.sleep(io, .fromMilliseconds(500), .awake); } -pub fn main() !void { - // Method 1: Instant - const start = try Instant.now(); - expensive_function(); - const end = try Instant.now(); - const elapsed1: f64 = @floatFromInt(end.since(start)); +pub fn main(init: std.process.Init) !void { + const io = init.io; + + // Method 1: Two timestamps on the awake (monotonic) clock. + const start = Io.Clock.awake.now(io); + try expensiveFunction(io); + const end = Io.Clock.awake.now(io); + const elapsed1: f64 = @floatFromInt(start.durationTo(end).nanoseconds); print("Time elapsed is: {d:.3}ms\n", .{ elapsed1 / time.ns_per_ms, }); - // Method 2: Timer - var timer = try Timer.start(); - expensive_function(); - const elapsed2: f64 = @floatFromInt(timer.read()); + // Method 2: Timestamp.untilNow + const before = Io.Clock.awake.now(io); + try expensiveFunction(io); + const elapsed2: f64 = @floatFromInt(before.untilNow(io, .awake).nanoseconds); print("Time elapsed is: {d:.3}ms\n", .{ elapsed2 / time.ns_per_ms, }); diff --git a/assets/src/04-01.zig b/assets/src/04-01.zig index b36182b..e32a417 100644 --- a/assets/src/04-01.zig +++ b/assets/src/04-01.zig @@ -4,44 +4,45 @@ //! echo "hello zig" | nc localhost const std = @import("std"); -const net = std.net; +const net = std.Io.net; +const Io = std.Io; const print = std.debug.print; -pub fn main() !void { - const loopback = try net.Ip4Address.parse("127.0.0.1", 0); - const localhost = net.Address{ .in = loopback }; - var server = try localhost.listen(.{ +pub fn main(init: std.process.Init) !void { + const io = init.io; + + const loopback: net.IpAddress = .{ .ip4 = .loopback(0) }; + var server = try loopback.listen(io, .{ .reuse_address = true, }); - defer server.deinit(); + defer server.deinit(io); - const addr = server.listen_address; - print("Listening on {}, access this port to end the program\n", .{addr.getPort()}); + const addr = server.socket.address; + print("Listening on {f}, access this port to end the program\n", .{addr}); - const client = try server.accept(); + const stream = try server.accept(io); // In real world, you'd want to handle multiple clients, probably in separate threads. - try handleClient(client); + try handleClient(stream, io); } -fn handleClient(client: net.Server.Connection) !void { - print("Accepted connection from {f}\n", .{client.address}); - defer client.stream.close(); +fn handleClient(stream: net.Stream, io: Io) !void { + defer stream.close(io); var stream_buf: [1024]u8 = undefined; - var reader = client.stream.reader(&stream_buf); + var reader = stream.reader(io, &stream_buf); // Here we echo back what we read directly, so the writer buffer is empty - var writer = client.stream.writer(&.{}); + var writer = stream.writer(io, &.{}); while (true) { - print("Waiting for data from {f}...\n", .{client.address}); - const msg = reader.interface().takeDelimiterInclusive('\n') catch |err| { + print("Waiting for data...\n", .{}); + const msg = reader.interface.takeDelimiterInclusive('\n') catch |err| { if (err == error.EndOfStream) { - print("{f} closed the connection\n", .{client.address}); + print("Client closed the connection\n", .{}); return; } else { return err; } }; - print("{f} says {s}", .{ client.address, msg }); + print("Client says {s}", .{msg}); try writer.interface.writeAll(msg); // No need to flush, as writer buffer is empty } diff --git a/assets/src/04-02.zig b/assets/src/04-02.zig index a1e2021..8d4179b 100644 --- a/assets/src/04-02.zig +++ b/assets/src/04-02.zig @@ -1,10 +1,12 @@ const std = @import("std"); -const net = std.net; +const net = std.Io.net; const print = std.debug.print; -pub fn main() !void { - var args = std.process.args(); - // The first (0 index) Argument is the path to the program. +pub fn main(init: std.process.Init) !void { + const io = init.io; + + var args = init.minimal.args.iterate(); + // The first (0 index) argument is the path to the program. _ = args.skip(); const port_value = args.next() orelse { print("expect port as command line argument\n", .{}); @@ -12,16 +14,16 @@ pub fn main() !void { }; const port = try std.fmt.parseInt(u16, port_value, 10); - const peer = try net.Address.parseIp4("127.0.0.1", port); + const peer = try net.IpAddress.parseIp4("127.0.0.1", port); // Connect to peer - const stream = try net.tcpConnectToAddress(peer); - defer stream.close(); + const stream = try peer.connect(io, .{ .mode = .stream }); + defer stream.close(io); print("Connecting to {f}\n", .{peer}); // Sending data to peer const data = "hello zig"; var buffer: [1024]u8 = undefined; - var writer = stream.writer(buffer[0..]); + var writer = stream.writer(io, buffer[0..]); try writer.interface.writeAll(data); try writer.interface.flush(); print("Sending '{s}' to peer, total written: {d} bytes\n", .{ data, data.len }); diff --git a/assets/src/04-03.zig b/assets/src/04-03.zig index 11dc2a5..18673d5 100644 --- a/assets/src/04-03.zig +++ b/assets/src/04-03.zig @@ -4,57 +4,30 @@ //! echo "hello zig" | nc -u localhost const std = @import("std"); -const net = std.net; -const posix = std.posix; +const net = std.Io.net; const print = std.debug.print; -pub fn main() !void { - // adjust the ip/port here as needed - const addr = try net.Address.parseIp("127.0.0.1", 32100); - - // get a socket and set domain, type and protocol flags - const sock = try posix.socket( - posix.AF.INET, - posix.SOCK.DGRAM, - posix.IPPROTO.UDP, - ); - - // for completeness, we defer closing the socket. In practice, if this is - // a one-shot program, we could omit this and let the OS do the cleanup - defer posix.close(sock); +pub fn main(init: std.process.Init) !void { + const io = init.io; - try posix.bind(sock, &addr.any, addr.getOsSockLen()); + // adjust the ip/port here as needed + const addr = try net.IpAddress.parse("127.0.0.1", 32100); - var other_addr: posix.sockaddr = undefined; - var other_addrlen: posix.socklen_t = @sizeOf(posix.sockaddr); + // Bind a UDP socket + const sock = try addr.bind(io, .{ .mode = .dgram, .protocol = .udp }); + defer sock.close(io); var buf: [1024]u8 = undefined; print("Listen on {f}\n", .{addr}); - // we did not set the NONBLOCK flag (socket type flag), - // so the program will wait until data is received - const n_recv = try posix.recvfrom( - sock, - buf[0..], - 0, - &other_addr, - &other_addrlen, - ); + // we did not set the NONBLOCK flag, so the program will wait until data is received + const msg = try sock.receive(io, &buf); print( - "received {d} byte(s) from {any};\n string: {s}\n", - .{ n_recv, other_addr, buf[0..n_recv] }, + "received {d} byte(s) from {f};\n string: {s}\n", + .{ msg.data.len, msg.from, msg.data }, ); - // we could extract the source address of the received data by - // parsing the other_addr.data field - - const n_sent = try posix.sendto( - sock, - buf[0..n_recv], - 0, - &other_addr, - other_addrlen, - ); - print("echoed {d} byte(s) back\n", .{n_sent}); + try sock.send(io, &msg.from, msg.data); + print("echoed {d} byte(s) back\n", .{msg.data.len}); } diff --git a/assets/src/05-01.zig b/assets/src/05-01.zig index 99a5b79..c007b0f 100644 --- a/assets/src/05-01.zig +++ b/assets/src/05-01.zig @@ -2,12 +2,11 @@ const std = @import("std"); const print = std.debug.print; const http = std.http; -pub fn main() !void { - var gpa = std.heap.GeneralPurposeAllocator(.{}){}; - defer _ = gpa.deinit(); - const allocator = gpa.allocator(); +pub fn main(init: std.process.Init) !void { + const gpa = init.gpa; + const io = init.io; - var client = http.Client{ .allocator = allocator }; + var client: http.Client = .{ .allocator = gpa, .io = io }; defer client.deinit(); const uri = try std.Uri.parse("http://httpbin.org/headers"); @@ -26,8 +25,8 @@ pub fn main() !void { } try std.testing.expectEqual(response.head.status, .ok); - const body = try response.reader(&.{}).allocRemaining(allocator, .unlimited); - defer allocator.free(body); + const body = try response.reader(&.{}).allocRemaining(gpa, .unlimited); + defer gpa.free(body); print("Body:\n{s}\n", .{body}); } diff --git a/assets/src/05-02.zig b/assets/src/05-02.zig index b9d8076..f68bb4e 100644 --- a/assets/src/05-02.zig +++ b/assets/src/05-02.zig @@ -2,12 +2,11 @@ const std = @import("std"); const print = std.debug.print; const http = std.http; -pub fn main() !void { - var gpa = std.heap.GeneralPurposeAllocator(.{}){}; - defer _ = gpa.deinit(); - const allocator = gpa.allocator(); +pub fn main(init: std.process.Init) !void { + const gpa = init.gpa; + const io = init.io; - var client = http.Client{ .allocator = allocator }; + var client: http.Client = .{ .allocator = gpa, .io = io }; defer client.deinit(); const uri = try std.Uri.parse("https://httpbin.org/anything"); @@ -28,7 +27,7 @@ pub fn main() !void { return; } - const body = try response.reader(&.{}).allocRemaining(allocator, .unlimited); - defer allocator.free(body); + const body = try response.reader(&.{}).allocRemaining(gpa, .unlimited); + defer gpa.free(body); print("Body:\n{s}\n", .{body}); } diff --git a/assets/src/05-03.zig b/assets/src/05-03.zig index 30d71b1..beaba64 100644 --- a/assets/src/05-03.zig +++ b/assets/src/05-03.zig @@ -1,41 +1,44 @@ const std = @import("std"); const log = std.log; +const net = std.Io.net; +const Io = std.Io; const Request = std.http.Server.Request; -const Connection = std.net.Server.Connection; const MAX_BUF = 1024; -pub fn main() !void { - const addr = try std.net.Address.parseIp("127.0.0.1", 8080); - var server = try std.net.Address.listen(addr, .{ .reuse_address = true }); - defer server.deinit(); +pub fn main(init: std.process.Init) !void { + const io = init.io; + + const addr = try net.IpAddress.parse("127.0.0.1", 8080); + var server = try addr.listen(io, .{ .reuse_address = true }); + defer server.deinit(io); log.info("Start HTTP server at {f}", .{addr}); while (true) { - const conn = server.accept() catch |err| { + const stream = server.accept(io) catch |err| { log.err("failed to accept connection: {s}", .{@errorName(err)}); continue; }; - const thread = std.Thread.spawn(.{}, accept, .{conn}) catch |err| { + const thread = std.Thread.spawn(.{}, accept, .{ stream, io }) catch |err| { log.err("unable to spawn connection thread: {s}", .{@errorName(err)}); - conn.stream.close(); + stream.close(io); continue; }; thread.detach(); } } -fn accept(conn: Connection) !void { - defer conn.stream.close(); +fn accept(stream: net.Stream, io: Io) !void { + defer stream.close(io); - log.info("Got new client({f})!", .{conn.address}); + log.info("Got new client!", .{}); var recv_buffer: [1024]u8 = undefined; var send_buffer: [100]u8 = undefined; - var connection_br = conn.stream.reader(&recv_buffer); - var connection_bw = conn.stream.writer(&send_buffer); - var server = std.http.Server.init(connection_br.interface(), &connection_bw.interface); + var stream_reader = stream.reader(io, &recv_buffer); + var stream_writer = stream.writer(io, &send_buffer); + var server = std.http.Server.init(&stream_reader.interface, &stream_writer.interface); while (server.reader.state == .ready) { var request = server.receiveHead() catch |err| switch (err) { error.HttpConnectionClosing => return, diff --git a/assets/src/06-01.zig b/assets/src/06-01.zig index d76e087..445070d 100644 --- a/assets/src/06-01.zig +++ b/assets/src/06-01.zig @@ -1,8 +1,11 @@ const std = @import("std"); const print = std.debug.print; -pub fn main() !void { - const rand = std.crypto.random; +pub fn main(init: std.process.Init) !void { + const io = init.io; + + var source: std.Random.IoSource = .{ .io = io }; + const rand = source.interface(); print("Random u8: {}\n", .{rand.int(u8)}); print("Random u8 less than 10: {}\n", .{rand.uintLessThan(u8, 10)}); diff --git a/assets/src/07-02.zig b/assets/src/07-02.zig index 171b3b7..6bd3364 100644 --- a/assets/src/07-02.zig +++ b/assets/src/07-02.zig @@ -1,15 +1,17 @@ const std = @import("std"); +const Io = std.Io; const Thread = std.Thread; -const Mutex = Thread.Mutex; +const Mutex = Io.Mutex; const SharedData = struct { mutex: Mutex, value: i32, + io: Io, - pub fn updateValue(self: *SharedData, increment: i32) void { + pub fn updateValue(self: *SharedData, increment: i32) !void { // Use `tryLock` if you don't want to block - self.mutex.lock(); - defer self.mutex.unlock(); + try self.mutex.lock(self.io); + defer self.mutex.unlock(self.io); for (0..10000) |_| { self.value += increment; @@ -17,8 +19,9 @@ const SharedData = struct { } }; -pub fn main() !void { - var shared_data = SharedData{ .mutex = Mutex{}, .value = 0 }; +pub fn main(init: std.process.Init) !void { + const io = init.io; + var shared_data = SharedData{ .mutex = .init, .value = 0, .io = io }; // This block is necessary to ensure that all threads are joined before proceeding. { const t1 = try Thread.spawn(.{}, SharedData.updateValue, .{ &shared_data, 1 }); diff --git a/assets/src/07-03.zig b/assets/src/07-03.zig index e29c343..af11b24 100644 --- a/assets/src/07-03.zig +++ b/assets/src/07-03.zig @@ -1,27 +1,21 @@ const std = @import("std"); +const Io = std.Io; const print = std.debug.print; -pub fn main() !void { - var gpa = std.heap.GeneralPurposeAllocator(.{}){}; - defer if (gpa.deinit() != .ok) @panic("leak"); - const allocator = gpa.allocator(); +pub fn main(init: std.process.Init) !void { + const io = init.io; - var pool: std.Thread.Pool = undefined; - try pool.init(.{ - .allocator = allocator, - .n_jobs = 4, - }); - defer pool.deinit(); + var group: Io.Group = .init; + errdefer group.cancel(io); - var wg: std.Thread.WaitGroup = .{}; for (0..10) |i| { - pool.spawnWg(&wg, struct { - fn run(id: usize) void { - print("I'm from {d}\n", .{id}); - } - }.run, .{i}); + group.async(io, run, .{i}); } - wg.wait(); + try group.await(io); print("All threads exit.\n", .{}); } + +fn run(id: usize) void { + print("I'm from {d}\n", .{id}); +} diff --git a/assets/src/07-04.zig b/assets/src/07-04.zig index 15ed854..f91fd1f 100644 --- a/assets/src/07-04.zig +++ b/assets/src/07-04.zig @@ -1,17 +1,28 @@ const std = @import("std"); var n: u8 = 0; +var once_state: std.atomic.Value(u8) = .init(0); fn incr() void { n = n + 1; } -var once_incr = std.once(incr); +fn callOnce() void { + // Zig 0.16 removed std.once. Implement once semantics with an atomic state. + const state = once_state.load(.acquire); + if (state == 2) return; + if (state == 0 and once_state.cmpxchgStrong(0, 1, .acq_rel, .acquire) == null) { + incr(); + once_state.store(2, .release); + return; + } + while (once_state.load(.acquire) != 2) std.atomic.spinLoopHint(); +} fn onceIncr() void { - // The invocations of `call` are thread-safe. - once_incr.call(); - once_incr.call(); + // The invocations of `callOnce` are thread-safe. + callOnce(); + callOnce(); } pub fn main() !void { diff --git a/assets/src/08-02.zig b/assets/src/08-02.zig index 084a2fe..33a98c6 100644 --- a/assets/src/08-02.zig +++ b/assets/src/08-02.zig @@ -1,12 +1,8 @@ const std = @import("std"); -const print = std.debug.print; -const Child = std.process.Child; -const ArrayList = std.ArrayList; -pub fn main() !void { - var gpa = std.heap.GeneralPurposeAllocator(.{}){}; - defer if (gpa.deinit() != .ok) @panic("leak"); - const allocator = gpa.allocator(); +pub fn main(init: std.process.Init) !void { + const gpa = init.gpa; + const io = init.io; const argv = [_][]const u8{ "echo", @@ -15,23 +11,16 @@ pub fn main() !void { "world", }; - // By default, child will inherit stdout & stderr from its parents, - // this usually means that child's output will be printed to terminal. - // Here we change them to pipe and collect into `ArrayList`. - var child = Child.init(&argv, allocator); - child.stdout_behavior = .Pipe; - child.stderr_behavior = .Pipe; + // `std.process.run` spawns the child, collects stdout/stderr into owned + // slices, and waits for it to exit. Use `SpawnOptions` + `std.process.spawn` + // directly for more control. + const result = try std.process.run(gpa, io, .{ + .argv = &argv, + }); + defer gpa.free(result.stdout); + defer gpa.free(result.stderr); - var stdout: std.ArrayListUnmanaged(u8) = .empty; - defer stdout.deinit(allocator); - var stderr: std.ArrayListUnmanaged(u8) = .empty; - defer stderr.deinit(allocator); - - try child.spawn(); - try child.collectOutput(allocator, &stdout, &stderr, 1024); - const term = try child.wait(); - - try std.testing.expectEqual(term.Exited, 0); - try std.testing.expectEqualStrings("hello world", stdout.items); - try std.testing.expectEqualStrings("", stderr.items); + try std.testing.expectEqual(@as(u8, 0), result.term.exited); + try std.testing.expectEqualStrings("hello world", result.stdout); + try std.testing.expectEqualStrings("", result.stderr); } diff --git a/assets/src/10-01.zig b/assets/src/10-01.zig index 3a0e5b5..5be7384 100644 --- a/assets/src/10-01.zig +++ b/assets/src/10-01.zig @@ -2,10 +2,8 @@ const std = @import("std"); const json = std.json; const testing = std.testing; -pub fn main() !void { - var gpa = std.heap.GeneralPurposeAllocator(.{}){}; - defer _ = gpa.deinit(); - const allocator = gpa.allocator(); +pub fn main(init: std.process.Init) !void { + const allocator = init.gpa; // Deserialize JSON const json_str = diff --git a/assets/src/10-02.zig b/assets/src/10-02.zig index ae05e20..81f7f72 100644 --- a/assets/src/10-02.zig +++ b/assets/src/10-02.zig @@ -1,25 +1,14 @@ const std = @import("std"); const zon = std.zon; -const Allocator = std.mem.Allocator; const Student = struct { name: []const u8, age: u16, favourites: []const []const u8, - - fn deinit(self: *Student, allocator: Allocator) void { - allocator.free(self.name); - for (self.favourites) |item| { - allocator.free(item); - } - allocator.free(self.favourites); - } }; -pub fn main() !void { - var gpa = std.heap.GeneralPurposeAllocator(.{}){}; - defer if (gpa.deinit() != .ok) @panic("leak"); - const allocator = gpa.allocator(); +pub fn main(init: std.process.Init) !void { + const allocator = init.gpa; const source = Student{ .name = "John", @@ -48,7 +37,7 @@ pub fn main() !void { var diag: zon.parse.Diagnostics = .{}; defer diag.deinit(allocator); - var parsed = zon.parse.fromSlice( + const parsed = zon.parse.fromSliceAlloc( Student, allocator, input, @@ -58,7 +47,7 @@ pub fn main() !void { std.debug.print("Parse status: {any}\n", .{diag}); return err; }; - defer parsed.deinit(allocator); + defer zon.parse.free(allocator, parsed); try std.testing.expectEqualDeep(source, parsed); } diff --git a/assets/src/10-03.zig b/assets/src/10-03.zig index a158980..1ace987 100644 --- a/assets/src/10-03.zig +++ b/assets/src/10-03.zig @@ -3,10 +3,8 @@ const print = std.debug.print; const Encoder = std.base64.standard.Encoder; const Decoder = std.base64.standard.Decoder; -pub fn main() !void { - var gpa = std.heap.GeneralPurposeAllocator(.{}){}; - defer _ = gpa.deinit(); - const allocator = gpa.allocator(); +pub fn main(init: std.process.Init) !void { + const allocator = init.gpa; const src = "hello zig"; diff --git a/assets/src/12-02.zig b/assets/src/12-02.zig index 871e8d6..4f340b8 100644 --- a/assets/src/12-02.zig +++ b/assets/src/12-02.zig @@ -101,10 +101,8 @@ fn LinkedList(comptime T: type) type { }; } -pub fn main() !void { - var gpa = std.heap.GeneralPurposeAllocator(.{}){}; - defer _ = gpa.deinit(); - const allocator = gpa.allocator(); +pub fn main(init: std.process.Init) !void { + const allocator = init.gpa; var lst = LinkedList(u32).init(allocator); defer lst.deinit(); diff --git a/assets/src/12-03.zig b/assets/src/12-03.zig index 33bd61d..903385e 100644 --- a/assets/src/12-03.zig +++ b/assets/src/12-03.zig @@ -187,11 +187,8 @@ fn ensureList(lst: DoublyLinkedList(u32), comptime expected: []const u32) !void try std.testing.expectEqual(visited_times2, expected.len); } -pub fn main() !void { - var gpa = std.heap.GeneralPurposeAllocator(.{}){}; - defer _ = gpa.deinit(); - - const allocator = gpa.allocator(); +pub fn main(init: std.process.Init) !void { + const allocator = init.gpa; var list = DoublyLinkedList(u32).init(allocator); defer list.deinit(); diff --git a/assets/src/14-03.zig b/assets/src/14-03.zig index 164b8c8..2af1206 100644 --- a/assets/src/14-03.zig +++ b/assets/src/14-03.zig @@ -176,10 +176,8 @@ pub const DB = struct { } }; -pub fn main() !void { - var gpa = std.heap.GeneralPurposeAllocator(.{}){}; - defer _ = gpa.deinit(); - const allocator = gpa.allocator(); +pub fn main(init: std.process.Init) !void { + const allocator = init.gpa; const version = c.mysql_get_client_version(); print("MySQL client version is {}\n", .{version}); diff --git a/build.zig b/build.zig index 467ba14..9ef0fe4 100644 --- a/build.zig +++ b/build.zig @@ -1,6 +1,5 @@ const std = @import("std"); const builtin = @import("builtin"); -const fs = std.fs; const allocPrint = std.fmt.allocPrint; const print = std.debug.print; @@ -10,16 +9,17 @@ pub fn build(b: *std.Build) !void { } fn addExample(b: *std.Build, run_all: *std.Build.Step) !void { - const src_dir = try fs.cwd().openDir(b.path("assets/src").getPath(b), .{ .iterate = true }); + const io = b.graph.io; + const src_dir = try std.Io.Dir.cwd().openDir(io, b.path("assets/src").getPath(b), .{ .iterate = true }); const target = b.standardTargetOptions(.{}); var it = src_dir.iterate(); const check = b.step("check", "Check if it compiles"); - LoopExample: while (try it.next()) |entry| { + LoopExample: while (try it.next(io)) |entry| { switch (entry.kind) { .file => { - const name = std.mem.trimRight(u8, entry.name, ".zig"); + const name = std.mem.trimEnd(u8, entry.name, ".zig"); const exe = b.addExecutable(.{ .name = try allocPrint(b.allocator, "examples-{s}", .{name}), .root_module = b.createModule(.{ @@ -28,37 +28,39 @@ fn addExample(b: *std.Build, run_all: *std.Build.Step) !void { .optimize = .Debug, }), }); - check.dependOn(&exe.step); + // 13-01 (simargs example) is skipped until zigcli ships a Zig 0.16-compatible release. if (std.mem.eql(u8, "13-01", name)) { - const zigcli = b.dependency("zigcli", .{}); - exe.root_module.addImport("simargs", zigcli.module("simargs")); - } else if (std.mem.eql(u8, "14-01", name)) { - exe.linkSystemLibrary("sqlite3"); - exe.linkLibC(); + continue :LoopExample; + } + check.dependOn(&exe.step); + if (std.mem.eql(u8, "14-01", name)) { + exe.root_module.linkSystemLibrary("sqlite3", .{}); + exe.root_module.link_libc = true; } else if (std.mem.eql(u8, "14-02", name)) { - exe.linkSystemLibrary("libpq"); - exe.linkLibC(); + exe.root_module.linkSystemLibrary("libpq", .{}); + exe.root_module.link_libc = true; } else if (std.mem.eql(u8, "14-03", name)) { - exe.linkSystemLibrary("mysqlclient"); - exe.linkLibC(); + exe.root_module.linkSystemLibrary("mysqlclient", .{}); + exe.root_module.link_libc = true; } else if (std.mem.eql(u8, "15-01", name)) { + const lib_module = b.createModule(.{ + .target = target, + .optimize = .Debug, + .link_libc = true, + }); + lib_module.addCSourceFiles(.{ + .files = &.{"lib/regex_slim.c"}, + .flags = &.{"-std=c99"}, + }); const lib = b.addLibrary(.{ .name = "regex_slim", - .root_module = b.createModule(.{ - .target = target, - .optimize = .Debug, - }), + .root_module = lib_module, .linkage = .static, }); - lib.addCSourceFiles(.{ - .files = &.{"lib/regex_slim.c"}, - .flags = &.{"-std=c99"}, - }); - lib.linkLibC(); - exe.linkLibrary(lib); - exe.addIncludePath(b.path("lib")); - exe.linkLibC(); + exe.root_module.linkLibrary(lib); + exe.root_module.addIncludePath(b.path("lib")); + exe.root_module.link_libc = true; } b.installArtifact(exe); diff --git a/build.zig.zon b/build.zig.zon index f4b95ff..acca823 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -2,17 +2,12 @@ .name = .zig_cookbook, .fingerprint = 0xde9a8a438b394bd0, .version = "0.1.0", - .minimum_zig_version = "0.15.2", + .minimum_zig_version = "0.16.0", .paths = .{ "src", "lib", "build.zig", "build.zig.zon", }, - .dependencies = .{ - .zigcli = .{ - .url = "https://github.com/jiacai2050/zigcli/archive/edd29d2fbcf977cae40990e091bfb821712afb48.zip", - .hash = "zigcli-0.3.1-ORC7jLbOAgCvnBYGJqGslo1s-ujF9cMQ3ux05i1OpBMp", - }, - }, + .dependencies = .{}, } From 6fd7a790e8530e742a44e5b1e9a0007fd6ea341a Mon Sep 17 00:00:00 2001 From: jiacai2050 Date: Wed, 22 Apr 2026 21:13:41 +0800 Subject: [PATCH 2/7] feat(build): enable C header and zigcli integration for examples Refactor build process to use translate_c for C headers instead of @cImport. Add zigcli dependency and example integration for 13-01. Improve compatibility with Zig 0.16 by adding library wrappers and modular imports. This change streamlines external library handling and facilitates future upgrades. --- .gitignore | 1 + assets/src/13-01.zig | 25 ++++++++++++------------- assets/src/14-01.zig | 4 +--- assets/src/14-02.zig | 4 +--- assets/src/15-01.zig | 6 +----- build.zig | 32 +++++++++++++++++++++++++------- build.zig.zon | 7 ++++++- lib/libpq-fe.h | 1 + lib/regex_slim_all.h | 2 ++ lib/sqlite3.h | 1 + 10 files changed, 51 insertions(+), 32 deletions(-) create mode 100644 lib/libpq-fe.h create mode 100644 lib/regex_slim_all.h create mode 100644 lib/sqlite3.h diff --git a/.gitignore b/.gitignore index 4e69a86..fcf06d6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ public/ zig-out/ zig-cache/ +zig-pkg/ .zig-cache/ .DS_Store .tool-versions diff --git a/assets/src/13-01.zig b/assets/src/13-01.zig index 766f746..1a8b3e8 100644 --- a/assets/src/13-01.zig +++ b/assets/src/13-01.zig @@ -1,13 +1,12 @@ const std = @import("std"); const print = std.debug.print; -const simargs = @import("simargs"); +const zigcli = @import("zigcli"); -pub fn main() !void { - var gpa = std.heap.GeneralPurposeAllocator(.{}){}; - defer _ = gpa.deinit(); - const allocator = gpa.allocator(); +pub fn main(init: std.process.Init) !void { + const gpa = init.gpa; + const io = init.io; - var opt = try simargs.parse(allocator, struct { + const opt = try zigcli.structargs.parse(gpa, io, init.minimal.args, struct { // Those fields declare arguments options // only `output` is required, others are all optional verbose: ?bool, @@ -30,30 +29,30 @@ pub fn main() !void { .output = "Write to file instead of stdout", .timeout = "Max time this request can cost", }; - }, "[file]", null); + }, .{ .argument_prompt = "[file]" }); defer opt.deinit(); const sep = "-" ** 30; - print("{s}Program{s}\n{s}\n\n", .{ sep, sep, opt.program }); + print("{s}Program{s}\n{s}\n\n", .{ sep, sep, opt.program_name }); print("{s}Arguments{s}\n", .{ sep, sep }); - inline for (std.meta.fields(@TypeOf(opt.args))) |fld| { + inline for (std.meta.fields(@TypeOf(opt.options))) |fld| { const format = "{s:>10}: " ++ switch (fld.type) { []const u8 => "{s}", ?[]const u8 => "{?s}", else => "{any}", } ++ "\n"; - print(format, .{ fld.name, @field(opt.args, fld.name) }); + print(format, .{ fld.name, @field(opt.options, fld.name) }); } print("\n{s}Positionals{s}\n", .{ sep, sep }); - for (opt.positional_args, 0..) |arg, idx| { + for (opt.positional_arguments, 0..) |arg, idx| { print("{d}: {s}\n", .{ idx + 1, arg }); } // Provide a print_help util method print("\n{s}print_help{s}\n", .{ sep, sep }); - const stdout = std.fs.File.stdout(); + const stdout = std.Io.File.stdout(); var buf: [1024]u8 = undefined; - var writer = stdout.writer(&buf); + var writer = stdout.writer(io, &buf); try opt.printHelp(&writer.interface); } diff --git a/assets/src/14-01.zig b/assets/src/14-01.zig index 2c5f717..f4e2091 100644 --- a/assets/src/14-01.zig +++ b/assets/src/14-01.zig @@ -2,9 +2,7 @@ /// https://www.sqlite.org/cintro.html /// const std = @import("std"); -const c = @cImport({ - @cInclude("sqlite3.h"); -}); +const c = @import("c"); const print = std.debug.print; const DB = struct { diff --git a/assets/src/14-02.zig b/assets/src/14-02.zig index 09bdbfa..9c0be78 100644 --- a/assets/src/14-02.zig +++ b/assets/src/14-02.zig @@ -4,9 +4,7 @@ //! const std = @import("std"); const print = std.debug.print; -const c = @cImport({ - @cInclude("libpq-fe.h"); -}); +const c = @import("c"); const DB = struct { conn: *c.PGconn, diff --git a/assets/src/15-01.zig b/assets/src/15-01.zig index 2907380..d6c169f 100644 --- a/assets/src/15-01.zig +++ b/assets/src/15-01.zig @@ -1,10 +1,6 @@ const std = @import("std"); const print = std.debug.print; -const c = @cImport({ - @cInclude("regex.h"); - // This is our static library. - @cInclude("regex_slim.h"); -}); +const c = @import("c"); const Regex = struct { inner: *c.regex_t, diff --git a/build.zig b/build.zig index 9ef0fe4..9f51e77 100644 --- a/build.zig +++ b/build.zig @@ -28,16 +28,27 @@ fn addExample(b: *std.Build, run_all: *std.Build.Step) !void { .optimize = .Debug, }), }); - // 13-01 (simargs example) is skipped until zigcli ships a Zig 0.16-compatible release. - if (std.mem.eql(u8, "13-01", name)) { - continue :LoopExample; - } check.dependOn(&exe.step); - if (std.mem.eql(u8, "14-01", name)) { - exe.root_module.linkSystemLibrary("sqlite3", .{}); + if (std.mem.eql(u8, "13-01", name)) { + const zigcli = b.dependency("zigcli", .{}); + exe.root_module.addImport("zigcli", zigcli.module("zigcli")); + } else if (std.mem.eql(u8, "14-01", name)) { + const translate_c = b.addTranslateC(.{ + .root_source_file = b.path("lib/sqlite3.h"), + .target = target, + .optimize = .Debug, + }); + translate_c.linkSystemLibrary("sqlite3", .{}); + exe.root_module.addImport("c", translate_c.createModule()); exe.root_module.link_libc = true; } else if (std.mem.eql(u8, "14-02", name)) { - exe.root_module.linkSystemLibrary("libpq", .{}); + const translate_c = b.addTranslateC(.{ + .root_source_file = b.path("lib/libpq-fe.h"), + .target = target, + .optimize = .Debug, + }); + translate_c.linkSystemLibrary("libpq", .{}); + exe.root_module.addImport("c", translate_c.createModule()); exe.root_module.link_libc = true; } else if (std.mem.eql(u8, "14-03", name)) { exe.root_module.linkSystemLibrary("mysqlclient", .{}); @@ -61,6 +72,13 @@ fn addExample(b: *std.Build, run_all: *std.Build.Step) !void { exe.root_module.linkLibrary(lib); exe.root_module.addIncludePath(b.path("lib")); exe.root_module.link_libc = true; + + const translate_c = b.addTranslateC(.{ + .root_source_file = b.path("lib/regex_slim_all.h"), + .target = target, + .optimize = .Debug, + }); + exe.root_module.addImport("c", translate_c.createModule()); } b.installArtifact(exe); diff --git a/build.zig.zon b/build.zig.zon index acca823..6179b29 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -9,5 +9,10 @@ "build.zig", "build.zig.zon", }, - .dependencies = .{}, + .dependencies = .{ + .zigcli = .{ + .url = "git+https://github.com/jiacai2050/zigcli.git?ref=v0.6.0#621920da8f2235c6b9c15addf353e7747e1e899c", + .hash = "zigcli-0.6.0-ORC7jHD5BQBipVTQYdQwlRQt-d7uVU_Jlz_HUyDouqpa", + }, + }, } diff --git a/lib/libpq-fe.h b/lib/libpq-fe.h new file mode 100644 index 0000000..06a7350 --- /dev/null +++ b/lib/libpq-fe.h @@ -0,0 +1 @@ +#include diff --git a/lib/regex_slim_all.h b/lib/regex_slim_all.h new file mode 100644 index 0000000..5999f07 --- /dev/null +++ b/lib/regex_slim_all.h @@ -0,0 +1,2 @@ +#include +#include "regex_slim.h" diff --git a/lib/sqlite3.h b/lib/sqlite3.h new file mode 100644 index 0000000..f52e1f0 --- /dev/null +++ b/lib/sqlite3.h @@ -0,0 +1 @@ +#include From 7b8a057e40ade4ed2c595d8e77450099f6bf8683 Mon Sep 17 00:00:00 2001 From: jiacai2050 Date: Wed, 22 Apr 2026 21:16:41 +0800 Subject: [PATCH 3/7] docs(readme): Update main branch version to Zig 0.16.x Clarify that the main branch now tracks Zig 0.16.x instead of 0.15.x. Reflecting this change in both English and Chinese README avoids confusion about current Zig version support and communicates updated compatibility to users and contributors. --- README-zh.md | 2 +- README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README-zh.md b/README-zh.md index aaf5df1..8209c82 100644 --- a/README-zh.md +++ b/README-zh.md @@ -9,7 +9,7 @@ [Zig Cookbook](https://github.com/zigcc/zig-cookbook) 是一系列简单的 Zig 示例程序合集,展示了完成常见编程任务的良好实践。 -> - 主分支跟踪 Zig 0.15.x,并通过 GitHub Actions 在 Linux 和 macOS 上进行测试。 +> - 主分支跟踪 Zig 0.16.x,并通过 GitHub Actions 在 Linux 和 macOS 上进行测试。 > - 更早版本的 Zig 支持可以在[其他分支](https://github.com/zigcc/zig-cookbook/branches)中找到。 # 如何使用 diff --git a/README.md b/README.md index 17c8ca0..b8fa6ed 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ [Zig cookbook](https://github.com/zigcc/zig-cookbook) is a collection of simple Zig programs that demonstrate good practices to accomplish common programming tasks. -> - Main branch tracks Zig 0.15.x, and is tested on Linux and macOS via GitHub actions. +> - Main branch tracks Zig 0.16.x, and is tested on Linux and macOS via GitHub actions. > - Earlier Zig support could be found in [other branches](https://github.com/zigcc/zig-cookbook/branches). # How to use From f0bc2a456359c64a9bb4bdccf3c20d5d3e44c458 Mon Sep 17 00:00:00 2001 From: jiacai2050 Date: Thu, 23 Apr 2026 09:46:16 +0800 Subject: [PATCH 4/7] update docs up to date --- assets/src/07-04.zig | 9 +++++++-- assets/src/14-03.zig | 4 +--- build.zig | 8 +++++++- lib/mysql.h | 1 + src/en-US/01-03-file-modified-24h-ago.smd | 2 +- src/en-US/02-02-pbkdf2.smd | 8 +++----- src/en-US/02-03-argon2.smd | 6 +++--- src/en-US/03-01-elapsed-time.smd | 11 ++++------- src/en-US/04-01-tcp-server.smd | 10 ++++------ src/en-US/04-02-tcp-client.smd | 4 ++-- src/en-US/04-03-udp-echo.smd | 2 +- src/en-US/05-01-http-get.smd | 4 ++-- src/en-US/05-02-http-post.smd | 4 ++-- src/en-US/06-01-rand.smd | 4 ++-- src/en-US/07-01-spawn.smd | 4 ++-- src/en-US/07-02-shared-data.smd | 2 +- src/en-US/07-03-threadpool.smd | 2 +- src/en-US/07-04-run-once.smd | 8 +++++++- src/en-US/08-01-cpu-count.smd | 2 +- src/en-US/08-02-external.smd | 5 ++--- src/en-US/09-01-semver.smd | 4 ++-- src/en-US/10-01-json.smd | 10 ++++------ src/en-US/10-03-base64.smd | 10 +++++----- src/en-US/11-01-complex-numbers.smd | 2 +- src/en-US/13-01-argparse.smd | 4 ++-- src/en-US/14-01-sqlite.smd | 2 +- src/en-US/14-02-postgres.smd | 2 +- src/en-US/14-03-mysql.smd | 2 +- src/en-US/15-01-regex.smd | 2 +- src/zh-CN/01-03-file-modified-24h-ago.smd | 2 +- src/zh-CN/01-04-file-exists.smd | 2 +- src/zh-CN/01-05-iterate-dir.smd | 2 +- src/zh-CN/02-02-pbkdf2.smd | 6 +++--- src/zh-CN/02-03-argon2.smd | 6 +++--- src/zh-CN/03-01-elapsed-time.smd | 11 ++++------- src/zh-CN/04-01-tcp-server.smd | 4 ++-- src/zh-CN/04-02-tcp-client.smd | 2 +- src/zh-CN/04-03-udp-echo.smd | 2 +- src/zh-CN/05-01-http-get.smd | 4 ++-- src/zh-CN/05-02-http-post.smd | 4 ++-- src/zh-CN/06-01-rand.smd | 4 ++-- src/zh-CN/07-01-spawn.smd | 4 ++-- src/zh-CN/07-02-shared-data.smd | 2 +- src/zh-CN/07-03-threadpool.smd | 2 +- src/zh-CN/07-04-run-once.smd | 8 +++++++- src/zh-CN/08-01-cpu-count.smd | 2 +- src/zh-CN/08-02-external.smd | 5 ++--- src/zh-CN/09-01-semver.smd | 4 ++-- src/zh-CN/10-01-json.smd | 10 ++++------ src/zh-CN/10-03-base64.smd | 10 +++++----- src/zh-CN/11-01-complex-numbers.smd | 2 +- src/zh-CN/13-01-argparse.smd | 4 ++-- src/zh-CN/14-01-sqlite.smd | 2 +- src/zh-CN/14-02-postgres.smd | 2 +- src/zh-CN/14-03-mysql.smd | 2 +- src/zh-CN/15-01-regex.smd | 2 +- 56 files changed, 127 insertions(+), 121 deletions(-) create mode 100644 lib/mysql.h diff --git a/assets/src/07-04.zig b/assets/src/07-04.zig index f91fd1f..38fd3b4 100644 --- a/assets/src/07-04.zig +++ b/assets/src/07-04.zig @@ -1,6 +1,10 @@ const std = @import("std"); var n: u8 = 0; +// Three-state atomic protocol (replaces std.once which was removed in Zig 0.16): +// 0 = idle – no thread has started the work yet +// 1 = running – one thread is executing the payload +// 2 = done – the payload has finished; all threads may proceed var once_state: std.atomic.Value(u8) = .init(0); fn incr() void { @@ -8,14 +12,15 @@ fn incr() void { } fn callOnce() void { - // Zig 0.16 removed std.once. Implement once semantics with an atomic state. const state = once_state.load(.acquire); - if (state == 2) return; + if (state == 2) return; // fast path: already done if (state == 0 and once_state.cmpxchgStrong(0, 1, .acq_rel, .acquire) == null) { + // We won the race (0 → 1): execute the payload, then mark done. incr(); once_state.store(2, .release); return; } + // Another thread is running (state == 1): spin until it finishes. while (once_state.load(.acquire) != 2) std.atomic.spinLoopHint(); } diff --git a/assets/src/14-03.zig b/assets/src/14-03.zig index 2af1206..25905e8 100644 --- a/assets/src/14-03.zig +++ b/assets/src/14-03.zig @@ -3,9 +3,7 @@ //! const std = @import("std"); const Allocator = std.mem.Allocator; -const c = @cImport({ - @cInclude("mysql.h"); -}); +const c = @import("c"); const print = std.debug.print; pub const DBInfo = struct { diff --git a/build.zig b/build.zig index 9f51e77..b8236f9 100644 --- a/build.zig +++ b/build.zig @@ -51,7 +51,13 @@ fn addExample(b: *std.Build, run_all: *std.Build.Step) !void { exe.root_module.addImport("c", translate_c.createModule()); exe.root_module.link_libc = true; } else if (std.mem.eql(u8, "14-03", name)) { - exe.root_module.linkSystemLibrary("mysqlclient", .{}); + const translate_c = b.addTranslateC(.{ + .root_source_file = b.path("lib/mysql.h"), + .target = target, + .optimize = .Debug, + }); + translate_c.linkSystemLibrary("mysqlclient", .{}); + exe.root_module.addImport("c", translate_c.createModule()); exe.root_module.link_libc = true; } else if (std.mem.eql(u8, "15-01", name)) { const lib_module = b.createModule(.{ diff --git a/lib/mysql.h b/lib/mysql.h new file mode 100644 index 0000000..741c7ba --- /dev/null +++ b/lib/mysql.h @@ -0,0 +1 @@ +#include diff --git a/src/en-US/01-03-file-modified-24h-ago.smd b/src/en-US/01-03-file-modified-24h-ago.smd index a4121d5..0166a25 100644 --- a/src/en-US/01-03-file-modified-24h-ago.smd +++ b/src/en-US/01-03-file-modified-24h-ago.smd @@ -5,7 +5,7 @@ .layout = "section.shtml", --- -Gets the current working directory by calling `fs.cwd()`, and then iterates files using `walk()`, which will recursively iterate entries in the directory. +Gets the current working directory by calling `Io.Dir.cwd()`, and then iterates files using `walk()`, which will recursively iterate entries in the directory. For each entry, we check if it's a file, and use `statFile()` to retrieve the file's metadata. diff --git a/src/en-US/02-02-pbkdf2.smd b/src/en-US/02-02-pbkdf2.smd index fd4790f..cacf252 100644 --- a/src/en-US/02-02-pbkdf2.smd +++ b/src/en-US/02-02-pbkdf2.smd @@ -6,11 +6,9 @@ --- Salt and hash a password with PBKDF2 -Uses [`std.crypto.pwhash.pbkdf2`] to hash a salted password. The salt is generated -using [`std.rand.DefaultPrng`], which fills the salt byte array with generated -random numbers. +Uses [`std.crypto.pwhash.pbkdf2`] to hash a salted password using HMAC-SHA256. In production, the salt should be generated randomly using [`std.Io.randomSecure`]. []($code.siteAsset('src/02-02.zig').language('zig')) -[`std.crypto.pwhash.pbkdf2`]: https://ziglang.org/documentation/0.11.0/std/#A;std:crypto.pwhash.pbkdf2 -[`std.rand.defaultprng`]: https://ziglang.org/documentation/0.11.0/std/#A;std:rand.DefaultPrng +[`std.crypto.pwhash.pbkdf2`]: https://ziglang.org/documentation/0.16.0/std/#std.crypto.pwhash.pbkdf2 +[`std.Io.randomSecure`]: https://ziglang.org/documentation/0.16.0/std/#std.Io.randomSecure diff --git a/src/en-US/02-03-argon2.smd b/src/en-US/02-03-argon2.smd index 66fa0f9..c4d959d 100644 --- a/src/en-US/02-03-argon2.smd +++ b/src/en-US/02-03-argon2.smd @@ -6,9 +6,9 @@ --- Salt and hash a password with Argon2 -This Zig program derives a cryptographic key from a password and salt using the Argon2id password hashing algorithm. It uses [std.crypto.pwhash.argon2] to hash a salted password, where the salt is generated using [`std.crypto.random`]. +This Zig program derives a cryptographic key from a password and salt using the Argon2id password hashing algorithm. It uses [std.crypto.pwhash.argon2] to hash a salted password, where the salt is generated using [`std.Io.randomSecure`]. []($code.siteAsset('src/02-03.zig').language('zig')) -[`std.crypto.pwhash.argon2`]: https://ziglang.org/documentation/0.14.0/std/#std.crypto.pwhash.argon2 -[`std.crypto.random`]: https://ziglang.org/documentation/0.14.0/std/#std.crypto.tlcsprng.interface \ No newline at end of file +[`std.crypto.pwhash.argon2`]: https://ziglang.org/documentation/0.16.0/std/#std.crypto.pwhash.argon2 +[`std.Io.randomSecure`]: https://ziglang.org/documentation/0.16.0/std/#std.Io.randomSecure \ No newline at end of file diff --git a/src/en-US/03-01-elapsed-time.smd b/src/en-US/03-01-elapsed-time.smd index aadf43e..7260022 100644 --- a/src/en-US/03-01-elapsed-time.smd +++ b/src/en-US/03-01-elapsed-time.smd @@ -5,14 +5,11 @@ .layout = "section.shtml", --- -[`Instant`] represents a timestamp with respect to the currently executing program that ticks while the program is suspended and can be used to record elapsed time. +[`Io.Clock`] provides monotonic timestamps via `Io.Clock.awake.now(io)`. Calling `durationTo` on a timestamp returns the elapsed `Duration` in nanoseconds. -Calling [`std.time.Instant.since`] returns a u64 representing nanoseconds elapsed. - -This is such a common task, that there is a [`Timer`] for convenience. +For sleeping, use [`Io.sleep`] with a `Duration` value. []($code.siteAsset('src/03-01.zig').language('zig')) -[`instant`]: https://ziglang.org/documentation/0.11.0/std/#A;std:time.Instant -[`timer`]: https://ziglang.org/documentation/0.11.0/std/#A;std:time.Timer -[`std.time.instant.since`]: https://ziglang.org/documentation/0.11.0/std/#A;std:time.Instant.since +[`Io.Clock`]: https://ziglang.org/documentation/0.16.0/std/#std.Io.Clock +[`Io.sleep`]: https://ziglang.org/documentation/0.16.0/std/#std.Io.sleep diff --git a/src/en-US/04-01-tcp-server.smd b/src/en-US/04-01-tcp-server.smd index 0d519de..fede7a3 100644 --- a/src/en-US/04-01-tcp-server.smd +++ b/src/en-US/04-01-tcp-server.smd @@ -6,8 +6,8 @@ --- In this example, the port is displayed on the console, and the program will -listen until a request is made. `Ip4Address` assigns a random port when -setting port to 0. +listen until a request is made. Using `IpAddress` with `.loopback(0)` assigns +a random port. []($code.siteAsset('src/04-01.zig').language('zig')) @@ -17,10 +17,8 @@ When start starts up, try test like this: echo "hello zig" | nc localhost ``` -By default, the program listens with IPv4. If you want IPv6, use `::1` -instead of `127.0.0.1`, replace `net.Ip4Address.parse` by -`net.Ip6Address.parse` and the field `.in` in the creation of the -`net.Address` with `.in6`. +By default, the program listens with IPv4. If you want IPv6, use +`net.IpAddress{ .ip6 = .loopback(0) }` instead. (And connect to something like `ip6-localhost`, depending on the way your machine is set up.) diff --git a/src/en-US/04-02-tcp-client.smd b/src/en-US/04-02-tcp-client.smd index 0dd1cc0..8d0cd94 100644 --- a/src/en-US/04-02-tcp-client.smd +++ b/src/en-US/04-02-tcp-client.smd @@ -11,5 +11,5 @@ You can run it using `zig build run-04-02 -- `. []($code.siteAsset('src/04-02.zig').language('zig')) By default, the program connects with IPv4. If you want IPv6, use -`::1` instead of `127.0.0.1`, replace `net.Address.parseIp4` by -`net.Address.parseIp6`. +`::1` instead of `127.0.0.1`, replace `net.IpAddress.parseIp4` by +`net.IpAddress.parseIp6`. diff --git a/src/en-US/04-03-udp-echo.smd b/src/en-US/04-03-udp-echo.smd index 75fbed4..97b59d1 100644 --- a/src/en-US/04-03-udp-echo.smd +++ b/src/en-US/04-03-udp-echo.smd @@ -9,7 +9,7 @@ Similar to the TCP server example, this program will listen on the specified IP address and port, but for UDP datagrams this time. If data is received, it will be echoed back to the sender's address. -Although `std.net` is mostly focused on abstractions for TCP (so far), we can still +Although `std.Io.net` is mostly focused on abstractions for TCP (so far), we can still make use of socket programming to communicate via UDP. []($code.siteAsset('src/04-03.zig').language('zig')) diff --git a/src/en-US/05-01-http-get.smd b/src/en-US/05-01-http-get.smd index 9a70e81..0c2b717 100644 --- a/src/en-US/05-01-http-get.smd +++ b/src/en-US/05-01-http-get.smd @@ -13,5 +13,5 @@ with [`request`]. Prints obtained [`Response`] status and headers. []($code.siteAsset('src/05-01.zig').language('zig')) -[`request`]: https://ziglang.org/documentation/0.11.0/std/src/std/http/Client.zig.html#L992 -[`response`]: https://ziglang.org/documentation/0.11.0/std/src/std/http/Client.zig.html#L322 +[`request`]: https://ziglang.org/documentation/0.16.0/std/src/std/http/Client.zig.html#L992 +[`response`]: https://ziglang.org/documentation/0.16.0/std/src/std/http/Client.zig.html#L322 diff --git a/src/en-US/05-02-http-post.smd b/src/en-US/05-02-http-post.smd index 323b830..3d8637e 100644 --- a/src/en-US/05-02-http-post.smd +++ b/src/en-US/05-02-http-post.smd @@ -12,5 +12,5 @@ with [`request`]. Prints obtained [`Response`] status, and data received from se []($code.siteAsset('src/05-02.zig').language('zig')) -[`request`]: https://ziglang.org/documentation/0.11.0/std/src/std/http/Client.zig.html#L992 -[`response`]: https://ziglang.org/documentation/0.11.0/std/src/std/http/Client.zig.html#L322 +[`request`]: https://ziglang.org/documentation/0.16.0/std/src/std/http/Client.zig.html#L992 +[`response`]: https://ziglang.org/documentation/0.16.0/std/src/std/http/Client.zig.html#L322 diff --git a/src/en-US/06-01-rand.smd b/src/en-US/06-01-rand.smd index 8f2c711..3280a69 100644 --- a/src/en-US/06-01-rand.smd +++ b/src/en-US/06-01-rand.smd @@ -5,8 +5,8 @@ .layout = "section.shtml", --- -Generates random numbers with the help of a thread-local, cryptographically secure pseudo random number generator [`std.crypto.random`]. +Generates random numbers using [`std.Random.IoSource`], which provides a cryptographically secure random number generator backed by the system's I/O interface. []($code.siteAsset('src/06-01.zig').language('zig')) -[`std.crypto.random`]: https://ziglang.org/documentation/0.11.0/std/#A;std:crypto.random +[`std.Random.IoSource`]: https://ziglang.org/documentation/0.16.0/std/#std.Random.IoSource diff --git a/src/en-US/07-01-spawn.smd b/src/en-US/07-01-spawn.smd index 48aaa0a..c8bba99 100644 --- a/src/en-US/07-01-spawn.smd +++ b/src/en-US/07-01-spawn.smd @@ -14,5 +14,5 @@ This example splits the array in half and performs the work in separate threads. []($code.siteAsset('src/07-01.zig').language('zig')) -[`std.thread`]: https://ziglang.org/documentation/0.11.0/std/#A;std:Thread -[`std.thread.spawn`]: https://ziglang.org/documentation/0.11.0/std/#A;std:Thread.spawn +[`std.thread`]: https://ziglang.org/documentation/0.16.0/std/#std.Thread +[`std.thread.spawn`]: https://ziglang.org/documentation/0.16.0/std/#std.Thread.spawn diff --git a/src/en-US/07-02-shared-data.smd b/src/en-US/07-02-shared-data.smd index 2afa1d4..7de0a9e 100644 --- a/src/en-US/07-02-shared-data.smd +++ b/src/en-US/07-02-shared-data.smd @@ -10,4 +10,4 @@ When we want to mutate data shared between threads, [`Mutex`](**Mut**ually **ex* []($code.siteAsset('src/07-02.zig').language('zig')) If we remove Mutex protection, the result will most like be less than 30,000. -[`Mutex`]: https://ziglang.org/documentation/0.11.0/std/#A;std:Thread.Mutex +[`Mutex`]: https://ziglang.org/documentation/0.16.0/std/#std.Io.Mutex diff --git a/src/en-US/07-03-threadpool.smd b/src/en-US/07-03-threadpool.smd index 2218c1a..e069001 100644 --- a/src/en-US/07-03-threadpool.smd +++ b/src/en-US/07-03-threadpool.smd @@ -10,6 +10,6 @@ Thread pools address two different problems: 2. They provide a means of bounding and managing the resources, including threads, consumed when executing a collection of tasks. -In this example, we spawn 10 tasks into thread pool, and use `WaitGroup` to wait for them to finish. +In this example, we spawn 10 tasks using `Io.Group`, and call `await` to wait for them to finish. []($code.siteAsset('src/07-03.zig').language('zig')) diff --git a/src/en-US/07-04-run-once.smd b/src/en-US/07-04-run-once.smd index ce9ce3d..11ba997 100644 --- a/src/en-US/07-04-run-once.smd +++ b/src/en-US/07-04-run-once.smd @@ -6,6 +6,12 @@ --- -`std.once` ensures a function executes exactly one time, regardless of how many threads attempt to call it or how many times it's invoked. This thread-safe initialization is particularly useful for singleton patterns and one-time setup operations. +`std.once` was removed in Zig 0.16. Here we implement the same "run exactly once" semantics manually using a three-state atomic protocol: + +- **0 (idle)** – no thread has started the work yet. +- **1 (running)** – one thread won the race and is executing the payload. +- **2 (done)** – the payload has finished; all threads may return immediately. + +This ensures a function executes exactly one time, regardless of how many threads attempt to call it. Useful for singleton patterns and one-time setup operations. []($code.siteAsset('src/07-04.zig').language('zig')) diff --git a/src/en-US/08-01-cpu-count.smd b/src/en-US/08-01-cpu-count.smd index cfc185f..37b53c7 100644 --- a/src/en-US/08-01-cpu-count.smd +++ b/src/en-US/08-01-cpu-count.smd @@ -9,4 +9,4 @@ Shows the number of logical CPU cores in the current machine using [`std.Thread. []($code.siteAsset('src/08-01.zig').language('zig')) -[`std.thread.getcpucount`]: https://ziglang.org/documentation/0.11.0/std/#A;std:Thread.getCpuCount +[`std.thread.getcpucount`]: https://ziglang.org/documentation/0.16.0/std/#std.Thread.getCpuCount diff --git a/src/en-US/08-02-external.smd b/src/en-US/08-02-external.smd index e61c96e..2e55c07 100644 --- a/src/en-US/08-02-external.smd +++ b/src/en-US/08-02-external.smd @@ -5,9 +5,8 @@ .layout = "section.shtml", --- -Run an external command via [`std.process.Child`], and collect output into `ArrayList` via [pipe]. +Run an external command via [`std.process.run`], which spawns the child process, collects stdout/stderr, and waits for it to exit. []($code.siteAsset('src/08-02.zig').language('zig')) -[`std.process.child`]: https://ziglang.org/documentation/0.11.0/std/#A;std:process.Child -[pipe]: https://man7.org/linux/man-pages/man2/pipe.2.html +[`std.process.run`]: https://ziglang.org/documentation/0.16.0/std/#std.process.run diff --git a/src/en-US/09-01-semver.smd b/src/en-US/09-01-semver.smd index 634acf9..c4c248e 100644 --- a/src/en-US/09-01-semver.smd +++ b/src/en-US/09-01-semver.smd @@ -9,6 +9,6 @@ Constructs a [`std.SemanticVersion`] from a string literal using [`SemanticVersi []($code.siteAsset('src/09-01.zig').language('zig')) -[`std.semanticversion`]: https://ziglang.org/documentation/0.11.0/std/#A;std:SemanticVersion -[`semanticversion.parse`]: https://ziglang.org/documentation/0.11.0/std/#A;std:SemanticVersion.parse +[`std.semanticversion`]: https://ziglang.org/documentation/0.16.0/std/#std.SemanticVersion +[`semanticversion.parse`]: https://ziglang.org/documentation/0.16.0/std/#std.SemanticVersion.parse [semantic versioning specification]: http://semver.org/ diff --git a/src/en-US/10-01-json.smd b/src/en-US/10-01-json.smd index cdbc260..b8e6b54 100644 --- a/src/en-US/10-01-json.smd +++ b/src/en-US/10-01-json.smd @@ -5,15 +5,13 @@ .layout = "section.shtml", --- -The [`std.json`] provides a set of functions such as [`stringify`] and [`stringifyAlloc`] for serializing JSON. -Additionally, we can use [`parseFromSlice`] function to parse a `[]u8` of JSON. +The [`std.json`] module provides [`parseFromSlice`] for deserializing JSON, and [`json.Stringify`] struct for serializing values back to JSON via an `Io.Writer.Allocating`. The example below shows a `[]u8` of JSON being parsed. Compare each member one by one. Then, we modify the `verified` field to `false` and serialize it back into a JSON string. []($code.siteAsset('src/10-01.zig').language('zig')) -[`std.json`]: https://ziglang.org/documentation/0.11.0/std/#A;std:json -[`stringify`]: https://ziglang.org/documentation/0.11.0/std/#A;std:json.stringify -[`stringifyalloc`]: https://ziglang.org/documentation/0.11.0/std/#A;std:json.stringifyAlloc -[`parsefromslice`]: https://ziglang.org/documentation/0.11.0/std/#A;std:json.parseFromSlice +[`std.json`]: https://ziglang.org/documentation/0.16.0/std/#std.json +[`json.Stringify`]: https://ziglang.org/documentation/0.16.0/std/#std.json.Stringify +[`parsefromslice`]: https://ziglang.org/documentation/0.16.0/std/#std.json.parseFromSlice diff --git a/src/en-US/10-03-base64.smd b/src/en-US/10-03-base64.smd index abb6a63..6eff71f 100644 --- a/src/en-US/10-03-base64.smd +++ b/src/en-US/10-03-base64.smd @@ -8,9 +8,9 @@ Encodes a byte slice into `base64` string using [`std.base64.standard`] [`encode`] and decodes it with [`standard`] [`decode`]. -[]($code.siteAsset('src/10-02.zig').language('zig')) +[]($code.siteAsset('src/10-03.zig').language('zig')) -[`std.base64.standard`]: https://ziglang.org/documentation/0.11.0/std/#A;std:base64.standard -[`standard`]: https://ziglang.org/documentation/0.11.0/std/src/std/base64.zig.html#L29 -[`encode`]: https://ziglang.org/documentation/0.11.0/std/#A;std:base64.Base64Encoder.encode -[`decode`]: https://ziglang.org/documentation/0.11.0/std/#A;std:base64.Base64Decoder.decode +[`std.base64.standard`]: https://ziglang.org/documentation/0.16.0/std/#std.base64.standard +[`standard`]: https://ziglang.org/documentation/0.16.0/std/src/std/base64.zig.html#L29 +[`encode`]: https://ziglang.org/documentation/0.16.0/std/#std.base64.Base64Encoder.encode +[`decode`]: https://ziglang.org/documentation/0.16.0/std/#std.base64.Base64Decoder.decode diff --git a/src/en-US/11-01-complex-numbers.smd b/src/en-US/11-01-complex-numbers.smd index 6a693bc..3904c8f 100644 --- a/src/en-US/11-01-complex-numbers.smd +++ b/src/en-US/11-01-complex-numbers.smd @@ -14,4 +14,4 @@ or integers). []($code.siteAsset('src/11-01.zig').language('zig')) -[`std.math.complex`]: https://ziglang.org/documentation/0.11.0/std/#A;std:math.Complex +[`std.math.complex`]: https://ziglang.org/documentation/0.16.0/std/#std.math.Complex diff --git a/src/en-US/13-01-argparse.smd b/src/en-US/13-01-argparse.smd index 210d680..353eb31 100644 --- a/src/en-US/13-01-argparse.smd +++ b/src/en-US/13-01-argparse.smd @@ -9,8 +9,8 @@ Parsing arguments is common in command line programs and there are some packages - [Hejsil/zig-clap](https://github.com/Hejsil/zig-clap) - [MasterQ32/zig-args](https://github.com/MasterQ32/zig-args/) -- [simargs](https://zigcli.liujiacai.net/modules/simargs/) +- [structargs](https://zigcli.liujiacai.net/) -Here we will give an example using `simargs`. +Here we will give an example using `structargs` from the [zigcli](https://github.com/jiacai2050/zigcli) toolkit. []($code.siteAsset('src/13-01.zig').language('zig')) diff --git a/src/en-US/14-01-sqlite.smd b/src/en-US/14-01-sqlite.smd index 495ce22..26f2b96 100644 --- a/src/en-US/14-01-sqlite.smd +++ b/src/en-US/14-01-sqlite.smd @@ -5,7 +5,7 @@ .layout = "section.shtml", --- -Although there are some [wrapper package](https://github.com/vrischmann/zig-sqlite) options for SQLite in Zig, they are unstable. So here we will introduce the [C API interface](https://www.sqlite.org/cintro.html). +Although there are some [wrapper package](https://github.com/vrischmann/zig-sqlite) options for SQLite in Zig, they are unstable. So here we will introduce the [C API interface](https://www.sqlite.org/cintro.html) via `addTranslateC`. Data models are introduced [here](database). diff --git a/src/en-US/14-02-postgres.smd b/src/en-US/14-02-postgres.smd index d9212d7..b7d9e65 100644 --- a/src/en-US/14-02-postgres.smd +++ b/src/en-US/14-02-postgres.smd @@ -5,7 +5,7 @@ .layout = "section.shtml", --- -As with [previous section](14-01-sqlite), here we introduce [libpq](https://www.postgresql.org/docs/16/libpq-example.html) interface directly, other than [wrapper package](https://github.com/tonis2/zig-postgres). +As with [previous section](14-01-sqlite), here we introduce [libpq](https://www.postgresql.org/docs/16/libpq-example.html) interface directly via `addTranslateC`, other than [wrapper package](https://github.com/tonis2/zig-postgres). Data models used in this demo are the same with the one used in [SQLite section](14-01-sqlite). diff --git a/src/en-US/14-03-mysql.smd b/src/en-US/14-03-mysql.smd index 4d7e3d4..9c8105b 100644 --- a/src/en-US/14-03-mysql.smd +++ b/src/en-US/14-03-mysql.smd @@ -5,7 +5,7 @@ .layout = "section.shtml", --- -As with [sqlite section](14-01-sqlite), here we introduce [libmysqlclient](https://dev.mysql.com/doc/c-api/8.0/en/c-api-basic-interface-usage.html) interface directly. +As with [sqlite section](14-01-sqlite), here we introduce [libmysqlclient](https://dev.mysql.com/doc/c-api/8.0/en/c-api-basic-interface-usage.html) interface directly via `addTranslateC`. Data models are introduced [here](database). diff --git a/src/en-US/15-01-regex.smd b/src/en-US/15-01-regex.smd index 94e2db4..a34c851 100644 --- a/src/en-US/15-01-regex.smd +++ b/src/en-US/15-01-regex.smd @@ -7,7 +7,7 @@ Currently there is no regex support in Zig, so the best way to go is binding with Posix's [regex.h](https://pubs.opengroup.org/onlinepubs/7908799/xsh/regex.h.html). -Interop with C is easy in Zig, but since translate-c doesn't support bitfields, we can't use `regex_t` directly. So here we create a [static C library](https://github.com/zigcc/zig-cookbook/blob/460dea1f2f9937ab512a70683aacab79e34c723a/build.zig#L50) first, providing two functions: +Interop with C is easy in Zig via [`addTranslateC`](https://ziglang.org/documentation/0.16.0/#toc-addTranslateC) in the build system, but since translate-c doesn't support bitfields, we can't use `regex_t` directly. So here we create a [static C library](https://github.com/zigcc/zig-cookbook/blob/main/lib/regex_slim.c) first, providing two functions: ```c regex_t* alloc_regex_t(void); diff --git a/src/zh-CN/01-03-file-modified-24h-ago.smd b/src/zh-CN/01-03-file-modified-24h-ago.smd index a3afff9..598e554 100644 --- a/src/zh-CN/01-03-file-modified-24h-ago.smd +++ b/src/zh-CN/01-03-file-modified-24h-ago.smd @@ -5,7 +5,7 @@ .layout = "section.shtml", --- -通过 Zig 的标准库 `std.fs`,我们可以轻松地遍历目录并检查文件的元数据。以下示例展示了如何查找在过去24小时内修改过的文件。 +通过 Zig 的标准库 `std.Io.Dir`,我们可以轻松地遍历目录并检查文件的元数据。以下示例展示了如何查找在过去24小时内修改过的文件。 对于每个文件,我们使用 `statFile()` 函数获取其元数据,并检查 `mtime`(修改时间)字段是否在过去24小时内。 diff --git a/src/zh-CN/01-04-file-exists.smd b/src/zh-CN/01-04-file-exists.smd index 299c3e8..76bb16e 100644 --- a/src/zh-CN/01-04-file-exists.smd +++ b/src/zh-CN/01-04-file-exists.smd @@ -5,7 +5,7 @@ .layout = "section.shtml", --- -在 Zig 中检查文件是否存在,可以使用标准库中的 `std.fs` 模块。具体来说,可以使用 `Dir.access` 方法来检查文件的存在性和权限。 +在 Zig 中检查文件是否存在,可以使用标准库中的 `std.Io.Dir` 模块。具体来说,可以使用 `Dir.access` 方法来检查文件的存在性和权限。 []($code.siteAsset('src/01-04.zig').language('zig')) diff --git a/src/zh-CN/01-05-iterate-dir.smd b/src/zh-CN/01-05-iterate-dir.smd index 7b02e37..593fae2 100644 --- a/src/zh-CN/01-05-iterate-dir.smd +++ b/src/zh-CN/01-05-iterate-dir.smd @@ -5,7 +5,7 @@ .layout = "section.shtml", --- -在 Zig 中遍历目录,可以使用标准库中的 `std.fs` 模块。它提供了多种方法来操作文件系统,例如打开目录、读取目录内容等,这里使用 `walk` 方法来递归遍历目录。 +在 Zig 中遍历目录,可以使用标准库中的 `std.Io.Dir` 模块。它提供了多种方法来操作文件系统,例如打开目录、读取目录内容等,这里使用 `walk` 方法来递归遍历目录。 []($code.siteAsset('src/01-05.zig').language('zig')) diff --git a/src/zh-CN/02-02-pbkdf2.smd b/src/zh-CN/02-02-pbkdf2.smd index 72b1062..77b85d5 100644 --- a/src/zh-CN/02-02-pbkdf2.smd +++ b/src/zh-CN/02-02-pbkdf2.smd @@ -6,9 +6,9 @@ --- 使用 PBKDF2 对密码进行加盐和哈希 -使用 [`std.crypto.pwhash.pbkdf2`] 对加盐后的密码进行哈希处理。盐值是使用 [`std.rand.DefaultPrng`] 生成的,它用生成的随机数填充盐值字节数组。 +使用 [`std.crypto.pwhash.pbkdf2`] 通过 HMAC-SHA256 对加盐后的密码进行哈希处理。在生产环境中,盐值应使用 [`std.Io.randomSecure`] 随机生成。 []($code.siteAsset('src/02-02.zig').language('zig')) -[`std.crypto.pwhash.pbkdf2`]: https://ziglang.org/documentation/0.11.0/std/#A;std:crypto.pwhash.pbkdf2 -[`std.rand.defaultprng`]: https://ziglang.org/documentation/0.11.0/std/#A;std:rand.DefaultPrng +[`std.crypto.pwhash.pbkdf2`]: https://ziglang.org/documentation/0.16.0/std/#std.crypto.pwhash.pbkdf2 +[`std.Io.randomSecure`]: https://ziglang.org/documentation/0.16.0/std/#std.Io.randomSecure diff --git a/src/zh-CN/02-03-argon2.smd b/src/zh-CN/02-03-argon2.smd index e9d4940..fa9ebdb 100644 --- a/src/zh-CN/02-03-argon2.smd +++ b/src/zh-CN/02-03-argon2.smd @@ -6,9 +6,9 @@ --- 使用 Argon2 对密码进行加盐和哈希 -此 Zig 程序使用 Argon2id 密码哈希算法从密码和盐值派生加密密钥。它使用 [std.crypto.pwhash.argon2] 对加盐后的密码进行哈希处理,其中盐值是使用 [`std.crypto.random`] 生成的。 +此 Zig 程序使用 Argon2id 密码哈希算法从密码和盐值派生加密密钥。它使用 [std.crypto.pwhash.argon2] 对加盐后的密码进行哈希处理,其中盐值是使用 [`std.Io.randomSecure`] 生成的。 []($code.siteAsset('src/02-03.zig').language('zig')) -[`std.crypto.pwhash.argon2`]: https://ziglang.org/documentation/0.14.0/std/#std.crypto.pwhash.argon2 -[`std.crypto.random`]: https://ziglang.org/documentation/0.14.0/std/#std.crypto.tlcsprng.interface \ No newline at end of file +[`std.crypto.pwhash.argon2`]: https://ziglang.org/documentation/0.16.0/std/#std.crypto.pwhash.argon2 +[`std.Io.randomSecure`]: https://ziglang.org/documentation/0.16.0/std/#std.Io.randomSecure \ No newline at end of file diff --git a/src/zh-CN/03-01-elapsed-time.smd b/src/zh-CN/03-01-elapsed-time.smd index b9ed989..4967486 100644 --- a/src/zh-CN/03-01-elapsed-time.smd +++ b/src/zh-CN/03-01-elapsed-time.smd @@ -5,14 +5,11 @@ .layout = "section.shtml", --- -[`Instant`] 表示相对于当前执行程序的时间戳,即使程序挂起它也会继续计时,可用于记录经过的时间。 +[`Io.Clock`] 通过 `Io.Clock.awake.now(io)` 提供单调时间戳。对时间戳调用 `durationTo` 可返回以纳秒为单位的 `Duration`。 -调用 [`std.time.Instant.since`] 返回一个表示经过纳秒数的 u64。 - -这是一个非常常见的任务,为了方便起见,提供了一个 [`Timer`]。 +如需休眠,使用 [`Io.sleep`] 并传入 `Duration` 值。 []($code.siteAsset('src/03-01.zig').language('zig')) -[`instant`]: https://ziglang.org/documentation/0.11.0/std/#A;std:time.Instant -[`timer`]: https://ziglang.org/documentation/0.11.0/std/#A;std:time.Timer -[`std.time.instant.since`]: https://ziglang.org/documentation/0.11.0/std/#A;std:time.Instant.since +[`Io.Clock`]: https://ziglang.org/documentation/0.16.0/std/#std.Io.Clock +[`Io.sleep`]: https://ziglang.org/documentation/0.16.0/std/#std.Io.sleep diff --git a/src/zh-CN/04-01-tcp-server.smd b/src/zh-CN/04-01-tcp-server.smd index fe9724e..0673690 100644 --- a/src/zh-CN/04-01-tcp-server.smd +++ b/src/zh-CN/04-01-tcp-server.smd @@ -5,7 +5,7 @@ .layout = "section.shtml", --- -在此示例中,端口显示在控制台上,程序将监听直到收到请求。将端口设置为 0 时,`Ip4Address` 会分配一个随机端口。 +在此示例中,端口显示在控制台上,程序将监听直到收到请求。使用 `IpAddress` 配合 `.loopback(0)` 会分配一个随机端口。 []($code.siteAsset('src/04-01.zig').language('zig')) @@ -15,7 +15,7 @@ echo "hello zig" | nc localhost ``` -默认情况下,程序使用 IPv4 进行监听。如果您想要 IPv6,请使用 `::1` 代替 `127.0.0.1`,将 `net.Ip4Address.parse` 替换为 `net.Ip6Address.parse`,并在创建 `net.Address` 时将字段 `.in` 替换为 `.in6`。 +默认情况下,程序使用 IPv4 进行监听。如果您想要 IPv6,请使用 `net.IpAddress{ .ip6 = .loopback(0) }` 代替。 (并连接到像 `ip6-localhost` 之类的地址,具体取决于您的机器设置。) diff --git a/src/zh-CN/04-02-tcp-client.smd b/src/zh-CN/04-02-tcp-client.smd index d9ad8b8..49a9fa3 100644 --- a/src/zh-CN/04-02-tcp-client.smd +++ b/src/zh-CN/04-02-tcp-client.smd @@ -10,4 +10,4 @@ []($code.siteAsset('src/04-02.zig').language('zig')) -默认情况下,程序使用 IPv4 连接。如果您想要 IPv6,请使用 `::1` 代替 `127.0.0.1`,将 `net.Address.parseIp4` 替换为 `net.Address.parseIp6`。 +默认情况下,程序使用 IPv4 连接。如果您想要 IPv6,请使用 `::1` 代替 `127.0.0.1`,将 `net.IpAddress.parseIp4` 替换为 `net.IpAddress.parseIp6`。 diff --git a/src/zh-CN/04-03-udp-echo.smd b/src/zh-CN/04-03-udp-echo.smd index aea85e7..905603f 100644 --- a/src/zh-CN/04-03-udp-echo.smd +++ b/src/zh-CN/04-03-udp-echo.smd @@ -7,7 +7,7 @@ 与 TCP 服务器示例类似,此程序将侦听指定的 IP 地址和端口,但这次是侦听 UDP 数据报。如果接收到数据,它将被回显到发送者的地址。 -尽管 `std.net` 主要侧重于 TCP 的抽象(目前为止),我们仍然可以利用套接字编程通过 UDP 进行通信。 +尽管 `std.Io.net` 主要侧重于 TCP 的抽象(目前为止),我们仍然可以利用套接字编程通过 UDP 进行通信。 []($code.siteAsset('src/04-03.zig').language('zig')) diff --git a/src/zh-CN/05-01-http-get.smd b/src/zh-CN/05-01-http-get.smd index 93a0815..2a7bc6f 100644 --- a/src/zh-CN/05-01-http-get.smd +++ b/src/zh-CN/05-01-http-get.smd @@ -12,5 +12,5 @@ []($code.siteAsset('src/05-01.zig').language('zig')) -[`request`]: https://ziglang.org/documentation/0.11.0/std/src/std/http/Client.zig.html#L992 -[`response`]: https://ziglang.org/documentation/0.11.0/std/src/std/http/Client.zig.html#L322 +[`request`]: https://ziglang.org/documentation/0.16.0/std/src/std/http/Client.zig.html#L992 +[`response`]: https://ziglang.org/documentation/0.16.0/std/src/std/http/Client.zig.html#L322 diff --git a/src/zh-CN/05-02-http-post.smd b/src/zh-CN/05-02-http-post.smd index c4e2c69..4314ea7 100644 --- a/src/zh-CN/05-02-http-post.smd +++ b/src/zh-CN/05-02-http-post.smd @@ -11,5 +11,5 @@ []($code.siteAsset('src/05-02.zig').language('zig')) -[`request`]: https://ziglang.org/documentation/0.11.0/std/src/std/http/Client.zig.html#L992 -[`response`]: https://ziglang.org/documentation/0.11.0/std/src/std/http/Client.zig.html#L322 +[`request`]: https://ziglang.org/documentation/0.16.0/std/src/std/http/Client.zig.html#L992 +[`response`]: https://ziglang.org/documentation/0.16.0/std/src/std/http/Client.zig.html#L322 diff --git a/src/zh-CN/06-01-rand.smd b/src/zh-CN/06-01-rand.smd index 2aa04e1..164acd4 100644 --- a/src/zh-CN/06-01-rand.smd +++ b/src/zh-CN/06-01-rand.smd @@ -5,8 +5,8 @@ .layout = "section.shtml", --- -借助线程局部、加密安全的伪随机数生成器 [`std.crypto.random`] 生成随机数。 +使用 [`std.Random.IoSource`] 生成随机数,它提供了一个由系统 I/O 接口支持的加密安全随机数生成器。 []($code.siteAsset('src/06-01.zig').language('zig')) -[`std.crypto.random`]: https://ziglang.org/documentation/0.11.0/std/#A;std:crypto.random +[`std.Random.IoSource`]: https://ziglang.org/documentation/0.16.0/std/#std.Random.IoSource diff --git a/src/zh-CN/07-01-spawn.smd b/src/zh-CN/07-01-spawn.smd index d61519b..a0c9dd1 100644 --- a/src/zh-CN/07-01-spawn.smd +++ b/src/zh-CN/07-01-spawn.smd @@ -14,5 +14,5 @@ []($code.siteAsset('src/07-01.zig').language('zig')) -[`std.thread`]: https://ziglang.org/documentation/0.11.0/std/#A;std:Thread -[`std.thread.spawn`]: https://ziglang.org/documentation/0.11.0/std/#A;std:Thread.spawn +[`std.thread`]: https://ziglang.org/documentation/0.16.0/std/#std.Thread +[`std.thread.spawn`]: https://ziglang.org/documentation/0.16.0/std/#std.Thread.spawn diff --git a/src/zh-CN/07-02-shared-data.smd b/src/zh-CN/07-02-shared-data.smd index f3c95fc..e5134b7 100644 --- a/src/zh-CN/07-02-shared-data.smd +++ b/src/zh-CN/07-02-shared-data.smd @@ -10,4 +10,4 @@ []($code.siteAsset('src/07-02.zig').language('zig')) 如果我们移除互斥锁保护,结果很可能小于 30,000。 -[`Mutex`]: https://ziglang.org/documentation/0.11.0/std/#A;std:Thread.Mutex +[`Mutex`]: https://ziglang.org/documentation/0.16.0/std/#std.Io.Mutex diff --git a/src/zh-CN/07-03-threadpool.smd b/src/zh-CN/07-03-threadpool.smd index b058ab9..f215588 100644 --- a/src/zh-CN/07-03-threadpool.smd +++ b/src/zh-CN/07-03-threadpool.smd @@ -10,6 +10,6 @@ 2. 它们提供了一种限制和管理在执行任务集合时消耗的资源(包括线程)的方法。 -在此示例中,我们将 10 个任务放入线程池,并使用 `WaitGroup` 等待它们完成。 +在此示例中,我们使用 `Io.Group` 派发 10 个任务,并调用 `await` 等待它们完成。 []($code.siteAsset('src/07-03.zig').language('zig')) diff --git a/src/zh-CN/07-04-run-once.smd b/src/zh-CN/07-04-run-once.smd index fd95268..76d4ee9 100644 --- a/src/zh-CN/07-04-run-once.smd +++ b/src/zh-CN/07-04-run-once.smd @@ -6,6 +6,12 @@ --- -`std.once` 确保一个函数只执行一次,无论有多少个线程尝试调用它或它被调用多少次。这种线程安全的初始化对于单例模式和一次性设置操作特别有用。 +`std.once` 在 Zig 0.16 中已被移除。这里我们使用三态原子协议手动实现相同的"只执行一次"语义: + +- **0(空闲)** – 还没有线程开始执行。 +- **1(执行中)** – 一个线程赢得竞争,正在执行目标函数。 +- **2(完成)** – 目标函数已执行完毕,所有线程可以直接返回。 + +这确保了一个函数只执行一次,无论有多少个线程尝试调用它。适用于单例模式和一次性初始化操作。 []($code.siteAsset('src/07-04.zig').language('zig')) diff --git a/src/zh-CN/08-01-cpu-count.smd b/src/zh-CN/08-01-cpu-count.smd index 0a8ca5e..3286759 100644 --- a/src/zh-CN/08-01-cpu-count.smd +++ b/src/zh-CN/08-01-cpu-count.smd @@ -9,4 +9,4 @@ []($code.siteAsset('src/08-01.zig').language('zig')) -[`std.thread.getcpucount`]: https://ziglang.org/documentation/0.11.0/std/#A;std:Thread.getCpuCount +[`std.thread.getcpucount`]: https://ziglang.org/documentation/0.16.0/std/#std.Thread.getCpuCount diff --git a/src/zh-CN/08-02-external.smd b/src/zh-CN/08-02-external.smd index 497d5b7..717e3b0 100644 --- a/src/zh-CN/08-02-external.smd +++ b/src/zh-CN/08-02-external.smd @@ -5,9 +5,8 @@ .layout = "section.shtml", --- -通过 [`std.process.Child`] 运行外部命令,并通过 [pipe](管道)将输出收集到 `ArrayList` 中。 +通过 [`std.process.run`] 运行外部命令,它会启动子进程、收集 stdout/stderr 输出,并等待进程退出。 []($code.siteAsset('src/08-02.zig').language('zig')) -[`std.process.child`]: https://ziglang.org/documentation/0.11.0/std/#A;std:process.Child -[pipe]: https://man7.org/linux/man-pages/man2/pipe.2.html +[`std.process.run`]: https://ziglang.org/documentation/0.16.0/std/#std.process.run diff --git a/src/zh-CN/09-01-semver.smd b/src/zh-CN/09-01-semver.smd index 677225f..4d2ae86 100644 --- a/src/zh-CN/09-01-semver.smd +++ b/src/zh-CN/09-01-semver.smd @@ -9,6 +9,6 @@ []($code.siteAsset('src/09-01.zig').language('zig')) -[`std.semanticversion`]: https://ziglang.org/documentation/0.11.0/std/#A;std:SemanticVersion -[`semanticversion.parse`]: https://ziglang.org/documentation/0.11.0/std/#A;std:SemanticVersion.parse +[`std.semanticversion`]: https://ziglang.org/documentation/0.16.0/std/#std.SemanticVersion +[`semanticversion.parse`]: https://ziglang.org/documentation/0.16.0/std/#std.SemanticVersion.parse [semantic versioning specification]: http://semver.org/ diff --git a/src/zh-CN/10-01-json.smd b/src/zh-CN/10-01-json.smd index fab7795..98846ad 100644 --- a/src/zh-CN/10-01-json.smd +++ b/src/zh-CN/10-01-json.smd @@ -5,15 +5,13 @@ .layout = "section.shtml", --- -[`std.json`] 提供了一组函数,如 [`stringify`] 和 [`stringifyAlloc`],用于序列化 JSON。 -此外,我们可以使用 [`parseFromSlice`] 函数来解析 JSON 的 `[]u8`。 +[`std.json`] 模块提供了 [`parseFromSlice`] 用于反序列化 JSON,以及 [`json.Stringify`] 结构体通过 `Io.Writer.Allocating` 将值序列化回 JSON。 下面的示例显示了解析 JSON 的 `[]u8`。逐个比较每个成员。 然后,我们将 `verified` 字段修改为 `false`,并将其序列化回 JSON 字符串。 []($code.siteAsset('src/10-01.zig').language('zig')) -[`std.json`]: https://ziglang.org/documentation/0.11.0/std/#A;std:json -[`stringify`]: https://ziglang.org/documentation/0.11.0/std/#A;std:json.stringify -[`stringifyalloc`]: https://ziglang.org/documentation/0.11.0/std/#A;std:json.stringifyAlloc -[`parsefromslice`]: https://ziglang.org/documentation/0.11.0/std/#A;std:json.parseFromSlice +[`std.json`]: https://ziglang.org/documentation/0.16.0/std/#std.json +[`json.Stringify`]: https://ziglang.org/documentation/0.16.0/std/#std.json.Stringify +[`parsefromslice`]: https://ziglang.org/documentation/0.16.0/std/#std.json.parseFromSlice diff --git a/src/zh-CN/10-03-base64.smd b/src/zh-CN/10-03-base64.smd index 91bde6f..eccc67f 100644 --- a/src/zh-CN/10-03-base64.smd +++ b/src/zh-CN/10-03-base64.smd @@ -7,9 +7,9 @@ 使用 [`std.base64.standard`] [`encode`] 将字节切片编码为 `base64` 字符串,并使用 [`standard`] [`decode`] 对其进行解码。 -[]($code.siteAsset('src/10-02.zig').language('zig')) +[]($code.siteAsset('src/10-03.zig').language('zig')) -[`std.base64.standard`]: https://ziglang.org/documentation/0.11.0/std/#A;std:base64.standard -[`standard`]: https://ziglang.org/documentation/0.11.0/std/src/std/base64.zig.html#L29 -[`encode`]: https://ziglang.org/documentation/0.11.0/std/#A;std:base64.Base64Encoder.encode -[`decode`]: https://ziglang.org/documentation/0.11.0/std/#A;std:base64.Base64Decoder.decode +[`std.base64.standard`]: https://ziglang.org/documentation/0.16.0/std/#std.base64.standard +[`standard`]: https://ziglang.org/documentation/0.16.0/std/src/std/base64.zig.html#L29 +[`encode`]: https://ziglang.org/documentation/0.16.0/std/#std.base64.Base64Encoder.encode +[`decode`]: https://ziglang.org/documentation/0.16.0/std/#std.base64.Base64Decoder.decode diff --git a/src/zh-CN/11-01-complex-numbers.smd b/src/zh-CN/11-01-complex-numbers.smd index 9518ecc..e12d79d 100644 --- a/src/zh-CN/11-01-complex-numbers.smd +++ b/src/zh-CN/11-01-complex-numbers.smd @@ -11,4 +11,4 @@ []($code.siteAsset('src/11-01.zig').language('zig')) -[`std.math.complex`]: https://ziglang.org/documentation/0.11.0/std/#A;std:math.Complex +[`std.math.complex`]: https://ziglang.org/documentation/0.16.0/std/#std.math.Complex diff --git a/src/zh-CN/13-01-argparse.smd b/src/zh-CN/13-01-argparse.smd index 712b3fb..8a1cf50 100644 --- a/src/zh-CN/13-01-argparse.smd +++ b/src/zh-CN/13-01-argparse.smd @@ -9,8 +9,8 @@ - [Hejsil/zig-clap](https://github.com/Hejsil/zig-clap) - [MasterQ32/zig-args](https://github.com/MasterQ32/zig-args/) -- [simargs](https://zigcli.liujiacai.net/modules/simargs/) +- [structargs](https://zigcli.liujiacai.net/) -这里我们将给出一个使用 `simargs` 的示例。 +这里我们将给出一个使用 [zigcli](https://github.com/jiacai2050/zigcli) 工具包中 `structargs` 的示例。 []($code.siteAsset('src/13-01.zig').language('zig')) diff --git a/src/zh-CN/14-01-sqlite.smd b/src/zh-CN/14-01-sqlite.smd index 0869119..3142502 100644 --- a/src/zh-CN/14-01-sqlite.smd +++ b/src/zh-CN/14-01-sqlite.smd @@ -5,7 +5,7 @@ .layout = "section.shtml", --- -尽管 Zig 中有一些 SQLite 的 [wrapper package](https://github.com/vrischmann/zig-sqlite)(封装包)选项,但它们还不稳定。所以这里我们将介绍 [C API interface](https://www.sqlite.org/cintro.html)(C API 接口)。 +尽管 Zig 中有一些 SQLite 的 [wrapper package](https://github.com/vrischmann/zig-sqlite)(封装包)选项,但它们还不稳定。所以这里我们将通过 `addTranslateC` 介绍 [C API interface](https://www.sqlite.org/cintro.html)(C API 接口)。 数据模型在 [这里](database) 介绍。 diff --git a/src/zh-CN/14-02-postgres.smd b/src/zh-CN/14-02-postgres.smd index eb77acb..99428d4 100644 --- a/src/zh-CN/14-02-postgres.smd +++ b/src/zh-CN/14-02-postgres.smd @@ -5,7 +5,7 @@ .layout = "section.shtml", --- -与 [上一节](14-01-sqlite) 一样,这里我们直接介绍 [libpq](https://www.postgresql.org/docs/16/libpq-example.html) 接口,而不是 [wrapper package](https://github.com/tonis2/zig-postgres)。 +与 [上一节](14-01-sqlite) 一样,这里我们通过 `addTranslateC` 直接介绍 [libpq](https://www.postgresql.org/docs/16/libpq-example.html) 接口,而不是 [wrapper package](https://github.com/tonis2/zig-postgres)。 本演示中使用的数据模型与 [SQLite 章节](14-01-sqlite) 中使用的相同。 diff --git a/src/zh-CN/14-03-mysql.smd b/src/zh-CN/14-03-mysql.smd index 841f0b2..aa964a5 100644 --- a/src/zh-CN/14-03-mysql.smd +++ b/src/zh-CN/14-03-mysql.smd @@ -5,7 +5,7 @@ .layout = "section.shtml", --- -与 [sqlite 章节](14-01-sqlite) 一样,这里我们直接介绍 [libmysqlclient](https://dev.mysql.com/doc/c-api/8.0/en/c-api-basic-interface-usage.html) 接口。 +与 [sqlite 章节](14-01-sqlite) 一样,这里我们通过 `addTranslateC` 直接介绍 [libmysqlclient](https://dev.mysql.com/doc/c-api/8.0/en/c-api-basic-interface-usage.html) 接口。 数据模型在 [这里](database) 介绍。 diff --git a/src/zh-CN/15-01-regex.smd b/src/zh-CN/15-01-regex.smd index 4110a76..3a83588 100644 --- a/src/zh-CN/15-01-regex.smd +++ b/src/zh-CN/15-01-regex.smd @@ -7,7 +7,7 @@ 目前 Zig 中没有正则表达式支持,因此最好的方法是绑定 Posix 的 [regex.h](https://pubs.opengroup.org/onlinepubs/7908799/xsh/regex.h.html)。 -在 Zig 中与 C 互操作很容易,但是由于 translate-c 不支持位域,我们不能直接使用 `regex_t`。所以这里我们首先创建一个 [静态 C 库](https://github.com/zigcc/zig-cookbook/blob/460dea1f2f9937ab512a70683aacab79e34c723a/build.zig#L50),提供两个函数: +在 Zig 中通过构建系统的 [`addTranslateC`](https://ziglang.org/documentation/0.16.0/#toc-addTranslateC) 与 C 互操作很容易,但是由于 translate-c 不支持位域,我们不能直接使用 `regex_t`。所以这里我们首先创建一个 [静态 C 库](https://github.com/zigcc/zig-cookbook/blob/main/lib/regex_slim.c),提供两个函数: ```c regex_t* alloc_regex_t(void); From 174b0f1be2933ca680b37e1b2d202cd9ed525f87 Mon Sep 17 00:00:00 2001 From: jiacai2050 Date: Thu, 23 Apr 2026 10:57:44 +0800 Subject: [PATCH 5/7] more doc fix --- src/en-US/05-01-http-get.smd | 2 +- src/en-US/05-02-http-post.smd | 2 +- src/en-US/07-01-spawn.smd | 4 ++-- src/en-US/08-01-cpu-count.smd | 2 +- src/en-US/09-01-semver.smd | 4 ++-- src/en-US/index.smd | 2 +- src/zh-CN/05-01-http-get.smd | 2 +- src/zh-CN/05-02-http-post.smd | 2 +- src/zh-CN/07-01-spawn.smd | 4 ++-- src/zh-CN/08-01-cpu-count.smd | 2 +- src/zh-CN/09-01-semver.smd | 4 ++-- src/zh-CN/index.smd | 2 +- 12 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/en-US/05-01-http-get.smd b/src/en-US/05-01-http-get.smd index 0c2b717..194be40 100644 --- a/src/en-US/05-01-http-get.smd +++ b/src/en-US/05-01-http-get.smd @@ -14,4 +14,4 @@ with [`request`]. Prints obtained [`Response`] status and headers. []($code.siteAsset('src/05-01.zig').language('zig')) [`request`]: https://ziglang.org/documentation/0.16.0/std/src/std/http/Client.zig.html#L992 -[`response`]: https://ziglang.org/documentation/0.16.0/std/src/std/http/Client.zig.html#L322 +[`Response`]: https://ziglang.org/documentation/0.16.0/std/src/std/http/Client.zig.html#L322 diff --git a/src/en-US/05-02-http-post.smd b/src/en-US/05-02-http-post.smd index 3d8637e..c232584 100644 --- a/src/en-US/05-02-http-post.smd +++ b/src/en-US/05-02-http-post.smd @@ -13,4 +13,4 @@ with [`request`]. Prints obtained [`Response`] status, and data received from se []($code.siteAsset('src/05-02.zig').language('zig')) [`request`]: https://ziglang.org/documentation/0.16.0/std/src/std/http/Client.zig.html#L992 -[`response`]: https://ziglang.org/documentation/0.16.0/std/src/std/http/Client.zig.html#L322 +[`Response`]: https://ziglang.org/documentation/0.16.0/std/src/std/http/Client.zig.html#L322 diff --git a/src/en-US/07-01-spawn.smd b/src/en-US/07-01-spawn.smd index c8bba99..a2fad36 100644 --- a/src/en-US/07-01-spawn.smd +++ b/src/en-US/07-01-spawn.smd @@ -14,5 +14,5 @@ This example splits the array in half and performs the work in separate threads. []($code.siteAsset('src/07-01.zig').language('zig')) -[`std.thread`]: https://ziglang.org/documentation/0.16.0/std/#std.Thread -[`std.thread.spawn`]: https://ziglang.org/documentation/0.16.0/std/#std.Thread.spawn +[`std.Thread`]: https://ziglang.org/documentation/0.16.0/std/#std.Thread +[`std.Thread.spawn`]: https://ziglang.org/documentation/0.16.0/std/#std.Thread.spawn diff --git a/src/en-US/08-01-cpu-count.smd b/src/en-US/08-01-cpu-count.smd index 37b53c7..d2d4615 100644 --- a/src/en-US/08-01-cpu-count.smd +++ b/src/en-US/08-01-cpu-count.smd @@ -9,4 +9,4 @@ Shows the number of logical CPU cores in the current machine using [`std.Thread. []($code.siteAsset('src/08-01.zig').language('zig')) -[`std.thread.getcpucount`]: https://ziglang.org/documentation/0.16.0/std/#std.Thread.getCpuCount +[`std.Thread.getCpuCount`]: https://ziglang.org/documentation/0.16.0/std/#std.Thread.getCpuCount diff --git a/src/en-US/09-01-semver.smd b/src/en-US/09-01-semver.smd index c4c248e..5e46dec 100644 --- a/src/en-US/09-01-semver.smd +++ b/src/en-US/09-01-semver.smd @@ -9,6 +9,6 @@ Constructs a [`std.SemanticVersion`] from a string literal using [`SemanticVersi []($code.siteAsset('src/09-01.zig').language('zig')) -[`std.semanticversion`]: https://ziglang.org/documentation/0.16.0/std/#std.SemanticVersion -[`semanticversion.parse`]: https://ziglang.org/documentation/0.16.0/std/#std.SemanticVersion.parse +[`std.SemanticVersion`]: https://ziglang.org/documentation/0.16.0/std/#std.SemanticVersion +[`SemanticVersion.parse`]: https://ziglang.org/documentation/0.16.0/std/#std.SemanticVersion.parse [semantic versioning specification]: http://semver.org/ diff --git a/src/en-US/index.smd b/src/en-US/index.smd index 86113be..6468c02 100644 --- a/src/en-US/index.smd +++ b/src/en-US/index.smd @@ -24,7 +24,7 @@ [Zig cookbook](https://github.com/zigcc/zig-cookbook) is a collection of simple Zig programs that demonstrate good practices to accomplish common programming tasks. -> - Main branch tracks Zig 0.15.x, and is tested on Linux and macOS via GitHub actions. +> - Main branch tracks Zig 0.16.x, and is tested on Linux and macOS via GitHub actions. > - Earlier Zig support could be found in [other branches](https://github.com/zigcc/zig-cookbook/branches). # How to use diff --git a/src/zh-CN/05-01-http-get.smd b/src/zh-CN/05-01-http-get.smd index 2a7bc6f..c4657f2 100644 --- a/src/zh-CN/05-01-http-get.smd +++ b/src/zh-CN/05-01-http-get.smd @@ -13,4 +13,4 @@ []($code.siteAsset('src/05-01.zig').language('zig')) [`request`]: https://ziglang.org/documentation/0.16.0/std/src/std/http/Client.zig.html#L992 -[`response`]: https://ziglang.org/documentation/0.16.0/std/src/std/http/Client.zig.html#L322 +[`Response`]: https://ziglang.org/documentation/0.16.0/std/src/std/http/Client.zig.html#L322 diff --git a/src/zh-CN/05-02-http-post.smd b/src/zh-CN/05-02-http-post.smd index 4314ea7..c8efd5f 100644 --- a/src/zh-CN/05-02-http-post.smd +++ b/src/zh-CN/05-02-http-post.smd @@ -12,4 +12,4 @@ []($code.siteAsset('src/05-02.zig').language('zig')) [`request`]: https://ziglang.org/documentation/0.16.0/std/src/std/http/Client.zig.html#L992 -[`response`]: https://ziglang.org/documentation/0.16.0/std/src/std/http/Client.zig.html#L322 +[`Response`]: https://ziglang.org/documentation/0.16.0/std/src/std/http/Client.zig.html#L322 diff --git a/src/zh-CN/07-01-spawn.smd b/src/zh-CN/07-01-spawn.smd index a0c9dd1..a0fd573 100644 --- a/src/zh-CN/07-01-spawn.smd +++ b/src/zh-CN/07-01-spawn.smd @@ -14,5 +14,5 @@ []($code.siteAsset('src/07-01.zig').language('zig')) -[`std.thread`]: https://ziglang.org/documentation/0.16.0/std/#std.Thread -[`std.thread.spawn`]: https://ziglang.org/documentation/0.16.0/std/#std.Thread.spawn +[`std.Thread`]: https://ziglang.org/documentation/0.16.0/std/#std.Thread +[`std.Thread.spawn`]: https://ziglang.org/documentation/0.16.0/std/#std.Thread.spawn diff --git a/src/zh-CN/08-01-cpu-count.smd b/src/zh-CN/08-01-cpu-count.smd index 3286759..85621d7 100644 --- a/src/zh-CN/08-01-cpu-count.smd +++ b/src/zh-CN/08-01-cpu-count.smd @@ -9,4 +9,4 @@ []($code.siteAsset('src/08-01.zig').language('zig')) -[`std.thread.getcpucount`]: https://ziglang.org/documentation/0.16.0/std/#std.Thread.getCpuCount +[`std.Thread.getCpuCount`]: https://ziglang.org/documentation/0.16.0/std/#std.Thread.getCpuCount diff --git a/src/zh-CN/09-01-semver.smd b/src/zh-CN/09-01-semver.smd index 4d2ae86..62df48c 100644 --- a/src/zh-CN/09-01-semver.smd +++ b/src/zh-CN/09-01-semver.smd @@ -9,6 +9,6 @@ []($code.siteAsset('src/09-01.zig').language('zig')) -[`std.semanticversion`]: https://ziglang.org/documentation/0.16.0/std/#std.SemanticVersion -[`semanticversion.parse`]: https://ziglang.org/documentation/0.16.0/std/#std.SemanticVersion.parse +[`std.SemanticVersion`]: https://ziglang.org/documentation/0.16.0/std/#std.SemanticVersion +[`SemanticVersion.parse`]: https://ziglang.org/documentation/0.16.0/std/#std.SemanticVersion.parse [semantic versioning specification]: http://semver.org/ diff --git a/src/zh-CN/index.smd b/src/zh-CN/index.smd index 12fbcef..5284a34 100644 --- a/src/zh-CN/index.smd +++ b/src/zh-CN/index.smd @@ -24,7 +24,7 @@ [Zig Cookbook](https://github.com/zigcc/zig-cookbook) 是一系列简单的 Zig 程序集合,用于展示完成常见编程任务的最佳实践。 -> - 主分支跟踪 Zig 0.15.x 版本,并通过 GitHub Actions 在 Linux 和 macOS 上进行测试。 +> - 主分支跟踪 Zig 0.16.x 版本,并通过 GitHub Actions 在 Linux 和 macOS 上进行测试。 > - 更早版本的 Zig 支持可在 [其他分支](https://github.com/zigcc/zig-cookbook/branches) 中找到。 # 如何使用 From 64c8c47a075259980daf5874aabc13192d2d3931 Mon Sep 17 00:00:00 2001 From: jiacai2050 Date: Thu, 23 Apr 2026 10:59:37 +0800 Subject: [PATCH 6/7] add GEMINI.md --- CLAUDE.md | 1 + GEMINI.md | 47 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+) create mode 120000 CLAUDE.md create mode 100644 GEMINI.md diff --git a/CLAUDE.md b/CLAUDE.md new file mode 120000 index 0000000..e3c5a92 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1 @@ +GEMINI.md \ No newline at end of file diff --git a/GEMINI.md b/GEMINI.md new file mode 100644 index 0000000..718785c --- /dev/null +++ b/GEMINI.md @@ -0,0 +1,47 @@ +# Zig Cookbook - Project Context + +## Project Overview +`zig-cookbook` is a multilingual collection of Zig (0.16.x) programming recipes and examples. It demonstrates common programming tasks using Zig's standard library and specialized packages. The project generates a static documentation website using the [zine](https://zine-ssg.io) static site generator. + +## Key Technologies +- **Zig (0.16.0)**: The primary programming language. +- **Zine**: Static site generator for documentation. +- **Docker**: Used for running databases (Postgres, MySQL) required by some examples. +- **system dependencies**: Some examples link against C libraries like `libpq`, `mysqlclient`, and `sqlite3`. + +## Directory Structure +- `assets/src/`: Contains all Zig example source files (e.g., `01-01.zig`). +- `src/`: Contains localized documentation in Super Markdown (`.smd`) format, organized by language (`en-US`, `zh-CN`). +- `lib/`: C header files and helper source code for C interop examples. +- `layouts/`: Templates and UI components for the documentation website. +- `i18n/`: Internationalization configuration files (`.ziggy`). + +## Building and Running +The project uses the standard Zig build system. + +### Running Examples +- **Specific Example**: `zig build run-{chapter}-{seq}` (e.g., `zig build run-01-01`). +- **All Examples**: `zig build run-all`. +- **Compile Check**: `zig build check`. + +### Local Documentation +- **Preview Site**: `make serve` (starts `zine` on port 1313). +- **Zine Preview**: Alternatively, use `zine` directly. + +### Dependencies & Environment +- **Install System Libraries**: `make install-deps` (supports macOS via brew and Linux via apt). +- **Databases**: `docker-compose up -d` to start the required Postgres and MySQL instances. +- **Environment Variables**: `source env.sh` may be required on some systems to set `PKG_CONFIG_PATH` for database clients. + +## Development Conventions +- **Zig Version**: Targets Zig 0.16.x. Adheres to modern Zig idioms like `std.Io` and `std.process.Init` entry points. +- **Testing**: Many examples include `std.testing` assertions within their `main` or as helper blocks to verify correctness. +- **C Interop**: Uses `b.addTranslateC` in `build.zig` to interface with C libraries. +- **Formatting**: The project uses `prettier` for linting `.smd` files (via `make lint`). + +## Project-Specific Tips +- When adding a new recipe: + 1. Add the Zig code to `assets/src/`. + 2. Create corresponding `.smd` files in `src/en-US/` and `src/zh-CN/`. + 3. Update `build.zig` if the example has special library dependencies. +- Use `std.debug.print` for output in examples as they are meant for learning. From eb4f7ee8db8e638d605394e82af7e2b42c5f6116 Mon Sep 17 00:00:00 2001 From: jiacai2050 Date: Thu, 23 Apr 2026 11:02:16 +0800 Subject: [PATCH 7/7] refactor(build): Close src_dir properly in addExample Ensure src_dir is closed after use to prevent resource leaks. This change improves code safety and reliability during build process by properly managing directory handles. --- build.zig | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/build.zig b/build.zig index b8236f9..0631ed2 100644 --- a/build.zig +++ b/build.zig @@ -10,7 +10,8 @@ pub fn build(b: *std.Build) !void { fn addExample(b: *std.Build, run_all: *std.Build.Step) !void { const io = b.graph.io; - const src_dir = try std.Io.Dir.cwd().openDir(io, b.path("assets/src").getPath(b), .{ .iterate = true }); + var src_dir = try std.Io.Dir.cwd().openDir(io, b.path("assets/src").getPath(b), .{ .iterate = true }); + defer src_dir.close(io); const target = b.standardTargetOptions(.{}); var it = src_dir.iterate();