From 1f2e4873027466248a041e9a06f0793213e9ff40 Mon Sep 17 00:00:00 2001 From: Přemysl Janouch Date: Mon, 27 Oct 2014 13:54:53 +0100 Subject: Woo we can draw over the network now --- utils.c | 216 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 216 insertions(+) (limited to 'utils.c') diff --git a/utils.c b/utils.c index e1580e0..f7c8fad 100644 --- a/utils.c +++ b/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. -- cgit v1.2.3-54-g00ecf