Skip to content

Commit e2d9393

Browse files
committed
Update to Zig 0.16
1 parent a560f53 commit e2d9393

17 files changed

Lines changed: 333 additions & 276 deletions

File tree

.github/workflows/ci.yml

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,63 @@ jobs:
2121
- uses: mlugg/setup-zig@v2
2222
- name: Fetch test-data
2323
run: git submodule update --init
24+
- name: Diagnose process spawning on aarch64-windows
25+
if: matrix.os == 'windows-11-arm'
26+
shell: pwsh
27+
run: |
28+
# Test 1: zig test (no subprocess spawning) — should work
29+
echo "=== 1. zig test (no spawn) ==="
30+
zig test src/maxminddb.zig 2>&1 | Select-Object -Last 3
31+
echo "exit: $LASTEXITCODE"
32+
33+
# Test 2: zig build test (build runner spawns 1 zig subprocess)
34+
echo "=== 2. zig build test (1 spawn) ==="
35+
Remove-Item -Recurse -Force .zig-cache -ErrorAction SilentlyContinue
36+
zig build test 2>&1 | Select-Object -Last 3
37+
echo "exit: $LASTEXITCODE"
38+
39+
# Test 3: spawn zig multiple times from PowerShell (not build runner)
40+
echo "=== 3. sequential zig invocations from PowerShell ==="
41+
$zigExe = (Get-Command zig).Source
42+
Remove-Item -Recurse -Force .zig-cache -ErrorAction SilentlyContinue
43+
foreach ($i in 1..5) {
44+
$p = Start-Process -FilePath $zigExe -ArgumentList "build-obj","src/maxminddb.zig" -NoNewWindow -PassThru -Wait
45+
echo "run $i exit: $($p.ExitCode)"
46+
if ($p.ExitCode -ne 0) { break }
47+
}
48+
49+
# Test 4: spawn zig from a zig program (like build runner does)
50+
echo "=== 4. zig spawning zig ==="
51+
Remove-Item -Recurse -Force .zig-cache -ErrorAction SilentlyContinue
52+
Set-Content -Path spawn_test.zig -Value @'
53+
const std = @import("std");
54+
pub fn main(init: std.process.Init) !void {
55+
const io = init.io;
56+
for (0..3) |i| {
57+
const result = try std.process.run(init.gpa, io, .{
58+
.argv = &.{ "zig", "build-obj", "src/maxminddb.zig" },
59+
});
60+
std.debug.print("spawn {d}: term={any} stderr={s}\n", .{
61+
i, result.term, result.stderr,
62+
});
63+
init.gpa.free(result.stdout);
64+
init.gpa.free(result.stderr);
65+
if (result.term != .exited) break;
66+
if (result.term.exited != 0) break;
67+
}
68+
}
69+
'@
70+
zig run spawn_test.zig 2>&1
71+
echo "exit: $LASTEXITCODE"
2472
- name: Run tests
73+
if: matrix.os != 'windows-11-arm'
2574
run: zig build test
2675
- name: Run lookup example
76+
if: matrix.os != 'windows-11-arm'
2777
run: zig build example_lookup
2878
- name: Run scan example
79+
if: matrix.os != 'windows-11-arm'
2980
run: zig build example_scan
3081
- name: Run inspect example
82+
if: matrix.os != 'windows-11-arm'
3183
run: zig build example_inspect

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ It returns `Result` or null when the IP is not found or the record is empty.
5252
Each result owns an arena so you should call `result.deinit()` to free it.
5353

