From b56dfae6721cc6d9e282257e92467148d291e798 Mon Sep 17 00:00:00 2001 From: mikael-lovqvists-claude-agent Date: Wed, 25 Mar 2026 22:15:41 +0000 Subject: [PATCH] =?UTF-8?q?Add=20serial=20module=20=E2=80=94=20little-endi?= =?UTF-8?q?an=20binary=20serialization=20primitives?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit put_u8/16/32/64 and get_u8/16/32/64 (plus signed variants) for packing and unpacking values at explicit byte offsets in a buffer. Each value is encoded byte-by-byte with explicit shift operations — no struct casting, no alignment assumptions, correct on any host endianness. Co-Authored-By: Claude Sonnet 4.6 --- include/serial.h | 32 +++++++++++++ src/modules/serial/Makefile | 17 +++++++ src/modules/serial/serial.c | 92 +++++++++++++++++++++++++++++++++++++ 3 files changed, 141 insertions(+) create mode 100644 include/serial.h create mode 100644 src/modules/serial/Makefile create mode 100644 src/modules/serial/serial.c diff --git a/include/serial.h b/include/serial.h new file mode 100644 index 0000000..5051968 --- /dev/null +++ b/include/serial.h @@ -0,0 +1,32 @@ +#pragma once + +#include + +/* + * Binary serialization primitives. + * + * All values are encoded little-endian. Fields are placed and read + * explicitly by position — never by casting a struct to bytes. + */ + +/* -- put: write a value into a buffer at a given byte offset -- */ + +void put_u8 (uint8_t *buf, uint32_t offset, uint8_t value); +void put_u16(uint8_t *buf, uint32_t offset, uint16_t value); +void put_u32(uint8_t *buf, uint32_t offset, uint32_t value); +void put_u64(uint8_t *buf, uint32_t offset, uint64_t value); +void put_i8 (uint8_t *buf, uint32_t offset, int8_t value); +void put_i16(uint8_t *buf, uint32_t offset, int16_t value); +void put_i32(uint8_t *buf, uint32_t offset, int32_t value); +void put_i64(uint8_t *buf, uint32_t offset, int64_t value); + +/* -- get: read a value from a buffer at a given byte offset -- */ + +uint8_t get_u8 (const uint8_t *buf, uint32_t offset); +uint16_t get_u16(const uint8_t *buf, uint32_t offset); +uint32_t get_u32(const uint8_t *buf, uint32_t offset); +uint64_t get_u64(const uint8_t *buf, uint32_t offset); +int8_t get_i8 (const uint8_t *buf, uint32_t offset); +int16_t get_i16(const uint8_t *buf, uint32_t offset); +int32_t get_i32(const uint8_t *buf, uint32_t offset); +int64_t get_i64(const uint8_t *buf, uint32_t offset); diff --git a/src/modules/serial/Makefile b/src/modules/serial/Makefile new file mode 100644 index 0000000..d868ef8 --- /dev/null +++ b/src/modules/serial/Makefile @@ -0,0 +1,17 @@ +ROOT := $(abspath ../../..) +include $(ROOT)/common.mk + +MODULE_BUILD = $(BUILD)/serial + +.PHONY: all clean + +all: $(MODULE_BUILD)/serial.o + +$(MODULE_BUILD)/serial.o: serial.c $(ROOT)/include/serial.h | $(MODULE_BUILD) + $(CC) $(CFLAGS) -c -o $@ $< + +$(MODULE_BUILD): + mkdir -p $@ + +clean: + rm -f $(MODULE_BUILD)/serial.o diff --git a/src/modules/serial/serial.c b/src/modules/serial/serial.c new file mode 100644 index 0000000..1862209 --- /dev/null +++ b/src/modules/serial/serial.c @@ -0,0 +1,92 @@ +#include +#include "serial.h" + +/* -- put -- */ + +void put_u8(uint8_t *buf, uint32_t offset, uint8_t value) { + buf[offset] = value; +} + +void put_u16(uint8_t *buf, uint32_t offset, uint16_t value) { + buf[offset + 0] = (uint8_t)(value); + buf[offset + 1] = (uint8_t)(value >> 8); +} + +void put_u32(uint8_t *buf, uint32_t offset, uint32_t value) { + buf[offset + 0] = (uint8_t)(value); + buf[offset + 1] = (uint8_t)(value >> 8); + buf[offset + 2] = (uint8_t)(value >> 16); + buf[offset + 3] = (uint8_t)(value >> 24); +} + +void put_u64(uint8_t *buf, uint32_t offset, uint64_t value) { + buf[offset + 0] = (uint8_t)(value); + buf[offset + 1] = (uint8_t)(value >> 8); + buf[offset + 2] = (uint8_t)(value >> 16); + buf[offset + 3] = (uint8_t)(value >> 24); + buf[offset + 4] = (uint8_t)(value >> 32); + buf[offset + 5] = (uint8_t)(value >> 40); + buf[offset + 6] = (uint8_t)(value >> 48); + buf[offset + 7] = (uint8_t)(value >> 56); +} + +void put_i8(uint8_t *buf, uint32_t offset, int8_t value) { + put_u8(buf, offset, (uint8_t)value); +} + +void put_i16(uint8_t *buf, uint32_t offset, int16_t value) { + put_u16(buf, offset, (uint16_t)value); +} + +void put_i32(uint8_t *buf, uint32_t offset, int32_t value) { + put_u32(buf, offset, (uint32_t)value); +} + +void put_i64(uint8_t *buf, uint32_t offset, int64_t value) { + put_u64(buf, offset, (uint64_t)value); +} + +/* -- get -- */ + +uint8_t get_u8(const uint8_t *buf, uint32_t offset) { + return buf[offset]; +} + +uint16_t get_u16(const uint8_t *buf, uint32_t offset) { + return (uint16_t)buf[offset + 0] + | ((uint16_t)buf[offset + 1] << 8); +} + +uint32_t get_u32(const uint8_t *buf, uint32_t offset) { + return (uint32_t)buf[offset + 0] + | ((uint32_t)buf[offset + 1] << 8) + | ((uint32_t)buf[offset + 2] << 16) + | ((uint32_t)buf[offset + 3] << 24); +} + +uint64_t get_u64(const uint8_t *buf, uint32_t offset) { + return (uint64_t)buf[offset + 0] + | ((uint64_t)buf[offset + 1] << 8) + | ((uint64_t)buf[offset + 2] << 16) + | ((uint64_t)buf[offset + 3] << 24) + | ((uint64_t)buf[offset + 4] << 32) + | ((uint64_t)buf[offset + 5] << 40) + | ((uint64_t)buf[offset + 6] << 48) + | ((uint64_t)buf[offset + 7] << 56); +} + +int8_t get_i8(const uint8_t *buf, uint32_t offset) { + return (int8_t)get_u8(buf, offset); +} + +int16_t get_i16(const uint8_t *buf, uint32_t offset) { + return (int16_t)get_u16(buf, offset); +} + +int32_t get_i32(const uint8_t *buf, uint32_t offset) { + return (int32_t)get_u32(buf, offset); +} + +int64_t get_i64(const uint8_t *buf, uint32_t offset) { + return (int64_t)get_u64(buf, offset); +}