Files
MeshCore/src/helpers/StorageLayout.h
T

127 lines
3.9 KiB
C++

#pragma once
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
namespace mesh {
class MainBoard;
namespace cli {
enum class StorageLayoutGetMatch : uint8_t {
NotMatched = 0,
Valid,
InvalidArguments,
};
// `config` is the portion following "get ". Keep recognition independent of
// any role's CLI wrapper so CommonCLI and Companion command 0x42 expose the
// same diagnostic and the same malformed-command response.
inline StorageLayoutGetMatch classifyStorageLayoutGet(const char* config) {
static const char key[] = "storage.layout";
const size_t key_len = sizeof(key) - 1;
if (config == nullptr || strncmp(config, key, key_len) != 0 ||
(config[key_len] != 0 && config[key_len] != ' ' &&
config[key_len] != '\t')) {
return StorageLayoutGetMatch::NotMatched;
}
return config[key_len] == 0 ? StorageLayoutGetMatch::Valid
: StorageLayoutGetMatch::InvalidArguments;
}
inline void formatStorageBytes(char* out, size_t out_len, uint64_t bytes) {
if (out == nullptr || out_len == 0) return;
static const char* const units[] = {"B", "KiB", "MiB", "GiB", "TiB"};
uint64_t unit_size = 1;
uint8_t unit = 0;
while (unit < 4 && bytes >= unit_size * 1024ULL) {
unit_size *= 1024ULL;
unit++;
}
if (unit == 0) {
snprintf(out, out_len, "%lu B", (unsigned long)bytes);
return;
}
uint64_t whole = bytes / unit_size;
uint32_t tenths =
(uint32_t)(((bytes % unit_size) * 10ULL + unit_size / 2ULL) /
unit_size);
if (tenths == 10) {
whole++;
tenths = 0;
}
snprintf(out, out_len, "%lu.%lu %s", (unsigned long)whole,
(unsigned long)tenths, units[unit]);
}
// The hardware-facing formatter walks ESP-IDF's live partition iterator. This
// small writer keeps its bounded text policy host-testable without mocking IDF.
class Esp32StorageLayoutWriter {
public:
Esp32StorageLayoutWriter(char* out, size_t out_len, uint32_t flash_bytes)
: _out(out), _out_len(out_len), _has_partition(false),
_truncated(false) {
if (_out == nullptr || _out_len == 0) return;
snprintf(_out, _out_len, "> int:esp32=%luK ext:none; ",
(unsigned long)(flash_bytes / 1024UL));
}
bool append(const char* label, uint32_t address, uint32_t size,
bool running) {
if (_out == nullptr || _out_len == 0 || _truncated) return false;
char entry[48];
snprintf(entry, sizeof(entry), "%s%s%s@0x%lX+%luK",
_has_partition ? "," : "", label != nullptr ? label : "",
running ? "*" : "", (unsigned long)address,
(unsigned long)(size / 1024UL));
const size_t used = strlen(_out);
const size_t entry_len = strlen(entry);
// Preserve one spare byte beyond the terminator, matching the original
// CommonCLI response policy and leaving room for an ellipsis when possible.
if (used + entry_len + 1 >= _out_len) {
_truncated = true;
return false;
}
memcpy(_out + used, entry, entry_len + 1);
_has_partition = true;
return true;
}
void finish() {
if (_out == nullptr || _out_len == 0) return;
const size_t used = strlen(_out);
if (!_has_partition) {
if (used < _out_len) {
snprintf(_out + used, _out_len - used, "partitions=none");
}
} else if (_truncated && used + 4 < _out_len) {
memcpy(_out + used, ",...", 5);
}
}
bool truncated() const { return _truncated; }
private:
char* _out;
size_t _out_len;
bool _has_partition;
bool _truncated;
};
void formatStorageLayout(MainBoard& board, char* reply, size_t reply_size);
// Returns false only when `config` is not this command. Recognized malformed
// requests are consumed and receive the canonical usage response.
bool handleStorageLayoutGet(const char* config, MainBoard& board, char* reply,
size_t reply_size);
} // namespace cli
} // namespace mesh