Pack RAK Full help text at compile time without changing output
This commit is contained in:
1 parent
0f8cd1e019
commit
4deff5b687
3 files changed
+162
-5
No files matched your search
@@ -22,4 +22,117 @@ size_t writeHelpChunks(Output& output, const char* text, size_t length) {
|
||||
return written;
|
||||
}
|
||||
|
||||
namespace help_packing {
|
||||
|
||||
// Help is ASCII. Bytes 0x80..0x8f encode these common fragments; the original
|
||||
// text exists only during constant evaluation, never alongside the packed data.
|
||||
constexpr size_t TokenCount = 16;
|
||||
constexpr char Dictionary[] =
|
||||
" get/set \0" " get \0" " set \0" "get/set \0"
|
||||
"bluetooth.\0" "pwrmgt.\0" "radio.\0" "default\0"
|
||||
"preamble\0" "on|off\0" " (saved; \0" " \0"
|
||||
"get \0" "set \0" "\r\n\0" " / \0";
|
||||
|
||||
struct TokenOffsets { uint8_t value[TokenCount + 1] = {}; };
|
||||
constexpr TokenOffsets tokenOffsets() {
|
||||
TokenOffsets result{};
|
||||
size_t offset = 0;
|
||||
for (size_t token = 0; token < TokenCount; ++token) {
|
||||
result.value[token] = static_cast<uint8_t>(offset);
|
||||
while (Dictionary[offset]) ++offset;
|
||||
++offset;
|
||||
}
|
||||
result.value[TokenCount] = static_cast<uint8_t>(offset);
|
||||
return result;
|
||||
}
|
||||
static_assert(sizeof(Dictionary) < 256, "Help dictionary offsets need one byte");
|
||||
constexpr TokenOffsets Offsets = tokenOffsets();
|
||||
constexpr size_t tokenLength(size_t token) {
|
||||
return Offsets.value[token + 1] - Offsets.value[token] - 1;
|
||||
}
|
||||
constexpr size_t matchToken(const char* text, size_t remaining) {
|
||||
for (size_t token = 0; token < TokenCount; ++token) {
|
||||
const char* fragment = Dictionary + Offsets.value[token];
|
||||
const size_t length = tokenLength(token);
|
||||
if (length > remaining || *text != *fragment) continue;
|
||||
size_t i = 1;
|
||||
while (i < length && text[i] == fragment[i]) ++i;
|
||||
if (i == length) return token;
|
||||
}
|
||||
return TokenCount;
|
||||
}
|
||||
|
||||
} // namespace help_packing
|
||||
|
||||
template<size_t N>
|
||||
constexpr bool validHelpText(const char (&text)[N]) {
|
||||
if (text[N - 1] != 0) return false;
|
||||
for (size_t i = 0; i + 1 < N; ++i) {
|
||||
if (text[i] == 0 || static_cast<uint8_t>(text[i]) >= 0x80) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template<size_t N>
|
||||
constexpr size_t packedHelpSize(const char (&text)[N]) {
|
||||
size_t packed = 0;
|
||||
for (size_t i = 0; i + 1 < N; ++packed) {
|
||||
const size_t token = help_packing::matchToken(text + i, N - i - 1);
|
||||
i += token < help_packing::TokenCount ? help_packing::tokenLength(token) : 1;
|
||||
}
|
||||
return packed;
|
||||
}
|
||||
|
||||
template<size_t N> struct PackedHelp {
|
||||
static_assert(N > 0, "A help image must contain text");
|
||||
uint8_t bytes[N] = {};
|
||||
};
|
||||
|
||||
template<size_t PackedSize, size_t N>
|
||||
constexpr PackedHelp<PackedSize> packHelp(const char (&text)[N]) {
|
||||
PackedHelp<PackedSize> result{};
|
||||
size_t packed = 0;
|
||||
for (size_t i = 0; i + 1 < N;) {
|
||||
const size_t token = help_packing::matchToken(text + i, N - i - 1);
|
||||
if (token < help_packing::TokenCount) {
|
||||
result.bytes[packed++] = static_cast<uint8_t>(0x80 + token);
|
||||
i += help_packing::tokenLength(token);
|
||||
} else {
|
||||
result.bytes[packed++] = static_cast<uint8_t>(text[i++]);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
template<typename Output>
|
||||
size_t writePackedHelp(Output& output, const uint8_t* packed, size_t length) {
|
||||
if (!packed) return 0;
|
||||
size_t position = 0, written = 0;
|
||||
const char* expansion = nullptr;
|
||||
while (position < length || expansion) {
|
||||
char chunk[64];
|
||||
size_t used = 0;
|
||||
while (used < sizeof(chunk) && (position < length || expansion)) {
|
||||
if (expansion) {
|
||||
const char next = *expansion++;
|
||||
if (next) chunk[used++] = next;
|
||||
else expansion = nullptr;
|
||||
} else {
|
||||
const uint8_t next = packed[position++];
|
||||
if (next >= 0x80) {
|
||||
const size_t token = next - 0x80;
|
||||
if (token >= help_packing::TokenCount) return written;
|
||||
expansion = help_packing::Dictionary + help_packing::Offsets.value[token];
|
||||
} else {
|
||||
chunk[used++] = static_cast<char>(next);
|
||||
}
|
||||
}
|
||||
}
|
||||
const size_t accepted = writeHelpChunks(output, chunk, used);
|
||||
written += accepted;
|
||||
if (accepted != used) break;
|
||||
}
|
||||
return written;
|
||||
}
|
||||
|
||||
} } // namespace mesh::companion
|
||||
@@ -10405,8 +10405,10 @@ void MyMesh::handleTerminalCommand(char* command, bool reader_prompt,
|
||||
} else if (strcmp(command, "help") == 0) {
|
||||
auto& output = terminalOutput();
|
||||
#if defined(RAK_4631) && defined(COMPANION_RADIO_FULL)
|
||||
// Keep every help byte and feature gate while sharing the printing code.
|
||||
static const char help_text[] =
|
||||
// Constant evaluation retains only the exact-sized packed help image.
|
||||
// The source fragments and every feature gate remain shared below.
|
||||
static constexpr auto packed_help = []() constexpr {
|
||||
constexpr char help_text[] =
|
||||
#define MESH_COMPANION_HELP_BEGIN
|
||||
#define MESH_COMPANION_HELP_END
|
||||
#define MESH_COMPANION_HELP_LINE_BEGIN
|
||||
@@ -10416,8 +10418,14 @@ void MyMesh::handleTerminalCommand(char* command, bool reader_prompt,
|
||||
#undef MESH_COMPANION_HELP_END
|
||||
#undef MESH_COMPANION_HELP_LINE_BEGIN
|
||||
#undef MESH_COMPANION_HELP_LINE_END
|
||||
;
|
||||
mesh::companion::writeHelpChunks(output, help_text, sizeof(help_text) - 1);
|
||||
;
|
||||
static_assert(mesh::companion::validHelpText(help_text),
|
||||
"Packed help requires ASCII without embedded NUL bytes");
|
||||
return mesh::companion::packHelp<
|
||||
mesh::companion::packedHelpSize(help_text)>(help_text);
|
||||
}();
|
||||
mesh::companion::writePackedHelp(output, packed_help.bytes,
|
||||
sizeof(packed_help.bytes));
|
||||
#else
|
||||
#define MESH_COMPANION_HELP_BEGIN output.print(
|
||||
#define MESH_COMPANION_HELP_END );
|
||||
|
||||
Reference in new issue
Block a user