From 1e507a15b83284ba3872a902ef00646e41a8d657 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 29 Nov 2025 22:17:07 +0000 Subject: [PATCH 1/6] Initial plan From 71fdcde8380d2fa8f4f142c77606d6b3b60ae2f4 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 29 Nov 2025 22:21:03 +0000 Subject: [PATCH 2/6] Initial analysis of BasicFrame and BasicFrameWithLen implementations Co-authored-by: rijesha <7819200+rijesha@users.noreply.github.com> --- package-lock.json | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 package-lock.json diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 00000000..12e3643b --- /dev/null +++ b/package-lock.json @@ -0,0 +1,6 @@ +{ + "name": "struct-frame", + "lockfileVersion": 3, + "requires": true, + "packages": {} +} From 0c22c051806a9968c8f7476c71552f86b2b66e37 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 29 Nov 2025 22:27:04 +0000 Subject: [PATCH 3/6] Add separate BasicFrame and BasicFrameWithLen implementations for C and C++ Co-authored-by: rijesha <7819200+rijesha@users.noreply.github.com> --- src/struct_frame/boilerplate/c/basic_frame.h | 401 +++++++++++++++++ .../boilerplate/c/basic_frame_with_len.h | 422 ++++++++++++++++++ .../boilerplate/cpp/basic_frame.hpp | 393 ++++++++++++++++ .../boilerplate/cpp/basic_frame_with_len.hpp | 401 +++++++++++++++++ 4 files changed, 1617 insertions(+) create mode 100644 src/struct_frame/boilerplate/c/basic_frame.h create mode 100644 src/struct_frame/boilerplate/c/basic_frame_with_len.h create mode 100644 src/struct_frame/boilerplate/cpp/basic_frame.hpp create mode 100644 src/struct_frame/boilerplate/cpp/basic_frame_with_len.hpp diff --git a/src/struct_frame/boilerplate/c/basic_frame.h b/src/struct_frame/boilerplate/c/basic_frame.h new file mode 100644 index 00000000..82c23149 --- /dev/null +++ b/src/struct_frame/boilerplate/c/basic_frame.h @@ -0,0 +1,401 @@ +/* + * BasicFrame - Simple frame format with CRC + * + * Format: [START1=0x90] [START2=0x91] [MSG_ID] [MSG...] [CRC1] [CRC2] + * + * This frame format requires a message ID to message length lookup function + * since the length is not included in the packet. + * + * Use Case: When all message lengths are known at compile time and bandwidth + * efficiency is important. + */ + +#pragma once + +#include +#include +#include +#include + +/* Frame constants */ +#define BASIC_FRAME_START_BYTE1 0x90 +#define BASIC_FRAME_START_BYTE2 0x91 +#define BASIC_FRAME_HEADER_SIZE 3 /* start1 + start2 + msg_id */ +#define BASIC_FRAME_FOOTER_SIZE 2 /* crc1 + crc2 */ +#define BASIC_FRAME_OVERHEAD (BASIC_FRAME_HEADER_SIZE + BASIC_FRAME_FOOTER_SIZE) + +/* Checksum result */ +typedef struct basic_frame_checksum { + uint8_t byte1; + uint8_t byte2; +} basic_frame_checksum_t; + +/* Parse result */ +typedef struct basic_frame_msg_info { + bool valid; + uint8_t msg_id; + uint8_t msg_len; + uint8_t* msg_data; +} basic_frame_msg_info_t; + +/* Parser state enumeration */ +typedef enum basic_frame_parser_state { + BASIC_FRAME_LOOKING_FOR_START1 = 0, + BASIC_FRAME_LOOKING_FOR_START2 = 1, + BASIC_FRAME_GETTING_MSG_ID = 2, + BASIC_FRAME_GETTING_PAYLOAD = 3 +} basic_frame_parser_state_t; + +/* Parser state structure */ +typedef struct basic_frame_parser { + basic_frame_parser_state_t state; + uint8_t* buffer; + size_t buffer_max_size; + size_t buffer_index; + size_t packet_size; + uint8_t msg_id; + /* User-provided function to get message length from msg_id */ + bool (*get_msg_length)(uint8_t msg_id, size_t* length); +} basic_frame_parser_t; + +/* Encode buffer structure */ +typedef struct basic_frame_encode_buffer { + uint8_t* data; + size_t max_size; + size_t size; + bool in_progress; +} basic_frame_encode_buffer_t; + +/*=========================================================================== + * Checksum Calculation + *===========================================================================*/ + +/** + * Calculate Fletcher-16 checksum over the given data + */ +static inline basic_frame_checksum_t basic_frame_checksum(const uint8_t* data, size_t length) { + basic_frame_checksum_t ck = {0, 0}; + for (size_t i = 0; i < length; i++) { + ck.byte1 = (uint8_t)(ck.byte1 + data[i]); + ck.byte2 = (uint8_t)(ck.byte2 + ck.byte1); + } + return ck; +} + +/*=========================================================================== + * Encoding Functions + *===========================================================================*/ + +/** + * Initialize an encode buffer + */ +static inline void basic_frame_encode_init(basic_frame_encode_buffer_t* buf, uint8_t* data, size_t max_size) { + buf->data = data; + buf->max_size = max_size; + buf->size = 0; + buf->in_progress = false; +} + +/** + * Reset the encode buffer + */ +static inline void basic_frame_encode_reset(basic_frame_encode_buffer_t* buf) { + buf->size = 0; + buf->in_progress = false; +} + +/** + * Encode a message into the buffer + * Returns the number of bytes written, or 0 on failure + */ +static inline size_t basic_frame_encode(uint8_t* buffer, size_t buffer_size, + uint8_t msg_id, const uint8_t* msg, size_t msg_size) { + size_t total_size = BASIC_FRAME_OVERHEAD + msg_size; + if (buffer_size < total_size) { + return 0; + } + + /* Write header */ + buffer[0] = BASIC_FRAME_START_BYTE1; + buffer[1] = BASIC_FRAME_START_BYTE2; + buffer[2] = msg_id; + + /* Write message data */ + if (msg_size > 0 && msg != NULL) { + memcpy(buffer + BASIC_FRAME_HEADER_SIZE, msg, msg_size); + } + + /* Calculate checksum over msg_id + msg data */ + basic_frame_checksum_t ck = basic_frame_checksum(buffer + 2, msg_size + 1); + buffer[BASIC_FRAME_HEADER_SIZE + msg_size] = ck.byte1; + buffer[BASIC_FRAME_HEADER_SIZE + msg_size + 1] = ck.byte2; + + return total_size; +} + +/** + * Encode a message using the encode buffer structure + */ +static inline bool basic_frame_encode_msg(basic_frame_encode_buffer_t* buf, + uint8_t msg_id, const void* msg, size_t msg_size) { + if (buf->in_progress) { + return false; + } + + size_t written = basic_frame_encode(buf->data + buf->size, + buf->max_size - buf->size, + msg_id, (const uint8_t*)msg, msg_size); + if (written == 0) { + return false; + } + + buf->size += written; + return true; +} + +/** + * Reserve space in buffer for zero-copy encoding + * Returns pointer to message data area, or NULL on failure + */ +static inline uint8_t* basic_frame_encode_reserve(basic_frame_encode_buffer_t* buf, + uint8_t msg_id, size_t msg_size) { + if (buf->in_progress) { + return NULL; + } + + size_t total_size = BASIC_FRAME_OVERHEAD + msg_size; + if (buf->size + total_size > buf->max_size) { + return NULL; + } + + uint8_t* packet_start = buf->data + buf->size; + + /* Write header */ + packet_start[0] = BASIC_FRAME_START_BYTE1; + packet_start[1] = BASIC_FRAME_START_BYTE2; + packet_start[2] = msg_id; + + buf->in_progress = true; + return packet_start + BASIC_FRAME_HEADER_SIZE; +} + +/** + * Finish a reserved encoding by adding checksum + */ +static inline bool basic_frame_encode_finish(basic_frame_encode_buffer_t* buf, size_t msg_size) { + if (!buf->in_progress) { + return false; + } + + uint8_t* packet_start = buf->data + buf->size; + + /* Calculate checksum over msg_id + msg data */ + basic_frame_checksum_t ck = basic_frame_checksum(packet_start + 2, msg_size + 1); + packet_start[BASIC_FRAME_HEADER_SIZE + msg_size] = ck.byte1; + packet_start[BASIC_FRAME_HEADER_SIZE + msg_size + 1] = ck.byte2; + + buf->size += BASIC_FRAME_OVERHEAD + msg_size; + buf->in_progress = false; + return true; +} + +/*=========================================================================== + * Decoding/Parsing Functions + *===========================================================================*/ + +/** + * Initialize a parser + * + * @param parser Parser structure to initialize + * @param buffer Buffer for storing incoming packet data + * @param buffer_size Maximum size of the buffer + * @param get_msg_length User-provided function to map msg_id to msg_length + */ +static inline void basic_frame_parser_init(basic_frame_parser_t* parser, + uint8_t* buffer, size_t buffer_size, + bool (*get_msg_length)(uint8_t msg_id, size_t* length)) { + parser->state = BASIC_FRAME_LOOKING_FOR_START1; + parser->buffer = buffer; + parser->buffer_max_size = buffer_size; + parser->buffer_index = 0; + parser->packet_size = 0; + parser->msg_id = 0; + parser->get_msg_length = get_msg_length; +} + +/** + * Reset parser state + */ +static inline void basic_frame_parser_reset(basic_frame_parser_t* parser) { + parser->state = BASIC_FRAME_LOOKING_FOR_START1; + parser->buffer_index = 0; + parser->packet_size = 0; + parser->msg_id = 0; +} + +/** + * Parse a single byte + * Returns a msg_info with valid=true when a complete valid message is received + */ +static inline basic_frame_msg_info_t basic_frame_parse_byte(basic_frame_parser_t* parser, uint8_t byte) { + basic_frame_msg_info_t result = {false, 0, 0, NULL}; + + switch (parser->state) { + case BASIC_FRAME_LOOKING_FOR_START1: + if (byte == BASIC_FRAME_START_BYTE1) { + parser->buffer[0] = byte; + parser->buffer_index = 1; + parser->state = BASIC_FRAME_LOOKING_FOR_START2; + } + break; + + case BASIC_FRAME_LOOKING_FOR_START2: + if (byte == BASIC_FRAME_START_BYTE2) { + parser->buffer[1] = byte; + parser->buffer_index = 2; + parser->state = BASIC_FRAME_GETTING_MSG_ID; + } else if (byte == BASIC_FRAME_START_BYTE1) { + /* Could be new packet starting */ + parser->buffer[0] = byte; + parser->buffer_index = 1; + } else { + parser->state = BASIC_FRAME_LOOKING_FOR_START1; + } + break; + + case BASIC_FRAME_GETTING_MSG_ID: + parser->buffer[2] = byte; + parser->buffer_index = 3; + parser->msg_id = byte; + + /* Get message length from user-provided function */ + size_t msg_length = 0; + if (parser->get_msg_length && parser->get_msg_length(byte, &msg_length)) { + parser->packet_size = BASIC_FRAME_OVERHEAD + msg_length; + if (parser->packet_size <= parser->buffer_max_size) { + parser->state = BASIC_FRAME_GETTING_PAYLOAD; + } else { + /* Packet too large for buffer */ + parser->state = BASIC_FRAME_LOOKING_FOR_START1; + } + } else { + /* Unknown message ID */ + parser->state = BASIC_FRAME_LOOKING_FOR_START1; + } + break; + + case BASIC_FRAME_GETTING_PAYLOAD: + if (parser->buffer_index < parser->buffer_max_size) { + parser->buffer[parser->buffer_index++] = byte; + } + + if (parser->buffer_index >= parser->packet_size) { + /* Packet complete, validate checksum */ + size_t msg_length = parser->packet_size - BASIC_FRAME_OVERHEAD; + basic_frame_checksum_t ck = basic_frame_checksum(parser->buffer + 2, msg_length + 1); + + if (ck.byte1 == parser->buffer[parser->packet_size - 2] && + ck.byte2 == parser->buffer[parser->packet_size - 1]) { + result.valid = true; + result.msg_id = parser->msg_id; + result.msg_len = (uint8_t)msg_length; + result.msg_data = parser->buffer + BASIC_FRAME_HEADER_SIZE; + } + + parser->state = BASIC_FRAME_LOOKING_FOR_START1; + } + break; + } + + return result; +} + +/** + * Parse a buffer of bytes + * Continues parsing from r_loc and returns when a message is found or buffer is exhausted + * Updates r_loc to the position after the found message + * Returns msg_info with valid=true when a complete valid message is found + */ +static inline basic_frame_msg_info_t basic_frame_parse_buffer(basic_frame_parser_t* parser, + const uint8_t* data, size_t data_size, + size_t* r_loc) { + basic_frame_msg_info_t result = {false, 0, 0, NULL}; + + while (*r_loc < data_size) { + result = basic_frame_parse_byte(parser, data[*r_loc]); + (*r_loc)++; + if (result.valid) { + return result; + } + } + + return result; +} + +/** + * Validate a complete packet in a buffer + * Useful for validating data received all at once + */ +static inline basic_frame_msg_info_t basic_frame_validate_packet(const uint8_t* buffer, size_t length) { + basic_frame_msg_info_t result = {false, 0, 0, NULL}; + + if (length < BASIC_FRAME_OVERHEAD) { + return result; + } + + /* Check start bytes */ + if (buffer[0] != BASIC_FRAME_START_BYTE1 || buffer[1] != BASIC_FRAME_START_BYTE2) { + return result; + } + + size_t msg_length = length - BASIC_FRAME_OVERHEAD; + + /* Validate checksum */ + basic_frame_checksum_t ck = basic_frame_checksum(buffer + 2, msg_length + 1); + if (ck.byte1 == buffer[length - 2] && ck.byte2 == buffer[length - 1]) { + result.valid = true; + result.msg_id = buffer[2]; + result.msg_len = (uint8_t)msg_length; + result.msg_data = (uint8_t*)(buffer + BASIC_FRAME_HEADER_SIZE); + } + + return result; +} + +/*=========================================================================== + * Helper Macros for Message Types + *===========================================================================*/ + +/** + * Generate helper functions for a specific message type + * + * Usage: BASIC_FRAME_MESSAGE_HELPERS(my_message, MyMessage, 10, 1) + * + * This creates: + * - my_message_encode(buf, msg) - Encode message to buffer + * - my_message_reserve(buf, &msg_ptr) - Reserve space for zero-copy + * - my_message_finish(buf) - Finish reserved encoding + * - my_message_get(info) - Get message copy from parse result + * - my_message_get_ref(info) - Get message pointer from parse result + */ +#define BASIC_FRAME_MESSAGE_HELPERS(funcname, typename, msg_size, msg_id) \ + static inline bool funcname##_encode(basic_frame_encode_buffer_t* buf, const typename* msg) { \ + return basic_frame_encode_msg(buf, (msg_id), msg, (msg_size)); \ + } \ + static inline bool funcname##_reserve(basic_frame_encode_buffer_t* buf, typename** msg) { \ + uint8_t* ptr = basic_frame_encode_reserve(buf, (msg_id), (msg_size)); \ + if (ptr) { \ + *msg = (typename*)ptr; \ + return true; \ + } \ + return false; \ + } \ + static inline bool funcname##_finish(basic_frame_encode_buffer_t* buf) { \ + return basic_frame_encode_finish(buf, (msg_size)); \ + } \ + static inline typename funcname##_get(basic_frame_msg_info_t info) { \ + return *(typename*)(info.msg_data); \ + } \ + static inline typename* funcname##_get_ref(basic_frame_msg_info_t info) { \ + return (typename*)(info.msg_data); \ + } diff --git a/src/struct_frame/boilerplate/c/basic_frame_with_len.h b/src/struct_frame/boilerplate/c/basic_frame_with_len.h new file mode 100644 index 00000000..1158da14 --- /dev/null +++ b/src/struct_frame/boilerplate/c/basic_frame_with_len.h @@ -0,0 +1,422 @@ +/* + * BasicFrameWithLen - Frame format with embedded length and CRC + * + * Format: [START1=0x90] [START2=0x92] [MSG_ID] [LEN] [MSG...] [CRC1] [CRC2] + * + * This frame format includes the message length in the packet header, + * so no msg_id to length lookup is required. + * + * Use Case: When message lengths may vary or are not known at compile time. + */ + +#pragma once + +#include +#include +#include +#include + +/* Frame constants */ +#define BASIC_FRAME_WITH_LEN_START_BYTE1 0x90 +#define BASIC_FRAME_WITH_LEN_START_BYTE2 0x92 +#define BASIC_FRAME_WITH_LEN_HEADER_SIZE 4 /* start1 + start2 + msg_id + len */ +#define BASIC_FRAME_WITH_LEN_FOOTER_SIZE 2 /* crc1 + crc2 */ +#define BASIC_FRAME_WITH_LEN_OVERHEAD (BASIC_FRAME_WITH_LEN_HEADER_SIZE + BASIC_FRAME_WITH_LEN_FOOTER_SIZE) +#define BASIC_FRAME_WITH_LEN_MAX_MSG_SIZE 255 /* Limited by 1-byte length field */ + +/* Checksum result */ +typedef struct basic_frame_with_len_checksum { + uint8_t byte1; + uint8_t byte2; +} basic_frame_with_len_checksum_t; + +/* Parse result */ +typedef struct basic_frame_with_len_msg_info { + bool valid; + uint8_t msg_id; + uint8_t msg_len; + uint8_t* msg_data; +} basic_frame_with_len_msg_info_t; + +/* Parser state enumeration */ +typedef enum basic_frame_with_len_parser_state { + BASIC_FRAME_WITH_LEN_LOOKING_FOR_START1 = 0, + BASIC_FRAME_WITH_LEN_LOOKING_FOR_START2 = 1, + BASIC_FRAME_WITH_LEN_GETTING_MSG_ID = 2, + BASIC_FRAME_WITH_LEN_GETTING_LENGTH = 3, + BASIC_FRAME_WITH_LEN_GETTING_PAYLOAD = 4 +} basic_frame_with_len_parser_state_t; + +/* Parser state structure */ +typedef struct basic_frame_with_len_parser { + basic_frame_with_len_parser_state_t state; + uint8_t* buffer; + size_t buffer_max_size; + size_t buffer_index; + size_t packet_size; + uint8_t msg_id; + uint8_t msg_len; +} basic_frame_with_len_parser_t; + +/* Encode buffer structure */ +typedef struct basic_frame_with_len_encode_buffer { + uint8_t* data; + size_t max_size; + size_t size; + bool in_progress; +} basic_frame_with_len_encode_buffer_t; + +/*=========================================================================== + * Checksum Calculation + *===========================================================================*/ + +/** + * Calculate Fletcher-16 checksum over the given data + */ +static inline basic_frame_with_len_checksum_t basic_frame_with_len_checksum(const uint8_t* data, size_t length) { + basic_frame_with_len_checksum_t ck = {0, 0}; + for (size_t i = 0; i < length; i++) { + ck.byte1 = (uint8_t)(ck.byte1 + data[i]); + ck.byte2 = (uint8_t)(ck.byte2 + ck.byte1); + } + return ck; +} + +/*=========================================================================== + * Encoding Functions + *===========================================================================*/ + +/** + * Initialize an encode buffer + */ +static inline void basic_frame_with_len_encode_init(basic_frame_with_len_encode_buffer_t* buf, + uint8_t* data, size_t max_size) { + buf->data = data; + buf->max_size = max_size; + buf->size = 0; + buf->in_progress = false; +} + +/** + * Reset the encode buffer + */ +static inline void basic_frame_with_len_encode_reset(basic_frame_with_len_encode_buffer_t* buf) { + buf->size = 0; + buf->in_progress = false; +} + +/** + * Encode a message into the buffer + * Returns the number of bytes written, or 0 on failure + */ +static inline size_t basic_frame_with_len_encode(uint8_t* buffer, size_t buffer_size, + uint8_t msg_id, const uint8_t* msg, uint8_t msg_size) { + size_t total_size = BASIC_FRAME_WITH_LEN_OVERHEAD + msg_size; + if (buffer_size < total_size) { + return 0; + } + + /* Write header */ + buffer[0] = BASIC_FRAME_WITH_LEN_START_BYTE1; + buffer[1] = BASIC_FRAME_WITH_LEN_START_BYTE2; + buffer[2] = msg_id; + buffer[3] = msg_size; + + /* Write message data */ + if (msg_size > 0 && msg != NULL) { + memcpy(buffer + BASIC_FRAME_WITH_LEN_HEADER_SIZE, msg, msg_size); + } + + /* Calculate checksum over msg_id + len + msg data */ + basic_frame_with_len_checksum_t ck = basic_frame_with_len_checksum(buffer + 2, msg_size + 2); + buffer[BASIC_FRAME_WITH_LEN_HEADER_SIZE + msg_size] = ck.byte1; + buffer[BASIC_FRAME_WITH_LEN_HEADER_SIZE + msg_size + 1] = ck.byte2; + + return total_size; +} + +/** + * Encode a message using the encode buffer structure + */ +static inline bool basic_frame_with_len_encode_msg(basic_frame_with_len_encode_buffer_t* buf, + uint8_t msg_id, const void* msg, uint8_t msg_size) { + if (buf->in_progress) { + return false; + } + + size_t written = basic_frame_with_len_encode(buf->data + buf->size, + buf->max_size - buf->size, + msg_id, (const uint8_t*)msg, msg_size); + if (written == 0) { + return false; + } + + buf->size += written; + return true; +} + +/** + * Reserve space in buffer for zero-copy encoding + * Returns pointer to message data area, or NULL on failure + */ +static inline uint8_t* basic_frame_with_len_encode_reserve(basic_frame_with_len_encode_buffer_t* buf, + uint8_t msg_id, uint8_t msg_size) { + if (buf->in_progress) { + return NULL; + } + + size_t total_size = BASIC_FRAME_WITH_LEN_OVERHEAD + msg_size; + if (buf->size + total_size > buf->max_size) { + return NULL; + } + + uint8_t* packet_start = buf->data + buf->size; + + /* Write header */ + packet_start[0] = BASIC_FRAME_WITH_LEN_START_BYTE1; + packet_start[1] = BASIC_FRAME_WITH_LEN_START_BYTE2; + packet_start[2] = msg_id; + packet_start[3] = msg_size; + + buf->in_progress = true; + return packet_start + BASIC_FRAME_WITH_LEN_HEADER_SIZE; +} + +/** + * Finish a reserved encoding by adding checksum + */ +static inline bool basic_frame_with_len_encode_finish(basic_frame_with_len_encode_buffer_t* buf, uint8_t msg_size) { + if (!buf->in_progress) { + return false; + } + + uint8_t* packet_start = buf->data + buf->size; + + /* Calculate checksum over msg_id + len + msg data */ + basic_frame_with_len_checksum_t ck = basic_frame_with_len_checksum(packet_start + 2, msg_size + 2); + packet_start[BASIC_FRAME_WITH_LEN_HEADER_SIZE + msg_size] = ck.byte1; + packet_start[BASIC_FRAME_WITH_LEN_HEADER_SIZE + msg_size + 1] = ck.byte2; + + buf->size += BASIC_FRAME_WITH_LEN_OVERHEAD + msg_size; + buf->in_progress = false; + return true; +} + +/*=========================================================================== + * Decoding/Parsing Functions + *===========================================================================*/ + +/** + * Initialize a parser + * + * @param parser Parser structure to initialize + * @param buffer Buffer for storing incoming packet data + * @param buffer_size Maximum size of the buffer + */ +static inline void basic_frame_with_len_parser_init(basic_frame_with_len_parser_t* parser, + uint8_t* buffer, size_t buffer_size) { + parser->state = BASIC_FRAME_WITH_LEN_LOOKING_FOR_START1; + parser->buffer = buffer; + parser->buffer_max_size = buffer_size; + parser->buffer_index = 0; + parser->packet_size = 0; + parser->msg_id = 0; + parser->msg_len = 0; +} + +/** + * Reset parser state + */ +static inline void basic_frame_with_len_parser_reset(basic_frame_with_len_parser_t* parser) { + parser->state = BASIC_FRAME_WITH_LEN_LOOKING_FOR_START1; + parser->buffer_index = 0; + parser->packet_size = 0; + parser->msg_id = 0; + parser->msg_len = 0; +} + +/** + * Parse a single byte + * Returns a msg_info with valid=true when a complete valid message is received + */ +static inline basic_frame_with_len_msg_info_t basic_frame_with_len_parse_byte( + basic_frame_with_len_parser_t* parser, uint8_t byte) { + + basic_frame_with_len_msg_info_t result = {false, 0, 0, NULL}; + + switch (parser->state) { + case BASIC_FRAME_WITH_LEN_LOOKING_FOR_START1: + if (byte == BASIC_FRAME_WITH_LEN_START_BYTE1) { + parser->buffer[0] = byte; + parser->buffer_index = 1; + parser->state = BASIC_FRAME_WITH_LEN_LOOKING_FOR_START2; + } + break; + + case BASIC_FRAME_WITH_LEN_LOOKING_FOR_START2: + if (byte == BASIC_FRAME_WITH_LEN_START_BYTE2) { + parser->buffer[1] = byte; + parser->buffer_index = 2; + parser->state = BASIC_FRAME_WITH_LEN_GETTING_MSG_ID; + } else if (byte == BASIC_FRAME_WITH_LEN_START_BYTE1) { + /* Could be new packet starting */ + parser->buffer[0] = byte; + parser->buffer_index = 1; + } else { + parser->state = BASIC_FRAME_WITH_LEN_LOOKING_FOR_START1; + } + break; + + case BASIC_FRAME_WITH_LEN_GETTING_MSG_ID: + parser->buffer[2] = byte; + parser->buffer_index = 3; + parser->msg_id = byte; + parser->state = BASIC_FRAME_WITH_LEN_GETTING_LENGTH; + break; + + case BASIC_FRAME_WITH_LEN_GETTING_LENGTH: + parser->buffer[3] = byte; + parser->buffer_index = 4; + parser->msg_len = byte; + parser->packet_size = BASIC_FRAME_WITH_LEN_OVERHEAD + byte; + + if (parser->packet_size <= parser->buffer_max_size) { + if (byte == 0) { + /* Zero-length message, go straight to validation */ + basic_frame_with_len_checksum_t ck = basic_frame_with_len_checksum(parser->buffer + 2, 2); + /* Need to wait for CRC bytes still */ + parser->state = BASIC_FRAME_WITH_LEN_GETTING_PAYLOAD; + } else { + parser->state = BASIC_FRAME_WITH_LEN_GETTING_PAYLOAD; + } + } else { + /* Packet too large for buffer */ + parser->state = BASIC_FRAME_WITH_LEN_LOOKING_FOR_START1; + } + break; + + case BASIC_FRAME_WITH_LEN_GETTING_PAYLOAD: + if (parser->buffer_index < parser->buffer_max_size) { + parser->buffer[parser->buffer_index++] = byte; + } + + if (parser->buffer_index >= parser->packet_size) { + /* Packet complete, validate checksum */ + basic_frame_with_len_checksum_t ck = basic_frame_with_len_checksum( + parser->buffer + 2, parser->msg_len + 2); + + if (ck.byte1 == parser->buffer[parser->packet_size - 2] && + ck.byte2 == parser->buffer[parser->packet_size - 1]) { + result.valid = true; + result.msg_id = parser->msg_id; + result.msg_len = parser->msg_len; + result.msg_data = parser->buffer + BASIC_FRAME_WITH_LEN_HEADER_SIZE; + } + + parser->state = BASIC_FRAME_WITH_LEN_LOOKING_FOR_START1; + } + break; + } + + return result; +} + +/** + * Parse a buffer of bytes + * Continues parsing from r_loc and returns when a message is found or buffer is exhausted + * Updates r_loc to the position after the found message + * Returns msg_info with valid=true when a complete valid message is found + */ +static inline basic_frame_with_len_msg_info_t basic_frame_with_len_parse_buffer( + basic_frame_with_len_parser_t* parser, + const uint8_t* data, size_t data_size, + size_t* r_loc) { + + basic_frame_with_len_msg_info_t result = {false, 0, 0, NULL}; + + while (*r_loc < data_size) { + result = basic_frame_with_len_parse_byte(parser, data[*r_loc]); + (*r_loc)++; + if (result.valid) { + return result; + } + } + + return result; +} + +/** + * Validate a complete packet in a buffer + * Useful for validating data received all at once + */ +static inline basic_frame_with_len_msg_info_t basic_frame_with_len_validate_packet( + const uint8_t* buffer, size_t length) { + + basic_frame_with_len_msg_info_t result = {false, 0, 0, NULL}; + + if (length < BASIC_FRAME_WITH_LEN_OVERHEAD) { + return result; + } + + /* Check start bytes */ + if (buffer[0] != BASIC_FRAME_WITH_LEN_START_BYTE1 || + buffer[1] != BASIC_FRAME_WITH_LEN_START_BYTE2) { + return result; + } + + uint8_t msg_len = buffer[3]; + size_t expected_length = BASIC_FRAME_WITH_LEN_OVERHEAD + msg_len; + + if (length != expected_length) { + return result; + } + + /* Validate checksum over msg_id + len + msg data */ + basic_frame_with_len_checksum_t ck = basic_frame_with_len_checksum(buffer + 2, msg_len + 2); + if (ck.byte1 == buffer[length - 2] && ck.byte2 == buffer[length - 1]) { + result.valid = true; + result.msg_id = buffer[2]; + result.msg_len = msg_len; + result.msg_data = (uint8_t*)(buffer + BASIC_FRAME_WITH_LEN_HEADER_SIZE); + } + + return result; +} + +/*=========================================================================== + * Helper Macros for Message Types + *===========================================================================*/ + +/** + * Generate helper functions for a specific message type + * + * Usage: BASIC_FRAME_WITH_LEN_MESSAGE_HELPERS(my_message, MyMessage, 10, 1) + * + * This creates: + * - my_message_encode(buf, msg) - Encode message to buffer + * - my_message_reserve(buf, &msg_ptr) - Reserve space for zero-copy + * - my_message_finish(buf) - Finish reserved encoding + * - my_message_get(info) - Get message copy from parse result + * - my_message_get_ref(info) - Get message pointer from parse result + */ +#define BASIC_FRAME_WITH_LEN_MESSAGE_HELPERS(funcname, typename, msg_size, msg_id) \ + static inline bool funcname##_encode(basic_frame_with_len_encode_buffer_t* buf, const typename* msg) { \ + return basic_frame_with_len_encode_msg(buf, (msg_id), msg, (msg_size)); \ + } \ + static inline bool funcname##_reserve(basic_frame_with_len_encode_buffer_t* buf, typename** msg) { \ + uint8_t* ptr = basic_frame_with_len_encode_reserve(buf, (msg_id), (msg_size)); \ + if (ptr) { \ + *msg = (typename*)ptr; \ + return true; \ + } \ + return false; \ + } \ + static inline bool funcname##_finish(basic_frame_with_len_encode_buffer_t* buf) { \ + return basic_frame_with_len_encode_finish(buf, (msg_size)); \ + } \ + static inline typename funcname##_get(basic_frame_with_len_msg_info_t info) { \ + return *(typename*)(info.msg_data); \ + } \ + static inline typename* funcname##_get_ref(basic_frame_with_len_msg_info_t info) { \ + return (typename*)(info.msg_data); \ + } diff --git a/src/struct_frame/boilerplate/cpp/basic_frame.hpp b/src/struct_frame/boilerplate/cpp/basic_frame.hpp new file mode 100644 index 00000000..27004904 --- /dev/null +++ b/src/struct_frame/boilerplate/cpp/basic_frame.hpp @@ -0,0 +1,393 @@ +/* + * BasicFrame - Simple frame format with CRC (C++ version) + * + * Format: [START1=0x90] [START2=0x91] [MSG_ID] [MSG...] [CRC1] [CRC2] + * + * This frame format requires a message ID to message length lookup function + * since the length is not included in the packet. + * + * Use Case: When all message lengths are known at compile time and bandwidth + * efficiency is important. + */ + +#pragma once + +#include +#include +#include +#include + +namespace StructFrame { + +/* Frame constants */ +constexpr uint8_t BASIC_FRAME_START_BYTE1 = 0x90; +constexpr uint8_t BASIC_FRAME_START_BYTE2 = 0x91; +constexpr size_t BASIC_FRAME_HEADER_SIZE = 3; /* start1 + start2 + msg_id */ +constexpr size_t BASIC_FRAME_FOOTER_SIZE = 2; /* crc1 + crc2 */ +constexpr size_t BASIC_FRAME_OVERHEAD = BASIC_FRAME_HEADER_SIZE + BASIC_FRAME_FOOTER_SIZE; + +/* Checksum result */ +struct BasicFrameChecksum { + uint8_t byte1; + uint8_t byte2; +}; + +/* Parse result */ +struct BasicFrameMsgInfo { + bool valid; + uint8_t msg_id; + uint8_t msg_len; + uint8_t* msg_data; + + BasicFrameMsgInfo() : valid(false), msg_id(0), msg_len(0), msg_data(nullptr) {} + BasicFrameMsgInfo(bool v, uint8_t id, uint8_t len, uint8_t* data) + : valid(v), msg_id(id), msg_len(len), msg_data(data) {} +}; + +/* Parser state enumeration */ +enum class BasicFrameParserState : uint8_t { + LookingForStart1 = 0, + LookingForStart2 = 1, + GettingMsgId = 2, + GettingPayload = 3 +}; + +/*=========================================================================== + * Checksum Calculation + *===========================================================================*/ + +/** + * Calculate Fletcher-16 checksum over the given data + */ +inline BasicFrameChecksum basic_frame_checksum(const uint8_t* data, size_t length) { + BasicFrameChecksum ck{0, 0}; + for (size_t i = 0; i < length; i++) { + ck.byte1 = static_cast(ck.byte1 + data[i]); + ck.byte2 = static_cast(ck.byte2 + ck.byte1); + } + return ck; +} + +/*=========================================================================== + * Encode Buffer Class + *===========================================================================*/ + +class BasicFrameEncodeBuffer { +public: + BasicFrameEncodeBuffer(uint8_t* data, size_t max_size) + : data_(data), max_size_(max_size), size_(0), in_progress_(false) {} + + void reset() { + size_ = 0; + in_progress_ = false; + } + + uint8_t* data() { return data_; } + const uint8_t* data() const { return data_; } + size_t size() const { return size_; } + size_t max_size() const { return max_size_; } + bool in_progress() const { return in_progress_; } + + /** + * Encode a message into the buffer + */ + bool encode(uint8_t msg_id, const void* msg, size_t msg_size) { + if (in_progress_) { + return false; + } + + size_t total_size = BASIC_FRAME_OVERHEAD + msg_size; + if (size_ + total_size > max_size_) { + return false; + } + + uint8_t* packet_start = data_ + size_; + + /* Write header */ + packet_start[0] = BASIC_FRAME_START_BYTE1; + packet_start[1] = BASIC_FRAME_START_BYTE2; + packet_start[2] = msg_id; + + /* Write message data */ + if (msg_size > 0 && msg != nullptr) { + std::memcpy(packet_start + BASIC_FRAME_HEADER_SIZE, msg, msg_size); + } + + /* Calculate checksum over msg_id + msg data */ + BasicFrameChecksum ck = basic_frame_checksum(packet_start + 2, msg_size + 1); + packet_start[BASIC_FRAME_HEADER_SIZE + msg_size] = ck.byte1; + packet_start[BASIC_FRAME_HEADER_SIZE + msg_size + 1] = ck.byte2; + + size_ += total_size; + return true; + } + + /** + * Reserve space in buffer for zero-copy encoding + * Returns pointer to message data area, or nullptr on failure + */ + uint8_t* reserve(uint8_t msg_id, size_t msg_size) { + if (in_progress_) { + return nullptr; + } + + size_t total_size = BASIC_FRAME_OVERHEAD + msg_size; + if (size_ + total_size > max_size_) { + return nullptr; + } + + uint8_t* packet_start = data_ + size_; + + /* Write header */ + packet_start[0] = BASIC_FRAME_START_BYTE1; + packet_start[1] = BASIC_FRAME_START_BYTE2; + packet_start[2] = msg_id; + + in_progress_ = true; + reserved_msg_size_ = msg_size; + return packet_start + BASIC_FRAME_HEADER_SIZE; + } + + /** + * Finish a reserved encoding by adding checksum + */ + bool finish() { + if (!in_progress_) { + return false; + } + + uint8_t* packet_start = data_ + size_; + + /* Calculate checksum over msg_id + msg data */ + BasicFrameChecksum ck = basic_frame_checksum(packet_start + 2, reserved_msg_size_ + 1); + packet_start[BASIC_FRAME_HEADER_SIZE + reserved_msg_size_] = ck.byte1; + packet_start[BASIC_FRAME_HEADER_SIZE + reserved_msg_size_ + 1] = ck.byte2; + + size_ += BASIC_FRAME_OVERHEAD + reserved_msg_size_; + in_progress_ = false; + return true; + } + +private: + uint8_t* data_; + size_t max_size_; + size_t size_; + bool in_progress_; + size_t reserved_msg_size_ = 0; +}; + +/*=========================================================================== + * Parser Class + *===========================================================================*/ + +class BasicFrameParser { +public: + using MsgLengthCallback = std::function; + + BasicFrameParser(uint8_t* buffer, size_t buffer_size, MsgLengthCallback msg_length_cb) + : state_(BasicFrameParserState::LookingForStart1), + buffer_(buffer), + buffer_max_size_(buffer_size), + buffer_index_(0), + packet_size_(0), + msg_id_(0), + get_msg_length_(std::move(msg_length_cb)) {} + + void reset() { + state_ = BasicFrameParserState::LookingForStart1; + buffer_index_ = 0; + packet_size_ = 0; + msg_id_ = 0; + } + + /** + * Parse a single byte + * Returns a BasicFrameMsgInfo with valid=true when a complete valid message is received + */ + BasicFrameMsgInfo parse_byte(uint8_t byte) { + BasicFrameMsgInfo result; + + switch (state_) { + case BasicFrameParserState::LookingForStart1: + if (byte == BASIC_FRAME_START_BYTE1) { + buffer_[0] = byte; + buffer_index_ = 1; + state_ = BasicFrameParserState::LookingForStart2; + } + break; + + case BasicFrameParserState::LookingForStart2: + if (byte == BASIC_FRAME_START_BYTE2) { + buffer_[1] = byte; + buffer_index_ = 2; + state_ = BasicFrameParserState::GettingMsgId; + } else if (byte == BASIC_FRAME_START_BYTE1) { + buffer_[0] = byte; + buffer_index_ = 1; + } else { + state_ = BasicFrameParserState::LookingForStart1; + } + break; + + case BasicFrameParserState::GettingMsgId: { + buffer_[2] = byte; + buffer_index_ = 3; + msg_id_ = byte; + + size_t msg_length = 0; + if (get_msg_length_ && get_msg_length_(byte, &msg_length)) { + packet_size_ = BASIC_FRAME_OVERHEAD + msg_length; + if (packet_size_ <= buffer_max_size_) { + state_ = BasicFrameParserState::GettingPayload; + } else { + state_ = BasicFrameParserState::LookingForStart1; + } + } else { + state_ = BasicFrameParserState::LookingForStart1; + } + break; + } + + case BasicFrameParserState::GettingPayload: + if (buffer_index_ < buffer_max_size_) { + buffer_[buffer_index_++] = byte; + } + + if (buffer_index_ >= packet_size_) { + size_t msg_length = packet_size_ - BASIC_FRAME_OVERHEAD; + BasicFrameChecksum ck = basic_frame_checksum(buffer_ + 2, msg_length + 1); + + if (ck.byte1 == buffer_[packet_size_ - 2] && + ck.byte2 == buffer_[packet_size_ - 1]) { + result.valid = true; + result.msg_id = msg_id_; + result.msg_len = static_cast(msg_length); + result.msg_data = buffer_ + BASIC_FRAME_HEADER_SIZE; + } + + state_ = BasicFrameParserState::LookingForStart1; + } + break; + } + + return result; + } + + /** + * Parse a buffer of bytes + */ + BasicFrameMsgInfo parse_buffer(const uint8_t* data, size_t data_size, size_t& r_loc) { + BasicFrameMsgInfo result; + + while (r_loc < data_size) { + result = parse_byte(data[r_loc]); + r_loc++; + if (result.valid) { + return result; + } + } + + return result; + } + +private: + BasicFrameParserState state_; + uint8_t* buffer_; + size_t buffer_max_size_; + size_t buffer_index_; + size_t packet_size_; + uint8_t msg_id_; + MsgLengthCallback get_msg_length_; +}; + +/*=========================================================================== + * Static Helper Functions + *===========================================================================*/ + +/** + * Encode a message directly into a buffer + * Returns the number of bytes written, or 0 on failure + */ +inline size_t basic_frame_encode(uint8_t* buffer, size_t buffer_size, + uint8_t msg_id, const uint8_t* msg, size_t msg_size) { + size_t total_size = BASIC_FRAME_OVERHEAD + msg_size; + if (buffer_size < total_size) { + return 0; + } + + buffer[0] = BASIC_FRAME_START_BYTE1; + buffer[1] = BASIC_FRAME_START_BYTE2; + buffer[2] = msg_id; + + if (msg_size > 0 && msg != nullptr) { + std::memcpy(buffer + BASIC_FRAME_HEADER_SIZE, msg, msg_size); + } + + BasicFrameChecksum ck = basic_frame_checksum(buffer + 2, msg_size + 1); + buffer[BASIC_FRAME_HEADER_SIZE + msg_size] = ck.byte1; + buffer[BASIC_FRAME_HEADER_SIZE + msg_size + 1] = ck.byte2; + + return total_size; +} + +/** + * Validate a complete packet in a buffer + */ +inline BasicFrameMsgInfo basic_frame_validate_packet(const uint8_t* buffer, size_t length) { + BasicFrameMsgInfo result; + + if (length < BASIC_FRAME_OVERHEAD) { + return result; + } + + if (buffer[0] != BASIC_FRAME_START_BYTE1 || buffer[1] != BASIC_FRAME_START_BYTE2) { + return result; + } + + size_t msg_length = length - BASIC_FRAME_OVERHEAD; + + BasicFrameChecksum ck = basic_frame_checksum(buffer + 2, msg_length + 1); + if (ck.byte1 == buffer[length - 2] && ck.byte2 == buffer[length - 1]) { + result.valid = true; + result.msg_id = buffer[2]; + result.msg_len = static_cast(msg_length); + result.msg_data = const_cast(buffer + BASIC_FRAME_HEADER_SIZE); + } + + return result; +} + +/*=========================================================================== + * Message Helper Template + *===========================================================================*/ + +/** + * Template helper for message encoding/decoding + */ +template +struct BasicFrameMessageHelper { + static constexpr uint8_t MSG_ID = MsgId; + static constexpr size_t MSG_SIZE = MsgSize; + + static bool encode(BasicFrameEncodeBuffer& buf, const T& msg) { + return buf.encode(MSG_ID, &msg, MSG_SIZE); + } + + static T* reserve(BasicFrameEncodeBuffer& buf) { + return reinterpret_cast(buf.reserve(MSG_ID, MSG_SIZE)); + } + + static bool finish(BasicFrameEncodeBuffer& buf) { + return buf.finish(); + } + + static T get(const BasicFrameMsgInfo& info) { + return *reinterpret_cast(info.msg_data); + } + + static T* get_ref(const BasicFrameMsgInfo& info) { + return reinterpret_cast(info.msg_data); + } +}; + +} // namespace StructFrame diff --git a/src/struct_frame/boilerplate/cpp/basic_frame_with_len.hpp b/src/struct_frame/boilerplate/cpp/basic_frame_with_len.hpp new file mode 100644 index 00000000..66662f6b --- /dev/null +++ b/src/struct_frame/boilerplate/cpp/basic_frame_with_len.hpp @@ -0,0 +1,401 @@ +/* + * BasicFrameWithLen - Frame format with embedded length and CRC (C++ version) + * + * Format: [START1=0x90] [START2=0x92] [MSG_ID] [LEN] [MSG...] [CRC1] [CRC2] + * + * This frame format includes the message length in the packet header, + * so no msg_id to length lookup is required. + * + * Use Case: When message lengths may vary or are not known at compile time. + */ + +#pragma once + +#include +#include +#include + +namespace StructFrame { + +/* Frame constants */ +constexpr uint8_t BASIC_FRAME_WITH_LEN_START_BYTE1 = 0x90; +constexpr uint8_t BASIC_FRAME_WITH_LEN_START_BYTE2 = 0x92; +constexpr size_t BASIC_FRAME_WITH_LEN_HEADER_SIZE = 4; /* start1 + start2 + msg_id + len */ +constexpr size_t BASIC_FRAME_WITH_LEN_FOOTER_SIZE = 2; /* crc1 + crc2 */ +constexpr size_t BASIC_FRAME_WITH_LEN_OVERHEAD = BASIC_FRAME_WITH_LEN_HEADER_SIZE + BASIC_FRAME_WITH_LEN_FOOTER_SIZE; +constexpr size_t BASIC_FRAME_WITH_LEN_MAX_MSG_SIZE = 255; /* Limited by 1-byte length field */ + +/* Checksum result */ +struct BasicFrameWithLenChecksum { + uint8_t byte1; + uint8_t byte2; +}; + +/* Parse result */ +struct BasicFrameWithLenMsgInfo { + bool valid; + uint8_t msg_id; + uint8_t msg_len; + uint8_t* msg_data; + + BasicFrameWithLenMsgInfo() : valid(false), msg_id(0), msg_len(0), msg_data(nullptr) {} + BasicFrameWithLenMsgInfo(bool v, uint8_t id, uint8_t len, uint8_t* data) + : valid(v), msg_id(id), msg_len(len), msg_data(data) {} +}; + +/* Parser state enumeration */ +enum class BasicFrameWithLenParserState : uint8_t { + LookingForStart1 = 0, + LookingForStart2 = 1, + GettingMsgId = 2, + GettingLength = 3, + GettingPayload = 4 +}; + +/*=========================================================================== + * Checksum Calculation + *===========================================================================*/ + +/** + * Calculate Fletcher-16 checksum over the given data + */ +inline BasicFrameWithLenChecksum basic_frame_with_len_checksum(const uint8_t* data, size_t length) { + BasicFrameWithLenChecksum ck{0, 0}; + for (size_t i = 0; i < length; i++) { + ck.byte1 = static_cast(ck.byte1 + data[i]); + ck.byte2 = static_cast(ck.byte2 + ck.byte1); + } + return ck; +} + +/*=========================================================================== + * Encode Buffer Class + *===========================================================================*/ + +class BasicFrameWithLenEncodeBuffer { +public: + BasicFrameWithLenEncodeBuffer(uint8_t* data, size_t max_size) + : data_(data), max_size_(max_size), size_(0), in_progress_(false) {} + + void reset() { + size_ = 0; + in_progress_ = false; + } + + uint8_t* data() { return data_; } + const uint8_t* data() const { return data_; } + size_t size() const { return size_; } + size_t max_size() const { return max_size_; } + bool in_progress() const { return in_progress_; } + + /** + * Encode a message into the buffer + */ + bool encode(uint8_t msg_id, const void* msg, uint8_t msg_size) { + if (in_progress_) { + return false; + } + + size_t total_size = BASIC_FRAME_WITH_LEN_OVERHEAD + msg_size; + if (size_ + total_size > max_size_) { + return false; + } + + uint8_t* packet_start = data_ + size_; + + /* Write header */ + packet_start[0] = BASIC_FRAME_WITH_LEN_START_BYTE1; + packet_start[1] = BASIC_FRAME_WITH_LEN_START_BYTE2; + packet_start[2] = msg_id; + packet_start[3] = msg_size; + + /* Write message data */ + if (msg_size > 0 && msg != nullptr) { + std::memcpy(packet_start + BASIC_FRAME_WITH_LEN_HEADER_SIZE, msg, msg_size); + } + + /* Calculate checksum over msg_id + len + msg data */ + BasicFrameWithLenChecksum ck = basic_frame_with_len_checksum(packet_start + 2, msg_size + 2); + packet_start[BASIC_FRAME_WITH_LEN_HEADER_SIZE + msg_size] = ck.byte1; + packet_start[BASIC_FRAME_WITH_LEN_HEADER_SIZE + msg_size + 1] = ck.byte2; + + size_ += total_size; + return true; + } + + /** + * Reserve space in buffer for zero-copy encoding + * Returns pointer to message data area, or nullptr on failure + */ + uint8_t* reserve(uint8_t msg_id, uint8_t msg_size) { + if (in_progress_) { + return nullptr; + } + + size_t total_size = BASIC_FRAME_WITH_LEN_OVERHEAD + msg_size; + if (size_ + total_size > max_size_) { + return nullptr; + } + + uint8_t* packet_start = data_ + size_; + + /* Write header */ + packet_start[0] = BASIC_FRAME_WITH_LEN_START_BYTE1; + packet_start[1] = BASIC_FRAME_WITH_LEN_START_BYTE2; + packet_start[2] = msg_id; + packet_start[3] = msg_size; + + in_progress_ = true; + reserved_msg_size_ = msg_size; + return packet_start + BASIC_FRAME_WITH_LEN_HEADER_SIZE; + } + + /** + * Finish a reserved encoding by adding checksum + */ + bool finish() { + if (!in_progress_) { + return false; + } + + uint8_t* packet_start = data_ + size_; + + /* Calculate checksum over msg_id + len + msg data */ + BasicFrameWithLenChecksum ck = basic_frame_with_len_checksum(packet_start + 2, reserved_msg_size_ + 2); + packet_start[BASIC_FRAME_WITH_LEN_HEADER_SIZE + reserved_msg_size_] = ck.byte1; + packet_start[BASIC_FRAME_WITH_LEN_HEADER_SIZE + reserved_msg_size_ + 1] = ck.byte2; + + size_ += BASIC_FRAME_WITH_LEN_OVERHEAD + reserved_msg_size_; + in_progress_ = false; + return true; + } + +private: + uint8_t* data_; + size_t max_size_; + size_t size_; + bool in_progress_; + uint8_t reserved_msg_size_ = 0; +}; + +/*=========================================================================== + * Parser Class + *===========================================================================*/ + +class BasicFrameWithLenParser { +public: + BasicFrameWithLenParser(uint8_t* buffer, size_t buffer_size) + : state_(BasicFrameWithLenParserState::LookingForStart1), + buffer_(buffer), + buffer_max_size_(buffer_size), + buffer_index_(0), + packet_size_(0), + msg_id_(0), + msg_len_(0) {} + + void reset() { + state_ = BasicFrameWithLenParserState::LookingForStart1; + buffer_index_ = 0; + packet_size_ = 0; + msg_id_ = 0; + msg_len_ = 0; + } + + /** + * Parse a single byte + * Returns a BasicFrameWithLenMsgInfo with valid=true when a complete valid message is received + */ + BasicFrameWithLenMsgInfo parse_byte(uint8_t byte) { + BasicFrameWithLenMsgInfo result; + + switch (state_) { + case BasicFrameWithLenParserState::LookingForStart1: + if (byte == BASIC_FRAME_WITH_LEN_START_BYTE1) { + buffer_[0] = byte; + buffer_index_ = 1; + state_ = BasicFrameWithLenParserState::LookingForStart2; + } + break; + + case BasicFrameWithLenParserState::LookingForStart2: + if (byte == BASIC_FRAME_WITH_LEN_START_BYTE2) { + buffer_[1] = byte; + buffer_index_ = 2; + state_ = BasicFrameWithLenParserState::GettingMsgId; + } else if (byte == BASIC_FRAME_WITH_LEN_START_BYTE1) { + buffer_[0] = byte; + buffer_index_ = 1; + } else { + state_ = BasicFrameWithLenParserState::LookingForStart1; + } + break; + + case BasicFrameWithLenParserState::GettingMsgId: + buffer_[2] = byte; + buffer_index_ = 3; + msg_id_ = byte; + state_ = BasicFrameWithLenParserState::GettingLength; + break; + + case BasicFrameWithLenParserState::GettingLength: + buffer_[3] = byte; + buffer_index_ = 4; + msg_len_ = byte; + packet_size_ = BASIC_FRAME_WITH_LEN_OVERHEAD + byte; + + if (packet_size_ <= buffer_max_size_) { + state_ = BasicFrameWithLenParserState::GettingPayload; + } else { + state_ = BasicFrameWithLenParserState::LookingForStart1; + } + break; + + case BasicFrameWithLenParserState::GettingPayload: + if (buffer_index_ < buffer_max_size_) { + buffer_[buffer_index_++] = byte; + } + + if (buffer_index_ >= packet_size_) { + BasicFrameWithLenChecksum ck = basic_frame_with_len_checksum(buffer_ + 2, msg_len_ + 2); + + if (ck.byte1 == buffer_[packet_size_ - 2] && + ck.byte2 == buffer_[packet_size_ - 1]) { + result.valid = true; + result.msg_id = msg_id_; + result.msg_len = msg_len_; + result.msg_data = buffer_ + BASIC_FRAME_WITH_LEN_HEADER_SIZE; + } + + state_ = BasicFrameWithLenParserState::LookingForStart1; + } + break; + } + + return result; + } + + /** + * Parse a buffer of bytes + */ + BasicFrameWithLenMsgInfo parse_buffer(const uint8_t* data, size_t data_size, size_t& r_loc) { + BasicFrameWithLenMsgInfo result; + + while (r_loc < data_size) { + result = parse_byte(data[r_loc]); + r_loc++; + if (result.valid) { + return result; + } + } + + return result; + } + +private: + BasicFrameWithLenParserState state_; + uint8_t* buffer_; + size_t buffer_max_size_; + size_t buffer_index_; + size_t packet_size_; + uint8_t msg_id_; + uint8_t msg_len_; +}; + +/*=========================================================================== + * Static Helper Functions + *===========================================================================*/ + +/** + * Encode a message directly into a buffer + * Returns the number of bytes written, or 0 on failure + */ +inline size_t basic_frame_with_len_encode(uint8_t* buffer, size_t buffer_size, + uint8_t msg_id, const uint8_t* msg, uint8_t msg_size) { + size_t total_size = BASIC_FRAME_WITH_LEN_OVERHEAD + msg_size; + if (buffer_size < total_size) { + return 0; + } + + buffer[0] = BASIC_FRAME_WITH_LEN_START_BYTE1; + buffer[1] = BASIC_FRAME_WITH_LEN_START_BYTE2; + buffer[2] = msg_id; + buffer[3] = msg_size; + + if (msg_size > 0 && msg != nullptr) { + std::memcpy(buffer + BASIC_FRAME_WITH_LEN_HEADER_SIZE, msg, msg_size); + } + + BasicFrameWithLenChecksum ck = basic_frame_with_len_checksum(buffer + 2, msg_size + 2); + buffer[BASIC_FRAME_WITH_LEN_HEADER_SIZE + msg_size] = ck.byte1; + buffer[BASIC_FRAME_WITH_LEN_HEADER_SIZE + msg_size + 1] = ck.byte2; + + return total_size; +} + +/** + * Validate a complete packet in a buffer + */ +inline BasicFrameWithLenMsgInfo basic_frame_with_len_validate_packet(const uint8_t* buffer, size_t length) { + BasicFrameWithLenMsgInfo result; + + if (length < BASIC_FRAME_WITH_LEN_OVERHEAD) { + return result; + } + + if (buffer[0] != BASIC_FRAME_WITH_LEN_START_BYTE1 || + buffer[1] != BASIC_FRAME_WITH_LEN_START_BYTE2) { + return result; + } + + uint8_t msg_len = buffer[3]; + size_t expected_length = BASIC_FRAME_WITH_LEN_OVERHEAD + msg_len; + + if (length != expected_length) { + return result; + } + + BasicFrameWithLenChecksum ck = basic_frame_with_len_checksum(buffer + 2, msg_len + 2); + if (ck.byte1 == buffer[length - 2] && ck.byte2 == buffer[length - 1]) { + result.valid = true; + result.msg_id = buffer[2]; + result.msg_len = msg_len; + result.msg_data = const_cast(buffer + BASIC_FRAME_WITH_LEN_HEADER_SIZE); + } + + return result; +} + +/*=========================================================================== + * Message Helper Template + *===========================================================================*/ + +/** + * Template helper for message encoding/decoding with BasicFrameWithLen + */ +template +struct BasicFrameWithLenMessageHelper { + static constexpr uint8_t MSG_ID = MsgId; + static constexpr uint8_t MSG_SIZE = MsgSize; + + static bool encode(BasicFrameWithLenEncodeBuffer& buf, const T& msg) { + return buf.encode(MSG_ID, &msg, MSG_SIZE); + } + + static T* reserve(BasicFrameWithLenEncodeBuffer& buf) { + return reinterpret_cast(buf.reserve(MSG_ID, MSG_SIZE)); + } + + static bool finish(BasicFrameWithLenEncodeBuffer& buf) { + return buf.finish(); + } + + static T get(const BasicFrameWithLenMsgInfo& info) { + return *reinterpret_cast(info.msg_data); + } + + static T* get_ref(const BasicFrameWithLenMsgInfo& info) { + return reinterpret_cast(info.msg_data); + } +}; + +} // namespace StructFrame From 7b9437e15400e862c787bfe3c844d499139b82a8 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 29 Nov 2025 22:31:49 +0000 Subject: [PATCH 4/6] Fix code review feedback: consistent API for reserve/finish, remove unused code Co-authored-by: rijesha <7819200+rijesha@users.noreply.github.com> --- src/struct_frame/boilerplate/c/basic_frame.h | 9 +++++++-- .../boilerplate/c/basic_frame_with_len.h | 18 ++++++++---------- 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/src/struct_frame/boilerplate/c/basic_frame.h b/src/struct_frame/boilerplate/c/basic_frame.h index 82c23149..20a2c365 100644 --- a/src/struct_frame/boilerplate/c/basic_frame.h +++ b/src/struct_frame/boilerplate/c/basic_frame.h @@ -64,6 +64,7 @@ typedef struct basic_frame_encode_buffer { size_t max_size; size_t size; bool in_progress; + size_t reserved_msg_size; /* Stored for finish() */ } basic_frame_encode_buffer_t; /*=========================================================================== @@ -94,6 +95,7 @@ static inline void basic_frame_encode_init(basic_frame_encode_buffer_t* buf, uin buf->max_size = max_size; buf->size = 0; buf->in_progress = false; + buf->reserved_msg_size = 0; } /** @@ -102,6 +104,7 @@ static inline void basic_frame_encode_init(basic_frame_encode_buffer_t* buf, uin static inline void basic_frame_encode_reset(basic_frame_encode_buffer_t* buf) { buf->size = 0; buf->in_progress = false; + buf->reserved_msg_size = 0; } /** @@ -176,18 +179,20 @@ static inline uint8_t* basic_frame_encode_reserve(basic_frame_encode_buffer_t* b packet_start[2] = msg_id; buf->in_progress = true; + buf->reserved_msg_size = msg_size; return packet_start + BASIC_FRAME_HEADER_SIZE; } /** * Finish a reserved encoding by adding checksum */ -static inline bool basic_frame_encode_finish(basic_frame_encode_buffer_t* buf, size_t msg_size) { +static inline bool basic_frame_encode_finish(basic_frame_encode_buffer_t* buf) { if (!buf->in_progress) { return false; } uint8_t* packet_start = buf->data + buf->size; + size_t msg_size = buf->reserved_msg_size; /* Calculate checksum over msg_id + msg data */ basic_frame_checksum_t ck = basic_frame_checksum(packet_start + 2, msg_size + 1); @@ -391,7 +396,7 @@ static inline basic_frame_msg_info_t basic_frame_validate_packet(const uint8_t* return false; \ } \ static inline bool funcname##_finish(basic_frame_encode_buffer_t* buf) { \ - return basic_frame_encode_finish(buf, (msg_size)); \ + return basic_frame_encode_finish(buf); \ } \ static inline typename funcname##_get(basic_frame_msg_info_t info) { \ return *(typename*)(info.msg_data); \ diff --git a/src/struct_frame/boilerplate/c/basic_frame_with_len.h b/src/struct_frame/boilerplate/c/basic_frame_with_len.h index 1158da14..c9024498 100644 --- a/src/struct_frame/boilerplate/c/basic_frame_with_len.h +++ b/src/struct_frame/boilerplate/c/basic_frame_with_len.h @@ -64,6 +64,7 @@ typedef struct basic_frame_with_len_encode_buffer { size_t max_size; size_t size; bool in_progress; + uint8_t reserved_msg_size; /* Stored for finish() */ } basic_frame_with_len_encode_buffer_t; /*=========================================================================== @@ -95,6 +96,7 @@ static inline void basic_frame_with_len_encode_init(basic_frame_with_len_encode_ buf->max_size = max_size; buf->size = 0; buf->in_progress = false; + buf->reserved_msg_size = 0; } /** @@ -103,6 +105,7 @@ static inline void basic_frame_with_len_encode_init(basic_frame_with_len_encode_ static inline void basic_frame_with_len_encode_reset(basic_frame_with_len_encode_buffer_t* buf) { buf->size = 0; buf->in_progress = false; + buf->reserved_msg_size = 0; } /** @@ -179,18 +182,20 @@ static inline uint8_t* basic_frame_with_len_encode_reserve(basic_frame_with_len_ packet_start[3] = msg_size; buf->in_progress = true; + buf->reserved_msg_size = msg_size; return packet_start + BASIC_FRAME_WITH_LEN_HEADER_SIZE; } /** * Finish a reserved encoding by adding checksum */ -static inline bool basic_frame_with_len_encode_finish(basic_frame_with_len_encode_buffer_t* buf, uint8_t msg_size) { +static inline bool basic_frame_with_len_encode_finish(basic_frame_with_len_encode_buffer_t* buf) { if (!buf->in_progress) { return false; } uint8_t* packet_start = buf->data + buf->size; + uint8_t msg_size = buf->reserved_msg_size; /* Calculate checksum over msg_id + len + msg data */ basic_frame_with_len_checksum_t ck = basic_frame_with_len_checksum(packet_start + 2, msg_size + 2); @@ -281,14 +286,7 @@ static inline basic_frame_with_len_msg_info_t basic_frame_with_len_parse_byte( parser->packet_size = BASIC_FRAME_WITH_LEN_OVERHEAD + byte; if (parser->packet_size <= parser->buffer_max_size) { - if (byte == 0) { - /* Zero-length message, go straight to validation */ - basic_frame_with_len_checksum_t ck = basic_frame_with_len_checksum(parser->buffer + 2, 2); - /* Need to wait for CRC bytes still */ - parser->state = BASIC_FRAME_WITH_LEN_GETTING_PAYLOAD; - } else { - parser->state = BASIC_FRAME_WITH_LEN_GETTING_PAYLOAD; - } + parser->state = BASIC_FRAME_WITH_LEN_GETTING_PAYLOAD; } else { /* Packet too large for buffer */ parser->state = BASIC_FRAME_WITH_LEN_LOOKING_FOR_START1; @@ -412,7 +410,7 @@ static inline basic_frame_with_len_msg_info_t basic_frame_with_len_validate_pack return false; \ } \ static inline bool funcname##_finish(basic_frame_with_len_encode_buffer_t* buf) { \ - return basic_frame_with_len_encode_finish(buf, (msg_size)); \ + return basic_frame_with_len_encode_finish(buf); \ } \ static inline typename funcname##_get(basic_frame_with_len_msg_info_t info) { \ return *(typename*)(info.msg_data); \ From d951163289d9296e6cbad5d0c526f5df78eaf731 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 29 Nov 2025 22:49:13 +0000 Subject: [PATCH 5/6] Update all cross-platform tests to use new BasicFrame format Co-authored-by: rijesha <7819200+rijesha@users.noreply.github.com> --- .../boilerplate/py/struct_frame_parser.py | 68 +++++++++++++++++++ tests/c/test_arrays.c | 32 ++++----- tests/c/test_basic_types.c | 34 +++++----- tests/c/test_cross_platform_deserialization.c | 11 ++- tests/c/test_cross_platform_serialization.c | 18 ++--- tests/cpp/test_arrays.cpp | 16 ++--- tests/cpp/test_basic_types.cpp | 20 ++---- .../test_cross_platform_deserialization.cpp | 59 ++++++++++------ .../cpp/test_cross_platform_serialization.cpp | 14 ++-- .../js/test_cross_platform_deserialization.js | 40 ++++++++--- tests/js/test_cross_platform_serialization.js | 42 ++++-------- .../py/test_cross_platform_deserialization.py | 23 +++---- tests/py/test_cross_platform_serialization.py | 4 +- .../ts/test_cross_platform_deserialization.ts | 40 ++++++++--- tests/ts/test_cross_platform_serialization.ts | 40 ++++------- 15 files changed, 272 insertions(+), 189 deletions(-) diff --git a/src/struct_frame/boilerplate/py/struct_frame_parser.py b/src/struct_frame/boilerplate/py/struct_frame_parser.py index 12186de9..3b4d92c0 100644 --- a/src/struct_frame/boilerplate/py/struct_frame_parser.py +++ b/src/struct_frame/boilerplate/py/struct_frame_parser.py @@ -16,6 +16,74 @@ def fletcher_checksum_calculation(buffer, start=0, end=None): return [byte1, byte2] +class BasicFrame: + """ + BasicFrame - Simple frame format with CRC + + Format: [START1=0x90] [START2=0x91] [MSG_ID] [MSG...] [CRC1] [CRC2] + + This frame format requires a message ID to message length lookup function + since the length is not included in the packet. + """ + + START_BYTE1 = 0x90 + START_BYTE2 = 0x91 + HEADER_SIZE = 3 # start1 + start2 + msg_id + FOOTER_SIZE = 2 # crc1 + crc2 + + def __init__(self): + self.packet = [] + self.desired_packet_length = 0 + self.state = 0 # 0: looking for start1, 1: looking for start2, 2: getting msg_id, 3: getting payload + + def encode_msg(self, msg): + """Encode a message object into a framed byte list""" + return self.encode(msg.pack(), msg.msg_id) + + def encode(self, data, msg_id): + """Encode data with a message ID into a framed byte list""" + output = [] + output.append(self.START_BYTE1) + output.append(self.START_BYTE2) + output.append(msg_id) + if len(data): + for b in data: + output.append(b) + # Calculate checksum on msg_id + data + checksum_data = [msg_id] + list(data) + checksum = fletcher_checksum_calculation(checksum_data) + output.append(checksum[0]) + output.append(checksum[1]) + return output + + def validate_packet(self, buffer): + """Validate a complete packet in a buffer""" + if len(buffer) < self.HEADER_SIZE + self.FOOTER_SIZE: + return None + + # Check start bytes + if buffer[0] != self.START_BYTE1 or buffer[1] != self.START_BYTE2: + return None + + msg_length = len(buffer) - self.HEADER_SIZE - self.FOOTER_SIZE + + # Validate checksum over msg_id + msg data + checksum = fletcher_checksum_calculation(buffer, 2, len(buffer) - self.FOOTER_SIZE) + if checksum[0] != buffer[-2] or checksum[1] != buffer[-1]: + return None + + return { + 'valid': True, + 'msg_id': buffer[2], + 'msg_len': msg_length, + 'msg_data': buffer[self.HEADER_SIZE:len(buffer) - self.FOOTER_SIZE] + } + + def get_msg_buffer(self, buffer): + """Get the message data portion from a validated buffer""" + return buffer[self.HEADER_SIZE:len(buffer) - self.FOOTER_SIZE] + + class BasicPacket: start_byte = 0x90 header_length = 0 diff --git a/tests/c/test_arrays.c b/tests/c/test_arrays.c index 063df9c6..c29b641d 100644 --- a/tests/c/test_arrays.c +++ b/tests/c/test_arrays.c @@ -3,7 +3,7 @@ #include #include "comprehensive_arrays.sf.h" -#include "struct_frame_default_frame.h" +#include "basic_frame.h" void print_failure_details(const char* label, const void* raw_data, size_t raw_data_size) { printf("\n"); @@ -51,8 +51,8 @@ int test_array_operations() { strncpy(msg.fixed_strings[1], "String2", 8); msg.bounded_strings.count = 2; - strncpy(msg.bounded_strings.data[0], "BoundedStr1", 16); - strncpy(msg.bounded_strings.data[1], "BoundedStr2", 16); + strncpy(msg.bounded_strings.data[0], "BoundedStr1", 12); + strncpy(msg.bounded_strings.data[1], "BoundedStr2", 12); msg.fixed_statuses[0] = 1; msg.fixed_statuses[1] = 2; @@ -72,46 +72,46 @@ int test_array_operations() { msg.bounded_sensors.data[0].status = 2; strncpy(msg.bounded_sensors.data[0].name, "Pressure", 16); - // Encode message into BasicPacket format + // Encode message into BasicFrame format uint8_t encode_buffer[1024]; - msg_encode_buffer buffer = {0}; - buffer.data = encode_buffer; + basic_frame_encode_buffer_t buffer; + basic_frame_encode_init(&buffer, encode_buffer, sizeof(encode_buffer)); - packet_format_t* format = &default_frame_format; - bool encoded = comprehensive_arrays_comprehensive_array_message_encode(&buffer, format, &msg); + bool encoded = basic_frame_encode_msg(&buffer, COMPREHENSIVE_ARRAYS_COMPREHENSIVE_ARRAY_MESSAGE_MSG_ID, + &msg, COMPREHENSIVE_ARRAYS_COMPREHENSIVE_ARRAY_MESSAGE_MAX_SIZE); if (!encoded) { print_failure_details("Encoding failed", NULL, 0); return 0; } - // Validate and decode the BasicPacket - msg_info_t decode_result = format->validate_packet(encode_buffer, buffer.size); + // Validate and decode the BasicFrame + basic_frame_msg_info_t decode_result = basic_frame_validate_packet(encode_buffer, buffer.size); if (!decode_result.valid) { print_failure_details("Validation failed", encode_buffer, buffer.size); return 0; } - ComprehensiveArraysComprehensiveArrayMessage decoded_msg = - comprehensive_arrays_comprehensive_array_message_get(decode_result); + ComprehensiveArraysComprehensiveArrayMessage* decoded_msg = + (ComprehensiveArraysComprehensiveArrayMessage*)decode_result.msg_data; // Compare original and decoded messages - if (decoded_msg.fixed_ints[0] != msg.fixed_ints[0]) { + if (decoded_msg->fixed_ints[0] != msg.fixed_ints[0]) { print_failure_details("Value mismatch: fixed_ints[0]", encode_buffer, buffer.size); return 0; } - if (decoded_msg.bounded_uints.count != msg.bounded_uints.count) { + if (decoded_msg->bounded_uints.count != msg.bounded_uints.count) { print_failure_details("Value mismatch: bounded_uints.count", encode_buffer, buffer.size); return 0; } - if (decoded_msg.bounded_uints.data[0] != msg.bounded_uints.data[0]) { + if (decoded_msg->bounded_uints.data[0] != msg.bounded_uints.data[0]) { print_failure_details("Value mismatch: bounded_uints.data[0]", encode_buffer, buffer.size); return 0; } - if (decoded_msg.fixed_sensors[0].id != msg.fixed_sensors[0].id) { + if (decoded_msg->fixed_sensors[0].id != msg.fixed_sensors[0].id) { print_failure_details("Value mismatch: fixed_sensors[0].id", encode_buffer, buffer.size); return 0; } diff --git a/tests/c/test_basic_types.c b/tests/c/test_basic_types.c index e31f0d7a..1b2c01c5 100644 --- a/tests/c/test_basic_types.c +++ b/tests/c/test_basic_types.c @@ -3,7 +3,7 @@ #include #include "basic_types.sf.h" -#include "struct_frame_default_frame.h" +#include "basic_frame.h" void print_failure_details(const char* label, const BasicTypesBasicTypesMessage* expected, const BasicTypesBasicTypesMessage* actual, @@ -59,46 +59,46 @@ int test_basic_types() { msg.description.length = strlen("Test description for basic types"); strncpy(msg.description.data, "Test description for basic types", msg.description.length); - // Encode message into BasicPacket format + // Encode message into BasicFrame format uint8_t encode_buffer[1024]; - msg_encode_buffer buffer = {0}; - buffer.data = encode_buffer; + basic_frame_encode_buffer_t buffer; + basic_frame_encode_init(&buffer, encode_buffer, sizeof(encode_buffer)); - packet_format_t* format = &default_frame_format; - bool encoded = basic_types_basic_types_message_encode(&buffer, format, &msg); + bool encoded = basic_frame_encode_msg(&buffer, BASIC_TYPES_BASIC_TYPES_MESSAGE_MSG_ID, + &msg, BASIC_TYPES_BASIC_TYPES_MESSAGE_MAX_SIZE); if (!encoded) { print_failure_details("Encoding failed", &msg, NULL, NULL, 0); return 0; } - // Validate and decode the BasicPacket - msg_info_t decode_result = format->validate_packet(encode_buffer, buffer.size); + // Validate and decode the BasicFrame + basic_frame_msg_info_t decode_result = basic_frame_validate_packet(encode_buffer, buffer.size); if (!decode_result.valid) { print_failure_details("Validation failed", &msg, NULL, encode_buffer, buffer.size); return 0; } - BasicTypesBasicTypesMessage decoded_msg = basic_types_basic_types_message_get(decode_result); + BasicTypesBasicTypesMessage* decoded_msg = (BasicTypesBasicTypesMessage*)decode_result.msg_data; // Compare original and decoded messages - if (decoded_msg.small_int != msg.small_int) { - print_failure_details("Value mismatch: small_int", &msg, &decoded_msg, encode_buffer, buffer.size); + if (decoded_msg->small_int != msg.small_int) { + print_failure_details("Value mismatch: small_int", &msg, decoded_msg, encode_buffer, buffer.size); return 0; } - if (decoded_msg.medium_int != msg.medium_int) { - print_failure_details("Value mismatch: medium_int", &msg, &decoded_msg, encode_buffer, buffer.size); + if (decoded_msg->medium_int != msg.medium_int) { + print_failure_details("Value mismatch: medium_int", &msg, decoded_msg, encode_buffer, buffer.size); return 0; } - if (decoded_msg.flag != msg.flag) { - print_failure_details("Value mismatch: flag", &msg, &decoded_msg, encode_buffer, buffer.size); + if (decoded_msg->flag != msg.flag) { + print_failure_details("Value mismatch: flag", &msg, decoded_msg, encode_buffer, buffer.size); return 0; } - if (decoded_msg.single_precision != msg.single_precision) { - print_failure_details("Value mismatch: single_precision", &msg, &decoded_msg, encode_buffer, buffer.size); + if (decoded_msg->single_precision != msg.single_precision) { + print_failure_details("Value mismatch: single_precision", &msg, decoded_msg, encode_buffer, buffer.size); return 0; } diff --git a/tests/c/test_cross_platform_deserialization.c b/tests/c/test_cross_platform_deserialization.c index e5d42028..b196aa3f 100644 --- a/tests/c/test_cross_platform_deserialization.c +++ b/tests/c/test_cross_platform_deserialization.c @@ -4,7 +4,7 @@ #include #include "serialization_test.sf.h" -#include "struct_frame_default_frame.h" +#include "basic_frame.h" void print_failure_details(const char* label, const void* raw_data, size_t raw_data_size) { printf("\n"); @@ -83,18 +83,17 @@ int read_and_validate_test_data(const char* filename) { return 0; } - packet_format_t* format = &default_frame_format; - msg_info_t decode_result = format->validate_packet(buffer, size); + basic_frame_msg_info_t decode_result = basic_frame_validate_packet(buffer, size); if (!decode_result.valid) { print_failure_details("Failed to decode data", buffer, size); return 0; } - SerializationTestSerializationTestMessage decoded_msg = - serialization_test_serialization_test_message_get(decode_result); + SerializationTestSerializationTestMessage* decoded_msg = + (SerializationTestSerializationTestMessage*)decode_result.msg_data; - if (!validate_message(&decoded_msg)) { + if (!validate_message(decoded_msg)) { printf(" Validation failed\n"); return 0; } diff --git a/tests/c/test_cross_platform_serialization.c b/tests/c/test_cross_platform_serialization.c index 757f2b51..c70ff61f 100644 --- a/tests/c/test_cross_platform_serialization.c +++ b/tests/c/test_cross_platform_serialization.c @@ -3,7 +3,7 @@ #include #include "serialization_test.sf.h" -#include "struct_frame_default_frame.h" +#include "basic_frame.h" void print_failure_details(const char* label, const void* raw_data, size_t raw_data_size) { printf("\n"); @@ -38,11 +38,11 @@ int create_test_data() { msg.test_array.data[2] = 300; uint8_t encode_buffer[512]; - msg_encode_buffer buffer = {0}; - buffer.data = encode_buffer; + basic_frame_encode_buffer_t buffer; + basic_frame_encode_init(&buffer, encode_buffer, sizeof(encode_buffer)); - packet_format_t* format = &default_frame_format; - bool encoded = serialization_test_serialization_test_message_encode(&buffer, format, &msg); + bool encoded = basic_frame_encode_msg(&buffer, SERIALIZATION_TEST_SERIALIZATION_TEST_MESSAGE_MSG_ID, + &msg, SERIALIZATION_TEST_SERIALIZATION_TEST_MESSAGE_MAX_SIZE); if (!encoded) { print_failure_details("Encoding failed", NULL, 0); @@ -59,16 +59,16 @@ int create_test_data() { fclose(file); // Self-validate - msg_info_t decode_result = format->validate_packet(encode_buffer, buffer.size); + basic_frame_msg_info_t decode_result = basic_frame_validate_packet(encode_buffer, buffer.size); if (!decode_result.valid) { print_failure_details("Self-validation failed", encode_buffer, buffer.size); return 0; } - SerializationTestSerializationTestMessage decoded_msg = - serialization_test_serialization_test_message_get(decode_result); + SerializationTestSerializationTestMessage* decoded_msg = + (SerializationTestSerializationTestMessage*)decode_result.msg_data; - if (decoded_msg.magic_number != 3735928559 || decoded_msg.test_array.count != 3) { + if (decoded_msg->magic_number != 3735928559 || decoded_msg->test_array.count != 3) { print_failure_details("Self-verification failed", encode_buffer, buffer.size); return 0; } diff --git a/tests/cpp/test_arrays.cpp b/tests/cpp/test_arrays.cpp index e4fd9ca8..93b6b270 100644 --- a/tests/cpp/test_arrays.cpp +++ b/tests/cpp/test_arrays.cpp @@ -1,4 +1,5 @@ #include "comprehensive_arrays.sf.hpp" +#include "basic_frame.hpp" #include #include @@ -24,19 +25,12 @@ int main() { msg.bounded_uints.data[1] = 200; msg.bounded_uints.data[2] = 300; - size_t msg_size = 0; - if (!StructFrame::get_message_length(COMPREHENSIVE_ARRAYS_COMPREHENSIVE_ARRAY_MESSAGE_MSG_ID, &msg_size)) { - print_failure_details("Failed to get message length"); - std::cout << "[TEST END] C++ Array Operations: FAIL\n\n"; - return 1; - } - - // Encode message into BasicPacket format + // Encode message into BasicFrame format uint8_t buffer[1024]; - StructFrame::BasicPacket format; - StructFrame::EncodeBuffer encoder(buffer, sizeof(buffer)); + StructFrame::BasicFrameEncodeBuffer encoder(buffer, sizeof(buffer)); - if (!encoder.encode(&format, COMPREHENSIVE_ARRAYS_COMPREHENSIVE_ARRAY_MESSAGE_MSG_ID, &msg, msg_size)) { + if (!encoder.encode(COMPREHENSIVE_ARRAYS_COMPREHENSIVE_ARRAY_MESSAGE_MSG_ID, &msg, + COMPREHENSIVE_ARRAYS_COMPREHENSIVE_ARRAY_MESSAGE_MAX_SIZE)) { print_failure_details("Failed to encode message"); std::cout << "[TEST END] C++ Array Operations: FAIL\n\n"; return 1; diff --git a/tests/cpp/test_basic_types.cpp b/tests/cpp/test_basic_types.cpp index 7694526f..2d0e9d71 100644 --- a/tests/cpp/test_basic_types.cpp +++ b/tests/cpp/test_basic_types.cpp @@ -1,4 +1,5 @@ #include "basic_types.sf.hpp" +#include "basic_frame.hpp" #include #include @@ -41,19 +42,12 @@ int main() { msg.description.length = 12; std::strncpy(msg.description.data, "Test message", sizeof(msg.description.data)); - size_t msg_size = 0; - if (!StructFrame::get_message_length(BASIC_TYPES_BASIC_TYPES_MESSAGE_MSG_ID, &msg_size)) { - print_failure_details("Failed to get message length"); - std::cout << "[TEST END] C++ Basic Types: FAIL\n\n"; - return 1; - } - - // Encode message into BasicPacket format + // Encode message into BasicFrame format uint8_t buffer[512]; - StructFrame::BasicPacket format; - StructFrame::EncodeBuffer encoder(buffer, sizeof(buffer)); + StructFrame::BasicFrameEncodeBuffer encoder(buffer, sizeof(buffer)); - if (!encoder.encode(&format, BASIC_TYPES_BASIC_TYPES_MESSAGE_MSG_ID, &msg, msg_size)) { + if (!encoder.encode(BASIC_TYPES_BASIC_TYPES_MESSAGE_MSG_ID, &msg, + BASIC_TYPES_BASIC_TYPES_MESSAGE_MAX_SIZE)) { print_failure_details("Failed to encode message", buffer, encoder.size()); std::cout << "[TEST END] C++ Basic Types: FAIL\n\n"; return 1; @@ -66,8 +60,8 @@ int main() { return 1; } - // Verify minimum packet structure (start byte + msg_id + data + checksums) - if (encoder.size() < 4) { + // Verify minimum packet structure (start bytes + msg_id + data + checksums) + if (encoder.size() < 5) { print_failure_details("Encoded data too small", buffer, encoder.size()); std::cout << "[TEST END] C++ Basic Types: FAIL\n\n"; return 1; diff --git a/tests/cpp/test_cross_platform_deserialization.cpp b/tests/cpp/test_cross_platform_deserialization.cpp index f83d7703..02188229 100644 --- a/tests/cpp/test_cross_platform_deserialization.cpp +++ b/tests/cpp/test_cross_platform_deserialization.cpp @@ -4,6 +4,7 @@ #include #include "serialization_test.sf.hpp" +#include "basic_frame.hpp" void print_failure_details(const char* label) { std::cout << "\n============================================================\n"; @@ -11,37 +12,50 @@ void print_failure_details(const char* label) { std::cout << "============================================================\n\n"; } -bool validate_basic_frame(const uint8_t* buffer, size_t size, uint32_t expected_magic) { - // Very basic frame validation - if (size < 4) { - std::cout << " Data too short\n"; +bool validate_message(const SerializationTestSerializationTestMessage* msg) { + // Expected values from expected_values.json + uint32_t expected_magic = 3735928559; // 0xDEADBEEF + const char* expected_string = "Cross-platform test!"; + float expected_float = 3.14159f; + bool expected_bool = true; + int expected_array[3] = {100, 200, 300}; + + if (msg->magic_number != expected_magic) { + std::cout << " Value mismatch: magic_number (expected " << expected_magic + << ", got " << msg->magic_number << ")\n"; return false; } - // Check start byte (BasicPacket uses 0x90) - if (buffer[0] != 0x90) { - std::cout << " Invalid start byte\n"; + if (std::strncmp(msg->test_string.data, expected_string, msg->test_string.length) != 0) { + std::cout << " Value mismatch: test_string\n"; return false; } - // Check message ID (SerializationTestMessage is 204) - if (buffer[1] != 204) { - std::cout << " Invalid message ID\n"; + if (std::fabs(msg->test_float - expected_float) > 0.0001f) { + std::cout << " Value mismatch: test_float (expected " << expected_float + << ", got " << msg->test_float << ")\n"; return false; } - // Extract magic number from payload (starts at byte 2, little-endian) - if (size < 6) { - std::cout << " Data too short for magic number\n"; + if (msg->test_bool != expected_bool) { + std::cout << " Value mismatch: test_bool\n"; return false; } - uint32_t magic_number = buffer[2] | (buffer[3] << 8) | (buffer[4] << 16) | (buffer[5] << 24); - if (magic_number != expected_magic) { - std::cout << " Magic number mismatch (expected " << expected_magic << ", got " << magic_number << ")\n"; + if (msg->test_array.count != 3) { + std::cout << " Value mismatch: test_array.count (expected 3, got " + << (int)msg->test_array.count << ")\n"; return false; } + for (int i = 0; i < 3; i++) { + if (msg->test_array.data[i] != expected_array[i]) { + std::cout << " Value mismatch: test_array[" << i << "] (expected " + << expected_array[i] << ", got " << msg->test_array.data[i] << ")\n"; + return false; + } + } + std::cout << " [OK] Data validated successfully\n"; return true; } @@ -63,10 +77,17 @@ bool read_and_validate_test_data(const char* filename) { return false; } - // Expected values from expected_values.json - uint32_t expected_magic = 3735928559; // 0xDEADBEEF + StructFrame::BasicFrameMsgInfo decode_result = StructFrame::basic_frame_validate_packet(buffer, size); + + if (!decode_result.valid) { + print_failure_details("Failed to decode data"); + return false; + } + + const SerializationTestSerializationTestMessage* decoded_msg = + reinterpret_cast(decode_result.msg_data); - if (!validate_basic_frame(buffer, size, expected_magic)) { + if (!validate_message(decoded_msg)) { std::cout << " Validation failed\n"; return false; } diff --git a/tests/cpp/test_cross_platform_serialization.cpp b/tests/cpp/test_cross_platform_serialization.cpp index 88a10ae1..592dc2dd 100644 --- a/tests/cpp/test_cross_platform_serialization.cpp +++ b/tests/cpp/test_cross_platform_serialization.cpp @@ -1,4 +1,5 @@ #include "serialization_test.sf.hpp" +#include "basic_frame.hpp" #include #include #include @@ -25,18 +26,11 @@ int main() { msg.test_array.data[1] = 200; msg.test_array.data[2] = 300; - size_t msg_size = 0; - if (!StructFrame::get_message_length(SERIALIZATION_TEST_SERIALIZATION_TEST_MESSAGE_MSG_ID, &msg_size)) { - print_failure_details("Failed to get message length"); - std::cout << "[TEST END] C++ Cross-Platform Serialization: FAIL\n\n"; - return 1; - } - uint8_t buffer[512]; - StructFrame::BasicPacket format; - StructFrame::EncodeBuffer encoder(buffer, sizeof(buffer)); + StructFrame::BasicFrameEncodeBuffer encoder(buffer, sizeof(buffer)); - if (!encoder.encode(&format, SERIALIZATION_TEST_SERIALIZATION_TEST_MESSAGE_MSG_ID, &msg, msg_size)) { + if (!encoder.encode(SERIALIZATION_TEST_SERIALIZATION_TEST_MESSAGE_MSG_ID, &msg, + SERIALIZATION_TEST_SERIALIZATION_TEST_MESSAGE_MAX_SIZE)) { print_failure_details("Failed to encode message"); std::cout << "[TEST END] C++ Cross-Platform Serialization: FAIL\n\n"; return 1; diff --git a/tests/js/test_cross_platform_deserialization.js b/tests/js/test_cross_platform_deserialization.js index 53ecf952..7f62fe0a 100644 --- a/tests/js/test_cross_platform_deserialization.js +++ b/tests/js/test_cross_platform_deserialization.js @@ -7,6 +7,12 @@ const fs = require('fs'); const path = require('path'); +// BasicFrame constants +const BASIC_FRAME_START_BYTE1 = 0x90; +const BASIC_FRAME_START_BYTE2 = 0x91; +const BASIC_FRAME_HEADER_SIZE = 3; // start1 + start2 + msg_id +const BASIC_FRAME_FOOTER_SIZE = 2; // crc1 + crc2 + function printFailureDetails(label) { console.log('\n============================================================'); console.log('FAILURE DETAILS: ' + label); @@ -25,26 +31,40 @@ function loadExpectedValues() { } function validateBasicFrame(buffer, expected) { - // Very basic frame validation - if (buffer.length < 4) { + // BasicFrame validation with 2 start bytes + if (buffer.length < BASIC_FRAME_HEADER_SIZE + BASIC_FRAME_FOOTER_SIZE) { console.log(' Data too short'); return false; } - // Check start byte - if (buffer[0] !== 0x90) { - console.log(' Invalid start byte'); + // Check start bytes + if (buffer[0] !== BASIC_FRAME_START_BYTE1 || buffer[1] !== BASIC_FRAME_START_BYTE2) { + console.log(' Invalid start bytes (expected 0x90 0x91, got 0x' + buffer[0].toString(16) + ' 0x' + buffer[1].toString(16) + ')'); return false; } - // Check message ID - if (buffer[1] !== 204) { - console.log(' Invalid message ID'); + // Check message ID (at offset 2 in BasicFrame) + if (buffer[2] !== 204) { + console.log(' Invalid message ID (expected 204, got ' + buffer[2] + ')'); + return false; + } + + // Validate checksum + const msgLen = buffer.length - BASIC_FRAME_HEADER_SIZE - BASIC_FRAME_FOOTER_SIZE; + let byte1 = buffer[2]; // Start with msg_id + let byte2 = buffer[2]; + for (let i = 0; i < msgLen; i++) { + byte1 = (byte1 + buffer[BASIC_FRAME_HEADER_SIZE + i]) & 0xFF; + byte2 = (byte2 + byte1) & 0xFF; + } + + if (byte1 !== buffer[buffer.length - 2] || byte2 !== buffer[buffer.length - 1]) { + console.log(' Checksum mismatch'); return false; } - // Extract magic number from payload (starts at byte 2) - const magicNumber = buffer.readUInt32LE(2); + // Extract magic number from payload (starts at offset 3 in BasicFrame) + const magicNumber = buffer.readUInt32LE(BASIC_FRAME_HEADER_SIZE); if (magicNumber !== expected.magic_number) { console.log(' Magic number mismatch (expected ' + expected.magic_number + ', got ' + magicNumber + ')'); return false; diff --git a/tests/js/test_cross_platform_serialization.js b/tests/js/test_cross_platform_serialization.js index 1ce6ffea..cc8b9e33 100644 --- a/tests/js/test_cross_platform_serialization.js +++ b/tests/js/test_cross_platform_serialization.js @@ -7,6 +7,12 @@ const fs = require('fs'); const path = require('path'); +// BasicFrame constants +const BASIC_FRAME_START_BYTE1 = 0x90; +const BASIC_FRAME_START_BYTE2 = 0x91; +const BASIC_FRAME_HEADER_SIZE = 3; // start1 + start2 + msg_id +const BASIC_FRAME_FOOTER_SIZE = 2; // crc1 + crc2 + function printFailureDetails(label, expectedValues, actualValues, rawData) { console.log('\n============================================================'); console.log('FAILURE DETAILS: ' + label); @@ -34,24 +40,6 @@ function printFailureDetails(label, expectedValues, actualValues, rawData) { console.log('============================================================\n'); } -let serialization_test_SerializationTestMessage; -let msg_encode; -let struct_frame_buffer; -let basic_frame_config; - -try { - const serializationTestModule = require('./serialization_test.sf'); - const structFrameModule = require('./struct_frame'); - const structFrameTypesModule = require('./struct_frame_types'); - - serialization_test_SerializationTestMessage = serializationTestModule.serialization_test_SerializationTestMessage; - msg_encode = structFrameModule.msg_encode; - struct_frame_buffer = structFrameTypesModule.struct_frame_buffer; - basic_frame_config = structFrameTypesModule.basic_frame_config; -} catch (error) { - // Skip test if generated modules are not available -} - function loadExpectedValues() { try { const jsonPath = path.join(__dirname, '../../expected_values.json'); @@ -71,9 +59,8 @@ function createTestData() { return false; } - // Create a full framed message manually that matches C/Python format - // Frame format: [start_byte] [msg_id] [payload] [checksum1] [checksum2] - const start_byte = 0x90; + // Create a full BasicFrame message that matches C/Python format + // Frame format: [START1=0x90] [START2=0x91] [MSG_ID] [payload] [checksum1] [checksum2] const msg_id = 204; // Create full payload matching the message structure: @@ -120,7 +107,7 @@ function createTestData() { offset += 4; } - // Calculate Fletcher checksum on msg_id + payload (consistent with C and Python) + // Calculate Fletcher checksum on msg_id + payload (consistent with C and Python BasicFrame) let byte1 = msg_id; // Start with msg_id let byte2 = msg_id; for (let i = 0; i < payload.length; i++) { @@ -128,11 +115,12 @@ function createTestData() { byte2 = (byte2 + byte1) & 0xFF; } - // Build complete frame - const frame = Buffer.alloc(2 + payloadSize + 2); - frame[0] = start_byte; - frame[1] = msg_id; - payload.copy(frame, 2); + // Build complete frame with BasicFrame format + const frame = Buffer.alloc(BASIC_FRAME_HEADER_SIZE + payloadSize + BASIC_FRAME_FOOTER_SIZE); + frame[0] = BASIC_FRAME_START_BYTE1; + frame[1] = BASIC_FRAME_START_BYTE2; + frame[2] = msg_id; + payload.copy(frame, BASIC_FRAME_HEADER_SIZE); frame[frame.length - 2] = byte1; frame[frame.length - 1] = byte2; diff --git a/tests/py/test_cross_platform_deserialization.py b/tests/py/test_cross_platform_deserialization.py index b1837b41..d5f4c94e 100644 --- a/tests/py/test_cross_platform_deserialization.py +++ b/tests/py/test_cross_platform_deserialization.py @@ -116,20 +116,13 @@ def read_and_validate_test_data(filename): sys.path.insert(0, '../generated/py') from serialization_test_sf import SerializationTestSerializationTestMessage - from struct_frame_parser import BasicPacket, FrameParser + from struct_frame_parser import BasicFrame - packet_formats = {0x90: BasicPacket()} - msg_definitions = {204: SerializationTestSerializationTestMessage} - parser = FrameParser(packet_formats, msg_definitions) - - decoded_msg = None - for byte in binary_data: - result = parser.parse_char(byte) - if result: - decoded_msg = result - break - - if not decoded_msg: + # Validate and decode using BasicFrame + basic_frame = BasicFrame() + result = basic_frame.validate_packet(list(binary_data)) + + if not result or not result['valid']: print_failure_details( "Failed to decode data", expected_values={"decoded_message": "valid"}, @@ -138,6 +131,10 @@ def read_and_validate_test_data(filename): ) return False + # Decode the message data + msg_data = bytes(result['msg_data']) + decoded_msg = SerializationTestSerializationTestMessage.create_unpack(msg_data) + # Load expected values and validate expected = load_expected_values() if not expected: diff --git a/tests/py/test_cross_platform_serialization.py b/tests/py/test_cross_platform_serialization.py index 9aac1665..99163fd2 100755 --- a/tests/py/test_cross_platform_serialization.py +++ b/tests/py/test_cross_platform_serialization.py @@ -49,7 +49,7 @@ def create_test_data(): try: sys.path.insert(0, '../generated/py') from serialization_test_sf import SerializationTestSerializationTestMessage - from struct_frame_parser import BasicPacket + from struct_frame_parser import BasicFrame # Load expected values from JSON expected = load_expected_values() @@ -65,7 +65,7 @@ def create_test_data(): test_array=expected['test_array'] ) - packet = BasicPacket() + packet = BasicFrame() encoded_data = packet.encode_msg(msg) with open('python_test_data.bin', 'wb') as f: diff --git a/tests/ts/test_cross_platform_deserialization.ts b/tests/ts/test_cross_platform_deserialization.ts index 81a753c4..34a522a2 100644 --- a/tests/ts/test_cross_platform_deserialization.ts +++ b/tests/ts/test_cross_platform_deserialization.ts @@ -1,6 +1,12 @@ import * as fs from 'fs'; import * as path from 'path'; +// BasicFrame constants +const BASIC_FRAME_START_BYTE1 = 0x90; +const BASIC_FRAME_START_BYTE2 = 0x91; +const BASIC_FRAME_HEADER_SIZE = 3; // start1 + start2 + msg_id +const BASIC_FRAME_FOOTER_SIZE = 2; // crc1 + crc2 + function printFailureDetails(label: string): void { console.log('\n============================================================'); console.log(`FAILURE DETAILS: ${label}`); @@ -19,26 +25,40 @@ function loadExpectedValues(): any { } function validateBasicFrame(buffer: Buffer, expected: any): boolean { - // Very basic frame validation - if (buffer.length < 4) { + // BasicFrame validation with 2 start bytes + if (buffer.length < BASIC_FRAME_HEADER_SIZE + BASIC_FRAME_FOOTER_SIZE) { console.log(' Data too short'); return false; } - // Check start byte - if (buffer[0] !== 0x90) { - console.log(' Invalid start byte'); + // Check start bytes + if (buffer[0] !== BASIC_FRAME_START_BYTE1 || buffer[1] !== BASIC_FRAME_START_BYTE2) { + console.log(` Invalid start bytes (expected 0x90 0x91, got 0x${buffer[0].toString(16)} 0x${buffer[1].toString(16)})`); return false; } - // Check message ID - if (buffer[1] !== 204) { - console.log(' Invalid message ID'); + // Check message ID (at offset 2 in BasicFrame) + if (buffer[2] !== 204) { + console.log(` Invalid message ID (expected 204, got ${buffer[2]})`); + return false; + } + + // Validate checksum + const msgLen = buffer.length - BASIC_FRAME_HEADER_SIZE - BASIC_FRAME_FOOTER_SIZE; + let byte1 = buffer[2]; // Start with msg_id + let byte2 = buffer[2]; + for (let i = 0; i < msgLen; i++) { + byte1 = (byte1 + buffer[BASIC_FRAME_HEADER_SIZE + i]) & 0xFF; + byte2 = (byte2 + byte1) & 0xFF; + } + + if (byte1 !== buffer[buffer.length - 2] || byte2 !== buffer[buffer.length - 1]) { + console.log(` Checksum mismatch`); return false; } - // Extract magic number from payload (starts at byte 2) - const magicNumber = buffer.readUInt32LE(2); + // Extract magic number from payload (starts at offset 3 in BasicFrame) + const magicNumber = buffer.readUInt32LE(BASIC_FRAME_HEADER_SIZE); if (magicNumber !== expected.magic_number) { console.log(` Magic number mismatch (expected ${expected.magic_number}, got ${magicNumber})`); return false; diff --git a/tests/ts/test_cross_platform_serialization.ts b/tests/ts/test_cross_platform_serialization.ts index 889a528f..790510be 100644 --- a/tests/ts/test_cross_platform_serialization.ts +++ b/tests/ts/test_cross_platform_serialization.ts @@ -28,23 +28,11 @@ function printFailureDetails(label: string, expectedValues?: any, actualValues?: console.log('============================================================\n'); } -let serialization_test_SerializationTestMessage: any; -let msg_encode: any; -let struct_frame_buffer: any; -let basic_frame_config: any; - -try { - const serializationTestModule = require('./serialization_test.sf'); - const structFrameModule = require('./struct_frame'); - const structFrameTypesModule = require('./struct_frame_types'); - - serialization_test_SerializationTestMessage = serializationTestModule.serialization_test_SerializationTestMessage; - msg_encode = structFrameModule.msg_encode; - struct_frame_buffer = structFrameTypesModule.struct_frame_buffer; - basic_frame_config = structFrameTypesModule.basic_frame_config; -} catch (error) { - // Skip test if generated modules are not available -} +// BasicFrame constants +const BASIC_FRAME_START_BYTE1 = 0x90; +const BASIC_FRAME_START_BYTE2 = 0x91; +const BASIC_FRAME_HEADER_SIZE = 3; // start1 + start2 + msg_id +const BASIC_FRAME_FOOTER_SIZE = 2; // crc1 + crc2 function loadExpectedValues(): any { try { @@ -65,9 +53,8 @@ function createTestData(): boolean { return false; } - // Create a full framed message manually that matches C/Python format - // Frame format: [start_byte] [msg_id] [payload] [checksum1] [checksum2] - const start_byte = 0x90; + // Create a full BasicFrame message that matches C/Python format + // Frame format: [START1=0x90] [START2=0x91] [MSG_ID] [payload] [checksum1] [checksum2] const msg_id = 204; // Create full payload matching the message structure: @@ -114,7 +101,7 @@ function createTestData(): boolean { offset += 4; } - // Calculate Fletcher checksum on msg_id + payload (consistent with C and Python) + // Calculate Fletcher checksum on msg_id + payload (consistent with C and Python BasicFrame) let byte1 = msg_id; // Start with msg_id let byte2 = msg_id; for (let i = 0; i < payload.length; i++) { @@ -122,11 +109,12 @@ function createTestData(): boolean { byte2 = (byte2 + byte1) & 0xFF; } - // Build complete frame - const frame = Buffer.alloc(2 + payloadSize + 2); - frame[0] = start_byte; - frame[1] = msg_id; - payload.copy(frame, 2); + // Build complete frame with BasicFrame format + const frame = Buffer.alloc(BASIC_FRAME_HEADER_SIZE + payloadSize + BASIC_FRAME_FOOTER_SIZE); + frame[0] = BASIC_FRAME_START_BYTE1; + frame[1] = BASIC_FRAME_START_BYTE2; + frame[2] = msg_id; + payload.copy(frame, BASIC_FRAME_HEADER_SIZE); frame[frame.length - 2] = byte1; frame[frame.length - 1] = byte2; From 866e1b85cc6f0e339e07f19e5dff84fd6e24458e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 29 Nov 2025 22:52:10 +0000 Subject: [PATCH 6/6] Fix strncpy to use sizeof for buffer safety Co-authored-by: rijesha <7819200+rijesha@users.noreply.github.com> --- tests/c/test_arrays.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/c/test_arrays.c b/tests/c/test_arrays.c index c29b641d..8cd4b13b 100644 --- a/tests/c/test_arrays.c +++ b/tests/c/test_arrays.c @@ -47,12 +47,12 @@ int test_array_operations() { msg.bounded_doubles.data[0] = 123.456; msg.bounded_doubles.data[1] = 789.012; - strncpy(msg.fixed_strings[0], "String1", 8); - strncpy(msg.fixed_strings[1], "String2", 8); + strncpy(msg.fixed_strings[0], "String1", sizeof(msg.fixed_strings[0])); + strncpy(msg.fixed_strings[1], "String2", sizeof(msg.fixed_strings[1])); msg.bounded_strings.count = 2; - strncpy(msg.bounded_strings.data[0], "BoundedStr1", 12); - strncpy(msg.bounded_strings.data[1], "BoundedStr2", 12); + strncpy(msg.bounded_strings.data[0], "BoundedStr1", sizeof(msg.bounded_strings.data[0])); + strncpy(msg.bounded_strings.data[1], "BoundedStr2", sizeof(msg.bounded_strings.data[1])); msg.fixed_statuses[0] = 1; msg.fixed_statuses[1] = 2;