diff --git a/.tangled/workflows/ci.yml b/.tangled/workflows/ci.yml index 321fb05..dcf8e73 100644 --- a/.tangled/workflows/ci.yml +++ b/.tangled/workflows/ci.yml @@ -23,3 +23,7 @@ steps: - name: run tests command: | /tangled/workspace/.zig/zig build test --summary all + + - name: compile tests for 32-bit arm (no 64-bit atomics) + command: | + /tangled/workspace/.zig/zig build test -Dtarget=arm-linux-musleabihf -Dcpu=arm1176jzf_s --summary all diff --git a/CHANGELOG.md b/CHANGELOG.md index 95f34f0..88eea0b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,19 @@ # changelog +## 0.4.4 + +- **fix**: zat compiles for 32-bit targets without native 64-bit atomics (ARMv6, e.g. a + Raspberry Pi 1 running zds). the three `std.atomic.Value(u64)` counters — the jetstream and + firehose `frames_this_connection` idle detectors and the xrpc `StreamProgress.bytes` stall + detector — only ever ask "did this change since last tick", so they are `usize` now. the + `cbor.Value`/`MapEntry` size asserts pin 64-bit layouts only, and `Cid.digest()` rejects a + varint digest length that does not fit `usize` instead of failing to coerce. +- **ci**: compile the test module for `arm-linux-musleabihf` (`arm1176jzf_s`) on every push so + 32-bit support cannot regress silently; `skip_foreign_checks` keeps the run step from trying + to execute the foreign binary. +- **docs**: tidy stray root docs, refresh the downstream list. +- **test**: cover pruning an empty node stranded as a left child. + ## 0.4.3 - **fix**: `Mst.collectBlocks` now emits the block for an empty node instead of diff --git a/build.zig b/build.zig index 2ea8d84..b063c25 100644 --- a/build.zig +++ b/build.zig @@ -67,6 +67,7 @@ pub fn build(b: *std.Build) void { } const run_tests = b.addRunArtifact(tests); + run_tests.skip_foreign_checks = true; const test_step = b.step("test", "run unit tests"); test_step.dependOn(&run_tests.step); diff --git a/build.zig.zon b/build.zig.zon index d547021..218e577 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -1,6 +1,6 @@ .{ .name = .zat, - .version = "0.4.3", + .version = "0.4.4", .fingerprint = 0x8da9db57ee82fbe4, .minimum_zig_version = "0.16.0-dev.3070+b22eb176b", .dependencies = .{ diff --git a/src/internal/repo/cbor.zig b/src/internal/repo/cbor.zig index 29d9ff3..a6ab371 100644 --- a/src/internal/repo/cbor.zig +++ b/src/internal/repo/cbor.zig @@ -130,10 +130,13 @@ pub const Value = union(enum) { }; } - // verify the Value union stayed slim after Cid optimization (was ~64, now 24) + // verify the Value union stayed slim after Cid optimization (was ~64, now 24). + // sizes are for 64-bit pointers; 32-bit targets are smaller and not pinned. comptime { - std.debug.assert(@sizeOf(Value) == 24); - std.debug.assert(@sizeOf(MapEntry) == 40); + if (@sizeOf(usize) == 8) { + std.debug.assert(@sizeOf(Value) == 24); + std.debug.assert(@sizeOf(MapEntry) == 40); + } } }; @@ -196,7 +199,7 @@ pub const Cid = struct { _ = readUvarint(self.raw, &pos) orelse return null; // version _ = readUvarint(self.raw, &pos) orelse return null; // codec _ = readUvarint(self.raw, &pos) orelse return null; // hash_fn - const digest_len = readUvarint(self.raw, &pos) orelse return null; + const digest_len = std.math.cast(usize, readUvarint(self.raw, &pos) orelse return null) orelse return null; if (pos + digest_len > self.raw.len) return null; return self.raw[pos..][0..digest_len]; } diff --git a/src/internal/streaming/firehose.zig b/src/internal/streaming/firehose.zig index 534a1c3..6353891 100644 --- a/src/internal/streaming/firehose.zig +++ b/src/internal/streaming/firehose.zig @@ -730,7 +730,7 @@ pub const FirehoseClient = struct { connected_this_attempt: bool = false, /// frames delivered on the current connection; written by the reader /// task and read by the idle watchdog, hence atomic. - frames_this_connection: std.atomic.Value(u64) = .init(0), + frames_this_connection: std.atomic.Value(usize) = .init(0), pub fn init(io: Io, allocator: Allocator, options: Options) FirehoseClient { return .{ @@ -903,7 +903,7 @@ pub const FirehoseClient = struct { var future = try self.io.concurrent(Reader.run, .{ self.io, &client, &ws_handler, &done, &read_err }); var joined = false; defer if (!joined) future.cancel(self.io); - var last_frames: u64 = 0; + var last_frames: usize = 0; while (true) { done.waitTimeout(self.io, .{ .duration = .{ .raw = Io.Duration.fromMilliseconds(@intCast(timeout_ms)), .clock = .awake } }) catch |err| switch (err) { error.Timeout => { diff --git a/src/internal/streaming/jetstream.zig b/src/internal/streaming/jetstream.zig index f4b2399..e2496ff 100644 --- a/src/internal/streaming/jetstream.zig +++ b/src/internal/streaming/jetstream.zig @@ -152,7 +152,7 @@ pub const JetstreamClient = struct { /// raw frames on the current connection; written by the reader task and /// read by the idle watchdog, hence atomic (events_this_connection stays /// plain: it is only read after the reader task is joined). - frames_this_connection: std.atomic.Value(u64) = .init(0), + frames_this_connection: std.atomic.Value(usize) = .init(0), pub fn init(io: Io, allocator: Allocator, options: Options) JetstreamClient { return .{ @@ -283,7 +283,7 @@ pub const JetstreamClient = struct { var future = try self.io.concurrent(Reader.run, .{ self.io, &client, &ws_handler, &done, &read_err }); var joined = false; defer if (!joined) future.cancel(self.io); - var last_frames: u64 = 0; + var last_frames: usize = 0; while (true) { done.waitTimeout(self.io, .{ .duration = .{ .raw = Io.Duration.fromMilliseconds(@intCast(timeout_ms)), .clock = .awake } }) catch |err| switch (err) { error.Timeout => { diff --git a/src/internal/xrpc/transport.zig b/src/internal/xrpc/transport.zig index d40a820..cbd9e01 100644 --- a/src/internal/xrpc/transport.zig +++ b/src/internal/xrpc/transport.zig @@ -416,7 +416,7 @@ fn parseHeaderInt(value: []const u8) ?u64 { } const StreamProgress = struct { - bytes: std.atomic.Value(u64) = .init(0), + bytes: std.atomic.Value(usize) = .init(0), done: std.atomic.Value(bool) = .init(false), }; @@ -467,7 +467,7 @@ fn streamResponseBody( const tick_ns: u64 = 100 * std.time.ns_per_ms; var idle_ns: u64 = 0; var elapsed_ns: u64 = 0; - var last_bytes: u64 = 0; + var last_bytes: usize = 0; while (!progress.done.load(.acquire)) { std.Io.Clock.Duration.sleep(