mirror of
https://github.com/torlando-tech/pyxis.git
synced 2026-06-24 00:11:51 +00:00
70d4aa6be9
Repins microReticulum + microLXMF onto the upstream-0.4.1 graft and adapts pyxis to the new src/microReticulum/ layout and 0.4.x APIs. The far-diverged 0.3.0 fork's Resource/Transport/Identity work is subsumed by upstream's reimplementation; only the still-needed fixes ride on the pinned branches (PKCS7/HMAC/X25519 crypto -- proven byte-identical to python RNS 1.3.1 -- Packet link-proof callback, Identity short-sig guard, and the bz2 layer + decompress-on-receive in Resource::assemble()). Consumer-side changes: - platformio.ini: pin microReticulum @2f21fee (pyxis-fixes-on-0.4.1) and microLXMF @33760d0 (chore/microreticulum-0.4.1-layout); bump microStore ceea8f5 -> c5fb69d (0.4.x requires the new BasicFileStore::init API); -std=gnu++11 -> gnu++17 (upstream requires C++17). - Namespace all microReticulum includes (angle + quote) to <microReticulum/...> for the relocated layout; shim-local Utilities/Stream.h|Print.h preserved. - Interface::send_outgoing now returns bool: update TCP/BLE/SX1262/Auto overrides with correct success/failure returns. - SDArchiveFileSystem::init(bool reformatOnFail=true) to match new microStore. - Static Transport::get_path_table() -> path_table(); instance getter unchanged. - Remove duplicate shim Cryptography/BZ2 (microReticulum provides it now; keep lib/libbz2 as the ESP32 bzlib provider). - patch_littlefs_paths.py: normalize microStore's LittleFS adapter paths to a leading "/" -- ESP32 Arduino LittleFS rejects "./"-prefixed paths, which silently broke the path store (no peer paths learned, all messaging blocked). Validated on T-Deck Plus: builds (RAM 27.5% / Flash 77.7%), boots stable (no WDT/panic), and a full on-device LXMF e2e (DIRECT + OPPORTUNISTIC + bz2-compressed-Resource receive) passes 5/5. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UWZuYkHBRqNb6BZHV8sTG5
178 lines
5.3 KiB
C++
178 lines
5.3 KiB
C++
/**
|
|
* @file BLEFragmenter.cpp
|
|
* @brief BLE-Reticulum Protocol v2.2 packet fragmenter implementation
|
|
*/
|
|
|
|
#include "BLEFragmenter.h"
|
|
#include <microReticulum/Log.h>
|
|
|
|
namespace RNS { namespace BLE {
|
|
|
|
BLEFragmenter::BLEFragmenter(size_t mtu) {
|
|
setMTU(mtu);
|
|
}
|
|
|
|
void BLEFragmenter::setMTU(size_t mtu) {
|
|
// Ensure MTU is at least the minimum
|
|
_mtu = (mtu >= MTU::MINIMUM) ? mtu : MTU::MINIMUM;
|
|
|
|
// Calculate payload size (MTU minus header)
|
|
if (_mtu > Fragment::HEADER_SIZE) {
|
|
_payload_size = _mtu - Fragment::HEADER_SIZE;
|
|
} else {
|
|
_payload_size = 0;
|
|
WARNING("BLEFragmenter: MTU too small for fragmentation");
|
|
}
|
|
}
|
|
|
|
bool BLEFragmenter::needsFragmentation(const Bytes& data) const {
|
|
return data.size() > _payload_size;
|
|
}
|
|
|
|
uint16_t BLEFragmenter::calculateFragmentCount(size_t data_size) const {
|
|
if (_payload_size == 0) return 0;
|
|
if (data_size == 0) return 1; // Empty data still produces one fragment
|
|
return static_cast<uint16_t>((data_size + _payload_size - 1) / _payload_size);
|
|
}
|
|
|
|
std::vector<Bytes> BLEFragmenter::fragment(const Bytes& data, uint16_t sequence_base) {
|
|
std::vector<Bytes> fragments;
|
|
|
|
if (_payload_size == 0) {
|
|
ERROR("BLEFragmenter: Cannot fragment with zero payload size");
|
|
return fragments;
|
|
}
|
|
|
|
// Handle empty data case
|
|
if (data.size() == 0) {
|
|
// Single empty END fragment
|
|
fragments.push_back(createFragment(Fragment::END, sequence_base, 1, Bytes()));
|
|
return fragments;
|
|
}
|
|
|
|
uint16_t total_fragments = calculateFragmentCount(data.size());
|
|
|
|
// Pre-allocate vector to avoid incremental reallocations
|
|
fragments.reserve(total_fragments);
|
|
|
|
size_t offset = 0;
|
|
|
|
for (uint16_t i = 0; i < total_fragments; i++) {
|
|
// Calculate payload size for this fragment
|
|
size_t remaining = data.size() - offset;
|
|
size_t chunk_size = (remaining < _payload_size) ? remaining : _payload_size;
|
|
|
|
// Extract payload chunk
|
|
Bytes payload(data.data() + offset, chunk_size);
|
|
offset += chunk_size;
|
|
|
|
// Determine fragment type
|
|
Fragment::Type type;
|
|
if (total_fragments == 1) {
|
|
// Single fragment - use END type
|
|
type = Fragment::END;
|
|
} else if (i == 0) {
|
|
// First of multiple fragments
|
|
type = Fragment::START;
|
|
} else if (i == total_fragments - 1) {
|
|
// Last fragment
|
|
type = Fragment::END;
|
|
} else {
|
|
// Middle fragment
|
|
type = Fragment::CONTINUE;
|
|
}
|
|
|
|
uint16_t sequence = sequence_base + i;
|
|
fragments.push_back(createFragment(type, sequence, total_fragments, payload));
|
|
}
|
|
|
|
{
|
|
char buf[80];
|
|
snprintf(buf, sizeof(buf), "BLEFragmenter: Fragmented %zu bytes into %zu fragments",
|
|
data.size(), fragments.size());
|
|
TRACE(buf);
|
|
}
|
|
|
|
return fragments;
|
|
}
|
|
|
|
Bytes BLEFragmenter::createFragment(Fragment::Type type, uint16_t sequence,
|
|
uint16_t total_fragments, const Bytes& payload) {
|
|
// Allocate buffer for header + payload
|
|
size_t total_size = Fragment::HEADER_SIZE + payload.size();
|
|
Bytes fragment(total_size);
|
|
uint8_t* ptr = fragment.writable(total_size);
|
|
fragment.resize(total_size);
|
|
|
|
// Byte 0: Type
|
|
ptr[0] = static_cast<uint8_t>(type);
|
|
|
|
// Bytes 1-2: Sequence number (big-endian)
|
|
ptr[1] = static_cast<uint8_t>((sequence >> 8) & 0xFF);
|
|
ptr[2] = static_cast<uint8_t>(sequence & 0xFF);
|
|
|
|
// Bytes 3-4: Total fragments (big-endian)
|
|
ptr[3] = static_cast<uint8_t>((total_fragments >> 8) & 0xFF);
|
|
ptr[4] = static_cast<uint8_t>(total_fragments & 0xFF);
|
|
|
|
// Bytes 5+: Payload
|
|
if (payload.size() > 0) {
|
|
memcpy(ptr + Fragment::HEADER_SIZE, payload.data(), payload.size());
|
|
}
|
|
|
|
return fragment;
|
|
}
|
|
|
|
bool BLEFragmenter::parseHeader(const Bytes& fragment, Fragment::Type& type,
|
|
uint16_t& sequence, uint16_t& total_fragments) {
|
|
if (fragment.size() < Fragment::HEADER_SIZE) {
|
|
return false;
|
|
}
|
|
|
|
const uint8_t* ptr = fragment.data();
|
|
|
|
// Byte 0: Type
|
|
uint8_t type_byte = ptr[0];
|
|
if (type_byte != Fragment::START &&
|
|
type_byte != Fragment::CONTINUE &&
|
|
type_byte != Fragment::END) {
|
|
return false;
|
|
}
|
|
type = static_cast<Fragment::Type>(type_byte);
|
|
|
|
// Bytes 1-2: Sequence number (big-endian)
|
|
sequence = (static_cast<uint16_t>(ptr[1]) << 8) | static_cast<uint16_t>(ptr[2]);
|
|
|
|
// Bytes 3-4: Total fragments (big-endian)
|
|
total_fragments = (static_cast<uint16_t>(ptr[3]) << 8) | static_cast<uint16_t>(ptr[4]);
|
|
|
|
// Validate total_fragments is non-zero
|
|
if (total_fragments == 0) {
|
|
return false;
|
|
}
|
|
|
|
// Validate sequence < total_fragments
|
|
if (sequence >= total_fragments) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
Bytes BLEFragmenter::extractPayload(const Bytes& fragment) {
|
|
if (fragment.size() <= Fragment::HEADER_SIZE) {
|
|
return Bytes();
|
|
}
|
|
|
|
return Bytes(fragment.data() + Fragment::HEADER_SIZE,
|
|
fragment.size() - Fragment::HEADER_SIZE);
|
|
}
|
|
|
|
bool BLEFragmenter::isValidFragment(const Bytes& fragment) {
|
|
Fragment::Type type;
|
|
uint16_t sequence, total;
|
|
return parseHeader(fragment, type, sequence, total);
|
|
}
|
|
|
|
}} // namespace RNS::BLE
|