49constexpr size_t HEADER_LEN = 10;
51void put_u32(
unsigned char *p, uint32_t v)
53 p[0] = (v >> 24) & 0xff;
54 p[1] = (v >> 16) & 0xff;
55 p[2] = (v >> 8) & 0xff;
59uint32_t get_u32(
const unsigned char *p)
61 return (uint32_t(p[0]) << 24) | (uint32_t(p[1]) << 16)
62 | (uint32_t(p[2]) << 8) | uint32_t(p[3]);
67bool read_exact(
int fd,
void *buf,
size_t n,
bool &eof_at_start)
71 unsigned char *p =
static_cast<unsigned char *
>(buf);
73 ssize_t r = ::read(fd, p + got, n - got);
75 if (got == 0) { eof_at_start =
true;
return false; }
76 throw std::runtime_error(
"KCMCP: truncated frame (peer closed mid-message)");
79 if (errno == EINTR)
continue;
80 throw std::runtime_error(std::string(
"KCMCP: read failed: ") + strerror(errno));
82 got +=
static_cast<size_t>(r);
87void write_all(
int fd,
const void *buf,
size_t n)
90 const unsigned char *p =
static_cast<const unsigned char *
>(buf);
92 ssize_t w = ::write(fd, p + sent, n - sent);
94 if (errno == EINTR)
continue;
95 throw std::runtime_error(std::string(
"KCMCP: write failed: ") + strerror(errno));
97 sent +=
static_cast<size_t>(w);
107 bool compressed =
false;
109 unsigned char hdr[HEADER_LEN];
111 if (!read_exact(
fd_, hdr, HEADER_LEN, eof_at_start)) {
112 if (eof_at_start && first)
114 throw std::runtime_error(
"KCMCP: truncated frame header");
116 Type type =
static_cast<Type>(hdr[0]);
117 uint8_t flags = hdr[1];
118 uint32_t request_id = get_u32(hdr + 2);
119 uint32_t payload_len = get_u32(hdr + 6);
123 "KCMCP: frame payload " + std::to_string(payload_len)
124 +
" exceeds max_payload " + std::to_string(
recv_max_));
139 throw std::runtime_error(
"KCMCP: interleaved MORE frames");
142 if (payload_len > 0) {
143 size_t base = out.
payload.size();
144 out.
payload.resize(base + payload_len);
146 if (!read_exact(
fd_, &out.
payload[base], payload_len, eof2))
147 throw std::runtime_error(
"KCMCP: truncated frame payload");
154 "KCMCP: COMPRESSED payloads are not supported by this server",
164 size_t n = payload.size() - off;
165 if (n > chunk) n = chunk;
166 bool more = (off + n) < payload.size();
167 unsigned char hdr[HEADER_LEN];
168 hdr[0] =
static_cast<uint8_t
>(type);
170 put_u32(hdr + 2, request_id);
171 put_u32(hdr + 6,
static_cast<uint32_t
>(n));
172 write_all(
fd_, hdr, HEADER_LEN);
174 write_all(
fd_, payload.data() + off, n);
176 }
while (off < payload.size());
181 if (payload.size() < 6)
183 const unsigned char *p =
reinterpret_cast<const unsigned char *
>(payload.data());
188 uint16_t options_len = (uint16_t(p[4]) << 8) | uint16_t(p[5]);
189 if (6u + options_len > payload.size())
191 out.
options = payload.substr(6, options_len);
192 out.
problem = payload.substr(6 + options_len);
197 const std::string &result)
200 out.push_back(
static_cast<char>(fmt));
202 uint16_t meta_len =
static_cast<uint16_t
>(meta_json.size());
203 out.push_back(
static_cast<char>((meta_len >> 8) & 0xff));
204 out.push_back(
static_cast<char>(meta_len & 0xff));
213 uint16_t c =
static_cast<uint16_t
>(code);
214 out.push_back(
static_cast<char>((c >> 8) & 0xff));
215 out.push_back(
static_cast<char>(c & 0xff));
bool recv(Message &out)
Read one logical message (concatenating MORE frames).
void send(Type type, uint32_t request_id, const std::string &payload)
Send a message, splitting payload across MORE-flagged frames no larger than the peer's limit.
Wire codec for KCMCP, the Knowledge Compiler / Model Counter Protocol (see doc/source/dev/kc-server-p...
std::string build_result(OutputFormat fmt, const std::string &meta_json, const std::string &result)
Build a RESULT payload (result_format byte + meta JSON + result bytes).
@ FLAG_MORE
payload continues in the next frame
@ FLAG_COMPRESSED
payload is zstd-compressed (unused here)
InputFormat
Input-format registry (REQUEST byte 1).
Type
Frame type (header byte 0).
const char * input_format_name(InputFormat fmt)
const char * output_format_name(OutputFormat fmt)
Operation
Operation registry (REQUEST byte 0 / HELLO operations names).
@ COMPRESSION_UNSUPPORTED
COMPRESSED frame flag set, but unsupported.
const char * operation_name(Operation op)
OutputFormat
Output-format registry (REQUEST byte 2 / RESULT byte 0; one shared space).
std::string build_error(ErrorCode code, const std::string &message)
Build an ERROR payload (u16 code + UTF-8 message).
bool parse_request(const std::string &payload, Request &out)
Decode a REQUEST payload; returns false if structurally malformed.
A fully reassembled inbound message (MORE frames concatenated).
Thrown by Connection on a protocol violation that warrants an ERROR frame (e.g.
std::string problem
the formula bytes
std::string options
UTF-8 JSON (may be empty == {}).
OutputFormat output_format