mirror of
https://github.com/torlando-tech/pyxis.git
synced 2026-08-24 19:50:09 +00:00
130 lines
4.8 KiB
C++
130 lines
4.8 KiB
C++
#pragma once
|
|
|
|
#include <algorithm>
|
|
#include <cstddef>
|
|
#include <cstdint>
|
|
#include <string>
|
|
#include <utility>
|
|
#include <vector>
|
|
#include "NomadNetMemory.h"
|
|
|
|
namespace UI::LXMF::NomadNet {
|
|
|
|
inline std::vector<uint8_t> no_form_request_data() {
|
|
// Link::request splices already encoded msgpack data into its envelope.
|
|
// NomadNet's no-form value is protocol-level nil, exactly one byte 0xc0.
|
|
return {0xc0};
|
|
}
|
|
|
|
inline void append_msgpack_string(std::vector<uint8_t>& output, const std::string& value) {
|
|
const std::size_t size = value.size();
|
|
if (size <= 31) {
|
|
output.push_back(static_cast<uint8_t>(0xa0 | size));
|
|
} else if (size <= 0xff) {
|
|
output.push_back(0xd9);
|
|
output.push_back(static_cast<uint8_t>(size));
|
|
} else {
|
|
output.push_back(0xda);
|
|
output.push_back(static_cast<uint8_t>(size >> 8));
|
|
output.push_back(static_cast<uint8_t>(size));
|
|
}
|
|
output.insert(output.end(), value.begin(), value.end());
|
|
}
|
|
|
|
inline std::vector<uint8_t> request_data(const std::string& fields) {
|
|
if (fields.empty()) return no_form_request_data();
|
|
|
|
std::vector<std::pair<std::string, std::string>> variables;
|
|
for (std::size_t start = 0; start <= fields.size();) {
|
|
const std::size_t end = fields.find('|', start);
|
|
const std::size_t length = (end == std::string::npos ? fields.size() : end) - start;
|
|
const std::string field = fields.substr(start, length);
|
|
const std::size_t equals = field.find('=');
|
|
if (equals != std::string::npos && field.find('=', equals + 1) == std::string::npos) {
|
|
const std::string name = field.substr(0, equals);
|
|
const std::string value = field.substr(equals + 1);
|
|
auto existing = std::find_if(variables.begin(), variables.end(),
|
|
[&](const std::pair<std::string, std::string>& variable) {
|
|
return variable.first == name;
|
|
});
|
|
if (existing == variables.end()) variables.emplace_back(name, value);
|
|
else existing->second = value;
|
|
}
|
|
if (end == std::string::npos) break;
|
|
start = end + 1;
|
|
}
|
|
|
|
std::vector<uint8_t> output;
|
|
output.reserve(fields.size() + variables.size() * 5 + 3);
|
|
if (variables.size() <= 15) {
|
|
output.push_back(static_cast<uint8_t>(0x80 | variables.size()));
|
|
} else {
|
|
output.push_back(0xde);
|
|
output.push_back(static_cast<uint8_t>(variables.size() >> 8));
|
|
output.push_back(static_cast<uint8_t>(variables.size()));
|
|
}
|
|
|
|
for (const auto& variable : variables) {
|
|
append_msgpack_string(output, "var_" + variable.first);
|
|
append_msgpack_string(output, variable.second);
|
|
}
|
|
return output;
|
|
}
|
|
|
|
class ResponseBuffer {
|
|
public:
|
|
static constexpr std::size_t MAX_BYTES = 64 * 1024;
|
|
|
|
bool assign(const uint8_t* data, std::size_t size) {
|
|
clear();
|
|
if ((!data && size != 0) || size > MAX_BYTES) return false;
|
|
if (size != 0) _bytes.assign(data, data + size);
|
|
return true;
|
|
}
|
|
const ExternalVector<uint8_t>& bytes() const { return _bytes; }
|
|
std::size_t size() const { return _bytes.size(); }
|
|
std::size_t capacity() const { return _bytes.capacity(); }
|
|
bool truncated() const { return false; }
|
|
void clear() { _bytes.clear(); }
|
|
void release() { ExternalVector<uint8_t>().swap(_bytes); }
|
|
ExternalVector<uint8_t> take() { ExternalVector<uint8_t> result; result.swap(_bytes); return result; }
|
|
|
|
private:
|
|
ExternalVector<uint8_t> _bytes;
|
|
};
|
|
|
|
inline bool normalize_response(const uint8_t* data, std::size_t size, ResponseBuffer& output) {
|
|
output.clear();
|
|
if (!data || size == 0) return false;
|
|
|
|
std::size_t offset = 0;
|
|
uint64_t payload = 0;
|
|
if (data[0] == 0xc4) {
|
|
if (size < 2) return false;
|
|
offset = 2;
|
|
payload = data[1];
|
|
} else if (data[0] == 0xc5) {
|
|
if (size < 3) return false;
|
|
offset = 3;
|
|
payload = (static_cast<uint64_t>(data[1]) << 8) | data[2];
|
|
} else if (data[0] == 0xc6) {
|
|
if (size < 5) return false;
|
|
offset = 5;
|
|
payload = (static_cast<uint64_t>(data[1]) << 24) |
|
|
(static_cast<uint64_t>(data[2]) << 16) |
|
|
(static_cast<uint64_t>(data[3]) << 8) | data[4];
|
|
} else {
|
|
// Pinned microReticulum 6ac0d32 exposes packet responses as decoded
|
|
// raw bytes, while Resource responses retain the encoded MessagePack
|
|
// value. Valid UTF-8 Micron cannot begin with the bin markers above,
|
|
// so the representations are unambiguous here.
|
|
return output.assign(data, size);
|
|
}
|
|
|
|
if (payload > ResponseBuffer::MAX_BYTES) return false;
|
|
if (payload != static_cast<uint64_t>(size - offset)) return false;
|
|
return output.assign(data + offset, static_cast<std::size_t>(payload));
|
|
}
|
|
|
|
} // namespace UI::LXMF::NomadNet
|