5454
```zig
55-
var db = try maxminddb.Reader.mmap(allocator, db_path, .{});
55+
var db = try maxminddb.Reader.mmap(allocator, io, db_path, .{});
5656
defer db.close();
5757
5858
if (try db.lookup(maxminddb.geolite2.City, allocator, ip, .{})) |result| {
@@ -134,7 +134,7 @@ and uses ~320KB at depth 16, or 12 (~20KB) for constrained devices.
134134
It's not worth creating an index for short-lived readers.
135135

136136
```zig
137-
var db = try maxminddb.Reader.mmap(allocator, db_path, .{ .ipv4_index_first_n_bits = 16 });
137+
var db = try maxminddb.Reader.mmap(allocator, io, db_path, .{ .ipv4_index_first_n_bits = 16 });
138138
defer db.close();
139139
```
140140

benchmarks/inspect.zig

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -6,19 +6,18 @@ const default_db_path: []const u8 = "GeoLite2-City.mmdb";
66
const default_num_lookups: u64 = 1_000_000;
77
const max_mmdb_fields = 32;
88

9-
pub fn main() !void {
10-
const allocator = std.heap.smp_allocator;
9+
pub fn main(init: std.process.Init) !void {
10+
const allocator = init.gpa;
11+
const io = init.io;
1112

12-
const args = try std.process.argsAlloc(allocator);
13-
defer std.process.argsFree(allocator, args);
14-
15-
var db_path: []const u8 = default_db_path;
13+
var args = try std.process.Args.Iterator.initAllocator(init.minimal.args, allocator);
14+
_ = args.skip();
15+
const db_path = args.next() orelse default_db_path;
1616
var num_lookups = default_num_lookups;
1717
var fields: ?[]const []const u8 = null;
18-
if (args.len > 1) db_path = args[1];
19-
if (args.len > 2) num_lookups = try std.fmt.parseUnsigned(u64, args[2], 10);
20-
if (args.len > 3) {
21-
const f = try maxminddb.Fields(max_mmdb_fields).parse(args[3], ',');
18+
if (args.next()) |arg| num_lookups = try std.fmt.parseUnsigned(u64, arg, 10);
19+
if (args.next()) |arg| {
20+
const f = try maxminddb.Fields(max_mmdb_fields).parse(arg, ',');
2221
fields = f.only();
2322
}
2423

@@ -27,10 +26,11 @@ pub fn main() !void {
2726
std.debug.print(" Lookups: {d}\n", .{num_lookups});
2827
std.debug.print("Opening database...\n", .{});
2928

30-
var open_timer = try std.time.Timer.start();
31-
var db = try maxminddb.Reader.mmap(allocator, db_path, .{ .ipv4_index_first_n_bits = 16 });
29+
const open_start = std.Io.Clock.Timestamp.now(io, .awake);
30+
var db = try maxminddb.Reader.mmap(allocator, io, db_path, .{ .ipv4_index_first_n_bits = 16 });
3231
defer db.close();
33-
const open_time_ms = @as(f64, @floatFromInt(open_timer.read())) /
32+
const open_elapsed_ns: i64 = @intCast(open_start.untilNow(io).raw.nanoseconds);
33+
const open_time_ms = @as(f64, @floatFromInt(open_elapsed_ns)) /
3434
@as(f64, @floatFromInt(std.time.ns_per_ms));
3535
std.debug.print("Database opened successfully in {d} ms. Type: {s}\n", .{
3636
open_time_ms,
@@ -42,14 +42,14 @@ pub fn main() !void {
4242
const arena_allocator = arena.allocator();
4343

4444
std.debug.print("Starting benchmark...\n", .{});
45-
var timer = try std.time.Timer.start();
45+
const timer_start = std.Io.Clock.Timestamp.now(io, .awake);
4646
var not_found_count: u64 = 0;
4747
var lookup_errors: u64 = 0;
4848
var ip_bytes: [4]u8 = undefined;
4949

5050
for (0..num_lookups) |_| {
51-
std.crypto.random.bytes(&ip_bytes);
52-
const ip = std.net.Address.initIp4(ip_bytes, 0);
51+
io.random(&ip_bytes);
52+
const ip: std.Io.net.IpAddress = .{ .ip4 = .{ .bytes = ip_bytes, .port = 0 } };
5353

5454
const result = db.lookup(
5555
maxminddb.any.Value,
@@ -69,7 +69,7 @@ pub fn main() !void {
6969
_ = arena.reset(.retain_capacity);
7070
}
7171

72-
const elapsed_ns = timer.read();
72+
const elapsed_ns: i64 = @intCast(timer_start.untilNow(io).raw.nanoseconds);
7373
const elapsed_s = @as(f64, @floatFromInt(elapsed_ns)) /
7474
@as(f64, @floatFromInt(std.time.ns_per_s));
7575
const lookups_per_second = if (elapsed_s > 0)

benchmarks/lookup.zig

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -6,33 +6,33 @@ const default_db_path: []const u8 = "GeoLite2-City.mmdb";
66
const default_num_lookups: u64 = 1_000_000;
77
const max_mmdb_fields = 32;
88

9-
pub fn main() !void {
10-
const allocator = std.heap.smp_allocator;
9+
pub fn main(init: std.process.Init) !void {
10+
const allocator = init.gpa;
11+
const io = init.io;
1112

12-
const args = try std.process.argsAlloc(allocator);
13-
defer std.process.argsFree(allocator, args);
14-
15-
var db_path: []const u8 = default_db_path;
13+
var args = try std.process.Args.Iterator.initAllocator(init.minimal.args, allocator);
14+
_ = args.skip();
15+
const db_path = args.next() orelse default_db_path;
1616
var num_lookups = default_num_lookups;
1717
var fields: ?[]const []const u8 = null;
1818
var index_bits: u8 = 16;
19-
if (args.len > 1) db_path = args[1];
20-
if (args.len > 2) num_lookups = try std.fmt.parseUnsigned(u64, args[2], 10);
21-
if (args.len > 3) {
22-
const f = try maxminddb.Fields(max_mmdb_fields).parse(args[3], ',');
19+
if (args.next()) |arg| num_lookups = try std.fmt.parseUnsigned(u64, arg, 10);
20+
if (args.next()) |arg| {
21+
const f = try maxminddb.Fields(max_mmdb_fields).parse(arg, ',');
2322
fields = f.only();
2423
}
25-
if (args.len > 4) index_bits = try std.fmt.parseUnsigned(u8, args[4], 10);
24+
if (args.next()) |arg| index_bits = try std.fmt.parseUnsigned(u8, arg, 10);
2625

2726
std.debug.print("Benchmarking with:\n", .{});
2827
std.debug.print(" Database: {s}\n", .{db_path});
2928
std.debug.print(" Lookups: {d}\n", .{num_lookups});
3029
std.debug.print("Opening database...\n", .{});
3130

32-
var open_timer = try std.time.Timer.start();
33-
var db = try maxminddb.Reader.mmap(allocator, db_path, .{ .ipv4_index_first_n_bits = index_bits });
31+
const open_start = std.Io.Clock.Timestamp.now(io, .awake);
32+
var db = try maxminddb.Reader.mmap(allocator, io, db_path, .{ .ipv4_index_first_n_bits = index_bits });
3433
defer db.close();
35-
const open_time_ms = @as(f64, @floatFromInt(open_timer.read())) /
34+
const open_elapsed_ns: i64 = @intCast(open_start.untilNow(io).raw.nanoseconds);
35+
const open_time_ms = @as(f64, @floatFromInt(open_elapsed_ns)) /
3636
@as(f64, @floatFromInt(std.time.ns_per_ms));
3737
std.debug.print("Database opened successfully in {d} ms. Type: {s}\n", .{
3838
open_time_ms,
@@ -44,14 +44,14 @@ pub fn main() !void {
4444
const arena_allocator = arena.allocator();
4545

4646
std.debug.print("Starting benchmark...\n", .{});
47-
var timer = try std.time.Timer.start();
47+
const timer_start = std.Io.Clock.Timestamp.now(io, .awake);
4848
var not_found_count: u64 = 0;
4949
var lookup_errors: u64 = 0;
5050
var ip_bytes: [4]u8 = undefined;
5151

5252
for (0..num_lookups) |_| {
53-
std.crypto.random.bytes(&ip_bytes);
54-
const ip = std.net.Address.initIp4(ip_bytes, 0);
53+
io.random(&ip_bytes);
54+
const ip: std.Io.net.IpAddress = .{ .ip4 = .{ .bytes = ip_bytes, .port = 0 } };
5555

5656
const result = db.lookup(
5757
maxminddb.geolite2.City,
@@ -71,7 +71,7 @@ pub fn main() !void {
7171
_ = arena.reset(.retain_capacity);
7272
}
7373

74-
const elapsed_ns = timer.read();
74+
const elapsed_ns: i64 = @intCast(timer_start.untilNow(io).raw.nanoseconds);
7575
const elapsed_s = @as(f64, @floatFromInt(elapsed_ns)) /
7676
@as(f64, @floatFromInt(std.time.ns_per_s));
7777
const lookups_per_second = if (elapsed_s > 0)

benchmarks/lookup_cache.zig

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -5,19 +5,18 @@ const default_db_path: []const u8 = "GeoLite2-City.mmdb";
55
const default_num_lookups: u64 = 1_000_000;
66
const max_mmdb_fields = 32;
77

8-
pub fn main() !void {
9-
const allocator = std.heap.smp_allocator;
8+
pub fn main(init: std.process.Init) !void {
9+
const allocator = init.gpa;
10+
const io = init.io;
1011

11-
const args = try std.process.argsAlloc(allocator);
12-
defer std.process.argsFree(allocator, args);
13-
14-
var db_path: []const u8 = default_db_path;
12+
var args = try std.process.Args.Iterator.initAllocator(init.minimal.args, allocator);
13+
_ = args.skip();
14+
const db_path = args.next() orelse default_db_path;
1515
var num_lookups = default_num_lookups;
1616
var fields: ?[]const []const u8 = null;
17-
if (args.len > 1) db_path = args[1];
18-
if (args.len > 2) num_lookups = try std.fmt.parseUnsigned(u64, args[2], 10);
19-
if (args.len > 3) {
20-
const f = try maxminddb.Fields(max_mmdb_fields).parse(args[3], ',');
17+
if (args.next()) |arg| num_lookups = try std.fmt.parseUnsigned(u64, arg, 10);
18+
if (args.next()) |arg| {
19+
const f = try maxminddb.Fields(max_mmdb_fields).parse(arg, ',');
2120
fields = f.only();
2221
}
2322

@@ -26,10 +25,11 @@ pub fn main() !void {
2625
std.debug.print(" Lookups: {d}\n", .{num_lookups});
2726
std.debug.print("Opening database...\n", .{});
2827

29-
var open_timer = try std.time.Timer.start();
30-
var db = try maxminddb.Reader.mmap(allocator, db_path, .{ .ipv4_index_first_n_bits = 16 });
28+
const open_start = std.Io.Clock.Timestamp.now(io, .awake);
29+
var db = try maxminddb.Reader.mmap(allocator, io, db_path, .{ .ipv4_index_first_n_bits = 16 });
3130
defer db.close();
32-
const open_time_ms = @as(f64, @floatFromInt(open_timer.read())) /
31+
const open_elapsed_ns: i64 = @intCast(open_start.untilNow(io).raw.nanoseconds);
32+
const open_time_ms = @as(f64, @floatFromInt(open_elapsed_ns)) /
3333
@as(f64, @floatFromInt(std.time.ns_per_ms));
3434
std.debug.print("Database opened successfully in {d} ms. Type: {s}\n", .{
3535
open_time_ms,
@@ -40,14 +40,14 @@ pub fn main() !void {
4040
defer cache.deinit();
4141

4242
std.debug.print("Starting benchmark...\n", .{});
43-
var timer = try std.time.Timer.start();
43+
const timer_start = std.Io.Clock.Timestamp.now(io, .awake);
4444
var not_found_count: u64 = 0;
4545
var lookup_errors: u64 = 0;
4646
var ip_bytes: [4]u8 = undefined;
4747

4848
for (0..num_lookups) |_| {
49-
std.crypto.random.bytes(&ip_bytes);
50-
const ip = std.net.Address.initIp4(ip_bytes, 0);
49+
io.random(&ip_bytes);
50+
const ip: std.Io.net.IpAddress = .{ .ip4 = .{ .bytes = ip_bytes, .port = 0 } };
5151

5252
const entry = db.find(ip, .{}) catch |err| {
5353
std.debug.print("! Lookup error for IP {any}: {any}\n", .{ ip, err });
@@ -65,7 +65,7 @@ pub fn main() !void {
6565
};
6666
}
6767

68-
const elapsed_ns = timer.read();
68+
const elapsed_ns: i64 = @intCast(timer_start.untilNow(io).raw.nanoseconds);
6969
const elapsed_s = @as(f64, @floatFromInt(elapsed_ns)) /
7070
@as(f64, @floatFromInt(std.time.ns_per_s));
7171
const lookups_per_second = if (elapsed_s > 0)

benchmarks/mycity.zig

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -12,26 +12,26 @@ const MyCity = struct {
1212
} = .{},
1313
};
1414

15-
pub fn main() !void {
16-
const allocator = std.heap.smp_allocator;
15+
pub fn main(init: std.process.Init) !void {
16+
const allocator = init.gpa;
17+
const io = init.io;
1718

18-
const args = try std.process.argsAlloc(allocator);
19-
defer std.process.argsFree(allocator, args);
20-
21-
var db_path: []const u8 = default_db_path;
19+
var args = try std.process.Args.Iterator.initAllocator(init.minimal.args, allocator);
20+
_ = args.skip();
21+
const db_path = args.next() orelse default_db_path;
2222
var num_lookups = default_num_lookups;
23-
if (args.len > 1) db_path = args[1];
24-
if (args.len > 2) num_lookups = try std.fmt.parseUnsigned(u64, args[2], 10);
23+
if (args.next()) |arg| num_lookups = try std.fmt.parseUnsigned(u64, arg, 10);
2524

2625
std.debug.print("Benchmarking with:\n", .{});
2726
std.debug.print(" Database: {s}\n", .{db_path});
2827
std.debug.print(" Lookups: {d}\n", .{num_lookups});
2928
std.debug.print("Opening database...\n", .{});
3029

31-
var open_timer = try std.time.Timer.start();
32-
var db = try maxminddb.Reader.mmap(allocator, db_path, .{ .ipv4_index_first_n_bits = 16 });
30+
const open_start = std.Io.Clock.Timestamp.now(io, .awake);
31+
var db = try maxminddb.Reader.mmap(allocator, io, db_path, .{ .ipv4_index_first_n_bits = 16 });
3332
defer db.close();
34-
const open_time_ms = @as(f64, @floatFromInt(open_timer.read())) /
33+
const open_elapsed_ns: i64 = @intCast(open_start.untilNow(io).raw.nanoseconds);
34+
const open_time_ms = @as(f64, @floatFromInt(open_elapsed_ns)) /
3535
@as(f64, @floatFromInt(std.time.ns_per_ms));
3636
std.debug.print("Database opened successfully in {d} ms. Type: {s}\n", .{
3737
open_time_ms,
@@ -43,14 +43,14 @@ pub fn main() !void {
4343
const arena_allocator = arena.allocator();
4444

4545
std.debug.print("Starting benchmark...\n", .{});
46-
var timer = try std.time.Timer.start();
46+
const timer_start = std.Io.Clock.Timestamp.now(io, .awake);
4747
var not_found_count: u64 = 0;
4848
var lookup_errors: u64 = 0;
4949
var ip_bytes: [4]u8 = undefined;
5050

5151
for (0..num_lookups) |_| {
52-
std.crypto.random.bytes(&ip_bytes);
53-
const ip = std.net.Address.initIp4(ip_bytes, 0);
52+
io.random(&ip_bytes);
53+
const ip: std.Io.net.IpAddress = .{ .ip4 = .{ .bytes = ip_bytes, .port = 0 } };
5454

5555
const result = db.lookup(
5656
MyCity,
@@ -70,7 +70,7 @@ pub fn main() !void {
7070
_ = arena.reset(.retain_capacity);
7171
}
7272

73-
const elapsed_ns = timer.read();
73+
const elapsed_ns: i64 = @intCast(timer_start.untilNow(io).raw.nanoseconds);
7474
const elapsed_s = @as(f64, @floatFromInt(elapsed_ns)) /
7575
@as(f64, @floatFromInt(std.time.ns_per_s));
7676
const lookups_per_second = if (elapsed_s > 0)

0 commit comments

Comments
 (0)