Parser & Framer Feature Matrix¶
This document provides a comprehensive matrix of parser and framer features across all supported languages in struct-frame.
Overview¶
struct-frame supports multiple parser types and framing strategies optimized for different use cases:
- Profile-Specific Parsers: Optimized parsers for individual frame profiles (STANDARD, SENSOR, IPC, BULK, NETWORK)
- Polyglot Parsers: Universal parsers that can handle multiple frame profiles in a single parser instance
- Buffer Parsing: Efficient in-place parsing of byte buffers without copying
- Zero-Copy Framing: Access message data directly from input buffer without copying to intermediate structures
Feature Matrix¶
| Language | Profile-Specific Parsers | Polyglot Parser | Buffer Parsing | Zero-Copy Support | Notes |
|---|---|---|---|---|---|
| C | ✅ Yes | ❌ No | ⚠️ Byte-by-byte | ✅ Yes | Profile parsers via profile_standard_t, profile_sensor_t, etc. |
| C++ | ✅ Yes | ❌ No | ✅ Yes | ✅ Yes | Buffer API: parse_buffer(), returns pointer into original buffer |
| Python | ✅ Yes | ✅ Yes | ⚠️ Byte-by-byte | ⚠️ Partial | Profile parsers available, polyglot parser is default |
| TypeScript | ✅ Yes | ❌ No | ⚠️ Byte-by-byte | ⚠️ Partial | Profile parsers via generated classes |
| JavaScript | ✅ Yes | ❌ No | ⚠️ Byte-by-byte | ⚠️ Partial | Profile parsers via generated classes |
| C# | ✅ Yes | ❌ No | ⚠️ Byte-by-byte | ⚠️ Partial | Profile parsers via generated classes |
| GraphQL | N/A | N/A | N/A | N/A | Schema generation only |
Legend¶
- ✅ Yes: Full support with optimized implementation
- ⚠️ Partial: Basic support, may have limitations or performance implications
- ⚠️ Byte-by-byte: Parser processes one byte at a time (no buffer scanning API)
- ❌ No: Not supported
- N/A: Not applicable for this language
Profile-Specific Parsers¶
Profile-specific parsers are optimized for a single frame format, eliminating runtime checks for profile detection and reducing overhead.
Supported Profiles¶
All languages support parsers for these standard profiles:
- STANDARD (
BasicDefault): General serial/UART communication - SENSOR (
TinyMinimal): Low-bandwidth sensor data - IPC (
NoneMinimal): Trusted inter-process communication - BULK (
BasicExtended): Large file transfers - NETWORK (
BasicExtendedMultiSystemStream): Multi-node mesh networks
Language-Specific Usage¶
C¶
#include "frame_parsers.h"
// Profile-specific parser (using STANDARD profile)
profile_standard_t parser;
uint8_t buffer[256];
profile_standard_init(&parser, buffer, sizeof(buffer), NULL);
// Parse byte-by-byte
frame_msg_info_t result = profile_standard_parse_byte(&parser, byte);
if (result.valid) {
// Process message: result.msg_id, result.msg_data, result.msg_len
}
C++¶
#include "frame_parsers.hpp"
using namespace FrameParsers;
// Profile-specific parser (using STANDARD profile)
uint8_t buffer[256];
ProfileStandard parser(buffer, sizeof(buffer));
// Option 1: Byte-by-byte parsing
FrameMsgInfo result = parser.parse_byte(byte);
// Option 2: Buffer parsing (efficient, zero-copy)
size_t consumed = 0;
FrameMsgInfo result = parser.parse_buffer(data_buffer, data_len, &consumed);
if (result.valid) {
// result.msg_data points into original data_buffer (zero-copy!)
process_message(result.msg_id, result.msg_data, result.msg_len);
data_buffer += consumed; // Advance buffer
data_len -= consumed;
}
// Profile aliases for convenience
using ProfileStandard = BasicDefaultParser; // Profile.STANDARD
using ProfileSensor = TinyMinimalParser; // Profile.SENSOR
using ProfileBulk = BasicExtendedParser; // Profile.BULK
using ProfileNetwork = BasicExtendedMultiSystemStreamParser; // Profile.NETWORK
Python¶
from struct_frame_parser import BasicDefaultParser
# Profile-specific parser (STANDARD profile)
parser = BasicDefaultParser()
# Parse byte-by-byte
result = parser.parse_byte(byte_val)
if result['valid']:
msg_id = result['msg_id']
msg_data = result['msg_data']
msg_len = result['msg_len']
TypeScript¶
import { BasicDefaultParser } from './frame_parsers_gen';
// Profile-specific parser (STANDARD profile)
const parser = new BasicDefaultParser();
// Parse byte-by-byte
const result = parser.parseByte(byte);
if (result.valid) {
const msgId = result.msgId;
const msgData = result.msgData;
const msgLen = result.msgLen;
}
Polyglot Parsers¶
Polyglot parsers can handle multiple frame profiles in a single parser instance, automatically detecting the profile from start bytes.
Trade-offs¶
| Aspect | Profile-Specific | Polyglot |
|---|---|---|
| Performance | ⚡ Faster (no profile detection) | Slower (runtime profile checks) |
| Code Size | Larger (separate parser per profile) | Smaller (single unified parser) |
| Flexibility | Fixed to one profile | Handles multiple profiles |
| Use Case | Known fixed profile | Dynamic or mixed profiles |
Language Support¶
Python (Default)¶
from struct_frame_parser import Parser
# Polyglot parser supporting multiple profiles
parser = Parser(
enabled_headers=[HeaderType.BASIC, HeaderType.TINY],
enabled_payloads=[PayloadType.DEFAULT, PayloadType.MINIMAL]
)
result = parser.parse_byte(byte_val)
# Automatically detects profile from start bytes
C++¶
Buffer Parsing¶
Buffer parsing allows scanning entire byte buffers for frames without byte-by-byte state machine overhead.
C++ Buffer Parsing API¶
The C++ implementation provides the most advanced buffer parsing support:
#include "frame_parsers.hpp"
using namespace FrameParsers;
BasicDefaultParser parser(internal_buffer, sizeof(internal_buffer));
// Efficient buffer scanning
const uint8_t* ring_buffer = get_uart_buffer();
size_t available = get_uart_available();
size_t consumed = 0;
// Scan for frames (no copying)
FrameMsgInfo result = parser.parse_buffer(ring_buffer, available, &consumed);
if (result.valid) {
// result.msg_data points directly into ring_buffer (zero-copy!)
// Process the message immediately or copy if needed for async processing
handle_message(result.msg_id, result.msg_data, result.msg_len);
// Advance ring buffer read pointer
advance_ring_buffer(consumed);
}
Benefits¶
- Performance: Scans memory directly, no byte-by-byte state machine overhead
- Zero-Copy: Message data pointer points into original buffer (when CRC valid)
- Ring Buffer Support: Works seamlessly with circular buffers
- No std Library: Pure pointer arithmetic, embedded-friendly
- Error Recovery: Automatically skips junk bytes before valid frame
Use Cases¶
- High-Throughput Serial: Parse multiple frames from DMA buffer
- Network Packets: Scan UDP/TCP payload for embedded frames
- File Processing: Parse frame log files efficiently
- Ring Buffers: Direct parsing from UART/SPI circular buffers
Zero-Copy Support¶
Zero-copy parsing avoids intermediate memory copies by returning pointers into the original input buffer.
C++ (Full Support)¶
// Buffer parsing returns pointer into original buffer
FrameMsgInfo result = parser.parse_buffer(uart_buffer, uart_len, &consumed);
// result.msg_data points into uart_buffer - no copy!
Constraints¶
Zero-copy is only possible when:
- CRC is valid: Message integrity verified before returning pointer
- Buffer remains valid: Caller must not free/reuse buffer while using msg_data
- No framing modifications: Buffer content is unmodified
When to Copy¶
Copy the message data if:
- Asynchronous processing (buffer may be reused)
- Long-term storage (buffer is temporary)
- Buffer is ring/circular (wrapping may occur)
// Copy for async processing
std::vector<uint8_t> message_copy(result.msg_data, result.msg_data + result.msg_len);
async_queue.push(MessageTask{result.msg_id, std::move(message_copy)});
Best Practices¶
Choosing a Parser Type¶
- Embedded C++ with High Throughput: Use C++ buffer parsing with profile-specific parser
- Embedded C with Low Resources: Use C byte-by-byte with profile-specific parser
- Python for Flexibility: Use polyglot parser for dynamic environments
- Web/Desktop Apps: Use TypeScript/C# profile-specific for type safety
Optimizing Performance¶
- Use Profile-Specific Parsers: When profile is known at compile time
- Use Buffer Parsing: For batch processing or high-throughput scenarios
- Minimize CRC Checks: Profile-specific parsers do CRC once per frame
- Zero-Copy When Possible: Avoid unnecessary memory copies
Error Handling¶
// C++ with buffer parsing
size_t consumed = 0;
FrameMsgInfo result = parser.parse_buffer(buffer, len, &consumed);
if (result.valid) {
process_message(result);
} else if (consumed > 0) {
// Skipped some junk bytes, keep parsing
buffer += consumed;
len -= consumed;
} else {
// Not enough data for complete frame, wait for more
wait_for_more_data();
}
Future Enhancements¶
Planned improvements to parser feature matrix:
- [ ] C buffer parsing API (similar to C++)
- [ ] Python buffer parsing API (with memoryview support)
- [ ] TypeScript/JavaScript buffer parsing (Uint8Array)
- [ ] C# buffer parsing (Span
) - [ ] Profile-specific polyglot parser (detect profile once, then switch to optimized path)
See Also¶
- Framing Guide - Choosing the right profile
- Framing Architecture - How framing works
- C++ SDK - C++ API documentation
- C SDK - C API documentation (TBD)
- Python SDK - Python API documentation