summaryrefslogtreecommitdiff
path: root/src/message.zig
diff options
context:
space:
mode:
Diffstat (limited to 'src/message.zig')
-rw-r--r--src/message.zig610
1 files changed, 343 insertions, 267 deletions
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());