From 1fc696dc5119033b5bfe20ee186ec7d830094370 Mon Sep 17 00:00:00 2001 From: Vftdan Date: Wed, 23 Oct 2024 14:09:28 +0200 Subject: [PATCH] Stream abstraction, [WIP] integer (de)serealization --- src/common/util/byte_serdes.h | 83 +++++++++++++++++++++++++++++++ src/common/util/byte_slice.h | 60 ++++++++++++++++++++++ src/common/util/byte_stream.c | 81 ++++++++++++++++++++++++++++++ src/common/util/byte_stream.h | 93 +++++++++++++++++++++++++++++++++++ src/server/main.c | 1 + 5 files changed, 318 insertions(+) create mode 100644 src/common/util/byte_serdes.h create mode 100644 src/common/util/byte_slice.h create mode 100644 src/common/util/byte_stream.c create mode 100644 src/common/util/byte_stream.h diff --git a/src/common/util/byte_serdes.h b/src/common/util/byte_serdes.h new file mode 100644 index 0000000..00ea91f --- /dev/null +++ b/src/common/util/byte_serdes.h @@ -0,0 +1,83 @@ +#ifndef COMMON_UTIL_BYTE_SERDES_H_ +#define COMMON_UTIL_BYTE_SERDES_H_ + +#include "common/util/byte_stream.h" + +#include + +HEADER_FN void +byteser_bytes_lcrop(AbstractOutputByteStream *stream, ByteSlice bytes, size_t size, uint8_t pad_value) +{ + ByteSlice pad_slc = VAR_BYTE_SLICE_CONST(pad_value); + while (size > bytes.length) { + byte_stream_write(stream, pad_slc); + --size; + } + byte_slice_advance_inplace(&bytes, bytes.length - size); + if (bytes.data && bytes.length) { + byte_stream_write(stream, bytes); + } +} + +HEADER_FN void +byteser_net_integral_signed(AbstractOutputByteStream *stream, intmax_t value, size_t size) +{ + int8_t buf[sizeof(value)]; + for (size_t i = 0; i < sizeof(buf); ++i) { + buf[size - i - 1] = value & 0xFF; + value >>= 8; + } + byteser_bytes_lcrop(stream, VAR_BYTE_SLICE_CONST(buf), size, value < 0 ? -1 : 0); +} + +HEADER_FN void +byteser_net_integral_unsigned(AbstractOutputByteStream *stream, uintmax_t value, size_t size) +{ + uint8_t buf[sizeof(value)]; + for (size_t i = 0; i < sizeof(buf); ++i) { + buf[size - i - 1] = value & 0xFF; + value >>= 8; + } + byteser_bytes_lcrop(stream, VAR_BYTE_SLICE_CONST(buf), size, 0); +} + +// TODO finish deserialization +HEADER_FN bool +bytedeser_net_integral_signed(AbstractInputByteStream *stream, intmax_t *value_ptr, size_t size) +{ + *value_ptr = 0; + int8_t buf[sizeof(*value_ptr)] = {0,}; + int8_t dummy; + MutByteSlice dummy_slc = VAR_BYTE_SLICE_MUT(dummy); + while (size > sizeof(buf)) { + byte_stream_read_to(stream, dummy_slc); + --size; + } + MutByteSlice buf_slc = VAR_BYTE_SLICE_MUT(buf); + byte_slice_advance_inplace(&buf_slc.as_ByteSlice, buf_slc.length - size); + if (!buf_slc.data || !buf_slc.length) { + return true; // Successfully read 0 bytes + } + if (byte_stream_is_end(stream)) { + return false; // Failed to read bytes + } + if (byte_stream_read_to(stream, buf_slc) != buf_slc.length) { + return false; // Failed to read enough bytes + } + + assert(buf_slc.data - (uint8_t*) buf < (ssize_t) sizeof(buf)); + int8_t sign = *(int8_t*) buf_slc.data < 0 ? -1 : 0; + for (int8_t *ptr = buf; (uint8_t*) ptr != buf_slc.data; ++ptr) { + *ptr = sign; + } + + intmax_t value = 0; + for (size_t i = 0; i < sizeof(buf); ++i) { + value <<= 8; + value |= 0xFF & (intmax_t) buf[i]; + } + *value_ptr = value; + return true; +} + +#endif /* end of include guard: COMMON_UTIL_BYTE_SERDES_H_ */ diff --git a/src/common/util/byte_slice.h b/src/common/util/byte_slice.h new file mode 100644 index 0000000..ad03cb9 --- /dev/null +++ b/src/common/util/byte_slice.h @@ -0,0 +1,60 @@ +#ifndef COMMON_UTIL_BYTE_SLICE_H_ +#define COMMON_UTIL_BYTE_SLICE_H_ + +#include "common/defs.h" + +typedef struct { + const uint8_t *data; + size_t length; +} ByteSlice; + +typedef union { + struct { + uint8_t *data; + size_t length; + }; + ByteSlice as_ByteSlice; +} MutByteSlice; + +#define EMPTY_BYTE_SLICE (ByteSlice) { .data = NULL, .length = 0 } +#define EMPTY_MUT_BYTE_SLICE (MutByteSlice) { .data = NULL, .length = 0 } +#define VAR_BYTE_SLICE_CONST(x) (ByteSlice) { .data = (const void*) &(x), .length = sizeof(x) } +#define VAR_BYTE_SLICE_MUT(x) (MutByteSlice) { .data = (void*) &(x), .length = sizeof(x) } + +HEADER_FN void +byte_slice_advance_inplace(ByteSlice *self, size_t amount) +{ + if (amount >= self->length) { + *self = EMPTY_BYTE_SLICE; + return; + } + self->length -= amount; + self->data += amount; +} + +HEADER_FN MutByteSlice +byte_slice_alloc(size_t length) +{ + if (!length) { + return EMPTY_MUT_BYTE_SLICE; + } + uint8_t *data = T_ALLOC(length, uint8_t); + if (!data) { + return EMPTY_MUT_BYTE_SLICE; + } + return (MutByteSlice) { + .data = data, + .length = length, + }; +} + +HEADER_FN MutByteSlice +byte_slice_free(MutByteSlice slice) +{ + if (slice.data) { + free(slice.data); + } + return EMPTY_MUT_BYTE_SLICE; +} + +#endif /* end of include guard: COMMON_UTIL_BYTE_SLICE_H_ */ diff --git a/src/common/util/byte_stream.c b/src/common/util/byte_stream.c new file mode 100644 index 0000000..f1556b8 --- /dev/null +++ b/src/common/util/byte_stream.c @@ -0,0 +1,81 @@ +#include "byte_stream.h" + +typedef struct { + AbstractInputByteStream as_AbstractInputByteStream; + FILE *file; +} FileInputByteStream; + +typedef struct { + AbstractOutputByteStream as_AbstractOutputByteStream; + FILE *file; +} FileOutputByteStream; + +static size_t +file_steam_read(AbstractInputByteStream *self, MutByteSlice buf) +{ + return fread(buf.data, 1, buf.length, DOWNCAST(FileInputByteStream, AbstractInputByteStream, self)->file); +} + +static bool +file_steam_is_end(AbstractInputByteStream *self) +{ + FILE *file = DOWNCAST(FileInputByteStream, AbstractInputByteStream, self)->file; + return (feof(file) || ferror(file)) ? true : false; +} + +static void +file_steam_write(AbstractOutputByteStream *self, ByteSlice bytes) +{ + fwrite(bytes.data, 1, bytes.length, DOWNCAST(FileOutputByteStream, AbstractOutputByteStream, self)->file); +} + +static void +file_input_stream_destroy(AbstractInputByteStream *self) +{ + free(self); +} + +static void +file_output_stream_destroy(AbstractOutputByteStream *self) +{ + free(self); +} + +AbstractInputByteStream *file_as_input_byte_stream(FILE *file) +{ + if (!file) { + return NULL; + } + FileInputByteStream *self = T_ALLOC(1, FileInputByteStream); + if (self == NULL) { + return NULL; + } + *self = (FileInputByteStream) { + .as_AbstractInputByteStream = { + .read = &file_steam_read, + .is_end = &file_steam_is_end, + .destroy = &file_input_stream_destroy, + }, + .file = file, + }; + return &self->as_AbstractInputByteStream; +} + +AbstractOutputByteStream *file_as_output_byte_stream(FILE *file) +{ + if (!file) { + return NULL; + } + FileOutputByteStream *self = T_ALLOC(1, FileOutputByteStream); + if (self == NULL) { + return NULL; + } + *self = (FileOutputByteStream) { + .as_AbstractOutputByteStream = { + .write = &file_steam_write, + .destroy = &file_output_stream_destroy, + }, + .file = file, + }; + return &self->as_AbstractOutputByteStream; +} diff --git a/src/common/util/byte_stream.h b/src/common/util/byte_stream.h new file mode 100644 index 0000000..26b8ac5 --- /dev/null +++ b/src/common/util/byte_stream.h @@ -0,0 +1,93 @@ +#ifndef COMMON_UTIL_BYTE_STREAM_H_ +#define COMMON_UTIL_BYTE_STREAM_H_ + +#include "common/util/byte_slice.h" + +#include + +typedef struct abstract_input_byte_stream AbstractInputByteStream; +typedef struct abstract_output_byte_stream AbstractOutputByteStream; + +struct abstract_input_byte_stream { + size_t (*read)(AbstractInputByteStream *self, MutByteSlice buf); + bool (*is_end)(AbstractInputByteStream *self); + void (*destroy)(AbstractInputByteStream *self); +}; + +struct abstract_output_byte_stream { + void (*write)(AbstractOutputByteStream *self, ByteSlice bytes); + void (*destroy)(AbstractOutputByteStream *self); +}; + +// file is borrowed, not moved +AbstractInputByteStream *file_as_input_byte_stream(FILE *file); +AbstractOutputByteStream *file_as_output_byte_stream(FILE *file); + +HEADER_FN size_t +byte_stream_read_to(AbstractInputByteStream *stream, MutByteSlice buf) +{ + if (!stream->read) { + return 0; + } + return stream->read(stream, buf); +} + +HEADER_FN MutByteSlice +byte_stream_read_alloc(AbstractInputByteStream *stream, size_t amount) +{ + if (!stream->read) { + return EMPTY_MUT_BYTE_SLICE; + } + MutByteSlice buf = byte_slice_alloc(amount); + if (!buf.data) { + return EMPTY_MUT_BYTE_SLICE; + } + size_t actual = stream->read(stream, buf); + if (actual < buf.length) { + buf.length = actual; + } + return buf; +} + +HEADER_FN bool +byte_stream_is_end(AbstractInputByteStream *stream) +{ + if (!stream->read) { + return true; + } + if (!stream->is_end) { + return false; + } + return stream->is_end(stream); +} + +HEADER_FN void +byte_stream_write(AbstractOutputByteStream *stream, ByteSlice bytes) +{ + if (!stream->write) { + return; + } + return stream->write(stream, bytes); +} + +HEADER_FN void +input_byte_stream_destroy(AbstractInputByteStream *stream) +{ + if (!stream->destroy) { + free(stream); + return; + } + stream->destroy(stream); +} + +HEADER_FN void +output_byte_stream_destroy(AbstractOutputByteStream *stream) +{ + if (!stream->destroy) { + free(stream); + return; + } + stream->destroy(stream); +} + +#endif /* end of include guard: COMMON_UTIL_BYTE_STREAM_H_ */ diff --git a/src/server/main.c b/src/server/main.c index 89d9c9c..1d09335 100644 --- a/src/server/main.c +++ b/src/server/main.c @@ -2,6 +2,7 @@ #include #include "common/util/hash_table.h" +#include "common/util/byte_serdes.h" int main(int argc, char **argv)