diff --git a/include/pocketpy/common/serialize.h b/include/pocketpy/common/serialize.h new file mode 100644 index 00000000..757c21ae --- /dev/null +++ b/include/pocketpy/common/serialize.h @@ -0,0 +1,55 @@ +#pragma once + +#include + +#include "pocketpy/common/vector.h" +#include "pocketpy/common/str.h" + + +typedef struct c11_serializer { + c11_vector data; +} c11_serializer; + +void c11_serializer__ctor(c11_serializer* self, int16_t magic, int8_t major_ver, int8_t minor_ver); +void c11_serializer__dtor(c11_serializer* self); +void c11_serializer__write_cstr(c11_serializer* self, const char*); +void c11_serializer__write_bytes(c11_serializer* self, const void* data, int size); +void* c11_serializer__submit(c11_serializer* self, int* size); + +typedef struct c11_deserializer { + char error_msg[64]; + const uint8_t* data; + int size; + int index; + int8_t major_ver; + int8_t minor_ver; +} c11_deserializer; + +void c11_deserializer__ctor(c11_deserializer* self, const void* data, int size); +void c11_deserializer__dtor(c11_deserializer* self); +bool c11_deserializer__check_header(c11_deserializer* self, int16_t magic, int8_t major_ver, int8_t minor_ver); +const char* c11_deserializer__read_cstr(c11_deserializer* self); +void* c11_deserializer__read_bytes(c11_deserializer* self, int size); + + +#define DEF_ATOMIC_INLINE_RW(name, T) \ + inline void c11_serializer__write_##name(c11_serializer* self, T value){ \ + c11_serializer__write_bytes(self, &value, sizeof(T)); \ + } \ + inline T c11_deserializer__read_##name(c11_deserializer* self){ \ + T* p = (T*)(self->data + self->index); \ + self->index += sizeof(T); \ + return *p; \ + } + + +DEF_ATOMIC_INLINE_RW(size, int) +DEF_ATOMIC_INLINE_RW(i8, int8_t) +DEF_ATOMIC_INLINE_RW(i16, int16_t) +DEF_ATOMIC_INLINE_RW(i32, int32_t) +DEF_ATOMIC_INLINE_RW(i64, int64_t) +DEF_ATOMIC_INLINE_RW(f32, float) +DEF_ATOMIC_INLINE_RW(f64, double) +DEF_ATOMIC_INLINE_RW(type, py_Type) + +#undef DEF_ATOMIC_INLINE_RW diff --git a/src/common/serialize.c b/src/common/serialize.c new file mode 100644 index 00000000..8a8b7521 --- /dev/null +++ b/src/common/serialize.c @@ -0,0 +1,96 @@ +#include "pocketpy/common/serialize.h" + +// >>> ord('🥕') +// 129365 +// >>> ord('🍋') +// 127819 + +static uint32_t c11__checksum_32bit(const void* data, int size){ + const uint8_t* p = (const uint8_t*)data; + uint32_t res = 0; + for(int i = 0; i < size; i++){ + res = res * 31 + p[i]; + } + return res; +} + +void c11_serializer__ctor(c11_serializer* self, int16_t magic, int8_t major_ver, int8_t minor_ver){ + c11_vector__ctor(&self->data, 1); + c11_serializer__write_i16(self, magic); + c11_serializer__write_i8(self, major_ver); + c11_serializer__write_i8(self, minor_ver); +} + +void c11_serializer__dtor(c11_serializer* self){ + c11_vector__dtor(&self->data); +} + +void c11_serializer__write_cstr(c11_serializer *self, const char* cstr) { + int len = (int)strlen(cstr); + c11_serializer__write_bytes(self, cstr, len + 1); +} + +void c11_serializer__write_bytes(c11_serializer* self, const void* data, int size){ + c11_vector__extend(uint8_t, &self->data, data, size); +} + +void* c11_serializer__submit(c11_serializer* self, int* size){ + uint32_t checksum = c11__checksum_32bit(self->data.data, self->data.length); + c11_serializer__write_bytes(self, &checksum, sizeof(uint32_t)); + return c11_vector__submit(&self->data, size); +} + +void c11_deserializer__ctor(c11_deserializer* self, const void* data, int size){ + self->data = (const uint8_t*)data; + self->size = size; + self->index = 0; + self->error_msg[0] = '\0'; +} + +void c11_deserializer__dtor(c11_deserializer* self){ + // nothing to do +} + +bool c11_deserializer__error(c11_deserializer* self, const char* msg){ + snprintf(self->error_msg, sizeof(self->error_msg), "%s", msg); + return false; +} + +bool c11_deserializer__check_header(c11_deserializer* self, int16_t magic, int8_t major_ver, int8_t minor_ver){ + if(self->size < 8){ + return c11_deserializer__error(self, "bad header: size < 8"); + } + // read 16bit magic + int16_t file_magic = c11_deserializer__read_i16(self); + if(file_magic != magic){ + return c11_deserializer__error(self, "bad header: magic mismatch"); + } + // read 16bit version + self->major_ver = c11_deserializer__read_i8(self); + self->minor_ver = c11_deserializer__read_i8(self); + + // check checksum + uint32_t checksum; + memcpy(&checksum, self->data + self->size - 4, sizeof(uint32_t)); + uint32_t actual_checksum = c11__checksum_32bit(self->data, self->size - 4); + if(checksum != actual_checksum){ + return c11_deserializer__error(self, "bad header: checksum mismatch"); + } + // check version + if(self->major_ver != major_ver || self->minor_ver > minor_ver){ + return c11_deserializer__error(self, "bad header: version mismatch"); + } + return true; +} + +const char* c11_deserializer__read_cstr(c11_deserializer* self){ + const char* p = (const char*)(self->data + self->index); + self->index += (strlen(p) + 1); + return p; +} + +void* c11_deserializer__read_bytes(c11_deserializer* self, int size){ + void* p = (void*)(self->data + self->index); + self->index += size; + return p; +} \ No newline at end of file