#include "XenoWire.h" #include #include namespace xeno::wire { void encode(QByteArray& out, Header header, const QByteArray& payload) { // payloadLen is authoritative from the payload, never trusted from the // caller — a mismatch here would desynchronize the framing. header.payloadLen = static_cast(payload.size()); out.reserve(out.size() + kHeaderSize + payload.size()); std::array headerBytes{}; qToLittleEndian(header.streamId, headerBytes.data() + 0); qToLittleEndian(header.seq, headerBytes.data() + 4); headerBytes[12] = static_cast(header.tag); qToLittleEndian(header.payloadLen, headerBytes.data() + 13); out.append(headerBytes.data(), kHeaderSize); out.append(payload); } bool decode(QByteArray& buf, QVector& frames) { // Validate the whole buffer before touching it: on a violation the caller // must be able to inspect the offending bytes. int offset = 0; while (buf.size() - offset >= kHeaderSize) { const auto* headerBytes = buf.constData() + offset; const auto payloadLen = qFromLittleEndian(headerBytes + 13); if (payloadLen > kMaxPayload) { return false; } const int frameSize = kHeaderSize + static_cast(payloadLen); if (buf.size() - offset < frameSize) { break; // Incomplete trailing frame; leave it for the next read. } Frame frame; frame.header.streamId = qFromLittleEndian(headerBytes + 0); frame.header.seq = qFromLittleEndian(headerBytes + 4); frame.header.tag = static_cast(headerBytes[12]); frame.header.payloadLen = payloadLen; frame.payload = buf.mid(offset + kHeaderSize, static_cast(payloadLen)); frames.append(frame); offset += frameSize; } if (offset > 0) { buf.remove(0, offset); } return true; } QByteArray bytesPayload(quint64 sentUs, const QByteArray& data) { QByteArray out; out.reserve(8 + data.size()); std::array stamp{}; qToLittleEndian(sentUs, stamp.data()); out.append(stamp.data(), 8); out.append(data); return out; } bool splitBytesPayload(const QByteArray& payload, quint64* sentUs, QByteArray* data) { if (payload.size() < 8) { return false; } *sentUs = qFromLittleEndian(payload.constData()); *data = payload.mid(8); return true; } QByteArray resizePayload(int cols, int rows) { QByteArray out; out.reserve(8); std::array dims{}; qToLittleEndian(static_cast(cols), dims.data() + 0); qToLittleEndian(static_cast(rows), dims.data() + 4); out.append(dims.data(), 8); return out; } bool splitResizePayload(const QByteArray& payload, int* cols, int* rows) { if (payload.size() < 8) { return false; } *cols = static_cast(qFromLittleEndian(payload.constData() + 0)); *rows = static_cast(qFromLittleEndian(payload.constData() + 4)); return true; } QByteArray exitPayload(int code) { QByteArray out; out.reserve(4); std::array codeBytes{}; qToLittleEndian(static_cast(code), codeBytes.data()); out.append(codeBytes.data(), 4); return out; } bool splitExitPayload(const QByteArray& payload, int* code) { if (payload.size() < 4) { return false; } // i32 on the wire by design: cast through qint32 so negative exit codes // round trip exactly. *code = static_cast(qFromLittleEndian(payload.constData())); return true; } } // namespace xeno::wire