Woo we can draw over the network now
This commit is contained in:
parent
29bec0c0e0
commit
1f2e487302
425
autistdraw.c
425
autistdraw.c
|
@ -34,10 +34,21 @@
|
|||
#include "config.h"
|
||||
#include "utils.c"
|
||||
|
||||
#define PALETTE_WIDTH 9 ///< Width of the palette
|
||||
#define TOP_BAR_CUTOFF 3 ///< Height of the top bar
|
||||
#define PALETTE_WIDTH 9 ///< Width of the palette
|
||||
#define TOP_BAR_CUTOFF 3 ///< Height of the top bar
|
||||
|
||||
#define BITMAP_BLOCK_SIZE 50 ///< Step for extending bitmap size
|
||||
#define BITMAP_BLOCK_SIZE 50 ///< Step for extending bitmap size
|
||||
|
||||
#define PROTOCOL_VERSION 1 ///< Network protocol version
|
||||
|
||||
enum
|
||||
{
|
||||
MESSAGE_HELLO, ///< Server/client hello
|
||||
MESSAGE_GET_BITMAP, ///< Request bitmap data
|
||||
MESSAGE_PUT_POINT, ///< Request to place a point
|
||||
|
||||
MESSAGE_COUNT ///< Total number of messages
|
||||
};
|
||||
|
||||
typedef enum network_mode network_mode_t;
|
||||
enum network_mode
|
||||
|
@ -47,12 +58,20 @@ enum network_mode
|
|||
NETWORK_MODE_CLIENT ///< We're a client
|
||||
};
|
||||
|
||||
typedef struct write_req write_req_t;
|
||||
struct write_req
|
||||
{
|
||||
uv_write_t req; ///< libuv write request
|
||||
uv_buf_t buf; ///< The data to be written
|
||||
};
|
||||
|
||||
typedef struct client client_t;
|
||||
struct client
|
||||
{
|
||||
LIST_HEADER (client_t)
|
||||
|
||||
uv_tcp_t handle; ///< TCP connection handle
|
||||
struct msg_reader msg_reader; ///< Client message reader
|
||||
};
|
||||
|
||||
typedef struct app_context app_context_t;
|
||||
|
@ -70,6 +89,7 @@ struct app_context
|
|||
|
||||
// Client:
|
||||
uv_tcp_t server_fd; ///< Connection to the server
|
||||
struct msg_reader msg_reader; ///< Server message reader
|
||||
|
||||
// Server:
|
||||
uv_tcp_t listen_fd; ///< Listening FD
|
||||
|
@ -98,20 +118,14 @@ struct app_context
|
|||
uint8_t current_color_right; ///< Right mouse button color
|
||||
};
|
||||
|
||||
static void remove_client (app_context_t *app, client_t *client);
|
||||
static void on_server_disconnected (app_context_t *app);
|
||||
|
||||
static void
|
||||
app_init (app_context_t *self)
|
||||
{
|
||||
memset (self, 0, sizeof *self);
|
||||
}
|
||||
|
||||
static void
|
||||
remove_client (app_context_t *app, client_t *client)
|
||||
{
|
||||
// TODO: cancel any write requests?
|
||||
// XXX: should we unref it?
|
||||
uv_close ((uv_handle_t *) &client->handle, NULL);
|
||||
LIST_UNLINK (app->clients, client);
|
||||
free (client);
|
||||
msg_reader_init (&self->msg_reader);
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -120,11 +134,150 @@ app_free (app_context_t *self)
|
|||
if (self->tk)
|
||||
termo_destroy (self->tk);
|
||||
while (self->clients)
|
||||
// XXX: we probably shouldn't do this from here
|
||||
remove_client (self, self->clients);
|
||||
|
||||
free (self->bitmap);
|
||||
msg_reader_free (&self->msg_reader);
|
||||
}
|
||||
|
||||
// --- Server-client messaging -------------------------------------------------
|
||||
|
||||
static write_req_t *
|
||||
flush_writer (struct msg_writer *writer)
|
||||
{
|
||||
size_t len;
|
||||
void *data = msg_writer_flush (writer, &len);
|
||||
write_req_t *req = xcalloc (1, sizeof *req);
|
||||
req->buf = uv_buf_init (data, len);
|
||||
return req;
|
||||
}
|
||||
|
||||
static void
|
||||
on_data_written_to_client (uv_write_t *req, int status)
|
||||
{
|
||||
write_req_t *wr = (write_req_t *) req;
|
||||
app_context_t *app = req->handle->loop->data;
|
||||
client_t *client = req->data;
|
||||
|
||||
if (status)
|
||||
// Write failed
|
||||
remove_client (app, client);
|
||||
|
||||
free (wr->buf.base);
|
||||
free (wr);
|
||||
}
|
||||
|
||||
static void
|
||||
flush_writer_to_client (struct msg_writer *writer, client_t *client)
|
||||
{
|
||||
write_req_t *wr = flush_writer (writer);
|
||||
wr->req.data = client;
|
||||
(void) uv_write (&wr->req, (uv_stream_t *) &client->handle,
|
||||
&wr->buf, 1, on_data_written_to_client);
|
||||
// XXX: should we put the request on a list so that we can get rid of it?
|
||||
}
|
||||
|
||||
static void
|
||||
on_data_written_to_server (uv_write_t *req, int status)
|
||||
{
|
||||
write_req_t *wr = (write_req_t *) req;
|
||||
app_context_t *app = req->handle->loop->data;
|
||||
|
||||
if (status)
|
||||
// Write failed
|
||||
on_server_disconnected (app);
|
||||
|
||||
free (wr->buf.base);
|
||||
free (wr);
|
||||
}
|
||||
|
||||
static void
|
||||
flush_writer_to_server (struct msg_writer *writer, app_context_t *app)
|
||||
{
|
||||
write_req_t *wr = flush_writer (writer);
|
||||
(void) uv_write (&wr->req, (uv_stream_t *) &app->server_fd,
|
||||
&wr->buf, 1, on_data_written_to_server);
|
||||
// XXX: should we put the request on a list so that we can get rid of it?
|
||||
}
|
||||
|
||||
static void
|
||||
send_draw_point_response (client_t *client, int x, int y, uint8_t color)
|
||||
{
|
||||
struct msg_writer writer;
|
||||
msg_writer_init (&writer);
|
||||
msg_writer_u8 (&writer, MESSAGE_PUT_POINT);
|
||||
msg_writer_i32 (&writer, x);
|
||||
msg_writer_i32 (&writer, y);
|
||||
msg_writer_u8 (&writer, color);
|
||||
|
||||
flush_writer_to_client (&writer, client);
|
||||
}
|
||||
|
||||
static void
|
||||
send_draw_point_request (app_context_t *app, int x, int y, uint8_t color)
|
||||
{
|
||||
struct msg_writer writer;
|
||||
msg_writer_init (&writer);
|
||||
msg_writer_u8 (&writer, MESSAGE_PUT_POINT);
|
||||
msg_writer_i32 (&writer, x);
|
||||
msg_writer_i32 (&writer, y);
|
||||
msg_writer_u8 (&writer, color);
|
||||
|
||||
flush_writer_to_server (&writer, app);
|
||||
}
|
||||
|
||||
static void
|
||||
send_hello_request (app_context_t *app)
|
||||
{
|
||||
struct msg_writer writer;
|
||||
msg_writer_init (&writer);
|
||||
msg_writer_u8 (&writer, MESSAGE_HELLO);
|
||||
msg_writer_u8 (&writer, PROTOCOL_VERSION);
|
||||
|
||||
flush_writer_to_server (&writer, app);
|
||||
}
|
||||
|
||||
static void
|
||||
send_hello_response (client_t *client)
|
||||
{
|
||||
struct msg_writer writer;
|
||||
msg_writer_init (&writer);
|
||||
msg_writer_u8 (&writer, MESSAGE_HELLO);
|
||||
msg_writer_u8 (&writer, PROTOCOL_VERSION);
|
||||
|
||||
flush_writer_to_client (&writer, client);
|
||||
}
|
||||
|
||||
static void
|
||||
send_get_bitmap_request (app_context_t *app)
|
||||
{
|
||||
struct msg_writer writer;
|
||||
msg_writer_init (&writer);
|
||||
msg_writer_u8 (&writer, MESSAGE_GET_BITMAP);
|
||||
|
||||
flush_writer_to_server (&writer, app);
|
||||
}
|
||||
|
||||
static void
|
||||
send_get_bitmap_response (client_t *client, app_context_t *app)
|
||||
{
|
||||
struct msg_writer writer;
|
||||
msg_writer_init (&writer);
|
||||
msg_writer_u8 (&writer, MESSAGE_GET_BITMAP);
|
||||
msg_writer_i32 (&writer, app->bitmap_x);
|
||||
msg_writer_i32 (&writer, app->bitmap_y);
|
||||
msg_writer_u64 (&writer, app->bitmap_w);
|
||||
msg_writer_u64 (&writer, app->bitmap_h);
|
||||
|
||||
msg_writer_blob (&writer, app->bitmap,
|
||||
app->bitmap_w * app->bitmap_h * sizeof *app->bitmap);
|
||||
|
||||
flush_writer_to_client (&writer, client);
|
||||
}
|
||||
|
||||
// --- Server-client messaging -------------------------------------------------
|
||||
|
||||
static void
|
||||
display (const char *format, ...)
|
||||
{
|
||||
|
@ -306,6 +459,11 @@ draw_point (app_context_t *app, int x, int y, uint8_t color)
|
|||
addch (app->palette[color]);
|
||||
refresh ();
|
||||
}
|
||||
|
||||
// Broadcast the clients about the event
|
||||
if (app->mode == NETWORK_MODE_SERVER)
|
||||
for (client_t *iter = app->clients; iter; iter = iter->next)
|
||||
send_draw_point_response (iter, x, y, color);
|
||||
}
|
||||
|
||||
// --- Exports -----------------------------------------------------------------
|
||||
|
@ -538,7 +696,8 @@ on_key (app_context_t *app, termo_key_t *key)
|
|||
if (event != TERMO_MOUSE_PRESS && event != TERMO_MOUSE_DRAG)
|
||||
return true;
|
||||
|
||||
if (button == 2)
|
||||
// Middle mouse button, or Ctrl + left mouse button, moves the canvas
|
||||
if (button == 2 || (button == 1 && key->modifiers == TERMO_KEYMOD_CTRL))
|
||||
{
|
||||
if (event == TERMO_MOUSE_DRAG)
|
||||
{
|
||||
|
@ -568,7 +727,12 @@ on_key (app_context_t *app, termo_key_t *key)
|
|||
int canvas_y = app->corner_y + screen_y - TOP_BAR_CUTOFF;
|
||||
|
||||
if (screen_y >= TOP_BAR_CUTOFF)
|
||||
draw_point (app, canvas_x, canvas_y, *color);
|
||||
{
|
||||
if (app->mode == NETWORK_MODE_CLIENT)
|
||||
send_draw_point_request (app, canvas_x, canvas_y, *color);
|
||||
else
|
||||
draw_point (app, canvas_x, canvas_y, *color);
|
||||
}
|
||||
else if (screen_y > 0 && event != TERMO_MOUSE_DRAG)
|
||||
{
|
||||
int pair = (float) screen_x / COLS * PALETTE_WIDTH;
|
||||
|
@ -650,44 +814,231 @@ app_uv_allocator (uv_handle_t *handle, size_t suggested_size, uv_buf_t *buf)
|
|||
buf->len = sizeof app->read_buf;
|
||||
}
|
||||
|
||||
// --- Client-specific stuff ---------------------------------------------------
|
||||
|
||||
typedef void (*server_handler_fn) (app_context_t *, struct msg_unpacker *);
|
||||
|
||||
static void
|
||||
on_server_disconnected (app_context_t *app)
|
||||
{
|
||||
// TODO: cancel any write requests?
|
||||
// XXX: should we unref it?
|
||||
uv_close ((uv_handle_t *) &app->server_fd, NULL);
|
||||
|
||||
display ("Disconnected!");
|
||||
beep (); // Beep beep! Made a boo-boo.
|
||||
|
||||
// Let the user save the picture at least.
|
||||
// Also prevents us from trying to use the dead server handle.
|
||||
app->mode = NETWORK_MODE_STANDALONE;
|
||||
}
|
||||
|
||||
static void
|
||||
on_server_hello (app_context_t *app, struct msg_unpacker *unpacker)
|
||||
{
|
||||
(void) app;
|
||||
|
||||
uint8_t version;
|
||||
if (!msg_unpacker_u8 (unpacker, &version))
|
||||
return;
|
||||
|
||||
if (version != PROTOCOL_VERSION)
|
||||
// XXX: possibly incompatible version, disconnect?
|
||||
return;
|
||||
}
|
||||
|
||||
static void
|
||||
on_server_get_bitmap (app_context_t *app, struct msg_unpacker *unpacker)
|
||||
{
|
||||
int32_t x, y;
|
||||
uint64_t w, h;
|
||||
if (!msg_unpacker_i32 (unpacker, &x)
|
||||
|| !msg_unpacker_i32 (unpacker, &y)
|
||||
|| !msg_unpacker_u64 (unpacker, &w)
|
||||
|| !msg_unpacker_u64 (unpacker, &h))
|
||||
return;
|
||||
|
||||
// TODO: employ at least some RLE encoding
|
||||
size_t size = w * h * sizeof *app->bitmap;
|
||||
if ((h && w > SIZE_MAX / h) || w > SIZE_MAX || h > SIZE_MAX)
|
||||
return;
|
||||
|
||||
const void *data;
|
||||
if (!msg_unpacker_blob (unpacker, &data, size))
|
||||
return;
|
||||
|
||||
free (app->bitmap);
|
||||
app->bitmap = memcpy (xcalloc (1, size), data, size);
|
||||
app->bitmap_x = x;
|
||||
app->bitmap_y = y;
|
||||
app->bitmap_w = w;
|
||||
app->bitmap_h = h;
|
||||
|
||||
redraw_canvas (app);
|
||||
}
|
||||
|
||||
static void
|
||||
on_server_put_point (app_context_t *app, struct msg_unpacker *unpacker)
|
||||
{
|
||||
int32_t x, y;
|
||||
uint8_t color;
|
||||
|
||||
if (!msg_unpacker_i32 (unpacker, &x)
|
||||
|| !msg_unpacker_i32 (unpacker, &y)
|
||||
|| !msg_unpacker_u8 (unpacker, &color))
|
||||
return;
|
||||
|
||||
draw_point (app, x, y, color);
|
||||
}
|
||||
|
||||
static void
|
||||
on_server_data (uv_stream_t *stream, ssize_t nread, const uv_buf_t *buf)
|
||||
{
|
||||
(void) buf;
|
||||
|
||||
app_context_t *app = stream->loop->data;
|
||||
if (nread == UV_EOF || nread < 0)
|
||||
{
|
||||
// TODO: cancel any write requests?
|
||||
// XXX: should we unref it?
|
||||
uv_close ((uv_handle_t *) &app->server_fd, NULL);
|
||||
|
||||
display ("Disconnected!");
|
||||
beep (); // Beep beep! Made a boo-boo.
|
||||
|
||||
// Let the user save the picture at least.
|
||||
// Also prevents us from trying to use the dead server handle.
|
||||
app->mode = NETWORK_MODE_STANDALONE;
|
||||
on_server_disconnected (app);
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: process the data
|
||||
msg_reader_feed (&app->msg_reader, buf->base, nread);
|
||||
|
||||
static const server_handler_fn handlers[MESSAGE_COUNT] =
|
||||
{
|
||||
[MESSAGE_HELLO] = on_server_hello,
|
||||
[MESSAGE_GET_BITMAP] = on_server_get_bitmap,
|
||||
[MESSAGE_PUT_POINT] = on_server_put_point,
|
||||
};
|
||||
|
||||
void *msg;
|
||||
size_t len;
|
||||
while ((msg = msg_reader_get (&app->msg_reader, &len)))
|
||||
{
|
||||
struct msg_unpacker unpacker;
|
||||
msg_unpacker_init (&unpacker, msg, len);
|
||||
|
||||
uint8_t type;
|
||||
if (!msg_unpacker_u8 (&unpacker, &type)
|
||||
|| type >= MESSAGE_COUNT)
|
||||
// XXX: unknown message, disconnect?
|
||||
continue;
|
||||
|
||||
server_handler_fn handler = handlers[type];
|
||||
if (!handler)
|
||||
// XXX: unknown message, disconnect?
|
||||
continue;
|
||||
|
||||
handler (app, &unpacker);
|
||||
|
||||
if (msg_unpacker_get_available (&unpacker) > 0)
|
||||
// XXX: overlong message, disconnect?
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// --- Server-specific stuff ---------------------------------------------------
|
||||
|
||||
typedef bool (*client_handler_fn)
|
||||
(app_context_t *, client_t *, struct msg_unpacker *);
|
||||
|
||||
static void
|
||||
remove_client (app_context_t *app, client_t *client)
|
||||
{
|
||||
// TODO: cancel any write requests?
|
||||
// XXX: should we unref it?
|
||||
uv_close ((uv_handle_t *) &client->handle, NULL);
|
||||
LIST_UNLINK (app->clients, client);
|
||||
msg_reader_free (&client->msg_reader);
|
||||
free (client);
|
||||
}
|
||||
|
||||
static bool
|
||||
on_client_hello (app_context_t *app, client_t *client,
|
||||
struct msg_unpacker *unpacker)
|
||||
{
|
||||
(void) app;
|
||||
|
||||
uint8_t version;
|
||||
if (!msg_unpacker_u8 (unpacker, &version)
|
||||
|| version != PROTOCOL_VERSION)
|
||||
// Nope, I don't like you
|
||||
return false;
|
||||
|
||||
send_hello_response (client);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
on_client_get_bitmap (app_context_t *app, client_t *client,
|
||||
struct msg_unpacker *unpacker)
|
||||
{
|
||||
(void) unpacker;
|
||||
|
||||
send_get_bitmap_response (client, app);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
on_client_put_point (app_context_t *app, client_t *client,
|
||||
struct msg_unpacker *unpacker)
|
||||
{
|
||||
(void) client;
|
||||
|
||||
int32_t x, y;
|
||||
uint8_t color;
|
||||
if (!msg_unpacker_i32 (unpacker, &x)
|
||||
|| !msg_unpacker_i32 (unpacker, &y)
|
||||
|| !msg_unpacker_u8 (unpacker, &color))
|
||||
return false;
|
||||
|
||||
// The function takes care of broadcasting to all the other clients,
|
||||
// as well as back to the original sender
|
||||
draw_point (app, x, y, color);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void
|
||||
on_client_data (uv_stream_t *stream, ssize_t nread, const uv_buf_t *buf)
|
||||
{
|
||||
(void) buf;
|
||||
|
||||
app_context_t *app = stream->loop->data;
|
||||
client_t *client = stream->data;
|
||||
if (nread == UV_EOF || nread < 0)
|
||||
{
|
||||
remove_client (app, client);
|
||||
return;
|
||||
}
|
||||
goto disconnect; // Connection closed or error
|
||||
|
||||
// TODO: process the data
|
||||
msg_reader_feed (&client->msg_reader, buf->base, nread);
|
||||
|
||||
static const client_handler_fn handlers[MESSAGE_COUNT] =
|
||||
{
|
||||
[MESSAGE_HELLO] = on_client_hello,
|
||||
[MESSAGE_GET_BITMAP] = on_client_get_bitmap,
|
||||
[MESSAGE_PUT_POINT] = on_client_put_point,
|
||||
};
|
||||
|
||||
void *msg;
|
||||
size_t len;
|
||||
while ((msg = msg_reader_get (&client->msg_reader, &len)))
|
||||
{
|
||||
struct msg_unpacker unpacker;
|
||||
msg_unpacker_init (&unpacker, msg, len);
|
||||
|
||||
uint8_t type;
|
||||
if (!msg_unpacker_u8 (&unpacker, &type))
|
||||
goto disconnect; // Invalid message
|
||||
if (type >= MESSAGE_COUNT)
|
||||
goto disconnect; // Unknown message
|
||||
|
||||
client_handler_fn handler = handlers[type];
|
||||
if (!handler)
|
||||
goto disconnect; // Unknown message
|
||||
if (!handler (app, client, &unpacker))
|
||||
goto disconnect; // Invalid message
|
||||
if (msg_unpacker_get_available (&unpacker) > 0)
|
||||
goto disconnect; // Overlong message data
|
||||
}
|
||||
return;
|
||||
|
||||
disconnect:
|
||||
remove_client (app, client);
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -708,6 +1059,7 @@ on_new_client (uv_stream_t *server, int status)
|
|||
goto free_handle;
|
||||
|
||||
client->handle.data = client;
|
||||
msg_reader_init (&client->msg_reader);
|
||||
LIST_PREPEND (app->clients, client);
|
||||
return;
|
||||
|
||||
|
@ -907,6 +1259,9 @@ initialize_client (app_context_t *app, struct addrinfo *address)
|
|||
"error", "initialization failed", uv_strerror (err));
|
||||
exit (EXIT_FAILURE);
|
||||
}
|
||||
|
||||
send_hello_request (app);
|
||||
send_get_bitmap_request (app);
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
216
utils.c
216
utils.c
|
@ -216,6 +216,20 @@ str_append_printf (struct str *self, const char *fmt, ...)
|
|||
return size;
|
||||
}
|
||||
|
||||
static void
|
||||
str_remove_slice (struct str *self, size_t start, size_t length)
|
||||
{
|
||||
size_t end = start + length;
|
||||
if (end > self->len)
|
||||
end = self->len;
|
||||
memmove (self->str + start, self->str + end, self->len - end);
|
||||
self->str[self->len -= length] = '\0';
|
||||
|
||||
// Shrink the string if the allocation becomes way too large
|
||||
if (self->alloc >= STR_SHRINK_THRESHOLD && self->len < (self->alloc >> 2))
|
||||
self->str = xrealloc (self->str, self->alloc >>= 2);
|
||||
}
|
||||
|
||||
// --- Utilities ---------------------------------------------------------------
|
||||
|
||||
static bool
|
||||
|
@ -270,6 +284,208 @@ xstrtoul (unsigned long *out, const char *s, int base)
|
|||
return errno == 0 && !*end && end != s;
|
||||
}
|
||||
|
||||
// --- Message reader ----------------------------------------------------------
|
||||
|
||||
struct msg_reader
|
||||
{
|
||||
struct str buf; ///< Input buffer
|
||||
uint64_t offset; ///< Current offset in the buffer
|
||||
};
|
||||
|
||||
static void
|
||||
msg_reader_init (struct msg_reader *self)
|
||||
{
|
||||
str_init (&self->buf);
|
||||
self->offset = 0;
|
||||
}
|
||||
|
||||
static void
|
||||
msg_reader_free (struct msg_reader *self)
|
||||
{
|
||||
str_free (&self->buf);
|
||||
}
|
||||
|
||||
static void
|
||||
msg_reader_compact (struct msg_reader *self)
|
||||
{
|
||||
str_remove_slice (&self->buf, 0, self->offset);
|
||||
self->offset = 0;
|
||||
}
|
||||
|
||||
static void
|
||||
msg_reader_feed (struct msg_reader *self, const void *data, size_t len)
|
||||
{
|
||||
// TODO: have some mechanism to prevent flooding
|
||||
msg_reader_compact (self);
|
||||
str_append_data (&self->buf, data, len);
|
||||
}
|
||||
|
||||
static void *
|
||||
msg_reader_get (struct msg_reader *self, size_t *len)
|
||||
{
|
||||
// Try to read in the length of the message
|
||||
if (self->offset + sizeof (uint64_t) > self->buf.len)
|
||||
return NULL;
|
||||
|
||||
uint8_t *x = (uint8_t *) self->buf.str + self->offset;
|
||||
uint64_t msg_len
|
||||
= (uint64_t) x[0] << 56 | (uint64_t) x[1] << 48
|
||||
| (uint64_t) x[2] << 40 | (uint64_t) x[3] << 32
|
||||
| (uint64_t) x[4] << 24 | (uint64_t) x[5] << 16
|
||||
| (uint64_t) x[6] << 8 | (uint64_t) x[7];
|
||||
|
||||
if (msg_len < sizeof msg_len)
|
||||
{
|
||||
// The message is shorter than its header
|
||||
// TODO: have some mechanism to report errors
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (self->offset + msg_len < self->offset)
|
||||
{
|
||||
// Trying to read an insane amount of data but whatever
|
||||
msg_reader_compact (self);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Check if we've got the full message in the buffer and return it
|
||||
if (self->offset + msg_len > self->buf.len)
|
||||
return NULL;
|
||||
|
||||
// We have to subtract the header from the reported length
|
||||
void *data = self->buf.str + self->offset + sizeof msg_len;
|
||||
self->offset += msg_len;
|
||||
*len = msg_len - sizeof msg_len;
|
||||
return data;
|
||||
}
|
||||
|
||||
// --- Message unpacker --------------------------------------------------------
|
||||
|
||||
struct msg_unpacker
|
||||
{
|
||||
const char *data;
|
||||
size_t offset;
|
||||
size_t len;
|
||||
};
|
||||
|
||||
static void
|
||||
msg_unpacker_init (struct msg_unpacker *self, const void *data, size_t len)
|
||||
{
|
||||
self->data = data;
|
||||
self->len = len;
|
||||
self->offset = 0;
|
||||
}
|
||||
|
||||
static size_t
|
||||
msg_unpacker_get_available (struct msg_unpacker *self)
|
||||
{
|
||||
return self->len - self->offset;
|
||||
}
|
||||
|
||||
static bool
|
||||
msg_unpacker_blob (struct msg_unpacker *self, const void **data, size_t len)
|
||||
{
|
||||
if (self->len - self->offset < len)
|
||||
return false;
|
||||
*data = self->data + self->offset;
|
||||
self->offset += len;
|
||||
return true;
|
||||
}
|
||||
|
||||
#define UNPACKER_INT_BEGIN \
|
||||
if (self->len - self->offset < sizeof *value) \
|
||||
return false; \
|
||||
uint8_t *x = (uint8_t *) self->data + self->offset; \
|
||||
self->offset += sizeof *value;
|
||||
|
||||
static bool
|
||||
msg_unpacker_u8 (struct msg_unpacker *self, uint8_t *value)
|
||||
{
|
||||
UNPACKER_INT_BEGIN
|
||||
*value = x[0];
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
msg_unpacker_i32 (struct msg_unpacker *self, int32_t *value)
|
||||
{
|
||||
UNPACKER_INT_BEGIN
|
||||
*value
|
||||
= (uint32_t) x[0] << 24 | (uint32_t) x[1] << 16
|
||||
| (uint32_t) x[2] << 8 | (uint32_t) x[3];
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
msg_unpacker_u64 (struct msg_unpacker *self, uint64_t *value)
|
||||
{
|
||||
UNPACKER_INT_BEGIN
|
||||
*value
|
||||
= (uint64_t) x[0] << 56 | (uint64_t) x[1] << 48
|
||||
| (uint64_t) x[2] << 40 | (uint64_t) x[3] << 32
|
||||
| (uint64_t) x[4] << 24 | (uint64_t) x[5] << 16
|
||||
| (uint64_t) x[6] << 8 | (uint64_t) x[7];
|
||||
return true;
|
||||
}
|
||||
|
||||
#undef UNPACKER_INT_BEGIN
|
||||
|
||||
// --- Message packer and writer -----------------------------------------------
|
||||
|
||||
struct msg_writer
|
||||
{
|
||||
struct str buf; ///< Holds the message data
|
||||
};
|
||||
|
||||
static void
|
||||
msg_writer_init (struct msg_writer *self)
|
||||
{
|
||||
str_init (&self->buf);
|
||||
// Placeholder for message length
|
||||
str_append_data (&self->buf, "\x00\x00\x00\x00" "\x00\x00\x00\x00", 8);
|
||||
}
|
||||
|
||||
static void
|
||||
msg_writer_blob (struct msg_writer *self, const void *data, size_t len)
|
||||
{
|
||||
str_append_data (&self->buf, data, len);
|
||||
}
|
||||
|
||||
static void
|
||||
msg_writer_u8 (struct msg_writer *self, uint8_t x)
|
||||
{
|
||||
str_append_data (&self->buf, &x, 1);
|
||||
}
|
||||
|
||||
static void
|
||||
msg_writer_i32 (struct msg_writer *self, int32_t x)
|
||||
{
|
||||
uint32_t u = x;
|
||||
uint8_t tmp[4] = { u >> 24, u >> 16, u >> 8, u };
|
||||
str_append_data (&self->buf, tmp, sizeof tmp);
|
||||
}
|
||||
|
||||
static void
|
||||
msg_writer_u64 (struct msg_writer *self, uint64_t x)
|
||||
{
|
||||
uint8_t tmp[8] =
|
||||
{ x >> 56, x >> 48, x >> 40, x >> 32, x >> 24, x >> 16, x >> 8, x };
|
||||
str_append_data (&self->buf, tmp, sizeof tmp);
|
||||
}
|
||||
|
||||
static void *
|
||||
msg_writer_flush (struct msg_writer *self, size_t *len)
|
||||
{
|
||||
// Update the message length
|
||||
uint64_t x = self->buf.len;
|
||||
uint8_t tmp[8] =
|
||||
{ x >> 56, x >> 48, x >> 40, x >> 32, x >> 24, x >> 16, x >> 8, x };
|
||||
memcpy (self->buf.str, tmp, sizeof tmp);
|
||||
|
||||
*len = x;
|
||||
return str_steal (&self->buf);
|
||||
}
|
||||
|
||||
// --- Option handler ----------------------------------------------------------
|
||||
|
||||
// Simple wrapper for the getopt_long API to make it easier to use and maintain.
|
||||
|
|
Loading…
Reference in New Issue