aboutsummaryrefslogtreecommitdiff
path: root/utils.c
diff options
context:
space:
mode:
Diffstat (limited to 'utils.c')
-rw-r--r--utils.c216
1 files changed, 216 insertions, 0 deletions
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.