summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRobby Zambito <contact@robbyzambito.me>2026-01-14 18:58:13 -0500
committerRobby Zambito <contact@robbyzambito.me>2026-01-14 19:34:35 -0500
commit0d9c0c33fa7c7e73e2e8448ca2e8071584bfaf39 (patch)
treeae002275aea17b02d9769f30c51f496bb1215221
parentb3f1b00510a6be9a4f792b6fbf7fd61797603eaa (diff)
-rw-r--r--src/NetWriter.zig238
-rw-r--r--src/RawSocketWriter.zig32
-rw-r--r--src/message.zig610
-rw-r--r--src/root.zig2
4 files changed, 343 insertions, 539 deletions
diff --git a/src/NetWriter.zig b/src/NetWriter.zig
deleted file mode 100644
index f0f617d..0000000
--- a/src/NetWriter.zig
+++ /dev/null
@@ -1,238 +0,0 @@
-//! Wraps a writer with UDP headers.
-//! This is useful for wrapping RawSocket Writer with appropriate headers.
-
-rand: Random,
-wrapped: *Writer,
-interface: Writer,
-
-pub fn init(w: *Writer, buffer: []u8) !NetWriter {
- std.debug.assert(buffer.len > @sizeOf(EthernetHeaders) + @sizeOf(IpHeaders) + @sizeOf(UdpHeaders));
-
- var prng = Random.DefaultPrng.init(blk: {
- var seed: u64 = undefined;
- try posix.getrandom(mem.asBytes(&seed));
- break :blk seed;
- });
-
- return .{
- .rand = prng.random(),
- .wrapped = w,
- .interface = .{
- .vtable = &.{
- .drain = drain,
- // .flush = flush,
- },
- .buffer = buffer,
- },
- };
-}
-
-fn drain(io_w: *Writer, data: []const []const u8, splat: usize) Writer.Error!usize {
- const w: *NetWriter = @alignCast(@fieldParentPtr("interface", io_w));
- const headers_byte_len = comptime (EthernetHeaders.byte_len + (@bitSizeOf(IpHeaders) / 8) + @sizeOf(UdpHeaders));
- const headers: [headers_byte_len]u8 = blk: {
- const ether_headers: EthernetHeaders = .{
- .dest_mac = .{ 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff },
- .src_mac = src_blk: {
- var output_bytes: [6]u8 = undefined;
- output_bytes[0] = 0xee;
- w.rand.bytes(output_bytes[1..]);
- break :src_blk output_bytes;
- },
- .ether_type = 0x0800,
- };
-
- const total_len = Writer.countSplat(data, splat) + w.interface.end;
-
- const ip_headers: IpHeaders = .{
- // length of the packet minus eth header
- .total_length = @intCast(headers_byte_len + total_len - EthernetHeaders.byte_len), //@intCast(total_len),
- .ttl = 64,
- .protocol = 0x11,
- .src_ip = .{ 0xff, 0x02, 0x03, 0x04 },
- .dest_ip = .{ 0xff, 0xff, 0xff, 0xff },
- };
-
- const udp_headers: UdpHeaders = .{
- .src_port = 0xbbbb,
- .dest_port = 8888,
- .length = @intCast(total_len + @sizeOf(UdpHeaders)),
- };
-
- var buf: [headers_byte_len]u8 = undefined;
- var buf_w = Writer.fixed(&buf);
-
- _ = try ether_headers.write(&buf_w);
- try ip_headers.write(&buf_w);
- try buf_w.writeStruct(udp_headers, .big);
-
- break :blk buf;
- };
-
- _ = try w.wrapped.write(&headers);
- const total_len = try w.wrapped.writeSplatHeader(w.interface.buffered(), data, splat);
-
- try w.wrapped.flush();
- return total_len - w.interface.consumeAll();
-}
-
-const EthernetHeaders = struct {
- dest_mac: @Vector(6, u8),
-
- src_mac: @Vector(6, u8),
-
- ether_type: u16,
-
- fn write(hdr: EthernetHeaders, writer: *std.Io.Writer) Writer.Error!usize {
- try writer.writeInt(u48, @bitCast(hdr.dest_mac), .big);
- try writer.writeInt(u48, @bitCast(hdr.src_mac), .big);
- try writer.writeInt(u16, hdr.ether_type, .big);
- return byte_len;
- }
-
- const byte_len = blk: {
- var res: usize = 0;
- res += @bitSizeOf(u48) / 8;
- res += @bitSizeOf(u48) / 8;
- res += @bitSizeOf(u16) / 8;
- break :blk res;
- };
-
- fn bytes(hdr: EthernetHeaders) [byte_len]u8 {
- var res: [byte_len]u8 = undefined;
- hdr.write(Writer.fixed(&res)) catch unreachable;
- }
-};
-
-const IpHeaders = extern struct {
- dest_ip: @Vector(4, u8) align(1),
- src_ip: @Vector(4, u8) align(1),
- header_checksum: @Vector(2, u8) align(1) = .{ 0, 0 },
- protocol: u8 align(1) = 0,
- ttl: u8 align(1) = 0,
- fragment_and_flags: packed struct(u16) {
- fragment_offset: u13 = 0,
- ethernet_flags: u3 = 0,
- } = .{},
- // fragment_offset: u13 = 0,
- // ethernet_flags: u3 = 0,
- identification: u16 align(1) = 0,
- total_length: u16 align(1) = 0x04,
- type_of_service: u8 align(1) = 0,
- header_and_version: packed struct(u8) {
- header_length: u4 = 5,
- ip_version: u4 = 4,
- } = .{},
- // header_length: u4 = 5,
- // ip_version: u4 = 4,
-
- fn write(hdr: IpHeaders, writer: *std.Io.Writer) Writer.Error!void {
- try writer.writeInt(u8, 0x45, .big); // ip version and header length
- try writer.writeByte(hdr.type_of_service);
- try writer.writeInt(u16, hdr.total_length, .big);
- try writer.writeInt(u16, hdr.identification, .big);
- try writer.writeInt(u16, 0x00, .big); // ethernet flags and fragment offset
- try writer.writeByte(hdr.ttl);
- try writer.writeByte(hdr.protocol);
- try writer.writeInt(u16, @bitCast(hdr.header_checksum), .big);
- try writer.writeInt(u32, @bitCast(hdr.src_ip), .big);
- try writer.writeInt(u32, @bitCast(hdr.dest_ip), .big);
- }
-
- fn bytes(hdr: IpHeaders) [@bitSizeOf(IpHeaders) / 8]u8 {
- var res: [@bitSizeOf(IpHeaders) / 8]u8 = undefined;
- var w = Writer.fixed(&res);
- _ = hdr.write(&w) catch unreachable;
- return res;
- }
-};
-
-test IpHeaders {
- var a: IpHeaders = .{
- .dest_ip = .{ 1, 2, 3, 4 },
- .src_ip = .{ 5, 6, 7, 8 },
- // .header_checksum = .{ 0x1, 0x1 },
- // .protocol = 0,
- // .ttl = 0x64,
- // .identification = 3,
- // .type_of_service = 7,
- };
- // a.dest_ip = .{ 1, 2, 3, 4 };
- // a.src_ip = .{ 5, 6, 7, 8 };
- std.debug.print("ip struct: {}\n", .{a});
- std.debug.print("dest ip: {}\n", .{a.dest_ip});
- std.debug.print("src ip: {}\n", .{a.src_ip});
-
- std.debug.print("raw b : {x}\n", .{@as([@bitSizeOf(IpHeaders) / 8]u8, @bitCast(a))});
-
- std.debug.print("ip bytes: {x}\n", .{a.bytes()});
- var a_struct: [@bitSizeOf(IpHeaders) / 8]u8 = undefined;
- var a_struct_w = Writer.fixed(&a_struct);
- _ = a_struct_w.writeStruct(a, .big) catch unreachable;
- std.debug.print("ip struct: {x}\n", .{a_struct});
- try std.testing.expectEqual(a, a);
-}
-
-const UdpHeaders = packed struct {
- checksum: @Vector(2, u8) = .{ 0, 0 },
- length: u16,
- dest_port: u16,
- src_port: u16,
-
- fn write(hdr: UdpHeaders, writer: *std.Io.Writer) Writer.Error!void {
- try writer.writeStruct(hdr, .big);
- }
-
- fn bytes(hdr: UdpHeaders) [@sizeOf(UdpHeaders)]u8 {
- var res: [@sizeOf(UdpHeaders)]u8 = undefined;
- var w = Writer.fixed(&res);
- _ = hdr.write(&w) catch unreachable;
- return res;
- }
-};
-
-test UdpHeaders {
- const a: UdpHeaders = .{
- .src_port = 1,
- .dest_port = 2,
- .length = 3,
- .checksum = .{ 0x04, 0x05 },
- };
- var a_struct: [@sizeOf(UdpHeaders)]u8 = undefined;
- var a_struct_w = Writer.fixed(&a_struct);
- _ = a_struct_w.writeStruct(a, .big) catch unreachable;
- try std.testing.expectEqual(a.bytes(), a_struct);
-}
-
-const std = @import("std");
-const Random = std.Random;
-const posix = std.posix;
-const Writer = std.Io.Writer;
-const mem = std.mem;
-
-const NetWriter = @This();
-
-const saprusOptions: std.Io.net.BindOptions = .{
- .mode = .raw,
- .protocol = 0,
-};
-
-fn netSaprusBindIpPosix(
- userdata: ?*anyopaque,
- address: *const IpAddress,
- options: IpAddress.BindOptions,
-) IpAddress.BindError!net.Socket {
- if (!have_networking) return error.NetworkDown;
- const t: *Threaded = @ptrCast(@alignCast(userdata));
- const family = std.os.linux.PF.PACKET;
- const socket_fd = try openSocketPosix(t, family, options);
- errdefer posix.close(socket_fd);
- var storage: PosixAddress = undefined;
- var addr_len = addressToPosix(address, &storage);
- try posixBind(t, socket_fd, &storage.any, addr_len);
- try posixGetSockName(t, socket_fd, &storage.any, &addr_len);
- return .{
- .handle = socket_fd,
- .address = addressFromPosix(&storage),
- };
-}
diff --git a/src/RawSocketWriter.zig b/src/RawSocketWriter.zig
deleted file mode 100644
index cffe11a..0000000
--- a/src/RawSocketWriter.zig
+++ /dev/null
@@ -1,32 +0,0 @@
-const std = @import("std");
-const gcat = @import("gatorcat");
-const RawSocketWriter = @This();
-const Writer = std.Io.Writer;
-const assert = std.debug.assert;
-
-interface: Writer,
-socket: gcat.nic.RawSocket,
-
-fn drain(io_w: *std.Io.Writer, data: []const []const u8, splat: usize) Writer.Error!usize {
- const w: *RawSocketWriter = @alignCast(@fieldParentPtr("interface", io_w));
- const rem_buf = io_w.unusedCapacitySlice();
- var rem_w = Writer.fixed(rem_buf);
- const res = rem_w.writeSplat(data, splat) catch rem_buf.len;
- io_w.advance(res);
- const buffered = io_w.buffered();
- w.socket.linkLayer().send(buffered) catch return error.WriteFailed;
- _ = io_w.consumeAll();
-
- return res;
-}
-
-pub fn init(interface_name: [:0]const u8, buffer: []u8) !RawSocketWriter {
- std.debug.assert(buffer.len > 0);
- return .{
- .interface = .{
- .vtable = &.{ .drain = drain },
- .buffer = buffer,
- },
- .socket = try .init(interface_name),
- };
-}
diff --git a/src/message.zig b/src/message.zig
index eb15557..c705a0f 100644
--- a/src/message.zig
+++ b/src/message.zig
@@ -28,289 +28,365 @@ pub const MessageParseError = MessageTypeError || error{
InvalidMessage,
};
-// ZERO COPY STUFF
-// &payload could be a void value that is treated as a pointer to a [*]u8
-/// All Saprus messages
-pub const Message = packed struct {
- pub const Relay = packed struct {
- dest: @Vector(4, u8),
- payload: void,
-
- pub fn getPayload(self: *align(1) Relay) []u8 {
- const len: *u16 = @ptrFromInt(@intFromPtr(self) - @sizeOf(u16));
- return @as([*]u8, @ptrCast(&self.payload))[0 .. len.* - @bitSizeOf(Relay) / 8];
+pub const RelayMessage = struct {
+ dest: Dest,
+ payload: []const u8,
+
+ pub const @"type": PacketType = .relay;
+ pub const Dest = struct {
+ bytes: [4]u8,
+
+ /// Asserts bytes is less than or equal to 4 bytes
+ pub fn fromBytes(bytes: []const u8) Dest {
+ var buf: [4]u8 = @splat(0);
+ std.debug.assert(bytes.len <= buf.len);
+ @memcpy(buf[0..bytes.len], bytes);
+ return .{ .bytes = buf };
}
};
- pub const Connection = packed struct {
- src_port: u16, // random number > 1024
- dest_port: u16, // random number > 1024
- seq_num: u32 = 0,
- msg_id: u32 = 0,
- reserved: u8 = 0,
- options: ConnectionOptions = .{},
- payload: void,
-
- pub fn getPayload(self: *align(1) Connection) []u8 {
- const len: *u16 = @ptrFromInt(@intFromPtr(self) - @sizeOf(u16));
- return @as([*]u8, @ptrCast(&self.payload))[0 .. len.* - @bitSizeOf(Connection) / 8];
- }
-
- fn nativeFromNetworkEndian(self: *align(1) Connection) void {
- self.src_port = bigToNative(@TypeOf(self.src_port), self.src_port);
- self.dest_port = bigToNative(@TypeOf(self.dest_port), self.dest_port);
- self.seq_num = bigToNative(@TypeOf(self.seq_num), self.seq_num);
- self.msg_id = bigToNative(@TypeOf(self.msg_id), self.msg_id);
- }
-
- fn networkFromNativeEndian(self: *align(1) Connection) void {
- self.src_port = nativeToBig(@TypeOf(self.src_port), self.src_port);
- self.dest_port = nativeToBig(@TypeOf(self.dest_port), self.dest_port);
- self.seq_num = nativeToBig(@TypeOf(self.seq_num), self.seq_num);
- self.msg_id = nativeToBig(@TypeOf(self.msg_id), self.msg_id);
- }
- };
-
- const Self = @This();
- const SelfBytes = []align(1) u8;
-
- type: PacketType,
- length: u16,
- bytes: void = {},
-
- /// Takes a byte slice, and returns a Message struct backed by the slice.
- /// This properly initializes the top level headers within the slice.
- /// This is used for creating new messages. For reading messages from the network,
- /// see: networkBytesAsValue.
- pub fn init(@"type": PacketType, bytes: []u8) *align(1) Self {
- std.debug.assert(bytes.len >= @sizeOf(Self));
- const res: *align(1) Self = @ptrCast(bytes.ptr);
- res.type = @"type";
- res.length = @intCast(bytes.len - @sizeOf(Self));
- return res;
- }
-
- /// Compute the number of bytes required to store a given payload size for a given message type.
- pub fn calcSize(comptime @"type": PacketType, payload_len: usize) MessageTypeError!u16 {
- const header_size = @bitSizeOf(switch (@"type") {
- .relay => Relay,
- .connection => Connection,
- .file_transfer => return MessageTypeError.NotImplementedSaprusType,
- else => return MessageTypeError.UnknownSaprusType,
- }) / 8;
- return @intCast(payload_len + @sizeOf(Self) + header_size);
- }
-
- fn getRelay(self: *align(1) Self) *align(1) Relay {
- return std.mem.bytesAsValue(Relay, &self.bytes);
- }
- fn getConnection(self: *align(1) Self) *align(1) Connection {
- return std.mem.bytesAsValue(Connection, &self.bytes);
- }
- /// Access the message Saprus payload.
- pub fn getSaprusTypePayload(self: *align(1) Self) MessageTypeError!(union(PacketType) {
- relay: *align(1) Relay,
- file_transfer: void,
- connection: *align(1) Connection,
- }) {
- return switch (self.type) {
- .relay => .{ .relay = self.getRelay() },
- .connection => .{ .connection = self.getConnection() },
- .file_transfer => MessageTypeError.NotImplementedSaprusType,
- else => MessageTypeError.UnknownSaprusType,
- };
+ pub fn init(dest: Dest, payload: []const u8) RelayMessage {
+ return .{ .dest = dest, .payload = payload };
}
- /// Convert the message to native endianness from network endianness in-place.
- pub fn nativeFromNetworkEndian(self: *align(1) Self) MessageTypeError!void {
- self.type = @enumFromInt(bigToNative(
- @typeInfo(@TypeOf(self.type)).@"enum".tag_type,
- @intFromEnum(self.type),
- ));
- self.length = bigToNative(@TypeOf(self.length), self.length);
- errdefer {
- // If the payload specific headers fail, revert the top level header values
- self.type = @enumFromInt(nativeToBig(
- @typeInfo(@TypeOf(self.type)).@"enum".tag_type,
- @intFromEnum(self.type),
- ));
- self.length = nativeToBig(@TypeOf(self.length), self.length);
- }
- switch (try self.getSaprusTypePayload()) {
- .relay => {},
- .connection => |*con| con.*.nativeFromNetworkEndian(),
- // We know other values are unreachable,
- // because they would have returned an error from the switch condition.
- else => unreachable,
- }
+ /// Asserts that buf is large enough to fit the relay message.
+ pub fn toBytes(self: RelayMessage, buf: []u8) []u8 {
+ var out: Writer = .fixed(buf);
+ out.writeInt(u16, @intFromEnum(RelayMessage.type), .big) catch unreachable;
+ out.writeInt(u16, @intCast(self.payload.len + self.dest.bytes.len), .big) catch unreachable;
+ out.writeAll(&self.dest.bytes) catch unreachable;
+ out.writeAll(self.payload) catch unreachable;
+ return out.buffered();
}
- /// Convert the message to network endianness from native endianness in-place.
- pub fn networkFromNativeEndian(self: *align(1) Self) MessageTypeError!void {
- try switch (try self.getSaprusTypePayload()) {
- .relay => {},
- .connection => |*con| con.*.networkFromNativeEndian(),
- .file_transfer => MessageTypeError.NotImplementedSaprusType,
- else => MessageTypeError.UnknownSaprusType,
- };
- self.type = @enumFromInt(nativeToBig(
- @typeInfo(@TypeOf(self.type)).@"enum".tag_type,
- @intFromEnum(self.type),
- ));
- self.length = nativeToBig(@TypeOf(self.length), self.length);
+ pub fn fromBytes(buf: []const u8) RelayMessage {
+ var in: Reader = .fixed(buf);
}
- /// Convert network endian bytes to a native endian value in-place.
- pub fn networkBytesAsValue(bytes: SelfBytes) MessageParseError!*align(1) Self {
- const res = std.mem.bytesAsValue(Self, bytes);
- try res.nativeFromNetworkEndian();
- return .bytesAsValue(bytes);
- }
-
- /// Create a structured view of the bytes without initializing the length or type,
- /// and without converting the endianness.
- pub fn bytesAsValue(bytes: SelfBytes) MessageParseError!*align(1) Self {
- const res = std.mem.bytesAsValue(Self, bytes);
- return switch (res.type) {
- .relay, .connection => if (bytes.len == res.length + @sizeOf(Self))
- res
- else
- MessageParseError.InvalidMessage,
- .file_transfer => MessageParseError.NotImplementedSaprusType,
- else => MessageParseError.UnknownSaprusType,
+ test toBytes {
+ var buf: [1024]u8 = undefined;
+ const relay: RelayMessage = .init(
+ .fromBytes(&.{ 172, 18, 1, 30 }),
+ // zig fmt: off
+ &[_]u8{
+ 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x20, 0x65, 0x76, 0x65,
+ 0x6e, 0x74, 0x20, 0x6c, 0x6f, 0x67, 0x67, 0x65, 0x64
+ },
+ // zig fmt: on
+ );
+ // zig fmt: off
+ var expected = [_]u8{
+ 0x00, 0x3c, 0x00, 0x17, 0xac, 0x12, 0x01, 0x1e, 0x72,
+ 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x20, 0x65, 0x76, 0x65,
+ 0x6e, 0x74, 0x20, 0x6c, 0x6f, 0x67, 0x67, 0x65, 0x64
};
- }
-
- /// Deprecated.
- /// If I need the bytes, I should just pass around the slice that is backing this to begin with.
- pub fn asBytes(self: *align(1) Self) SelfBytes {
- const size = @sizeOf(Self) + self.length;
- return @as([*]align(1) u8, @ptrCast(self))[0..size];
+ // zig fmt: on
+ try std.testing.expectEqualSlices(u8, &expected, relay.toBytes(&buf));
}
};
-test "testing variable length zero copy struct" {
- {
- // Relay test
- const payload = "Hello darkness my old friend";
- var msg_bytes: [try Message.calcSize(.relay, payload.len)]u8 align(@alignOf(Message)) = undefined;
-
- // Create a view of the byte slice as a Message
- const msg: *align(1) Message = .init(.relay, &msg_bytes);
-
- {
- // Set the message values
- {
- // These are both set by the init call.
- // msg.type = .relay;
- // msg.length = payload_len;
- }
- const relay = (try msg.getSaprusTypePayload()).relay;
- relay.dest = .{ 1, 2, 3, 4 };
- @memcpy(relay.getPayload(), payload);
- }
-
- {
- // Print the message as hex using the network byte order
- try msg.networkFromNativeEndian();
- // We know the error from nativeFromNetworkEndian is unreachable because
- // it would have returned an error from networkFromNativeEndian.
- defer msg.nativeFromNetworkEndian() catch unreachable;
- std.debug.print("relay network bytes: {x}\n", .{msg_bytes});
- std.debug.print("bytes len: {d}\n", .{msg_bytes.len});
- }
-
- if (false) {
- // Illegal behavior
- std.debug.print("{any}\n", .{(try msg.getSaprusTypePayload()).connection});
- }
-
- try std.testing.expectEqualDeep(msg, try Message.bytesAsValue(msg.asBytes()));
- }
-
- {
- // Connection test
- const payload = "Hello darkness my old friend";
- var msg_bytes: [try Message.calcSize(.connection, payload.len)]u8 align(@alignOf(Message)) = undefined;
-
- // Create a view of the byte slice as a Message
- const msg: *align(1) Message = .init(.connection, &msg_bytes);
-
- {
- // Initializing connection header values
- const connection = (try msg.getSaprusTypePayload()).connection;
- connection.src_port = 1;
- connection.dest_port = 2;
- connection.seq_num = 3;
- connection.msg_id = 4;
- connection.reserved = 5;
- connection.options = @bitCast(@as(u8, 6));
- @memcpy(connection.getPayload(), payload);
- }
-
- {
- // Print the message as hex using the network byte order
- try msg.networkFromNativeEndian();
- // We know the error from nativeFromNetworkEndian is unreachable because
- // it would have returned an error from networkFromNativeEndian.
- defer msg.nativeFromNetworkEndian() catch unreachable;
- std.debug.print("connection network bytes: {x}\n", .{msg_bytes});
- std.debug.print("bytes len: {d}\n", .{msg_bytes.len});
- }
- }
-}
+// pub const ConnectionMessage = struct {
+// length: u16,
+// src_port: u16,
+// dest_port: u16,
+// seq_num: u32 = 0,
+// msg_id: u32 = 0,
+// // _reserved: u8 = 0,
+// options: ConnectionOptions = .{},
+// payload: []u8,
+
+// pub const type: PacketType = .connection;
+
+// /// Asserts that buf is large enough to fit the connection message.
+// fn toBytes(self: ConnectionMessage, buf: []u8) []u8 {
+// var out: Writer = .fixed(buf);
+// out.writeInt(u16, ConnectionMessage.type, .big) catch unreachable;
+// return w.buffered();
+// }
+// };
+
+// // ZERO COPY STUFF
+// // &payload could be a void value that is treated as a pointer to a [*]u8
+// /// All Saprus messages
+// pub const Message = packed struct {
+// pub const Relay = packed struct {
+// dest: @Vector(4, u8),
+// payload: void,
+
+// pub fn getPayload(self: *align(1) Relay) []u8 {
+// const len: *u16 = @ptrFromInt(@intFromPtr(self) - @sizeOf(u16));
+// return @as([*]u8, @ptrCast(&self.payload))[0 .. len.* - @bitSizeOf(Relay) / 8];
+// }
+// };
+// pub const Connection = packed struct {
+// src_port: u16, // random number > 1024
+// dest_port: u16, // random number > 1024
+// seq_num: u32 = 0,
+// msg_id: u32 = 0,
+// reserved: u8 = 0,
+// options: ConnectionOptions = .{},
+// payload: void,
+
+// pub fn getPayload(self: *align(1) Connection) []u8 {
+// const len: *u16 = @ptrFromInt(@intFromPtr(self) - @sizeOf(u16));
+// return @as([*]u8, @ptrCast(&self.payload))[0 .. len.* - @bitSizeOf(Connection) / 8];
+// }
+
+// fn nativeFromNetworkEndian(self: *align(1) Connection) void {
+// self.src_port = bigToNative(@TypeOf(self.src_port), self.src_port);
+// self.dest_port = bigToNative(@TypeOf(self.dest_port), self.dest_port);
+// self.seq_num = bigToNative(@TypeOf(self.seq_num), self.seq_num);
+// self.msg_id = bigToNative(@TypeOf(self.msg_id), self.msg_id);
+// }
+
+// fn networkFromNativeEndian(self: *align(1) Connection) void {
+// self.src_port = nativeToBig(@TypeOf(self.src_port), self.src_port);
+// self.dest_port = nativeToBig(@TypeOf(self.dest_port), self.dest_port);
+// self.seq_num = nativeToBig(@TypeOf(self.seq_num), self.seq_num);
+// self.msg_id = nativeToBig(@TypeOf(self.msg_id), self.msg_id);
+// }
+// };
+
+// const Self = @This();
+// const SelfBytes = []align(1) u8;
+
+// type: PacketType,
+// length: u16,
+// bytes: void = {},
+
+// /// Takes a byte slice, and returns a Message struct backed by the slice.
+// /// This properly initializes the top level headers within the slice.
+// /// This is used for creating new messages. For reading messages from the network,
+// /// see: networkBytesAsValue.
+// pub fn init(@"type": PacketType, bytes: []u8) *align(1) Self {
+// std.debug.assert(bytes.len >= @sizeOf(Self));
+// const res: *align(1) Self = @ptrCast(bytes.ptr);
+// res.type = @"type";
+// res.length = @intCast(bytes.len - @sizeOf(Self));
+// return res;
+// }
+
+// /// Compute the number of bytes required to store a given payload size for a given message type.
+// pub fn calcSize(comptime @"type": PacketType, payload_len: usize) MessageTypeError!u16 {
+// const header_size = @bitSizeOf(switch (@"type") {
+// .relay => Relay,
+// .connection => Connection,
+// .file_transfer => return MessageTypeError.NotImplementedSaprusType,
+// else => return MessageTypeError.UnknownSaprusType,
+// }) / 8;
+// return @intCast(payload_len + @sizeOf(Self) + header_size);
+// }
+
+// fn getRelay(self: *align(1) Self) *align(1) Relay {
+// return std.mem.bytesAsValue(Relay, &self.bytes);
+// }
+// fn getConnection(self: *align(1) Self) *align(1) Connection {
+// return std.mem.bytesAsValue(Connection, &self.bytes);
+// }
+
+// /// Access the message Saprus payload.
+// pub fn getSaprusTypePayload(self: *align(1) Self) MessageTypeError!(union(PacketType) {
+// relay: *align(1) Relay,
+// file_transfer: void,
+// connection: *align(1) Connection,
+// }) {
+// return switch (self.type) {
+// .relay => .{ .relay = self.getRelay() },
+// .connection => .{ .connection = self.getConnection() },
+// .file_transfer => MessageTypeError.NotImplementedSaprusType,
+// else => MessageTypeError.UnknownSaprusType,
+// };
+// }
+
+// /// Convert the message to native endianness from network endianness in-place.
+// pub fn nativeFromNetworkEndian(self: *align(1) Self) MessageTypeError!void {
+// self.type = @enumFromInt(bigToNative(
+// @typeInfo(@TypeOf(self.type)).@"enum".tag_type,
+// @intFromEnum(self.type),
+// ));
+// self.length = bigToNative(@TypeOf(self.length), self.length);
+// errdefer {
+// // If the payload specific headers fail, revert the top level header values
+// self.type = @enumFromInt(nativeToBig(
+// @typeInfo(@TypeOf(self.type)).@"enum".tag_type,
+// @intFromEnum(self.type),
+// ));
+// self.length = nativeToBig(@TypeOf(self.length), self.length);
+// }
+// switch (try self.getSaprusTypePayload()) {
+// .relay => {},
+// .connection => |*con| con.*.nativeFromNetworkEndian(),
+// // We know other values are unreachable,
+// // because they would have returned an error from the switch condition.
+// else => unreachable,
+// }
+// }
+
+// /// Convert the message to network endianness from native endianness in-place.
+// pub fn networkFromNativeEndian(self: *align(1) Self) MessageTypeError!void {
+// try switch (try self.getSaprusTypePayload()) {
+// .relay => {},
+// .connection => |*con| con.*.networkFromNativeEndian(),
+// .file_transfer => MessageTypeError.NotImplementedSaprusType,
+// else => MessageTypeError.UnknownSaprusType,
+// };
+// self.type = @enumFromInt(nativeToBig(
+// @typeInfo(@TypeOf(self.type)).@"enum".tag_type,
+// @intFromEnum(self.type),
+// ));
+// self.length = nativeToBig(@TypeOf(self.length), self.length);
+// }
+
+// /// Convert network endian bytes to a native endian value in-place.
+// pub fn networkBytesAsValue(bytes: SelfBytes) MessageParseError!*align(1) Self {
+// const res = std.mem.bytesAsValue(Self, bytes);
+// try res.nativeFromNetworkEndian();
+// return .bytesAsValue(bytes);
+// }
+
+// /// Create a structured view of the bytes without initializing the length or type,
+// /// and without converting the endianness.
+// pub fn bytesAsValue(bytes: SelfBytes) MessageParseError!*align(1) Self {
+// const res = std.mem.bytesAsValue(Self, bytes);
+// return switch (res.type) {
+// .relay, .connection => if (bytes.len == res.length + @sizeOf(Self))
+// res
+// else
+// MessageParseError.InvalidMessage,
+// .file_transfer => MessageParseError.NotImplementedSaprusType,
+// else => MessageParseError.UnknownSaprusType,
+// };
+// }
+
+// /// Deprecated.
+// /// If I need the bytes, I should just pass around the slice that is backing this to begin with.
+// pub fn asBytes(self: *align(1) Self) SelfBytes {
+// const size = @sizeOf(Self) + self.length;
+// return @as([*]align(1) u8, @ptrCast(self))[0..size];
+// }
+// };
+
+// test "testing variable length zero copy struct" {
+// {
+// // Relay test
+// const payload = "Hello darkness my old friend";
+// var msg_bytes: [try Message.calcSize(.relay, payload.len)]u8 align(@alignOf(Message)) = undefined;
+
+// // Create a view of the byte slice as a Message
+// const msg: *align(1) Message = .init(.relay, &msg_bytes);
+
+// {
+// // Set the message values
+// {
+// // These are both set by the init call.
+// // msg.type = .relay;
+// // msg.length = payload_len;
+// }
+// const relay = (try msg.getSaprusTypePayload()).relay;
+// relay.dest = .{ 1, 2, 3, 4 };
+// @memcpy(relay.getPayload(), payload);
+// }
+
+// {
+// // Print the message as hex using the network byte order
+// try msg.networkFromNativeEndian();
+// // We know the error from nativeFromNetworkEndian is unreachable because
+// // it would have returned an error from networkFromNativeEndian.
+// defer msg.nativeFromNetworkEndian() catch unreachable;
+// std.debug.print("relay network bytes: {x}\n", .{msg_bytes});
+// std.debug.print("bytes len: {d}\n", .{msg_bytes.len});
+// }
+
+// if (false) {
+// // Illegal behavior
+// std.debug.print("{any}\n", .{(try msg.getSaprusTypePayload()).connection});
+// }
+
+// try std.testing.expectEqualDeep(msg, try Message.bytesAsValue(msg.asBytes()));
+// }
+
+// {
+// // Connection test
+// const payload = "Hello darkness my old friend";
+// var msg_bytes: [try Message.calcSize(.connection, payload.len)]u8 align(@alignOf(Message)) = undefined;
+
+// // Create a view of the byte slice as a Message
+// const msg: *align(1) Message = .init(.connection, &msg_bytes);
+
+// {
+// // Initializing connection header values
+// const connection = (try msg.getSaprusTypePayload()).connection;
+// connection.src_port = 1;
+// connection.dest_port = 2;
+// connection.seq_num = 3;
+// connection.msg_id = 4;
+// connection.reserved = 5;
+// connection.options = @bitCast(@as(u8, 6));
+// @memcpy(connection.getPayload(), payload);
+// }
+
+// {
+// // Print the message as hex using the network byte order
+// try msg.networkFromNativeEndian();
+// // We know the error from nativeFromNetworkEndian is unreachable because
+// // it would have returned an error from networkFromNativeEndian.
+// defer msg.nativeFromNetworkEndian() catch unreachable;
+// std.debug.print("connection network bytes: {x}\n", .{msg_bytes});
+// std.debug.print("bytes len: {d}\n", .{msg_bytes.len});
+// }
+// }
+// }
const std = @import("std");
const Allocator = std.mem.Allocator;
-
-const asBytes = std.mem.asBytes;
-const nativeToBig = std.mem.nativeToBig;
-const bigToNative = std.mem.bigToNative;
-
-test "Round trip Relay toBytes and fromBytes" {
- if (false) {
- const gpa = std.testing.allocator;
- const msg = Message{
- .relay = .{
- .header = .{ .dest = .{ 255, 255, 255, 255 } },
- .payload = "Hello darkness my old friend",
- },
- };
-
- const to_bytes = try msg.toBytes(gpa);
- defer gpa.free(to_bytes);
-
- const from_bytes = try Message.fromBytes(to_bytes, gpa);
- defer from_bytes.deinit(gpa);
-
- try std.testing.expectEqualDeep(msg, from_bytes);
- }
- return error.SkipZigTest;
-}
-
-test "Round trip Connection toBytes and fromBytes" {
- if (false) {
- const gpa = std.testing.allocator;
- const msg = Message{
- .connection = .{
- .header = .{
- .src_port = 0,
- .dest_port = 0,
- },
- .payload = "Hello darkness my old friend",
- },
- };
-
- const to_bytes = try msg.toBytes(gpa);
- defer gpa.free(to_bytes);
-
- const from_bytes = try Message.fromBytes(to_bytes, gpa);
- defer from_bytes.deinit(gpa);
-
- try std.testing.expectEqualDeep(msg, from_bytes);
- }
- return error.SkipZigTest;
-}
+const Writer = std.Io.Writer;
+
+// const asBytes = std.mem.asBytes;
+// const nativeToBig = std.mem.nativeToBig;
+// const bigToNative = std.mem.bigToNative;
+
+// test "Round trip Relay toBytes and fromBytes" {
+// if (true) return error.SkipZigTest;
+// const gpa = std.testing.allocator;
+// const msg = Message{
+// .relay = .{
+// .header = .{ .dest = .{ 255, 255, 255, 255 } },
+// .payload = "Hello darkness my old friend",
+// },
+// };
+
+// const to_bytes = try msg.toBytes(gpa);
+// defer gpa.free(to_bytes);
+
+// const from_bytes = try Message.fromBytes(to_bytes, gpa);
+// defer from_bytes.deinit(gpa);
+
+// try std.testing.expectEqualDeep(msg, from_bytes);
+// }
+
+// test "Round trip Connection toBytes and fromBytes" {
+// if (false) {
+// const gpa = std.testing.allocator;
+// const msg = Message{
+// .connection = .{
+// .header = .{
+// .src_port = 0,
+// .dest_port = 0,
+// },
+// .payload = "Hello darkness my old friend",
+// },
+// };
+
+// const to_bytes = try msg.toBytes(gpa);
+// defer gpa.free(to_bytes);
+
+// const from_bytes = try Message.fromBytes(to_bytes, gpa);
+// defer from_bytes.deinit(gpa);
+
+// try std.testing.expectEqualDeep(msg, from_bytes);
+// }
+// return error.SkipZigTest;
+// }
test {
std.testing.refAllDeclsRecursive(@This());
diff --git a/src/root.zig b/src/root.zig
index ed3648a..5aa9d9d 100644
--- a/src/root.zig
+++ b/src/root.zig
@@ -1,7 +1,5 @@
pub const Client = @import("Client.zig");
pub const Connection = @import("Connection.zig");
-pub const RawSocketWriter = @import("RawSocketWriter.zig");
-// pub const NetWriter = @import("NetWriter.zig");
const msg = @import("message.zig");