2022-12-23 01:53:07 +01:00
|
|
|
const std = @import("std");
|
|
|
|
const testing = std.testing;
|
|
|
|
const fmt = std.fmt;
|
|
|
|
|
2023-01-07 16:46:39 +01:00
|
|
|
const net = std.net;
|
2022-12-23 01:53:07 +01:00
|
|
|
|
2023-01-07 16:46:39 +01:00
|
|
|
const ipc = @import("./main.zig");
|
|
|
|
const Message = ipc.Message;
|
|
|
|
const CBEventType = ipc.CBEvent.Type;
|
|
|
|
|
|
|
|
const Allocator = std.mem.Allocator;
|
2022-12-23 01:53:07 +01:00
|
|
|
|
2023-01-07 16:46:39 +01:00
|
|
|
const print_eq = @import("./util.zig").print_eq;
|
|
|
|
const print = std.debug.print;
|
2022-12-23 01:53:07 +01:00
|
|
|
|
2023-01-07 16:46:39 +01:00
|
|
|
const Event = ipc.Event;
|
2022-12-23 01:53:07 +01:00
|
|
|
|
2023-01-08 20:06:22 +01:00
|
|
|
/// SwitchDB
|
2023-01-08 20:45:35 +01:00
|
|
|
/// Functions read and write: handle
|
2023-01-08 20:06:22 +01:00
|
|
|
|
|
|
|
pub const SwitchDB = struct {
|
2022-12-23 01:53:07 +01:00
|
|
|
const Self = @This();
|
|
|
|
|
2023-01-07 19:04:05 +01:00
|
|
|
db: std.AutoArrayHashMap(i32, ManagedConnection),
|
2023-01-07 16:46:39 +01:00
|
|
|
|
2023-01-07 19:04:05 +01:00
|
|
|
pub fn init (allocator: Allocator) Self {
|
2022-12-23 01:53:07 +01:00
|
|
|
return Self {
|
2023-01-07 19:04:05 +01:00
|
|
|
.db = std.AutoArrayHashMap(i32, ManagedConnection).init(allocator),
|
2022-12-23 01:53:07 +01:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2023-01-07 19:04:05 +01:00
|
|
|
pub fn deinit (self: *Self) void {
|
2023-01-08 12:46:21 +01:00
|
|
|
self.db.deinit();
|
2022-12-23 01:53:07 +01:00
|
|
|
}
|
2023-01-07 16:46:39 +01:00
|
|
|
|
2023-01-08 12:46:21 +01:00
|
|
|
pub fn format(self: Self, comptime _: []const u8, _: fmt.FormatOptions, out_stream: anytype) !void {
|
|
|
|
for(self.db.keys()) |k,i| {
|
|
|
|
try fmt.format(out_stream, "({},{})", .{k, self.db.values()[i].dest});
|
|
|
|
}
|
|
|
|
}
|
2023-01-07 19:04:05 +01:00
|
|
|
|
2023-01-11 15:05:16 +01:00
|
|
|
pub fn add_switch(self: *Self, fd1: i32, fd2: i32) !void {
|
|
|
|
try self.db.put(fd1, ManagedConnection {.dest = fd2});
|
|
|
|
try self.db.put(fd2, ManagedConnection {.dest = fd1});
|
|
|
|
}
|
|
|
|
|
2023-01-10 17:09:34 +01:00
|
|
|
pub fn set_callbacks(self: *Self, fd: i32
|
|
|
|
, in : *const fn (origin: i32, m: *Message) CBEventType
|
|
|
|
, out : *const fn (origin: i32, m: *const Message) CBEventType) !void {
|
|
|
|
|
|
|
|
var managedconnection = self.db.get(fd) orelse return error.unregisteredFD;
|
|
|
|
managedconnection.in = in;
|
|
|
|
managedconnection.out = out;
|
|
|
|
}
|
|
|
|
|
2023-01-08 20:45:35 +01:00
|
|
|
/// Dig the "db" hashmap, perform "in" fn, may provide a message.
|
|
|
|
/// Errors from the "in" fn are reported as Zig errors.
|
2023-01-08 12:46:21 +01:00
|
|
|
pub fn read (self: *Self, fd: i32) !?Message {
|
2023-01-08 20:45:35 +01:00
|
|
|
// assert there is an entry with this fd as a key.
|
|
|
|
var managedconnection = self.db.get(fd) orelse return error.unregisteredFD;
|
2023-01-07 19:04:05 +01:00
|
|
|
var message: Message = undefined;
|
|
|
|
|
2023-01-08 20:45:35 +01:00
|
|
|
var r: CBEventType = managedconnection.in(fd, &message);
|
2023-01-07 19:04:05 +01:00
|
|
|
|
|
|
|
switch (r) {
|
|
|
|
// The message should be ignored (protocol specific).
|
2023-01-08 20:06:22 +01:00
|
|
|
CBEventType.IGNORE => { return null; },
|
2023-01-07 19:04:05 +01:00
|
|
|
// No error. A message was generated.
|
2023-01-08 20:06:22 +01:00
|
|
|
CBEventType.NO_ERROR => {
|
2023-01-08 20:45:35 +01:00
|
|
|
message.fd = managedconnection.dest;
|
2023-01-07 19:04:05 +01:00
|
|
|
return message;
|
|
|
|
},
|
2023-01-08 20:06:22 +01:00
|
|
|
CBEventType.FD_CLOSING => { return error.closeFD; },
|
2023-01-07 19:04:05 +01:00
|
|
|
// Generic error, or the message was read but with errors.
|
2023-01-08 20:06:22 +01:00
|
|
|
CBEventType.ERROR => {
|
2023-01-07 19:04:05 +01:00
|
|
|
return error.generic;
|
|
|
|
},
|
2023-01-08 12:46:21 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
unreachable;
|
2023-01-07 19:04:05 +01:00
|
|
|
}
|
2023-01-07 16:46:39 +01:00
|
|
|
|
2023-01-08 20:45:35 +01:00
|
|
|
/// Dig the "db" hashmap and perform "out" fn.
|
|
|
|
/// Errors from the "out" fn are reported as Zig errors.
|
2023-01-08 12:46:21 +01:00
|
|
|
pub fn write (self: *Self, message: Message) !void {
|
2023-01-08 20:45:35 +01:00
|
|
|
// assert there is an entry with this fd as a key.
|
|
|
|
var managedconnection = self.db.get(message.fd) orelse return error.unregisteredFD;
|
|
|
|
var r = managedconnection.out(managedconnection.dest, &message);
|
2023-01-07 19:04:05 +01:00
|
|
|
|
|
|
|
switch (r) {
|
|
|
|
// The message should be ignored (protocol specific).
|
|
|
|
// No error. A message was generated.
|
2023-01-08 20:06:22 +01:00
|
|
|
CBEventType.NO_ERROR => {
|
2023-01-07 19:04:05 +01:00
|
|
|
return;
|
|
|
|
},
|
2023-01-08 20:06:22 +01:00
|
|
|
CBEventType.FD_CLOSING => { return error.closeFD; },
|
2023-01-07 19:04:05 +01:00
|
|
|
// Generic error, or the message was read but with errors.
|
2023-01-08 20:06:22 +01:00
|
|
|
CBEventType.IGNORE,
|
|
|
|
CBEventType.ERROR => {
|
2023-01-07 19:04:05 +01:00
|
|
|
return error.generic;
|
|
|
|
},
|
2023-01-08 12:46:21 +01:00
|
|
|
}
|
2023-01-07 19:04:05 +01:00
|
|
|
|
|
|
|
unreachable;
|
|
|
|
}
|
2023-01-08 12:46:21 +01:00
|
|
|
|
2023-01-08 20:06:22 +01:00
|
|
|
pub fn handle_event_read (self: *Self, index: usize, fd: i32) Event {
|
2023-01-08 12:46:21 +01:00
|
|
|
var message: ?Message = null;
|
|
|
|
message = self.read (fd) catch |err| switch(err) {
|
|
|
|
error.closeFD => {
|
2023-01-08 20:06:22 +01:00
|
|
|
return Event.init(Event.Type.DISCONNECTION, index, fd, null);
|
2023-01-08 12:46:21 +01:00
|
|
|
},
|
2023-01-08 20:45:35 +01:00
|
|
|
error.unregisteredFD,
|
2023-01-08 12:46:21 +01:00
|
|
|
error.generic => {
|
2023-01-08 20:06:22 +01:00
|
|
|
return Event.init(Event.Type.ERROR, index, fd, null);
|
2023-01-08 12:46:21 +01:00
|
|
|
},
|
|
|
|
};
|
2023-01-08 20:06:22 +01:00
|
|
|
return Event.init(Event.Type.SWITCH_RX, index, fd, message);
|
2023-01-08 12:46:21 +01:00
|
|
|
}
|
|
|
|
|
2023-01-10 17:09:34 +01:00
|
|
|
/// Message is free'd in any case.
|
2023-01-08 20:06:22 +01:00
|
|
|
pub fn handle_event_write (self: *Self, index: usize, message: Message) Event {
|
2023-01-08 12:46:21 +01:00
|
|
|
defer message.deinit();
|
|
|
|
var fd = message.fd;
|
|
|
|
self.write(message) catch |err| switch(err) {
|
|
|
|
error.closeFD => {
|
2023-01-08 20:06:22 +01:00
|
|
|
return Event.init(Event.Type.DISCONNECTION, index, fd, null);
|
2023-01-08 12:46:21 +01:00
|
|
|
},
|
2023-01-08 20:45:35 +01:00
|
|
|
error.unregisteredFD,
|
2023-01-08 12:46:21 +01:00
|
|
|
error.generic => {
|
2023-01-08 20:06:22 +01:00
|
|
|
return Event.init(Event.Type.ERROR, index, fd, null);
|
2023-01-08 12:46:21 +01:00
|
|
|
},
|
|
|
|
};
|
2023-01-08 20:06:22 +01:00
|
|
|
return Event.init(Event.Type.SWITCH_TX, index, fd, null);
|
2023-01-08 12:46:21 +01:00
|
|
|
}
|
2023-01-08 20:45:35 +01:00
|
|
|
|
2023-01-10 17:09:34 +01:00
|
|
|
/// Simple wrapper around self.db.get.
|
|
|
|
pub fn getDest (self: *Self, fd: i32) !i32 {
|
|
|
|
return self.db.get(fd).?.dest;
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn nuke (self: *Self, fd: i32) void {
|
2023-01-08 20:45:35 +01:00
|
|
|
if (self.db.fetchSwapRemove(fd)) |kv| {
|
|
|
|
_ = self.db.swapRemove(kv.value.dest);
|
|
|
|
}
|
|
|
|
}
|
2022-12-23 01:53:07 +01:00
|
|
|
};
|
|
|
|
|
2023-01-08 20:06:22 +01:00
|
|
|
const ManagedConnection = struct {
|
2023-01-07 16:46:39 +01:00
|
|
|
dest : i32,
|
|
|
|
in : *const fn (origin: i32, m: *Message) CBEventType = default_in,
|
2023-01-08 20:06:22 +01:00
|
|
|
out : *const fn (origin: i32, m: *const Message) CBEventType = default_out,
|
2023-01-07 16:46:39 +01:00
|
|
|
};
|
2022-12-23 01:53:07 +01:00
|
|
|
|
2023-01-07 19:04:05 +01:00
|
|
|
test "creation and display" {
|
|
|
|
const config = .{.safety = true};
|
|
|
|
var gpa = std.heap.GeneralPurposeAllocator(config){};
|
|
|
|
defer _ = gpa.deinit();
|
|
|
|
const allocator = gpa.allocator();
|
|
|
|
|
2023-01-08 20:06:22 +01:00
|
|
|
var switchdb = SwitchDB.init(allocator);
|
2023-01-07 19:04:05 +01:00
|
|
|
defer switchdb.deinit();
|
|
|
|
|
2023-01-08 12:46:21 +01:00
|
|
|
try switchdb.db.put(5, ManagedConnection {.dest = 6});
|
|
|
|
try switchdb.db.put(6, ManagedConnection {.dest = 5});
|
|
|
|
|
|
|
|
try print_eq("{ (5,6)(6,5) }", .{switchdb});
|
2023-01-07 19:04:05 +01:00
|
|
|
}
|
|
|
|
|
2023-01-08 20:06:22 +01:00
|
|
|
fn successful_in (_: i32, m: *Message) CBEventType {
|
|
|
|
m.* = Message.init(8, std.heap.c_allocator, "coucou") catch {
|
|
|
|
return CBEventType.ERROR;
|
|
|
|
};
|
|
|
|
return CBEventType.NO_ERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
fn successful_out (_: i32, _: *const Message) CBEventType {
|
|
|
|
return CBEventType.NO_ERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
test "successful exchanges" {
|
|
|
|
const config = .{.safety = true};
|
|
|
|
var gpa = std.heap.GeneralPurposeAllocator(config){};
|
|
|
|
defer _ = gpa.deinit();
|
|
|
|
const allocator = gpa.allocator();
|
|
|
|
|
|
|
|
var switchdb = SwitchDB.init(allocator);
|
|
|
|
defer switchdb.deinit();
|
|
|
|
|
|
|
|
try switchdb.db.put(5, ManagedConnection {.dest = 6, .in = successful_in, .out = successful_out});
|
|
|
|
try switchdb.db.put(6, ManagedConnection {.dest = 5, .in = successful_in, .out = successful_out});
|
|
|
|
|
|
|
|
// should return a new message (hardcoded: fd 8, payload "coucou")
|
|
|
|
var event_1: Event = switchdb.handle_event_read (1, 5);
|
|
|
|
if (event_1.m) |m| { m.deinit(); }
|
|
|
|
else { return error.NoMessage; }
|
|
|
|
|
|
|
|
// should return a new message (hardcoded: fd 8, payload "coucou")
|
|
|
|
var event_2: Event = switchdb.handle_event_read (1, 6);
|
|
|
|
if (event_2.m) |m| { m.deinit(); }
|
|
|
|
else { return error.NoMessage; }
|
|
|
|
|
|
|
|
var message = try Message.init(6, allocator, "coucou");
|
|
|
|
var event_3 = switchdb.handle_event_write (5, message);
|
|
|
|
if (event_3.m) |_| { return error.ShouldNotCarryMessage; }
|
|
|
|
}
|
|
|
|
|
|
|
|
fn unsuccessful_in (_: i32, _: *Message) CBEventType {
|
|
|
|
return CBEventType.ERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
fn unsuccessful_out (_: i32, _: *const Message) CBEventType {
|
|
|
|
return CBEventType.ERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
test "unsuccessful exchanges" {
|
|
|
|
const config = .{.safety = true};
|
|
|
|
var gpa = std.heap.GeneralPurposeAllocator(config){};
|
|
|
|
defer _ = gpa.deinit();
|
|
|
|
const allocator = gpa.allocator();
|
|
|
|
|
|
|
|
var switchdb = SwitchDB.init(allocator);
|
|
|
|
defer switchdb.deinit();
|
|
|
|
|
|
|
|
try switchdb.db.put(5, ManagedConnection {.dest = 6, .in = unsuccessful_in, .out = unsuccessful_out});
|
|
|
|
try switchdb.db.put(6, ManagedConnection {.dest = 5, .in = unsuccessful_in, .out = unsuccessful_out});
|
|
|
|
|
|
|
|
// should return a new message (hardcoded: fd 8, payload "coucou")
|
|
|
|
var event_1: Event = switchdb.handle_event_read (1, 5);
|
|
|
|
if (event_1.m) |_| { return error.ShouldNotCarryMessage; }
|
|
|
|
|
|
|
|
// should return a new message (hardcoded: fd 8, payload "coucou")
|
|
|
|
var event_2: Event = switchdb.handle_event_read (1, 6);
|
|
|
|
if (event_2.m) |_| { return error.ShouldNotCarryMessage; }
|
|
|
|
|
|
|
|
var message = try Message.init(6, allocator, "coucou");
|
|
|
|
var event_3 = switchdb.handle_event_write (5, message);
|
|
|
|
if (event_3.m) |_| { return error.ShouldNotCarryMessage; }
|
|
|
|
}
|
2022-12-23 01:53:07 +01:00
|
|
|
|
2023-01-08 20:45:35 +01:00
|
|
|
test "nuke 'em" {
|
|
|
|
const config = .{.safety = true};
|
|
|
|
var gpa = std.heap.GeneralPurposeAllocator(config){};
|
|
|
|
defer _ = gpa.deinit();
|
|
|
|
const allocator = gpa.allocator();
|
|
|
|
|
|
|
|
var switchdb = SwitchDB.init(allocator);
|
|
|
|
defer switchdb.deinit();
|
|
|
|
|
|
|
|
try switchdb.db.put(5, ManagedConnection {.dest = 6, .in = unsuccessful_in, .out = unsuccessful_out});
|
|
|
|
try switchdb.db.put(6, ManagedConnection {.dest = 5, .in = unsuccessful_in, .out = unsuccessful_out});
|
|
|
|
|
2023-01-10 17:09:34 +01:00
|
|
|
try testing.expect(switchdb.db.count() == 2);
|
2023-01-08 20:45:35 +01:00
|
|
|
switchdb.nuke(5);
|
|
|
|
try testing.expect(switchdb.db.count() == 0);
|
|
|
|
}
|
|
|
|
|
2023-01-07 16:46:39 +01:00
|
|
|
fn default_in (origin: i32, m: *Message) CBEventType {
|
2023-01-11 16:30:08 +01:00
|
|
|
// print ("receiving a message originated from {}\n", .{origin});
|
2023-01-07 16:46:39 +01:00
|
|
|
var buffer: [2000000]u8 = undefined; // TODO: FIXME??
|
|
|
|
var packet_size: usize = undefined;
|
2022-12-23 01:53:07 +01:00
|
|
|
|
2023-01-07 16:46:39 +01:00
|
|
|
// This may be kinda hacky, idk.
|
|
|
|
var stream: net.Stream = .{ .handle = origin };
|
2023-01-08 20:06:22 +01:00
|
|
|
packet_size = stream.read(buffer[0..]) catch return CBEventType.ERROR;
|
2023-01-07 16:46:39 +01:00
|
|
|
|
|
|
|
// Let's handle this as a disconnection.
|
|
|
|
if (packet_size <= 4) {
|
|
|
|
return CBEventType.FD_CLOSING;
|
|
|
|
}
|
|
|
|
|
|
|
|
m.* = Message.read(origin, buffer[0..], std.heap.c_allocator)
|
2023-01-08 20:06:22 +01:00
|
|
|
catch return CBEventType.ERROR;
|
2023-01-07 16:46:39 +01:00
|
|
|
|
|
|
|
return CBEventType.NO_ERROR;
|
2022-12-23 01:53:07 +01:00
|
|
|
}
|
2023-01-07 16:46:39 +01:00
|
|
|
|
2023-01-11 16:30:08 +01:00
|
|
|
fn default_out (_: i32, m: *const Message) CBEventType {
|
|
|
|
// print ("sending a message originated from {}\n", .{origin});
|
2023-01-07 16:46:39 +01:00
|
|
|
// Message contains the fd, no need to search for
|
|
|
|
// the right structure to copy, let's just recreate
|
|
|
|
// a Stream from the fd.
|
|
|
|
var stream = net.Stream { .handle = m.fd };
|
|
|
|
|
|
|
|
var buffer: [200000]u8 = undefined; // TODO: buffer size
|
|
|
|
var fbs = std.io.fixedBufferStream(&buffer);
|
|
|
|
var writer = fbs.writer();
|
|
|
|
|
|
|
|
// returning basic errors, no details.
|
2023-01-08 20:06:22 +01:00
|
|
|
_ = m.write(writer) catch return CBEventType.ERROR;
|
|
|
|
_ = stream.write (fbs.getWritten()) catch return CBEventType.ERROR;
|
2023-01-07 16:46:39 +01:00
|
|
|
return CBEventType.NO_ERROR;
|
|
|
|
}
|