diff options
| -rw-r--r-- | src/main.zig | 24 | 
1 files changed, 24 insertions, 0 deletions
diff --git a/src/main.zig b/src/main.zig index e0674d4..9a0b8a4 100644 --- a/src/main.zig +++ b/src/main.zig @@ -2,6 +2,8 @@ const is_debug = builtin.mode == .Debug;  const base64Enc = std.base64.Base64Encoder.init(std.base64.standard_alphabet_chars, '=');  const base64Dec = std.base64.Base64Decoder.init(std.base64.standard_alphabet_chars, '='); +/// Type tag for SaprusMessage union. +/// This is the first value in the actual packet sent over the network.  const SaprusPacketType = enum(u16) {      relay = 0x003C,      file_transfer = 0x8888, @@ -9,6 +11,8 @@ const SaprusPacketType = enum(u16) {      _,  }; +/// Reserved option values. +/// Currently unused.  const SaprusConnectionOptions = packed struct(u8) {      opt1: bool = false,      opt2: bool = false, @@ -25,6 +29,7 @@ const SaprusError = error{      UnknownSaprusType,  }; +/// All Saprus messages  const SaprusMessage = union(SaprusPacketType) {      const Relay = struct {          const Header = packed struct { @@ -49,6 +54,7 @@ const SaprusMessage = union(SaprusPacketType) {      file_transfer: void, // unimplemented      connection: Connection, +    /// Should be called for any SaprusMessage that was declared using a function that you pass an allocator to.      fn deinit(self: SaprusMessage, allocator: Allocator) void {          switch (self) {              .relay => |r| allocator.free(r.payload), @@ -64,27 +70,39 @@ const SaprusMessage = union(SaprusPacketType) {          w: std.ArrayList(u8).Writer,          allocator: Allocator,      ) !void { +        // Create a growable string to store the base64 bytes in. +        // Doing this first so I can use the length of the encoded bytes for the length field.          var payload_list = std.ArrayList(u8).init(allocator);          defer payload_list.deinit();          const buf_w = payload_list.writer(); + +        // Write the payload bytes as base64 to the growable string.          try base64Enc.encodeWriter(buf_w, payload); +        // Write the packet body to the output writer.          try w.writeStructEndian(header, .big);          try w.writeInt(u16, @intCast(payload_list.items.len), .big);          try w.writeAll(payload_list.items);      } +    /// Caller is responsible for freeing the returned bytes.      fn toBytes(self: SaprusMessage, allocator: Allocator) ![]u8 { +        // Create a growable list of bytes to store the output in.          var buf = std.ArrayList(u8).init(allocator); +        // Create a writer for an easy interface to append arbitrary bytes.          const w = buf.writer(); + +        // Start with writing the message type, which is the first 16 bits of every Saprus message.          try w.writeInt(u16, @intFromEnum(self), .big); +        // Write the proper header and payload for the given packet type.          switch (self) {              .relay => |r| try toBytesAux(Relay.Header, r.header, r.payload, w, allocator),              .connection => |c| try toBytesAux(Connection.Header, c.header, c.payload, w, allocator),              .file_transfer => return SaprusError.NotImplementedSaprusType,          } +        // Collect the growable list as a slice and return it.          return buf.toOwnedSlice();      } @@ -94,22 +112,28 @@ const SaprusMessage = union(SaprusPacketType) {          r: std.io.FixedBufferStream([]const u8).Reader,          allocator: Allocator,      ) !SaprusMessage { +        // Read the header for the current message type.          const header = try r.readStructEndian(Header, .big); +        // Read the length of the base64 encoded payload.          const len = try r.readInt(u16, .big); +        // Read the base64 bytes into a list to be able to call the decoder on it.          var payload_buf = std.ArrayList(u8).init(allocator);          defer payload_buf.deinit();          try r.readAllArrayList(&payload_buf, len); +        // Create a buffer to store the payload in, and decode the base64 bytes into the payload field.          const payload = try allocator.alloc(u8, try base64Dec.calcSizeForSlice(payload_buf.items));          try base64Dec.decode(payload, payload_buf.items); +        // Return the type of SaprusMessage specified by the `packet` argument.          return @unionInit(SaprusMessage, @tagName(packet), .{              .header = header,              .payload = payload,          });      } +    /// Caller is responsible for calling .deinit on the returned value.      fn fromBytes(bytes: []const u8, allocator: Allocator) !SaprusMessage {          var s = std.io.fixedBufferStream(bytes);          const r = s.reader();  | 
