From a68ec280289e30017af18412e5926d207bd4baab Mon Sep 17 00:00:00 2001 From: Arran Ireland Date: Thu, 2 Oct 2025 10:40:00 +0100 Subject: [PATCH 1/5] build: upgrade to 0.15.1 --- build.zig | 46 ++++++++++++++++++++++++++++++++++------------ build.zig.zon | 8 ++++---- 2 files changed, 38 insertions(+), 16 deletions(-) diff --git a/build.zig b/build.zig index f7271e8..24fca8c 100644 --- a/build.zig +++ b/build.zig @@ -59,7 +59,7 @@ const Builder = struct { const write_files = self.b.addWriteFiles(); const bindings = @import("core/bindings.zig"); - const data = bindings.data(.c, self.b.allocator) catch |err| { + const data = bindings.data(self.b.allocator, .c) catch |err| { std.debug.print("Failed to generate header: {any}", .{err}); std.process.exit(1); }; @@ -75,13 +75,21 @@ const Builder = struct { pub fn examples(self: *Self) void { const step = self.b.step("example", "Run an example"); - const name = self.b.option([]const u8, "name", "The module name to run") orelse return; + const example_name = self.b.fmt("example-{s}", .{name}); + + const example_module = self.b.addModule( + example_name, + .{ + .root_source_file = self.b.path(self.b.fmt("examples/{s}.zig", .{name})), + .target = self.target, + .optimize = self.optimize, + }, + ); + const example = self.b.addExecutable(.{ - .name = self.b.fmt("example-{s}", .{name}), - .root_source_file = self.b.path(self.b.fmt("examples/{s}.zig", .{name})), - .target = self.target, - .optimize = self.optimize, + .name = example_name, + .root_module = example_module, }); self.addImport(example, .core); @@ -154,11 +162,18 @@ const Builder = struct { inline for (.{ .app, .core, .io }) |t| { const root = if (t == .app) "main" else "lib"; + const test_module = self.b.addModule( + @tagName(t), + .{ + .root_source_file = self.b.path(@tagName(t) ++ "/" ++ root ++ ".zig"), + .target = self.target, + .optimize = self.optimize, + }, + ); + const compile = self.b.addTest(.{ .name = @tagName(t), - .root_source_file = self.b.path(@tagName(t) ++ "/" ++ root ++ ".zig"), - .target = self.target, - .optimize = self.optimize, + .root_module = test_module, }); step.dependOn(&self.b.addRunArtifact(compile).step); @@ -195,11 +210,18 @@ const Builder = struct { }; inline for (test_names) |name| { + const test_module = self.b.addModule( + name, + .{ + .root_source_file = self.b.path(root_directory ++ "/" ++ name ++ ".zig"), + .target = self.target, + .optimize = self.optimize, + }, + ); + const t = self.b.addTest(.{ .name = name, - .root_source_file = self.b.path(root_directory ++ "/" ++ name ++ ".zig"), - .target = self.target, - .optimize = self.optimize, + .root_module = test_module, }); inline for (packages) |package| { diff --git a/build.zig.zon b/build.zig.zon index 05f7994..060ec42 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -1,7 +1,7 @@ .{ .name = .reticulum, - .version = "0.2.0", - .minimum_zig_version = "0.14.1", + .version = "0.3.0", + .minimum_zig_version = "0.15.1", .fingerprint = 0x357e04a91bafe743, .paths = .{ "build.zig", @@ -14,8 +14,8 @@ }, .dependencies = .{ .ohsnap = .{ - .url = "git+https://github.com/mnemnion/ohsnap#a140626e388ad3aea2a6a678183f5c0c03887fde", - .hash = "ohsnap-0.3.1-iWxzyu6bAADX1OdmK7FFg94X6RDfDvbt0iwQFH8mSFJE", + .url = "https://github.com/mnemnion/ohsnap/archive/refs/heads/trunk.tar.gz", + .hash = "ohsnap-0.4.1-iWxzyhicAAA90MEnwxA22VshYmsSH3t0dcqM9yib7ose", }, }, } From ab95c873e2097e0e84eb17ce6555af5bd0956fe8 Mon Sep 17 00:00:00 2001 From: Arran Ireland Date: Thu, 2 Oct 2025 10:40:15 +0100 Subject: [PATCH 2/5] core: upgrade to 0.15.1 --- core/Node.zig | 2 +- core/Ratchets.zig | 38 +++---- core/bindings.zig | 163 ++++++++++++++++--------------- core/crypto/Identity.zig | 14 ++- core/data.zig | 4 +- core/endpoint/Name.zig | 42 ++++---- core/internal/ThreadSafeFifo.zig | 22 ++--- core/node/Event.zig | 62 ++++++------ core/packet.zig | 20 ++-- core/packet/Builder.zig | 4 +- core/packet/Factory.zig | 39 ++++---- core/packet/Managed.zig | 28 +++--- 12 files changed, 222 insertions(+), 216 deletions(-) diff --git a/core/Node.zig b/core/Node.zig index 0a9dfb3..26eb45c 100644 --- a/core/Node.zig +++ b/core/Node.zig @@ -109,7 +109,7 @@ pub fn process(self: *Self) !void { fn eventsIn(self: *Self, interface: *Interface, now: u64) !void { while (interface.incoming.pop()) |event_in| { var event = event_in; - defer event.deinit(); + defer event.deinit(self.ally); try switch (event) { .announce => |*announce| self.announceTask(interface, announce, now), diff --git a/core/Ratchets.zig b/core/Ratchets.zig index 473f34d..df53be1 100644 --- a/core/Ratchets.zig +++ b/core/Ratchets.zig @@ -10,7 +10,7 @@ const System = @import("System.zig"); const Self = @This(); const Entry = struct { - ratchets: std.fifo.LinearFifo(Ratchet, .Dynamic), + ratchets: []Ratchet, last_rotation_time: u64, }; @@ -37,7 +37,7 @@ pub fn add(self: *Self, endpoint: Hash.Short, now: u64) !Ratchet { const key = try self.ally.dupe(u8, &endpoint); try self.entries.put(key, .{ - .ratchets = self.ally, + .ratchets = self.ally.alloc(Ratchet, max_ratchets), .last_rotation_time = now, }); @@ -46,25 +46,29 @@ pub fn add(self: *Self, endpoint: Hash.Short, now: u64) !Ratchet { } pub fn getRatchet(self: *Self, endpoint: Hash.Short, now: u64) !?Ratchet { - if (self.entries.getPtr(&endpoint)) |entry| { - const needs_rotating = now - entry.last_rotation_time >= rotation_period; + _ = self; + _ = endpoint; + _ = now; - if (needs_rotating) { - var seed: [crypto.X25519.seed_length]u8 = undefined; - self.rng.bytes(&seed); + // if (self.entries.getPtr(&endpoint)) |entry| { + // const needs_rotating = now - entry.last_rotation_time >= rotation_period; - const ratchet = try crypto.X25519.KeyPair.generateDeterministic(seed); + // if (needs_rotating) { + // var seed: [crypto.X25519.seed_length]u8 = undefined; + // self.rng.bytes(&seed); - if (entry.ratchets.count >= max_ratchets) { - entry.ratchets.discard(1); - } + // const ratchet = try crypto.X25519.KeyPair.generateDeterministic(seed); - entry.ratchets.writeItem(ratchet.public_key); - entry.last_rotation_time = now; - } + // if (entry.ratchets.count >= max_ratchets) { + // entry.ratchets.discard(1); + // } - return entry.ratchets.peekItem(entry.ratchets.count - 1); - } + // entry.ratchets.writeItem(ratchet.public_key); + // entry.last_rotation_time = now; + // } + + // return entry.ratchets.peekItem(entry.ratchets.count - 1); + // } return null; } @@ -74,7 +78,7 @@ pub fn deinit(self: *Self) void { while (entries.next()) |entry| { self.ally.free(entry.key_ptr.*); - entry.value_ptr.ratchets.deinit(); + // entry.value_ptr.ratchets.deinit(); } self.entries.deinit(); diff --git a/core/bindings.zig b/core/bindings.zig index 6faa5cd..1e2278a 100644 --- a/core/bindings.zig +++ b/core/bindings.zig @@ -12,8 +12,8 @@ const Language = enum { rust, }; -pub fn data(language: Language, ally: Allocator) !Bytes { - var exports = parseExports(@embedFile("exports.zig"), ally); +pub fn data(ally: Allocator, language: Language) !Bytes { + var exports = parseExports(ally, @embedFile("exports.zig")); return switch (language) { .c => try exports.c(), @@ -22,7 +22,7 @@ pub fn data(language: Language, ally: Allocator) !Bytes { }; } -fn parseExports(export_data: [:0]const u8, ally: Allocator) Exports { +fn parseExports(ally: Allocator, export_data: [:0]const u8) Exports { const Ast = std.zig.Ast; const DocComment = struct { @@ -51,71 +51,76 @@ fn parseExports(export_data: [:0]const u8, ally: Allocator) Exports { var exports = Exports.init(ally); for (root_declarations) |declaration_index| { - const declaration_tag = nodes.items(.tag)[declaration_index]; - const main_token = nodes.items(.main_token)[declaration_index]; + const declaration_index_value = @intFromEnum(declaration_index); + const declaration_tag = nodes.items(.tag)[declaration_index_value]; + const main_token = nodes.items(.main_token)[declaration_index_value]; - if (main_token <= 0 or tokens.items(.tag)[main_token - 1] != .keyword_pub) { - continue; - } + if (main_token <= 0 or tokens.items(.tag)[main_token - 1] != .keyword_pub) continue; if (declaration_tag == .fn_decl) { var buffer: [1]std.zig.Ast.Node.Index = undefined; const function_prototype = ast.fullFnProto( &buffer, - nodes.items(.data)[declaration_index].lhs, + nodes.items(.data)[declaration_index_value].node_and_node[0], ) orelse continue; - const function = exports.functions.addOne() catch continue; + const function = exports.functions.addOne(ally) catch continue; function.* = .{ .doc_comment = DocComment.search(ast, main_token), .name = ast.tokenSlice(function_prototype.name_token orelse continue), - .parameters = std.ArrayList(Function.Parameter).init(ally), - .return_value = ast.getNodeSource(function_prototype.ast.return_type), + .parameters = std.ArrayList(Function.Parameter).empty, + .return_value = ast.getNodeSource(function_prototype.ast.return_type.unwrap().?), }; var parameters = function_prototype.iterate(&ast); while (parameters.next()) |parameter| { const name = ast.tokenSlice(parameter.name_token orelse continue); - const @"type" = if (parameter.type_expr != 0) ast.getNodeSource(parameter.type_expr) else "unknown"; + const @"type" = if (parameter.type_expr) |index| ast.getNodeSource(index) else "unknown"; - function.parameters.append(.{ .name = name, .type = @"type" }) catch continue; + function.parameters.append(ally, .{ .name = name, .type = @"type" }) catch continue; } } else if (declaration_tag == .simple_var_decl) { const variable_declaration = ast.simpleVarDecl(declaration_index); - if (variable_declaration.ast.init_node == 0) continue; + if (variable_declaration.ast.init_node.unwrap() == null) continue; - switch (nodes.items(.tag)[variable_declaration.ast.init_node]) { + switch (nodes.items(.tag)[@intFromEnum(variable_declaration.ast.init_node)]) { .container_decl_arg, .container_decl, .container_decl_arg_trailing, .container_decl_trailing => {}, else => continue, } var buffer: [2]std.zig.Ast.Node.Index = undefined; - const container_declaration = ast.fullContainerDecl(&buffer, variable_declaration.ast.init_node) orelse continue; + const container_declaration = ast.fullContainerDecl(&buffer, variable_declaration.ast.init_node.unwrap().?) orelse continue; if (tokens.items(.tag)[container_declaration.ast.main_token] != .keyword_enum) continue; - const @"enum" = exports.enums.addOne() catch continue; + const @"enum" = exports.enums.addOne(ally) catch continue; @"enum".* = .{ .doc_comment = DocComment.search(ast, main_token), .name = ast.tokenSlice(main_token + 1), - .values = std.ArrayList(Enum.Value).init(ally), + .values = std.ArrayList(Enum.Value).empty, }; for (container_declaration.ast.members) |member_index| { - if (nodes.items(.tag)[member_index] != .container_field_init) { - continue; - } + const member_index_value = @intFromEnum(member_index); + + if (nodes.items(.tag)[member_index_value] != .container_field_init) continue; - const name = ast.tokenSlice(nodes.items(.main_token)[member_index]); - const member_data = nodes.items(.data)[member_index]; - const number = ast.getNodeSource(member_data.rhs); + const name = ast.tokenSlice(nodes.items(.main_token)[member_index_value]); + const member_data = nodes.items(.data)[member_index_value]; - @"enum".values.append(.{ .name = name, .number = number }) catch continue; + if (member_data.node_and_opt_node[1].unwrap()) |node| { + const number = ast.getNodeSource(node); + + @"enum".values.append( + ally, + .{ .name = name, .number = number }, + ) catch continue; + } } } } @@ -133,16 +138,16 @@ const Exports = struct { fn init(ally: Allocator) Self { return .{ .ally = ally, - .enums = std.ArrayList(Enum).init(ally), - .functions = std.ArrayList(Function).init(ally), + .enums = std.ArrayList(Enum).empty, + .functions = std.ArrayList(Function).empty, }; } fn c(self: *Self) !Bytes { - var bytes = Bytes.init(self.ally); + var bytes = Bytes.empty; const b = &bytes; - try b.appendSlice( + try b.appendSlice(self.ally, \\#ifndef RT_CORE_H \\#define RT_CORE_H \\ @@ -153,47 +158,47 @@ const Exports = struct { ); for (self.enums.items) |e| { - try b.appendSlice("// "); - try b.appendSlice(e.doc_comment); - try b.append('\n'); + try b.appendSlice(self.ally, "// "); + try b.appendSlice(self.ally, e.doc_comment); + try b.append(self.ally, '\n'); - try b.appendSlice("typedef enum {\n"); + try b.appendSlice(self.ally, "typedef enum {\n"); for (e.values.items) |v| { - try b.appendSlice(" "); - try b.appendSlice(v.name); - try b.appendSlice(" = "); - try b.appendSlice(v.number); - try b.appendSlice(",\n"); + try b.appendSlice(self.ally, " "); + try b.appendSlice(self.ally, v.name); + try b.appendSlice(self.ally, " = "); + try b.appendSlice(self.ally, v.number); + try b.appendSlice(self.ally, ",\n"); } - try b.appendSlice("} "); - try deriveType(b, e.name); - try b.appendSlice(";\n\n"); + try b.appendSlice(self.ally, "} "); + try self.deriveType(b, e.name); + try b.appendSlice(self.ally, ";\n\n"); } for (self.functions.items) |f| { - try b.appendSlice("// "); - try b.appendSlice(f.doc_comment); - try b.append('\n'); + try b.appendSlice(self.ally, "// "); + try b.appendSlice(self.ally, f.doc_comment); + try b.append(self.ally, '\n'); - try deriveType(b, f.return_value); - try b.append(' '); - try deriveName(b, f.name); - try b.append('('); + try self.deriveType(b, f.return_value); + try b.append(self.ally, ' '); + try self.deriveName(b, f.name); + try b.append(self.ally, '('); for (f.parameters.items, 0..) |p, i| { - try parameter(b, p.name, p.type); + try self.parameter(b, p.name, p.type); if (i != f.parameters.items.len - 1) { - try b.appendSlice(", "); + try b.appendSlice(self.ally, ", "); } } - try b.appendSlice(");\n\n"); + try b.appendSlice(self.ally, ");\n\n"); } - try b.appendSlice( + try b.appendSlice(self.ally, \\#endif \\ ); @@ -202,32 +207,34 @@ const Exports = struct { } fn rust(self: *Self) !Bytes { - return Bytes.init(self.ally); + _ = self; + return Bytes.empty; } fn js(self: *Self) !Bytes { - return Bytes.init(self.ally); + _ = self; + return Bytes.empty; } - fn parameter(b: *Bytes, name: []const u8, @"type": []const u8) !void { + fn parameter(self: *Self, b: *Bytes, name: []const u8, @"type": []const u8) !void { if (eql(u8, @"type", "lib.System.SimpleClock.Callback")) { - try b.appendSlice("int64_t (*"); - try b.appendSlice(name); - try b.appendSlice(")(void)"); + try b.appendSlice(self.ally, "int64_t (*"); + try b.appendSlice(self.ally, name); + try b.appendSlice(self.ally, ")(void)"); } else if (eql(u8, @"type", "lib.System.SimpleRng.Callback")) { - try b.appendSlice("void (*"); - try b.appendSlice(name); - try b.appendSlice(")(uint8_t* buf, "); - try deriveType(b, "usize"); - try b.appendSlice(" length)"); + try b.appendSlice(self.ally, "void (*"); + try b.appendSlice(self.ally, name); + try b.appendSlice(self.ally, ")(uint8_t* buf, "); + try self.deriveType(b, "usize"); + try b.appendSlice(self.ally, " length)"); } else if (eql(u8, @"type", "Error")) { - try reticulumType(b, @"type"); + try self.reticulumType(b, @"type"); } else { - try deriveType(b, @"type"); + try self.deriveType(b, @"type"); } } - fn deriveType(b: *Bytes, string: []const u8) !void { + fn deriveType(self: *Self, b: *Bytes, string: []const u8) !void { const translations = std.StaticStringMap([]const u8).initComptime(.{ .{ "c_int", "int" }, .{ "anyopaque", "void" }, @@ -249,30 +256,30 @@ const Exports = struct { }); if (translations.get(string)) |translation| { - try b.appendSlice(translation); + try b.appendSlice(self.ally, translation); } else { - try reticulumType(b, string); + try self.reticulumType(b, string); } } - fn reticulumType(b: *Bytes, string: []const u8) !void { - try deriveName(b, string); - try b.appendSlice("_t"); + fn reticulumType(self: *Self, b: *Bytes, string: []const u8) !void { + try self.deriveName(b, string); + try b.appendSlice(self.ally, "_t"); } - fn deriveName(b: *Bytes, string: []const u8) !void { - try b.appendSlice("rt_core"); + fn deriveName(self: *Self, b: *Bytes, string: []const u8) !void { + try b.appendSlice(self.ally, "rt_core"); if (string.len > 0 and !std.ascii.isUpper(string[0])) { - try b.append('_'); + try b.append(self.ally, '_'); } for (string) |char| { if (std.ascii.isUpper(char)) { - try b.append('_'); - try b.append(std.ascii.toLower(char)); + try b.append(self.ally, '_'); + try b.append(self.ally, std.ascii.toLower(char)); } else { - try b.append(char); + try b.append(self.ally, char); } } } diff --git a/core/crypto/Identity.zig b/core/crypto/Identity.zig index 88ffbf8..c7a7c3a 100644 --- a/core/crypto/Identity.zig +++ b/core/crypto/Identity.zig @@ -97,29 +97,27 @@ fn makeHash(public: Public) Hash { const t = std.testing; test "valid-signature" { - const allocator = t.allocator; var rng = std.crypto.random; const identity = try Self.random(&rng); - var message = try data.Bytes.initCapacity(allocator, 0); - defer message.deinit(); + var message = try data.Bytes.initCapacity(t.allocator, 0); + defer message.deinit(t.allocator); - try message.appendSlice("this is a message"); + try message.appendSlice(t.allocator, "this is a message"); const signature = try identity.sign(message); try signature.verify(message.items, identity.public.signature); } test "invalid-signature" { - const allocator = t.allocator; var rng = std.crypto.random; const identity1 = try Self.random(&rng); const identity2 = try Self.random(&rng); - var message = try data.Bytes.initCapacity(allocator, 0); - defer message.deinit(); + var message = try data.Bytes.initCapacity(t.allocator, 0); + defer message.deinit(t.allocator); - try message.appendSlice("this is a message"); + try message.appendSlice(t.allocator, "this is a message"); const signature = try identity1.sign(message); try t.expectError( diff --git a/core/data.zig b/core/data.zig index d946ed3..28227f4 100644 --- a/core/data.zig +++ b/core/data.zig @@ -4,7 +4,7 @@ const Allocator = std.mem.Allocator; pub const Bytes = std.ArrayList(u8); pub fn makeBytes(slice: []const u8, ally: Allocator) !Bytes { - var bytes = Bytes.init(ally); - try bytes.appendSlice(slice); + var bytes = Bytes.empty; + try bytes.appendSlice(ally, slice); return bytes; } diff --git a/core/endpoint/Name.zig b/core/endpoint/Name.zig index 35075cc..6dcab6d 100644 --- a/core/endpoint/Name.zig +++ b/core/endpoint/Name.zig @@ -25,14 +25,14 @@ hash: Hash, pub fn init(app_name: []const u8, aspects: []const []const u8, ally: Allocator) !Self { var self = Self{ .ally = ally, - .app_name = AppName.init(ally), - .aspects = Aspects.init(ally), + .app_name = AppName.empty, + .aspects = Aspects.empty, .hash = undefined, }; errdefer { - self.app_name.deinit(); - self.aspects.deinit(); + self.app_name.deinit(self.ally); + self.aspects.deinit(self.ally); } for (app_name) |char| { @@ -41,7 +41,7 @@ pub fn init(app_name: []const u8, aspects: []const []const u8, ally: Allocator) } } - try self.app_name.appendSlice(app_name); + try self.app_name.appendSlice(self.ally, app_name); for (aspects) |aspect| { for (aspect) |char| { @@ -50,22 +50,22 @@ pub fn init(app_name: []const u8, aspects: []const []const u8, ally: Allocator) } } - var new_aspect = Aspect.init(self.ally); - errdefer new_aspect.deinit(); + var new_aspect = Aspect.empty; + errdefer new_aspect.deinit(self.ally); - try new_aspect.appendSlice(aspect); - try self.aspects.append(new_aspect); + try new_aspect.appendSlice(self.ally, aspect); + try self.aspects.append(self.ally, new_aspect); } self.hash = blk: { - var name = data.Bytes.init(self.ally); - defer name.deinit(); + var name = data.Bytes.empty; + defer name.deinit(self.ally); - try name.appendSlice(app_name); + try name.appendSlice(self.ally, app_name); for (aspects) |aspect| { - try name.append('.'); - try name.appendSlice(aspect); + try name.append(self.ally, '.'); + try name.appendSlice(self.ally, aspect); } break :blk Hash.of(.{ @@ -78,23 +78,23 @@ pub fn init(app_name: []const u8, aspects: []const []const u8, ally: Allocator) pub fn clone(self: *const Self) !Self { var cloned = self.*; - cloned.app_name = try cloned.app_name.clone(); - cloned.aspects = Aspects.init(self.ally); + cloned.app_name = try cloned.app_name.clone(self.ally); + cloned.aspects = Aspects.empty; for (self.aspects.items) |aspect| { - try cloned.aspects.append(try aspect.clone()); + try cloned.aspects.append(self.ally, try aspect.clone(self.ally)); } return cloned; } pub fn deinit(self: *Self) void { - self.app_name.deinit(); + self.app_name.deinit(self.ally); - for (self.aspects.items) |aspect| { - aspect.deinit(); + for (self.aspects.items) |*aspect| { + aspect.deinit(self.ally); } - self.aspects.deinit(); + self.aspects.deinit(self.ally); self.* = undefined; } diff --git a/core/internal/ThreadSafeFifo.zig b/core/internal/ThreadSafeFifo.zig index ad62924..7c595c8 100644 --- a/core/internal/ThreadSafeFifo.zig +++ b/core/internal/ThreadSafeFifo.zig @@ -1,13 +1,12 @@ const std = @import("std"); const Allocator = std.mem.Allocator; -const LinearFifo = std.fifo.LinearFifo; pub fn ThreadSafeFifo(comptime T: type) type { return struct { pub const Error = Allocator.Error; const Self = @This(); - const Impl = LinearFifo(T, .Dynamic); + const Impl = std.PriorityQueue(T, void, compare); mutex: std.Thread.Mutex, impl: Impl, @@ -15,7 +14,7 @@ pub fn ThreadSafeFifo(comptime T: type) type { pub fn init(ally: Allocator) Self { return Self{ .mutex = .{}, - .impl = Impl.init(ally), + .impl = .init(ally, {}), }; } @@ -23,23 +22,22 @@ pub fn ThreadSafeFifo(comptime T: type) type { self.mutex.lock(); self.impl.deinit(); self.mutex.unlock(); - self.* = undefined; } pub fn push(self: *Self, element: T) Error!void { self.mutex.lock(); - defer { - self.mutex.unlock(); - } - try self.impl.writeItem(element); + defer self.mutex.unlock(); + try self.impl.add(element); } pub fn pop(self: *Self) ?T { self.mutex.lock(); - defer { - self.mutex.unlock(); - } - return self.impl.readItem(); + defer self.mutex.unlock(); + return self.impl.removeOrNull(); + } + + fn compare(_: void, _: T, _: T) std.math.Order { + return .eq; } }; } diff --git a/core/node/Event.zig b/core/node/Event.zig index 6d144a6..254d187 100644 --- a/core/node/Event.zig +++ b/core/node/Event.zig @@ -5,6 +5,8 @@ const Packet = @import("../packet.zig").Managed; const Payload = @import("../packet.zig").Payload; const Hash = @import("../crypto/Hash.zig"); +const Allocator = std.mem.Allocator; + // TODO: Perhaps distinguish between tasks and packets. pub const In = union(enum) { @@ -22,11 +24,11 @@ pub const In = union(enum) { payload: Payload, }; - pub fn deinit(self: *@This()) void { + pub fn deinit(self: *@This(), ally: Allocator) void { switch (self.*) { .announce => |*announce| { - if (announce.app_data) |app_data| { - app_data.deinit(); + if (announce.app_data) |*app_data| { + app_data.deinit(ally); } }, .packet => |*packet| { @@ -34,7 +36,7 @@ pub const In = union(enum) { }, .plain => |*plain| { plain.name.deinit(); - plain.payload.deinit(); + plain.payload.deinit(ally); }, } } @@ -52,53 +54,49 @@ pub const Out = union(enum) { } // TODO: Replace this with a cleaner implementation. - pub fn format(this: @This(), comptime fmt: []const u8, options: std.fmt.FormatOptions, w: anytype) !void { - _ = fmt; - _ = options; - + pub fn format(this: @This(), writer: *std.Io.Writer) !void { const F = struct { const Self = @This(); - writer: @TypeOf(w), + io_writer: *std.Io.Writer, indentation: u8 = 0, - fn init(writer: @TypeOf(w)) Self { + fn init(io_writer: *std.Io.Writer) Self { return .{ - .writer = writer, + .io_writer = io_writer, }; } fn indent(self: *Self) !void { for (0..self.indentation) |_| { - try self.writer.print(" ", .{}); + try self.io_writer.print(" ", .{}); } } fn entry(self: *Self, key: []const u8, comptime value_fmt: []const u8, args: anytype) !void { try self.indent(); - try self.writer.print(".{s} = ", .{key}); - try self.writer.print(value_fmt ++ ",\n", args); + try self.io_writer.print(".{s} = ", .{key}); + try self.io_writer.print(value_fmt ++ ",\n", args); } fn objectStart(self: *Self, key: []const u8, tag: []const u8) !void { try self.indent(); - try self.writer.print(".{s} = .{s}{{\n", .{ key, tag }); + try self.io_writer.print(".{s} = .{s}{{\n", .{ key, tag }); self.indentation += 2; } fn objectEnd(self: *Self) !void { self.indentation -= 2; try self.indent(); - try self.writer.print("}},\n", .{}); + try self.io_writer.print("}},\n", .{}); } fn print(self: *Self, comptime text: []const u8, args: anytype) !void { - try self.writer.print(text, args); + try self.io_writer.print(text, args); } }; - const hex = std.fmt.fmtSliceHexLower; - var f = F.init(w); + var f = F.init(writer); switch (this) { .packet => |p| { @@ -116,19 +114,19 @@ pub const Out = union(enum) { }); if (p.interface_access_code.items.len > 0) { - try f.entry("interface_access_code", "{x}", .{hex(p.interface_access_code.items)}); + try f.entry("interface_access_code", "{x}", .{p.interface_access_code.items}); } switch (p.endpoints) { .normal => |n| { try f.entry("endpoints", ".normal{{{x}}}", .{ - hex(&n.endpoint), + &n.endpoint, }); }, .transport => |t| { try f.entry("endpoints", ".transport{{{x}, {x}}}", .{ - hex(&t.endpoint), - hex(&t.transport_id), + &t.endpoint, + &t.transport_id, }); }, } @@ -139,26 +137,26 @@ pub const Out = union(enum) { .announce => |a| { try f.objectStart("payload", "announce"); - try f.entry("public.dh", "{x}", .{hex(&a.public.dh)}); - try f.entry("public.signature", "{x}", .{hex(&a.public.signature.bytes)}); - try f.entry("name_hash", "{x}", .{hex(&a.name_hash)}); - try f.entry("noise", "{x}", .{hex(&a.noise)}); + try f.entry("public.dh", "{x}", .{&a.public.dh}); + try f.entry("public.signature", "{x}", .{&a.public.signature.bytes}); + try f.entry("name_hash", "{x}", .{&a.name_hash}); + try f.entry("noise", "{x}", .{&a.noise}); var timestamp_bytes: [5]u8 = undefined; std.mem.writeInt(u40, ×tamp_bytes, a.timestamp, .big); - try f.entry("timestamp", "{x}", .{hex(×tamp_bytes)}); + try f.entry("timestamp", "{x}", .{×tamp_bytes}); if (a.ratchet) |*ratchet| { - try f.entry("ratchet", "{x}", .{hex(ratchet)}); + try f.entry("ratchet", "{x}", .{ratchet}); } - try f.entry("signature", "{x}", .{hex(&a.signature.toBytes())}); + try f.entry("signature", "{x}", .{&a.signature.toBytes()}); if (a.application_data.items.len > 0) { - try f.entry("application_data", "{x}", .{hex(a.application_data.items)}); + try f.entry("application_data", "{x}", .{a.application_data.items}); } try f.objectEnd(); }, .raw => |r| { - try f.entry("payload", ".raw{{{x}}}", .{hex(r.items)}); + try f.entry("payload", ".raw{{{x}}}", .{r.items}); }, .none => { try f.entry("payload", ".none", .{}); diff --git a/core/packet.zig b/core/packet.zig index 2214085..b63f36b 100644 --- a/core/packet.zig +++ b/core/packet.zig @@ -50,7 +50,7 @@ pub const Payload = union(enum) { }; } - pub fn clone(self: Self) !Self { + pub fn clone(self: Self, ally: Allocator) !Self { return switch (self) { .announce => |a| Self{ .announce = Announce{ @@ -60,11 +60,11 @@ pub const Payload = union(enum) { .timestamp = a.timestamp, .ratchet = a.ratchet, .signature = a.signature, - .application_data = try a.application_data.clone(), + .application_data = try a.application_data.clone(ally), }, }, .raw => |r| Self{ - .raw = try r.clone(), + .raw = try r.clone(ally), }, .none => Self.none, }; @@ -89,10 +89,10 @@ pub const Payload = union(enum) { }; } - pub fn deinit(self: *Self) void { + pub fn deinit(self: *Self, ally: Allocator) void { return switch (self.*) { - .announce => |*announce| announce.application_data.deinit(), - .raw => |*raw| raw.deinit(), + .announce => |*announce| announce.application_data.deinit(ally), + .raw => |*raw| raw.deinit(ally), .none => {}, }; } @@ -201,13 +201,13 @@ test "validate-raw-announce-roundtrip" { // Captured from reference implementation - with framing removed. const raw_announce = "71008133c7ce6d6be9b4070a3b98ee9ecab583dfe79d30200ee5e9f5c5615d45a5b000fb266456840e5f4d010a6fbb4025969f8db5415597e3d7a48431d0534e441d0bdeb78f1064f50b447291dd51617040dc9c40cb5b9adab1314ad270b1297d6fd46ec60bc318e2c0f0d908fc1c2bcdef00686f9b4ef17ec1b73f60b14df6709cb74164bd1890e26ff8a4634bbd855051ef959f413d7f7c8f9ff0f54ee81fb994c4e1975fe6f4b56fb26d2e107bd824d864a6932a2e2c02b1352ad9a31ce1cbeae72902effef1ccdeb7d004fbe527cd39111dc59d0e92c406696f6e323332c0"; - var bytes = std.ArrayList(u8).init(ally); - defer bytes.deinit(); + var bytes = std.ArrayList(u8).empty; + defer bytes.deinit(ally); var i: usize = 0; while (i < raw_announce.len) : (i += 2) { const byte = std.fmt.parseInt(u8, raw_announce[i .. i + 2], 16) catch break; - try bytes.append(byte); + try bytes.append(ally, byte); } var factory = Factory.init(ally, rng, .{}); @@ -262,7 +262,7 @@ test "validate-make-announce" { var raw_bytes = try data.Bytes.initCapacity(ally, announce_packet.size()); raw_bytes.expandToCapacity(); - defer raw_bytes.deinit(); + defer raw_bytes.deinit(ally); const raw_packet = try announce_packet.write(raw_bytes.items); var p = try factory.fromBytes(raw_packet); diff --git a/core/packet/Builder.zig b/core/packet/Builder.zig index a2f2c64..9928827 100644 --- a/core/packet/Builder.zig +++ b/core/packet/Builder.zig @@ -28,7 +28,7 @@ pub fn init(ally: Allocator) Self { return Self{ .ally = ally, .header = .{}, - .interface_access_code = data.Bytes.init(ally), + .interface_access_code = data.Bytes.empty, .endpoints = null, .context = .none, .payload = .none, @@ -41,7 +41,7 @@ pub fn setHeader(self: *Self, header: Header) *Self { } pub fn setInterfaceAccessCode(self: *Self, interface_access_code: []const u8) !*Self { - try self.interface_access_code.appendSlice(interface_access_code); + try self.interface_access_code.appendSlice(self.ally, interface_access_code); if (interface_access_code.len > 0) { self.header.interface = .authenticated; diff --git a/core/packet/Factory.zig b/core/packet/Factory.zig index a0a80b3..7cb336a 100644 --- a/core/packet/Factory.zig +++ b/core/packet/Factory.zig @@ -51,8 +51,8 @@ pub fn fromBytes(self: *Self, bytes: []const u8) Error!Packet { return Error.InvalidAuthentication; } - var interface_access_code = data.Bytes.init(self.ally); - errdefer interface_access_code.deinit(); + var interface_access_code = data.Bytes.empty; + errdefer interface_access_code.deinit(self.ally); if (self.config.access_code) |access_code| { if (bytes.len < index + access_code.len) { @@ -61,7 +61,7 @@ pub fn fromBytes(self: *Self, bytes: []const u8) Error!Packet { // TODO: I need to decrypt the packet here. - try interface_access_code.appendSlice(bytes[index .. index + access_code.len]); + try interface_access_code.appendSlice(self.ally, bytes[index .. index + access_code.len]); index += access_code.len; } @@ -155,17 +155,17 @@ pub fn fromBytes(self: *Self, bytes: []const u8) Error!Packet { announce.signature = Signature.fromBytes(signature_bytes); index += Signature.encoded_length; - var application_data = data.Bytes.init(self.ally); - try application_data.appendSlice(bytes[index..]); + var application_data = data.Bytes.empty; + try application_data.appendSlice(self.ally, bytes[index..]); announce.application_data = application_data; break :blk announce; }, }, else => .{ .raw = blk: { - var raw = data.Bytes.init(self.ally); - errdefer raw.deinit(); - try raw.appendSlice(bytes[index..]); + var raw = data.Bytes.empty; + errdefer raw.deinit(self.ally); + try raw.appendSlice(self.ally, bytes[index..]); break :blk raw; } }, }; @@ -191,31 +191,32 @@ pub fn makeAnnounce(self: *Self, endpoint: *const Endpoint, application_data: ?[ var ratchet: crypto.Identity.Ratchet = undefined; self.rng.bytes(&ratchet); announce.ratchet = ratchet; - announce.application_data = data.Bytes.init(self.ally); + announce.application_data = data.Bytes.empty; if (application_data) |app_data| { - try announce.application_data.appendSlice(app_data); + try announce.application_data.appendSlice(self.ally, app_data); } announce.signature = blk: { var arena = std.heap.ArenaAllocator.init(self.ally); defer arena.deinit(); - var bytes = data.Bytes.init(arena.allocator()); - try bytes.appendSlice(endpoint.hash.short()); - try bytes.appendSlice(announce.public.dh[0..]); - try bytes.appendSlice(announce.public.signature.bytes[0..]); - try bytes.appendSlice(announce.name_hash[0..]); - try bytes.appendSlice(announce.noise[0..]); + const arena_allocator = arena.allocator(); + var bytes = data.Bytes.empty; + try bytes.appendSlice(arena_allocator, endpoint.hash.short()); + try bytes.appendSlice(arena_allocator, announce.public.dh[0..]); + try bytes.appendSlice(arena_allocator, announce.public.signature.bytes[0..]); + try bytes.appendSlice(arena_allocator, announce.name_hash[0..]); + try bytes.appendSlice(arena_allocator, announce.noise[0..]); var timestamp_bytes: [5]u8 = undefined; std.mem.writeInt(u40, ×tamp_bytes, announce.timestamp, .big); - try bytes.appendSlice(×tamp_bytes); + try bytes.appendSlice(arena_allocator, ×tamp_bytes); if (announce.ratchet) |*r| { - try bytes.appendSlice(r[0..]); + try bytes.appendSlice(arena_allocator, r[0..]); } - try bytes.appendSlice(announce.application_data.items); + try bytes.appendSlice(arena_allocator, announce.application_data.items); break :blk try identity.sign(bytes); }; diff --git a/core/packet/Managed.zig b/core/packet/Managed.zig index a771e9b..673c87e 100644 --- a/core/packet/Managed.zig +++ b/core/packet/Managed.zig @@ -43,25 +43,25 @@ pub fn setTransport(self: *Self, transport_id: *const Hash.Short) !void { pub fn validate(self: *const Self) !void { switch (self.payload) { .announce => |a| { - var signed_data = std.ArrayList(u8).init(self.ally); - defer signed_data.deinit(); + var signed_data = std.ArrayList(u8).empty; + defer signed_data.deinit(self.ally); const endpoint_hash = self.endpoints.endpoint(); - try signed_data.appendSlice(endpoint_hash[0..]); - try signed_data.appendSlice(a.public.dh[0..]); - try signed_data.appendSlice(a.public.signature.bytes[0..]); - try signed_data.appendSlice(a.name_hash[0..]); - try signed_data.appendSlice(a.noise[0..]); + try signed_data.appendSlice(self.ally, endpoint_hash[0..]); + try signed_data.appendSlice(self.ally, a.public.dh[0..]); + try signed_data.appendSlice(self.ally, a.public.signature.bytes[0..]); + try signed_data.appendSlice(self.ally, a.name_hash[0..]); + try signed_data.appendSlice(self.ally, a.noise[0..]); var timestamp_bytes: [5]u8 = undefined; std.mem.writeInt(u40, ×tamp_bytes, a.timestamp, .big); - try signed_data.appendSlice(×tamp_bytes); + try signed_data.appendSlice(self.ally, ×tamp_bytes); if (a.ratchet) |*ratchet| { - try signed_data.appendSlice(ratchet[0..]); + try signed_data.appendSlice(self.ally, ratchet[0..]); } - try signed_data.appendSlice(a.application_data.items); + try signed_data.appendSlice(self.ally, a.application_data.items); var verifier = try a.signature.verifier(a.public.signature); verifier.update(signed_data.items); @@ -218,12 +218,12 @@ pub fn clone(self: *const Self) !Self { .context = self.context, .endpoints = self.endpoints, .header = self.header, - .interface_access_code = try self.interface_access_code.clone(), - .payload = try self.payload.clone(), + .interface_access_code = try self.interface_access_code.clone(self.ally), + .payload = try self.payload.clone(self.ally), }; } pub fn deinit(self: *Self) void { - self.interface_access_code.deinit(); - self.payload.deinit(); + self.interface_access_code.deinit(self.ally); + self.payload.deinit(self.ally); } From 7d06912ad0c6b33bd20ca309f4a96ed9b73a4fe2 Mon Sep 17 00:00:00 2001 From: Arran Ireland Date: Thu, 2 Oct 2025 10:40:37 +0100 Subject: [PATCH 3/5] io: upgrade to 0.15.1 --- io/driver/Tcp.zig | 75 +++++------ io/framing/hdlc.zig | 311 ++++++++++++++++++-------------------------- 2 files changed, 168 insertions(+), 218 deletions(-) diff --git a/io/driver/Tcp.zig b/io/driver/Tcp.zig index e2b6c23..db2cb9f 100644 --- a/io/driver/Tcp.zig +++ b/io/driver/Tcp.zig @@ -14,9 +14,10 @@ running: bool, node: *core.Node, host: []const u8, port: u16, -stream: ?std.net.Stream, -reader: ?hdlc.Reader(std.net.Stream.Reader, mtu), -writer: ?hdlc.Writer(std.net.Stream.Writer), +stream_reader: ?std.net.Stream.Reader, +stream_writer: ?std.net.Stream.Writer, +hdlc_reader: ?hdlc.Reader, +hdlc_writer: ?hdlc.Writer, pub fn init(node: *core.Node, host: []const u8, port: u16, ally: Allocator) !Self { return .{ @@ -25,9 +26,10 @@ pub fn init(node: *core.Node, host: []const u8, port: u16, ally: Allocator) !Sel .node = node, .host = try ally.dupe(u8, host), .port = port, - .stream = null, - .reader = null, - .writer = null, + .stream_reader = null, + .stream_writer = null, + .hdlc_reader = null, + .hdlc_writer = null, }; } @@ -35,13 +37,12 @@ pub fn deinit(self: *Self) void { self.running = false; self.ally.destroy(&self.host); - if (self.stream) |stream| { - stream.close(); - self.stream = null; + if (self.hdlc_reader) |r| { + self.ally.free(r.reader.buffer); } - if (self.reader) |*reader| { - reader.deinit(); + if (self.hdlc_writer) |w| { + self.ally.free(w.writer.buffer); } } @@ -49,17 +50,21 @@ pub fn run(self: *Self) !void { try self.connect(); self.running = true; - var reader = self.reader orelse return error.NoReader; - var frames: [1024][]const u8 = undefined; + var hdlc_reader = self.hdlc_reader orelse return error.NoReader; + // TODO: Replace with 2 * packet mtu. + var frame: [1024]u8 = @splat(0); while (self.running) { - const n = reader.readFrames(&frames) catch |err| { - if (err == error.EndOfStream) continue else return err; + const n = hdlc_reader.readFrame(&frame) catch |err| { + switch (err) { + error.EndOfStream => continue, + else => return err, + } }; - for (frames[0..n]) |frame| { - try self.handleFrame(frame); - } + if (n == 0) continue; + + try self.handleFrame(frame[0..n]); } } @@ -67,36 +72,32 @@ fn connect(self: *Self) !void { const address_list = try std.net.getAddressList(self.ally, self.host, self.port); defer address_list.deinit(); - if (address_list.addrs.len == 0) { - return error.FailedHostLookup; - } + if (address_list.addrs.len == 0) return error.FailedHostLookup; const address = address_list.addrs[0]; - self.stream = try std.net.tcpConnectToAddress(address); - self.reader = try hdlc.Reader(std.net.Stream.Reader, mtu).init( - self.stream.?.reader(), - self.ally, - ); - self.writer = hdlc.Writer(std.net.Stream.Writer).init( - self.stream.?.writer(), - ); - - log.info("connected to {s}:{} at {}", .{ self.host, self.port, address }); + var stream = try std.net.tcpConnectToAddress(address); + + self.stream_reader = stream.reader(try self.ally.alloc(u8, mtu)); + self.stream_writer = stream.writer(try self.ally.alloc(u8, mtu)); + self.hdlc_reader = hdlc.Reader.init(self.stream_reader.?.interface()); + self.hdlc_writer = hdlc.Writer.init(&self.stream_writer.?.interface); + + log.info("connected to {s}:{d} at {f}", .{ self.host, self.port, address }); } pub fn write(self: *Self, data: []const u8) !void { const writer = self.writer orelse return error.NotConnected; const n = try writer.writeFrame(data); - log.debug("sent {} bytes", .{n}); + log.debug("sent {d} bytes", .{n}); } fn handleFrame(self: *Self, data: []const u8) !void { - log.debug("handling frame ({} bytes)", .{data.len}); - log.debug("raw bytes: {}", .{std.fmt.fmtSliceHexLower(data)}); + log.debug("handling frame ({d} bytes)", .{data.len}); + // log.debug("raw bytes: {f}", .{std.fmt.hex(data)}); var factory = core.packet.Factory.init(self.ally, std.crypto.random, .{}); var packet = factory.fromBytes(data) catch |err| { - log.err("failed to parse packet: {}", .{err}); + log.err("failed to parse packet: {any}", .{err}); return; }; @@ -104,11 +105,11 @@ fn handleFrame(self: *Self, data: []const u8) !void { var event = core.Node.Event.Out{ .packet = packet }; defer event.deinit(); - log.info("{}", .{event}); + log.info("{f}", .{event}); if (packet.header.purpose == .announce) { packet.validate() catch |err| { - log.err("announce validation failed: {}", .{err}); + log.err("announce validation failed: {any}", .{err}); return; }; log.info("announce validation succeeded", .{}); diff --git a/io/framing/hdlc.zig b/io/framing/hdlc.zig index a0cc932..7745f82 100644 --- a/io/framing/hdlc.zig +++ b/io/framing/hdlc.zig @@ -8,237 +8,186 @@ const escape_mask = 0x20; const escaped_escape = 0x5d; const escaped_flag = 0x5e; -pub fn Writer(comptime W: type) type { - return struct { - impl: W, +pub const Writer = struct { + writer: *std.Io.Writer, - const Self = @This(); + const Self = @This(); - pub const Error = W.Error; + pub fn init(writer: *std.Io.Writer) Self { + return .{ + .writer = writer, + }; + } - pub fn init(impl: W) Self { - return .{ - .impl = impl, - }; - } - - pub fn writeFrame(self: Self, bytes: []const u8) Error!usize { - try self.impl.writeByte(flag); - - var start: usize = 0; - var index: usize = 0; + pub fn writeFrame(self: Self, payload: []const u8) !void { + try self.writer.writeByte(flag); - while (index < bytes.len) : (index += 1) { - const byte = bytes[index]; - - if (byte == escape or byte == flag) { - if (index > start) { - try self.impl.writeAll(bytes[start..index]); - } + var start: usize = 0; + var index: usize = 0; - try self.impl.writeByte(escape); - try self.impl.writeByte(byte ^ escape_mask); + while (index < payload.len) : (index += 1) { + const byte = payload[index]; - start = index + 1; + if (byte == escape or byte == flag) { + if (index > start) { + try self.writer.writeAll(payload[start..index]); } - } - - if (start < bytes.len) { - try self.impl.writeAll(bytes[start..]); - } - try self.impl.writeByte(flag); + try self.writer.writeByte(escape); + try self.writer.writeByte(byte ^ escape_mask); - return bytes.len; - } - }; -} - -pub fn Reader(comptime R: type, comptime size: usize) type { - return struct { - const Self = @This(); - - pub const Error = R.Error || error{EndOfStream}; - - impl: R, - escaped: bool = false, - buffer: []u8, - count: usize = 0, - ally: Allocator, - - pub fn init(impl: R, ally: Allocator) !Self { - return .{ - .impl = impl, - .ally = ally, - .buffer = try ally.alloc(u8, size), - }; + start = index + 1; + } } - pub fn deinit(self: *Self) void { - self.ally.free(self.buffer); + if (start < payload.len) { + try self.writer.writeAll(payload[start..]); } - pub fn readFrames(self: *Self, frame_buffer: [][]const u8) Error!usize { - const n = self.impl.read(self.buffer[self.count..]) catch |err| { - if (err == error.EndOfStream and self.count == 0) { - return 0; - } - return err; - }; - - self.count += n; - - var frame_count: usize = 0; - var input_index: usize = 0; - var data_index: usize = 0; - - while (frame_count < frame_buffer.len and input_index < self.count) { - const frame_start = data_index; - - while (input_index < self.count and data_index < self.buffer.len) { - const byte = self.buffer[input_index]; - input_index += 1; - - if (self.escaped) { - self.buffer[data_index] = byte ^ escape_mask; - data_index += 1; - self.escaped = false; - continue; + try self.writer.writeByte(flag); + } +}; + +pub const Reader = struct { + const Self = @This(); + + reader: *std.Io.Reader, + + pub fn init(reader: *std.Io.Reader) Self { + return .{ + .reader = reader, + }; + } + + // I'm assuming here that a valid frame can always be read in one call. + pub fn readFrame(self: *Self, frame: []u8) !usize { + var index: usize = 0; + var escaped = false; + + while (true) { + if (index >= frame.len) return error.FrameFull; + + switch (try self.reader.takeByte()) { + flag => break, + escape => escaped = true, + else => |byte| { + if (escaped) { + frame[index] = byte ^ escape_mask; + escaped = false; + } else { + frame[index] = byte; } - switch (byte) { - flag => { - if (data_index > frame_start) { - frame_buffer[frame_count] = self.buffer[frame_start..data_index]; - frame_count += 1; - } - break; - }, - escape => { - self.escaped = true; - }, - else => { - self.buffer[data_index] = byte; - data_index += 1; - }, - } - } - } - - if (input_index < self.count) { - const remaining = self.buffer[input_index..self.count]; - std.mem.copyForwards(u8, self.buffer[0..remaining.len], remaining); - self.count = remaining.len; - } else { - self.count = 0; + index += 1; + }, } - - return frame_count; } - }; -} + + return index; + } +}; const t = std.testing; test "write" { - var frames = std.ArrayList(u8).init(t.allocator); - defer frames.deinit(); + var io_writer = std.Io.Writer.Allocating.init(t.allocator); + defer io_writer.deinit(); - const frames_writer = frames.writer(); - const writer = Writer(@TypeOf(frames_writer)).init(frames_writer); + const writer = Writer.init(&io_writer.writer); const input = "this is some data"; - _ = try writer.writeFrame(input); - try t.expectEqualSlices(u8, [_]u8{flag} ++ input ++ [_]u8{flag}, frames.items); - frames.clearRetainingCapacity(); + try writer.writeFrame(input); + try t.expectEqualSlices(u8, [_]u8{flag} ++ input ++ [_]u8{flag}, io_writer.written()); + io_writer.clearRetainingCapacity(); const flag_input = [_]u8{flag}; - _ = try writer.writeFrame(&flag_input); - try t.expectEqualSlices(u8, &[_]u8{ flag, escape, escaped_flag, flag }, frames.items); - frames.clearRetainingCapacity(); + try writer.writeFrame(&flag_input); + try t.expectEqualSlices(u8, &[_]u8{ flag, escape, escaped_flag, flag }, io_writer.written()); + io_writer.clearRetainingCapacity(); const esc_input = [_]u8{escape}; const escaped_esc = [_]u8{ flag, escape, escaped_escape, flag }; - _ = try writer.writeFrame(&esc_input); - try t.expectEqualSlices(u8, &escaped_esc, frames.items); - frames.clearRetainingCapacity(); + try writer.writeFrame(&esc_input); + try t.expectEqualSlices(u8, &escaped_esc, io_writer.written()); + io_writer.clearRetainingCapacity(); const mixed_input = [_]u8{ 0x01, flag, 0x02, escape, 0x03, 0x04, escape_mask }; const mixed_expected = [_]u8{ flag, 0x01, escape, escaped_flag, 0x02, escape, escaped_escape, 0x03, 0x04, escape_mask, flag }; - _ = try writer.writeFrame(&mixed_input); - try t.expectEqualSlices(u8, &mixed_expected, frames.items); + try writer.writeFrame(&mixed_input); + try t.expectEqualSlices(u8, &mixed_expected, io_writer.written()); + io_writer.clearRetainingCapacity(); } test "read" { - const input = [_]u8{flag} ++ "this is some data" ++ [_]u8{flag}; - var stream = std.io.fixedBufferStream(input); - var reader = try Reader( - @TypeOf(stream.reader()), - input.len, - ).init(stream.reader(), t.allocator); - defer reader.deinit(); - - var frame_buffer: [1][]const u8 = undefined; - const n = try reader.readFrames(&frame_buffer); - - try t.expectEqual(frame_buffer.len, n); - try t.expectEqualSlices(u8, "this is some data", frame_buffer[0]); + const payload = "this is some data"; + var io_reader = std.Io.Reader.fixed([_]u8{flag} ++ payload ++ [_]u8{flag}); + var reader = Reader.init(&io_reader); + var frame: [32]u8 = @splat(0); + + var n = try reader.readFrame(&frame); + try t.expectEqual(0, n); + + n = try reader.readFrame(&frame); + try t.expectEqualSlices(u8, payload, frame[0..n]); + + const err = reader.readFrame(&frame); + try t.expectError(error.EndOfStream, err); } test "read-multiple-flags" { - const input = [_]u8{ flag, 0x01, 0x02, flag, 0x03, 0x04, flag }; - var stream = std.io.fixedBufferStream(&input); - var reader = try Reader( - @TypeOf(stream.reader()), - input.len, - ).init(stream.reader(), t.allocator); - defer reader.deinit(); - - var frame_buffer: [2][]const u8 = undefined; - const n = try reader.readFrames(&frame_buffer); - - try t.expectEqual(frame_buffer.len, n); - try t.expectEqualSlices(u8, &[_]u8{ 0x01, 0x02 }, frame_buffer[0]); - try t.expectEqualSlices(u8, &[_]u8{ 0x03, 0x04 }, frame_buffer[1]); + var io_reader = std.Io.Reader.fixed(&[_]u8{ flag, 0x01, 0x02, flag, 0x03, 0x04, flag }); + var reader = Reader.init(&io_reader); + var frame: [32]u8 = @splat(0); + + var n = try reader.readFrame(&frame); + try t.expectEqual(0, n); + + n = try reader.readFrame(&frame); + try t.expectEqualSlices(u8, &[_]u8{ 0x01, 0x02 }, frame[0..n]); + + n = try reader.readFrame(&frame); + try t.expectEqualSlices(u8, &[_]u8{ 0x03, 0x04 }, frame[0..n]); + + const err = reader.readFrame(&frame); + try t.expectError(error.EndOfStream, err); } test "read-mixed-data" { - const input = [_]u8{ flag, 0x01, escape, escaped_flag, 0x02, escape, escaped_escape, 0x03, flag }; - var stream = std.io.fixedBufferStream(&input); - var reader = try Reader( - @TypeOf(stream.reader()), - input.len, - ).init(stream.reader(), t.allocator); - defer reader.deinit(); + var io_reader = std.Io.Reader.fixed( + &[_]u8{ flag, 0x01, escape, escaped_flag, 0x02, escape, escaped_escape, 0x03, flag }, + ); + var reader = Reader.init(&io_reader); + var frame: [32]u8 = @splat(0); - var frame_buffer: [1][]const u8 = undefined; - const n = try reader.readFrames(&frame_buffer); + var n = try reader.readFrame(&frame); + try t.expectEqual(0, n); + n = try reader.readFrame(&frame); const unescaped = [_]u8{ 0x01, flag, 0x02, escape, 0x03 }; - try t.expectEqual(frame_buffer.len, n); - try t.expectEqualSlices(u8, &unescaped, frame_buffer[0]); + try t.expectEqualSlices(u8, &unescaped, frame[0..n]); + + const err = reader.readFrame(&frame); + try t.expectError(error.EndOfStream, err); } test "round-trip" { - var frames = std.ArrayList(u8).init(t.allocator); - defer frames.deinit(); - - const frames_writer = frames.writer(); - const writer = Writer(@TypeOf(frames_writer)).init(frames_writer); + var io_writer = std.Io.Writer.Allocating.init(t.allocator); + defer io_writer.deinit(); const input = [_]u8{ 0x00, flag, 0xaa, escape, 0xff, flag, escape }; - _ = try writer.writeFrame(&input); + const writer = Writer.init(&io_writer.writer); + try writer.writeFrame(&input); + + const escaped = [_]u8{ flag, 0x00, escape, escaped_flag, 0xaa, escape, escaped_escape, 0xff, escape, escaped_flag, escape, escaped_escape, flag }; + try t.expectEqualSlices(u8, &escaped, io_writer.written()); - var stream = std.io.fixedBufferStream(frames.items); - var reader = try Reader( - @TypeOf(stream.reader()), - input.len * 2, - ).init(stream.reader(), t.allocator); - defer reader.deinit(); + var io_reader = std.Io.Reader.fixed(io_writer.written()); + var reader = Reader.init(&io_reader); + var frame: [32]u8 = @splat(0); - var frame_buffer: [1][]const u8 = undefined; - const n = try reader.readFrames(&frame_buffer); + var n = try reader.readFrame(&frame); + try t.expectEqual(0, n); - try t.expectEqual(frame_buffer.len, n); - try t.expectEqualSlices(u8, &input, frame_buffer[0]); + n = try reader.readFrame(&frame); + try t.expectEqualSlices(u8, &input, frame[0..n]); } From 25b5539259b904490fdcc9347dccc46a13fdba11 Mon Sep 17 00:00:00 2001 From: Arran Ireland Date: Thu, 2 Oct 2025 10:40:53 +0100 Subject: [PATCH 4/5] test: upgrade to 0.15.1 --- test/simulation/kit/Simulator.zig | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/simulation/kit/Simulator.zig b/test/simulation/kit/Simulator.zig index 5489b3b..c124250 100644 --- a/test/simulation/kit/Simulator.zig +++ b/test/simulation/kit/Simulator.zig @@ -55,7 +55,7 @@ pub fn deinit(self: *Self) void { event.deinit(); } - interface.event_buffer.deinit(); + interface.event_buffer.deinit(self.ally); } self.nodes.deinit(); @@ -103,7 +103,7 @@ pub fn fromTopology(comptime topology: anytype, system: *core.System, ally: Allo .api = try simulator_node.node.addInterface(interface_config), .config = interface_config, .to = @tagName(interface.to), - .event_buffer = std.ArrayList(core.Node.Event.Out).init(self.ally), + .event_buffer = std.ArrayList(core.Node.Event.Out).empty, }; try simulator_node.interfaces.put(interface_field.name, {}); @@ -204,7 +204,7 @@ pub fn processNodes(self: *Self) !void { var interface = self.interfaces.getPtr(interface_name.*).?; while (interface.api.collectEvent()) |event_out| { - try interface.event_buffer.append(event_out); + try interface.event_buffer.append(self.ally, event_out); } } } From 39ac8c5ab5f0ea6e88cf6dad45f055d5276aef06 Mon Sep 17 00:00:00 2001 From: Arran Ireland Date: Thu, 2 Oct 2025 10:41:11 +0100 Subject: [PATCH 5/5] ci: enable linux debug builds --- .github/workflows/ci.yml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 47554ce..2c07f54 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,8 +11,6 @@ on: - '**.c' - '**.h' -# Remove guard against Linux Debug when #35 is closed. - jobs: test: strategy: @@ -28,7 +26,6 @@ jobs: - uses: mlugg/setup-zig@v2 - name: Debug - if: matrix.os != 'ubuntu-latest' run: zig build test -Doptimize=Debug - name: Release @@ -48,7 +45,6 @@ jobs: - uses: mlugg/setup-zig@v2 - name: Debug - if: matrix.os != 'ubuntu-latest' run: zig build example -Dname=tcp -Doptimize=Debug -Dci=true - name: Release @@ -68,7 +64,6 @@ jobs: - uses: mlugg/setup-zig@v2 - name: Debug - if: matrix.os != 'ubuntu-latest' run: zig build c -Doptimize=Debug - name: Release @@ -88,7 +83,6 @@ jobs: - uses: mlugg/setup-zig@v2 - name: Debug - if: matrix.os != 'ubuntu-latest' run: zig build wasm -Doptimize=Debug - name: Release