Files
Dario Lipicar 29afbac532 Extract the Logos protocol layer from logos-cpp-sdk (lp_* C ABI + protocol semver) (#2)
* Extract the Logos protocol layer from logos-cpp-sdk

Transports (plain TCP/TLS, qt_local, qt_remote/QRO, mock), token manager,
consumer core (LogosAPIClient/LogosAPIConsumer incl. the capability
auto-requestModule flow), ModuleProxy, the abstract LogosProviderObject
interface, and the canonical QVariant<->JSON conversion — now behind the
language-neutral lp_* C ABI (logos_protocol.h) carrying the protocol
semver (LOGOS_PROTOCOL_VERSION_*, lp_protocol_version()).

Bytes crossing the ABI use the lossless {"_bytes": base64url} tagging
(NUL-safe), matching the plain wire encoding.

Provider lp_* surface is compiled groundwork; serving lands with module
authoring.

* consumer: typed requestModule for the capability flow

Port of logos-cpp-sdk master f5a127dd ('use updated capability module',
cpp-sdk#85, Iuri Matias) — the touched files (logos_api_client.cpp,
logos_api_consumer.{h,cpp}) moved into this repo in the P1 extraction.
The capability auto-requestModule path now calls a typed std::string
helper on the consumer (which acquires the capability object directly)
instead of a stringly invokeRemoteMethod round-trip. 111/111 tests.
2026-06-12 18:59:01 -03:00

77 lines
2.3 KiB
C++

#include "rpc_framing.h"
#include <cstring>
namespace logos::plain {
namespace {
void writeBe32(std::vector<uint8_t>& out, uint32_t n)
{
out.push_back(static_cast<uint8_t>((n >> 24) & 0xff));
out.push_back(static_cast<uint8_t>((n >> 16) & 0xff));
out.push_back(static_cast<uint8_t>((n >> 8) & 0xff));
out.push_back(static_cast<uint8_t>( n & 0xff));
}
uint32_t readBe32(const uint8_t* p)
{
return (uint32_t(p[0]) << 24) | (uint32_t(p[1]) << 16)
| (uint32_t(p[2]) << 8) | uint32_t(p[3]);
}
} // anonymous namespace
std::vector<uint8_t>
encodeFrame(MessageType tag, const std::vector<uint8_t>& payload)
{
// Total frame body length = 1 byte (tag) + payload bytes. The 4-byte
// length prefix is not included in the length value it describes.
const uint64_t bodyLen = 1u + payload.size();
if (bodyLen > kMaxFrameLength)
throw FramingError("frame too large");
std::vector<uint8_t> out;
out.reserve(4 + bodyLen);
writeBe32(out, static_cast<uint32_t>(bodyLen));
out.push_back(static_cast<uint8_t>(tag));
out.insert(out.end(), payload.begin(), payload.end());
return out;
}
std::vector<uint8_t>
encodeFrame(IWireCodec& codec, const AnyMessage& msg)
{
return encodeFrame(messageTypeOf(msg), codec.encode(msg));
}
void FrameReader::append(const uint8_t* data, std::size_t len)
{
m_buf.insert(m_buf.end(), data, data + len);
}
bool FrameReader::next(MessageType& tag, std::vector<uint8_t>& payload)
{
// Need at least 4 bytes for the length prefix.
if (m_buf.size() < 4) return false;
const uint32_t bodyLen = readBe32(m_buf.data());
if (bodyLen == 0) throw FramingError("zero-length frame");
if (bodyLen > kMaxFrameLength) throw FramingError("frame length exceeds cap");
const std::size_t total = 4u + bodyLen;
if (m_buf.size() < total) return false;
// Tag + payload.
tag = static_cast<MessageType>(m_buf[4]);
payload.assign(m_buf.begin() + 5, m_buf.begin() + total);
// Drop the consumed bytes. erase from front is O(n) in buffer size;
// acceptable while frames are small and rare. Can switch to a ring
// buffer if profiling ever flags this.
m_buf.erase(m_buf.begin(), m_buf.begin() + total);
return true;
}
} // namespace logos::plain