-
Notifications
You must be signed in to change notification settings - Fork 5
fix: add compatibility for Zig 0.16 ArrayList and timer changes #23
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -16,28 +16,51 @@ const pack = msgpack.Pack( | |
| bufferType.read, | ||
| ); | ||
|
|
||
| const is_zig_16 = builtin.zig_version.minor >= 16; | ||
|
|
||
| /// Get monotonic time in nanoseconds (cross-platform) | ||
| fn getTimeNs() u64 { | ||
| if (builtin.os.tag == .windows) { | ||
| const w = std.os.windows; | ||
| var counter: w.LARGE_INTEGER = undefined; | ||
| _ = w.ntdll.RtlQueryPerformanceCounter(&counter); | ||
| var freq: w.LARGE_INTEGER = undefined; | ||
| _ = w.ntdll.RtlQueryPerformanceFrequency(&freq); | ||
| const ns = std.time.ns_per_s; | ||
| const c: u64 = @bitCast(counter); | ||
| const f: u64 = @bitCast(freq); | ||
| return @intCast(@divTrunc(@as(u128, c) * ns, @as(u128, f))); | ||
| } else if (builtin.os.tag != .windows) { | ||
| var ts: std.c.timespec = undefined; | ||
| _ = std.c.clock_gettime(std.c.CLOCK.MONOTONIC, &ts); | ||
| return @as(u64, @intCast(ts.tv_sec)) * std.time.ns_per_s + @as(u64, @intCast(ts.tv_nsec)); | ||
| } else @compileError("unsupported OS for benchmark"); | ||
|
Comment on lines
+33
to
+37
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The |
||
| } | ||
|
Comment on lines
+22
to
+38
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The manual implementation of |
||
|
|
||
| /// Benchmark timer helper | ||
| /// Run a benchmark and print results | ||
| fn benchmark( | ||
| comptime name: []const u8, | ||
| comptime iterations: usize, | ||
| comptime func: fn (allocator: std.mem.Allocator) anyerror!void, | ||
| ) !void { | ||
| var gpa = std.heap.GeneralPurposeAllocator(.{}){}; | ||
| defer _ = gpa.deinit(); | ||
| const allocator = gpa.allocator(); | ||
| const allocator = std.heap.page_allocator; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Using |
||
|
|
||
| // Warmup | ||
| for (0..10) |_| { | ||
| try func(allocator); | ||
| } | ||
|
|
||
| // Actual benchmark | ||
| var timer = try std.time.Timer.start(); | ||
| var legacy_timer: if (is_zig_16) void else std.time.Timer = if (is_zig_16) {} else undefined; | ||
| if (!is_zig_16) legacy_timer = try std.time.Timer.start(); | ||
| const start_ns = if (is_zig_16) getTimeNs() else 0; | ||
|
Comment on lines
+55
to
+57
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The conditional logic for handling the timer across Zig versions is quite clunky. Instead of manually implementing |
||
|
|
||
| for (0..iterations) |_| { | ||
| try func(allocator); | ||
| } | ||
| const elapsed_ns = timer.read(); | ||
|
|
||
| const elapsed_ns = if (is_zig_16) getTimeNs() - start_ns else legacy_timer.read(); | ||
|
|
||
| const avg_ns = elapsed_ns / iterations; | ||
| const ops_per_sec = if (avg_ns > 0) (1_000_000_000 / avg_ns) else 0; | ||
|
|
@@ -850,7 +873,7 @@ fn benchMixedTypesRead(allocator: std.mem.Allocator) !void { | |
| // Main Benchmark Runner | ||
| // ============================================================================ | ||
|
|
||
| pub fn main() !void { | ||
| fn runBenchmarks() !void { | ||
| std.debug.print("\n", .{}); | ||
| std.debug.print("=" ** 80 ++ "\n", .{}); | ||
| std.debug.print("MessagePack Benchmark Suite\n", .{}); | ||
|
|
@@ -936,3 +959,16 @@ pub fn main() !void { | |
| std.debug.print("Benchmark Complete\n", .{}); | ||
| std.debug.print("=" ** 80 ++ "\n", .{}); | ||
| } | ||
|
|
||
| const BenchEntry = if (is_zig_16) struct { | ||
| pub fn main(init: std.process.Init) !void { | ||
| _ = &init; | ||
| try runBenchmarks(); | ||
| } | ||
| } else struct { | ||
| pub fn main() !void { | ||
| try runBenchmarks(); | ||
| } | ||
| }; | ||
|
|
||
| pub const main = BenchEntry.main; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -1316,7 +1316,7 @@ pub const Payload = union(enum) { | |
| var new_heap = if (current_zig.minor == 14) | ||
| std.ArrayList(Payload).init(alloc) | ||
| else | ||
| std.ArrayList(Payload){}; | ||
| std.ArrayList(Payload).initCapacity(alloc, 0) catch unreachable; | ||
|
Comment on lines
1316
to
+1319
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Using |
||
|
|
||
| // Copy existing items from stack buffer to heap | ||
| for (buffer[0..len.*]) |item| { | ||
|
|
@@ -2857,7 +2857,7 @@ pub fn PackWithLimits( | |
| var parse_stack = if (current_zig.minor == 14) | ||
| std.ArrayList(ParseState).init(allocator) | ||
| else | ||
| std.ArrayList(ParseState){}; | ||
| try std.ArrayList(ParseState).initCapacity(allocator, 0); | ||
|
Comment on lines
2857
to
+2860
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Similar to the previous comment, |
||
| defer if (current_zig.minor == 14) parse_stack.deinit() else parse_stack.deinit(allocator); | ||
| errdefer cleanupParseStack(&parse_stack, allocator); | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -6,6 +6,8 @@ const allocator = std.testing.allocator; | |
| const expect = std.testing.expect; | ||
| const Payload = msgpack.Payload; | ||
|
|
||
| const has_new_io = builtin.zig_version.minor >= 15; | ||
|
|
||
| fn u8eql(a: []const u8, b: []const u8) bool { | ||
| return std.mem.eql(u8, a, b); | ||
| } | ||
|
|
@@ -76,7 +78,7 @@ test "PackerIO: corrupted length field" { | |
| var input = if (builtin.zig_version.minor == 14) | ||
| std.ArrayList(u8).init(allocator) | ||
| else | ||
| std.ArrayList(u8){}; | ||
| std.ArrayList(u8).initCapacity(allocator, 0) catch unreachable; | ||
|
Comment on lines
78
to
+81
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The use of |
||
| defer if (builtin.zig_version.minor == 14) input.deinit() else input.deinit(allocator); | ||
|
|
||
| // str32 claiming 1MB but only providing a few bytes | ||
|
|
@@ -134,7 +136,7 @@ test "PackerIO: truncated array cleanup" { | |
| var input = if (builtin.zig_version.minor == 14) | ||
| std.ArrayList(u8).init(allocator) | ||
| else | ||
| std.ArrayList(u8){}; | ||
| std.ArrayList(u8).initCapacity(allocator, 0) catch unreachable; | ||
| defer if (builtin.zig_version.minor == 14) input.deinit() else input.deinit(allocator); | ||
|
|
||
| // Create array header for 3 elements (0x93) | ||
|
|
@@ -1424,7 +1426,7 @@ test "large maps" { | |
| var keys = if (builtin.zig_version.minor == 14) | ||
| std.ArrayList([]u8).init(allocator) | ||
| else | ||
| std.ArrayList([]u8){}; | ||
| std.ArrayList([]u8).initCapacity(allocator, 0) catch unreachable; | ||
| defer { | ||
| for (keys.items) |key| { | ||
| allocator.free(key); | ||
|
|
@@ -1852,7 +1854,7 @@ test "actual map32 format" { | |
| var keys = if (builtin.zig_version.minor == 14) | ||
| std.ArrayList([]u8).init(allocator) | ||
| else | ||
| std.ArrayList([]u8){}; | ||
| std.ArrayList([]u8).initCapacity(allocator, 0) catch unreachable; | ||
| defer { | ||
| for (keys.items) |key| { | ||
| allocator.free(key); | ||
|
|
@@ -2058,7 +2060,7 @@ test "format markers verification" { | |
| var test_keys = if (builtin.zig_version.minor == 14) | ||
| std.ArrayList([]u8).init(allocator) | ||
| else | ||
| std.ArrayList([]u8){}; | ||
| std.ArrayList([]u8).initCapacity(allocator, 0) catch unreachable; | ||
| defer { | ||
| for (test_keys.items) |key| { | ||
| allocator.free(key); | ||
|
|
@@ -3413,7 +3415,7 @@ test "fuzz: mixed payload sequence" { | |
| var payloads = if (builtin.zig_version.minor == 14) | ||
| std.ArrayList(Payload).init(allocator) | ||
| else | ||
| std.ArrayList(Payload){}; | ||
| std.ArrayList(Payload).initCapacity(allocator, 0) catch unreachable; | ||
| defer { | ||
| for (payloads.items) |payload| { | ||
| payload.free(allocator); | ||
|
|
@@ -3453,7 +3455,7 @@ test "iterative parser: normal nested depth (100 layers)" { | |
| var input = if (builtin.zig_version.minor == 14) | ||
| std.ArrayList(u8).init(allocator) | ||
| else | ||
| std.ArrayList(u8){}; | ||
| std.ArrayList(u8).initCapacity(allocator, 0) catch unreachable; | ||
| defer if (builtin.zig_version.minor == 14) input.deinit() else input.deinit(allocator); | ||
|
|
||
| const depth = 100; | ||
|
|
@@ -3502,7 +3504,7 @@ test "iterative parser: max depth exceeded" { | |
| var input = if (builtin.zig_version.minor == 14) | ||
| std.ArrayList(u8).init(allocator) | ||
| else | ||
| std.ArrayList(u8){}; | ||
| std.ArrayList(u8).initCapacity(allocator, 0) catch unreachable; | ||
| defer if (builtin.zig_version.minor == 14) input.deinit() else input.deinit(allocator); | ||
|
|
||
| const depth = 100; | ||
|
|
@@ -3548,7 +3550,7 @@ test "iterative parser: array too large" { | |
| var input = if (builtin.zig_version.minor == 14) | ||
| std.ArrayList(u8).init(allocator) | ||
| else | ||
| std.ArrayList(u8){}; | ||
| std.ArrayList(u8).initCapacity(allocator, 0) catch unreachable; | ||
| defer if (builtin.zig_version.minor == 14) input.deinit() else input.deinit(allocator); | ||
|
|
||
| // array16 marker + length | ||
|
|
@@ -3580,7 +3582,7 @@ test "iterative parser: deep nested maps" { | |
| var input = if (builtin.zig_version.minor == 14) | ||
| std.ArrayList(u8).init(allocator) | ||
| else | ||
| std.ArrayList(u8){}; | ||
| std.ArrayList(u8).initCapacity(allocator, 0) catch unreachable; | ||
| defer if (builtin.zig_version.minor == 14) input.deinit() else input.deinit(allocator); | ||
|
|
||
| const depth = 50; | ||
|
|
@@ -3775,7 +3777,7 @@ test "fuzz: deep mixed nesting" { | |
| var input = if (builtin.zig_version.minor == 14) | ||
| std.ArrayList(u8).init(allocator) | ||
| else | ||
| std.ArrayList(u8){}; | ||
| std.ArrayList(u8).initCapacity(allocator, 0) catch unreachable; | ||
| defer if (builtin.zig_version.minor == 14) input.deinit() else input.deinit(allocator); | ||
|
|
||
| for (0..depth) |_| { | ||
|
|
@@ -3823,7 +3825,7 @@ test "malicious: array32 claims 4 billion elements" { | |
| var input = if (builtin.zig_version.minor == 14) | ||
| std.ArrayList(u8).init(allocator) | ||
| else | ||
| std.ArrayList(u8){}; | ||
| std.ArrayList(u8).initCapacity(allocator, 0) catch unreachable; | ||
| defer if (builtin.zig_version.minor == 14) input.deinit() else input.deinit(allocator); | ||
|
|
||
| // array32 claiming 0xFFFFFFFF (4 billion) elements | ||
|
|
@@ -3858,7 +3860,7 @@ test "malicious: map32 claims 4 billion pairs" { | |
| var input = if (builtin.zig_version.minor == 14) | ||
| std.ArrayList(u8).init(allocator) | ||
| else | ||
| std.ArrayList(u8){}; | ||
| std.ArrayList(u8).initCapacity(allocator, 0) catch unreachable; | ||
| defer if (builtin.zig_version.minor == 14) input.deinit() else input.deinit(allocator); | ||
|
|
||
| // map32 claiming 0xFFFFFFFF pairs | ||
|
|
@@ -3903,7 +3905,7 @@ test "malicious: extremely deep nesting (2000 layers)" { | |
| var input = if (builtin.zig_version.minor == 14) | ||
| std.ArrayList(u8).init(allocator) | ||
| else | ||
| std.ArrayList(u8){}; | ||
| std.ArrayList(u8).initCapacity(allocator, 0) catch unreachable; | ||
| defer if (builtin.zig_version.minor == 14) input.deinit() else input.deinit(allocator); | ||
|
|
||
| // 2000 layers of nesting (far exceeds limit of 100) | ||
|
|
@@ -3939,7 +3941,7 @@ test "corrupted: truncated array data" { | |
| var input = if (builtin.zig_version.minor == 14) | ||
| std.ArrayList(u8).init(allocator) | ||
| else | ||
| std.ArrayList(u8){}; | ||
| std.ArrayList(u8).initCapacity(allocator, 0) catch unreachable; | ||
| defer if (builtin.zig_version.minor == 14) input.deinit() else input.deinit(allocator); | ||
|
|
||
| // array claiming 10 elements but data is incomplete | ||
|
|
@@ -3971,7 +3973,7 @@ test "map with non-string key (integer key)" { | |
| var input = if (builtin.zig_version.minor == 14) | ||
| std.ArrayList(u8).init(allocator) | ||
| else | ||
| std.ArrayList(u8){}; | ||
| std.ArrayList(u8).initCapacity(allocator, 0) catch unreachable; | ||
| defer if (builtin.zig_version.minor == 14) input.deinit() else input.deinit(allocator); | ||
|
|
||
| // map with integer key (now valid - keys can be any Payload type) | ||
|
|
@@ -4023,7 +4025,7 @@ test "malicious: mixed depth and breadth attack" { | |
| var input = if (builtin.zig_version.minor == 14) | ||
| std.ArrayList(u8).init(allocator) | ||
| else | ||
| std.ArrayList(u8){}; | ||
| std.ArrayList(u8).initCapacity(allocator, 0) catch unreachable; | ||
| defer if (builtin.zig_version.minor == 14) input.deinit() else input.deinit(allocator); | ||
|
|
||
| // Build: [ [100 items], [100 items], ... ] nested 60 levels deep | ||
|
|
@@ -4059,7 +4061,7 @@ test "edge case: empty containers at various depths" { | |
| var input = if (builtin.zig_version.minor == 14) | ||
| std.ArrayList(u8).init(allocator) | ||
| else | ||
| std.ArrayList(u8){}; | ||
| std.ArrayList(u8).initCapacity(allocator, 0) catch unreachable; | ||
| defer if (builtin.zig_version.minor == 14) input.deinit() else input.deinit(allocator); | ||
|
|
||
| const depths = [_]usize{ 0, 1, 5, 10, 20 }; | ||
|
|
@@ -4133,7 +4135,7 @@ test "corrupted: nested arrays with mismatched counts" { | |
| var input = if (builtin.zig_version.minor == 14) | ||
| std.ArrayList(u8).init(allocator) | ||
| else | ||
| std.ArrayList(u8){}; | ||
| std.ArrayList(u8).initCapacity(allocator, 0) catch unreachable; | ||
| defer if (builtin.zig_version.minor == 14) input.deinit() else input.deinit(allocator); | ||
|
|
||
| // Outer array claims 3 elements, but we provide different structure | ||
|
|
@@ -5004,8 +5006,6 @@ test "memory alignment: large integer array serialization" { | |
| // std.io.Reader and std.io.Writer Tests (Zig 0.15+) | ||
| // ============================================================================ | ||
|
|
||
| const has_new_io = builtin.zig_version.minor >= 15; | ||
|
|
||
| test "PackerIO: basic write and read with fixed Reader/Writer" { | ||
| if (!has_new_io) return error.SkipZigTest; | ||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The claim that
std.heap.GeneralPurposeAllocatoris removed in Zig 0.16 is incorrect. It remains a core part of the standard library, although its internal implementation details or its location withinstd.heapmay have evolved. Switching topage_allocatorin benchmarks is a regression in measurement quality as it is significantly slower due to per-allocation system calls.