Initial library structure, build system and packet implementation
This commit is contained in:
parent
f02cd37a40
commit
dad6a6819c
5 changed files with 381 additions and 0 deletions
324
src/packet.zig
Normal file
324
src/packet.zig
Normal file
|
|
@ -0,0 +1,324 @@
|
|||
///////////////// Imports
|
||||
//
|
||||
|
||||
const std = @import("std");
|
||||
|
||||
///////////////// Constants
|
||||
//
|
||||
|
||||
const expect = std.testing.expect;
|
||||
const memeql = std.mem.eql;
|
||||
const asBytes = std.mem.asBytes;
|
||||
|
||||
const MAX_DATA_SIZE = 465;
|
||||
const ADDRESS_SIZE = 16;
|
||||
|
||||
///////////////// Enums
|
||||
//
|
||||
|
||||
const IfacFlag = enum(u1) {
|
||||
open = 0, // Packet for publically accessible interface
|
||||
authenticated = 1, // Interface authentication is included in packet
|
||||
};
|
||||
const HeaderType = enum(u1) {
|
||||
type1 = 0, // One address field
|
||||
type2 = 1, // Two address fields
|
||||
};
|
||||
const ContextFlag = enum(u1) { // Meaning depends on packet context
|
||||
unset = 0,
|
||||
set = 1,
|
||||
};
|
||||
const PropagationType = enum(u1) { broadcast = 0, transport = 1 };
|
||||
|
||||
const DestinationType = enum(u2) { //
|
||||
single = 0b0,
|
||||
group = 0b01,
|
||||
plain = 0b10,
|
||||
link = 0b11,
|
||||
};
|
||||
|
||||
const PacketType = enum(u2) { //
|
||||
data = 0b0,
|
||||
announce = 0b01,
|
||||
link_request = 0b10,
|
||||
proof = 0b11,
|
||||
};
|
||||
|
||||
const PacketContext = enum(u8) {
|
||||
none = 0x00, // Generic data packet
|
||||
resource = 0x01, // Packet is part of a resource
|
||||
resource_adv = 0x02, // Packet is a resource advertisement
|
||||
resource_req = 0x03, // Packet is a resource part request
|
||||
resource_hmu = 0x04, // Packet is a resource hashmap update
|
||||
resource_prf = 0x05, // Packet is a resource proof
|
||||
resource_icl = 0x06, // Packet is a resource initiator cancel message
|
||||
resource_rcl = 0x07, // Packet is a resource receiver cancel message
|
||||
cache_request = 0x08, // Packet is a cache request
|
||||
request = 0x09, // Packet is a request
|
||||
response = 0x0A, // Packet is a response to a request
|
||||
path_response = 0x0B, // Packet is a response to a path request
|
||||
command = 0x0C, // Packet is a command
|
||||
command_status = 0x0D, // Packet is a status of an executed command
|
||||
channel = 0x0E, // Packet contains link channel data
|
||||
keepalive = 0xFA, // Packet is a keepalive packet
|
||||
linkidentify = 0xFB, // Packet is a link peer identification proof
|
||||
linkclose = 0xFC, // Packet is a link close message
|
||||
linkproof = 0xFD, // Packet is a link packet proof
|
||||
lrrtt = 0xFE, // Packet is a link request round-trip time measurement
|
||||
lrproof = 0xFF, // Packet is a link request proof
|
||||
};
|
||||
|
||||
///////////////// Structs
|
||||
//
|
||||
|
||||
const PacketHeader = packed struct {
|
||||
ifac: IfacFlag,
|
||||
header: HeaderType,
|
||||
context: ContextFlag,
|
||||
propagation: PropagationType,
|
||||
destination: DestinationType,
|
||||
packet: PacketType,
|
||||
hops: u8, //
|
||||
};
|
||||
|
||||
const Packet = struct {
|
||||
header: PacketHeader,
|
||||
address1: [ADDRESS_SIZE]u8,
|
||||
address2: [ADDRESS_SIZE]u8,
|
||||
context: PacketContext,
|
||||
data: []const u8, //
|
||||
};
|
||||
|
||||
///////////////// Functions
|
||||
//
|
||||
|
||||
pub fn serializePacket(packet: Packet, output_buffer: []u8) !usize {
|
||||
const has_two_addresses = packet.header.header == HeaderType.type2;
|
||||
|
||||
// Compute size of final packet (in bytes)
|
||||
var target_size: u16 = @sizeOf(Packet);
|
||||
if (!has_two_addresses) {
|
||||
target_size -= packet.address2.len;
|
||||
}
|
||||
// TODO check data len
|
||||
|
||||
if (output_buffer.len < target_size) {
|
||||
return error.BufferTooShort;
|
||||
}
|
||||
|
||||
// Write buffer
|
||||
var offset: usize = 0;
|
||||
|
||||
@memcpy(output_buffer[offset .. offset + @sizeOf(PacketHeader)], asBytes(&packet.header));
|
||||
offset += @sizeOf(PacketHeader);
|
||||
|
||||
@memcpy(output_buffer[offset .. offset + ADDRESS_SIZE], asBytes(&packet.address1));
|
||||
offset += ADDRESS_SIZE;
|
||||
if (has_two_addresses) {
|
||||
@memcpy(output_buffer[offset .. offset + ADDRESS_SIZE], asBytes(&packet.address2));
|
||||
offset += ADDRESS_SIZE;
|
||||
}
|
||||
|
||||
@memcpy(output_buffer[offset .. offset + @sizeOf(PacketContext)], asBytes(&packet.context));
|
||||
offset += @sizeOf(PacketContext);
|
||||
|
||||
@memcpy(output_buffer[offset .. offset + packet.data.len], packet.data);
|
||||
offset += packet.data.len;
|
||||
|
||||
if (target_size != offset) {
|
||||
return error.InternalError;
|
||||
}
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
||||
fn copyToPacket(dst: anytype, src: []const u8, offset: usize, count: usize) !usize {
|
||||
if (src.len > offset + count) {
|
||||
return error.BufferTooShort;
|
||||
}
|
||||
@memcpy(@as(*u8, &dst), src[offset .. offset + count]);
|
||||
|
||||
return offset + count;
|
||||
}
|
||||
|
||||
// Reads the message buffer and builds the corresponding packet struct inside dest_packet
|
||||
pub fn deserializePacket(message_buffer: []u8, dest_packet: *Packet) !void {
|
||||
var offset: usize = 0;
|
||||
|
||||
offset = try copyToPacket(&dest_packet.header, message_buffer, offset, @sizeOf(PacketHeader));
|
||||
offset = try copyToPacket(&dest_packet.address1, message_buffer, offset, ADDRESS_SIZE);
|
||||
const has_two_addresses = dest_packet.header.header == HeaderType.type2;
|
||||
if (has_two_addresses) {
|
||||
offset = try copyToPacket(&dest_packet.address2, message_buffer, offset, ADDRESS_SIZE);
|
||||
}
|
||||
offset = try copyToPacket(&dest_packet.context, message_buffer, offset, @sizeOf(PacketContext));
|
||||
// TODO compute data length
|
||||
const data_len = 0;
|
||||
offset = try copyToPacket(&dest_packet.data, message_buffer, offset, data_len);
|
||||
}
|
||||
|
||||
///////////////// Tests
|
||||
//
|
||||
|
||||
// Serializes the given packet and makes sure the output is correct
|
||||
fn testPacketSerialization(packet: Packet) !void {
|
||||
const buf_size = comptime @sizeOf(Packet) + MAX_DATA_SIZE;
|
||||
var buf: [buf_size]u8 = undefined;
|
||||
const res: usize = try serializePacket(packet, &buf);
|
||||
var expected_res: usize = @sizeOf(PacketHeader) + packet.address1.len + @sizeOf(PacketContext) + packet.data.len;
|
||||
const has_second_address = packet.header.header == HeaderType.type2;
|
||||
if (has_second_address) {
|
||||
expected_res += packet.address2.len;
|
||||
}
|
||||
|
||||
try expect(res == expected_res);
|
||||
|
||||
var offset: usize = 0;
|
||||
|
||||
try expect(memeql(u8, buf[offset .. offset + @sizeOf(PacketHeader)], asBytes(&packet.header)));
|
||||
offset += @sizeOf(PacketHeader);
|
||||
|
||||
try expect(memeql(u8, buf[offset .. offset + packet.address1.len], asBytes(&packet.address1)));
|
||||
offset += packet.address1.len;
|
||||
|
||||
try expect(memeql(u8, buf[offset .. offset + @sizeOf(PacketContext)], asBytes(&packet.context)));
|
||||
offset += @sizeOf(PacketContext);
|
||||
|
||||
try expect(memeql(u8, buf[offset .. offset + packet.data.len], packet.data));
|
||||
offset += packet.data.len;
|
||||
}
|
||||
|
||||
test "Structs size" {
|
||||
try expect(@sizeOf(PacketHeader) == 2);
|
||||
}
|
||||
|
||||
test "Basic serialization: header type 1, max data size" {
|
||||
const header: PacketHeader = .{
|
||||
.ifac = IfacFlag.open,
|
||||
.header = HeaderType.type1,
|
||||
.context = ContextFlag.set,
|
||||
.propagation = PropagationType.transport,
|
||||
.destination = DestinationType.single,
|
||||
.packet = PacketType.announce,
|
||||
.hops = 0,
|
||||
};
|
||||
|
||||
const data_size = MAX_DATA_SIZE;
|
||||
const data: [data_size]u8 = undefined;
|
||||
|
||||
const packet: Packet = .{ //
|
||||
.header = header,
|
||||
.address1 = undefined,
|
||||
.address2 = undefined,
|
||||
.context = PacketContext.none,
|
||||
.data = &data,
|
||||
};
|
||||
|
||||
try testPacketSerialization(packet);
|
||||
}
|
||||
|
||||
test "Basic serialization: header type 2, max data size" {
|
||||
const header: PacketHeader = .{
|
||||
.ifac = IfacFlag.open,
|
||||
.header = HeaderType.type2,
|
||||
.context = ContextFlag.set,
|
||||
.propagation = PropagationType.transport,
|
||||
.destination = DestinationType.single,
|
||||
.packet = PacketType.announce,
|
||||
.hops = 0,
|
||||
};
|
||||
|
||||
const data_size = MAX_DATA_SIZE;
|
||||
const data: [data_size]u8 = undefined;
|
||||
|
||||
const packet: Packet = .{ //
|
||||
.header = header,
|
||||
.address1 = undefined,
|
||||
.address2 = undefined,
|
||||
.context = PacketContext.none,
|
||||
.data = &data,
|
||||
};
|
||||
|
||||
try testPacketSerialization(packet);
|
||||
}
|
||||
|
||||
test "Basic serialization: header type 1, medium data size" {
|
||||
const header: PacketHeader = .{
|
||||
.ifac = IfacFlag.open,
|
||||
.header = HeaderType.type1,
|
||||
.context = ContextFlag.set,
|
||||
.propagation = PropagationType.transport,
|
||||
.destination = DestinationType.single,
|
||||
.packet = PacketType.announce,
|
||||
.hops = 0,
|
||||
};
|
||||
|
||||
const data_size = MAX_DATA_SIZE / 2 + 3;
|
||||
const data: [data_size]u8 = undefined;
|
||||
|
||||
const packet: Packet = .{ //
|
||||
.header = header,
|
||||
.address1 = undefined,
|
||||
.address2 = undefined,
|
||||
.context = PacketContext.none,
|
||||
.data = &data,
|
||||
};
|
||||
|
||||
try testPacketSerialization(packet);
|
||||
}
|
||||
|
||||
test "Basic serialization: header type 2, medium data size" {
|
||||
const header: PacketHeader = .{
|
||||
.ifac = IfacFlag.open,
|
||||
.header = HeaderType.type2,
|
||||
.context = ContextFlag.set,
|
||||
.propagation = PropagationType.transport,
|
||||
.destination = DestinationType.single,
|
||||
.packet = PacketType.announce,
|
||||
.hops = 0,
|
||||
};
|
||||
|
||||
const data_size = MAX_DATA_SIZE / 2 + 3;
|
||||
const data: [data_size]u8 = undefined;
|
||||
|
||||
const packet: Packet = .{ //
|
||||
.header = header,
|
||||
.address1 = undefined,
|
||||
.address2 = undefined,
|
||||
.context = PacketContext.none,
|
||||
.data = &data,
|
||||
};
|
||||
|
||||
try testPacketSerialization(packet);
|
||||
}
|
||||
|
||||
test "Serialize / Deserialize Packet: Header type2, Medium data size" {
|
||||
const header: PacketHeader = .{
|
||||
.ifac = IfacFlag.open,
|
||||
.header = HeaderType.type2,
|
||||
.context = ContextFlag.set,
|
||||
.propagation = PropagationType.transport,
|
||||
.destination = DestinationType.single,
|
||||
.packet = PacketType.announce,
|
||||
.hops = 0,
|
||||
};
|
||||
|
||||
const data_size = MAX_DATA_SIZE / 2 + 3;
|
||||
const data: [data_size]u8 = undefined;
|
||||
|
||||
const packet: Packet = .{ //
|
||||
.header = header,
|
||||
.address1 = undefined,
|
||||
.address2 = undefined,
|
||||
.context = PacketContext.none,
|
||||
.data = &data,
|
||||
};
|
||||
|
||||
const buf_size = comptime @sizeOf(Packet) + MAX_DATA_SIZE;
|
||||
var buf: [buf_size]u8 = undefined;
|
||||
var res_packet: Packet = undefined;
|
||||
_ = try serializePacket(packet, &buf);
|
||||
try deserializePacket(&buf, &res_packet);
|
||||
|
||||
try expect(res_packet == packet);
|
||||
}
|
||||
1
src/root.zig
Normal file
1
src/root.zig
Normal file
|
|
@ -0,0 +1 @@
|
|||
const packet = @import("packet.zig");
|
||||
Loading…
Add table
Add a link
Reference in a new issue