Program Listing for File parse.hpp
↰ Return to documentation for file (eshet/parse.hpp)
#pragma once
#include "data.hpp"
namespace eshet {
namespace detail {
class Parser {
public:
Parser(const uint8_t *data, size_t size) : data(data), size(size) {}
uint8_t read8() {
if (size - pos < 1)
throw ProtocolError();
return data[pos++];
}
uint16_t read16() {
if (size - pos < 2)
throw ProtocolError();
uint16_t value = ((uint16_t)data[pos] << 8) + data[pos + 1];
pos += 2;
return value;
}
uint32_t read32() {
if (size - pos < 4)
throw ProtocolError();
uint32_t value = ((uint32_t)data[pos] << 24) +
((uint32_t)data[pos + 1] << 16) +
((uint32_t)data[pos + 2] << 8) + data[pos + 3];
pos += 4;
return value;
}
std::string read_string() {
size_t end;
for (end = pos; end < size; end++)
if (data[end] == 0)
break;
if (end == size)
throw ProtocolError();
std::string value{(const char *)data + pos, (const char *)data + end};
pos = end + 1;
return value;
}
msgpack::object_handle read_msgpack() {
if (size - pos < 1)
throw ProtocolError();
msgpack::object_handle value =
msgpack::unpack((char *)data + pos, size - pos);
pos = size;
return value;
}
void check_empty() {
if (pos != size)
throw ProtocolError();
}
private:
const uint8_t *data;
size_t size;
size_t pos = 0;
};
// hold a buffer for message construction, with operations for writing various
// types of data
struct SendBuf {
explicit SendBuf(size_t size) : sbuf(size) {}
void start_msg(uint8_t type) {
sbuf.clear();
uint8_t header[] = {0x47, 0, 0, type};
sbuf.write((char *)header, sizeof(header));
}
void write8(uint8_t value) { sbuf.write((char *)&value, sizeof(value)); }
void write16(uint16_t value) {
uint8_t data[] = {(uint8_t)(value >> 8), (uint8_t)(value & 0xff)};
sbuf.write((char *)data, sizeof(data));
}
void write_string(const std::string &s) {
sbuf.write(s.c_str(), s.size() + 1);
}
template <typename T> void write_msgpack(const T &value) {
msgpack::pack(sbuf, value);
}
void write_size() {
size_t size = sbuf.size() - 3;
uint8_t size_fmt[] = {(uint8_t)(size >> 8), (uint8_t)(size & 0xff)};
sbuf.data()[1] = size_fmt[0];
sbuf.data()[2] = size_fmt[1];
}
// methods for writing common eshet command formats
void write_path(uint8_t message, uint16_t id, const std::string &path) {
start_msg(message);
write16(id);
write_string(path);
write_size();
}
void write_pack(uint8_t message, uint16_t id, const msgpack::object &value) {
start_msg(message);
write16(id);
write_msgpack(value);
write_size();
}
void write_path_pack(uint8_t message, uint16_t id, const std::string &path,
const msgpack::object &value) {
start_msg(message);
write16(id);
write_string(path);
write_msgpack(value);
write_size();
}
// methods for writing whole eshet commands
void write_hello(const std::optional<msgpack::object_handle> &id,
uint16_t server_timeout) {
start_msg(id ? 0x02 : 0x01);
write8(1);
write16(server_timeout);
if (id)
write_msgpack(**id);
write_size();
}
void write_reply(uint16_t id, const Success &success) {
write_pack(0x05, id, *success.value);
}
void write_reply(uint16_t id, const Error &error) {
write_pack(0x06, id, *error.value);
}
void write_reply(uint16_t id, const Result &result) {
std::visit([&](const auto &r) { write_reply(id, r); }, result);
}
void write_action_register(uint16_t id, const std::string &path) {
write_path(0x10, id, path);
}
void write_action_call(uint16_t id, const std::string &path,
const msgpack::object &args) {
write_path_pack(0x11, id, path, args);
}
void write_state_register(uint16_t id, const std::string &path) {
write_path(0x40, id, path);
}
void write_state_observe(uint16_t id, const std::string &path) {
write_path(0x46, id, path);
}
void write_state_changed(uint16_t id, const std::string &path,
const Known &state) {
write_path_pack(0x41, id, path, *state.value);
}
void write_state_changed(uint16_t id, const std::string &path,
const Unknown &state) {
write_path(0x42, id, path);
}
void write_state_changed(uint16_t id, const std::string &path,
const StateUpdate &state) {
std::visit([&](const auto &s) { write_state_changed(id, path, s); }, state);
}
void write_event_register(uint16_t id, const std::string &path) {
write_path(0x30, id, path);
}
void write_event_emit(uint16_t id, const std::string &path,
const msgpack::object &value) {
write_path_pack(0x31, id, path, value);
}
void write_event_listen(uint16_t id, const std::string &path) {
write_path(0x32, id, path);
}
void write_ping(uint16_t id) {
start_msg(0x09);
write16(id);
write_size();
}
void write_get(uint16_t id, const std::string &path) {
write_path(0x23, id, path);
}
void write_set(uint16_t id, const std::string &path,
const msgpack::object &value) {
write_path_pack(0x24, id, path, value);
}
msgpack::sbuffer sbuf;
};
} // namespace detail
} // namespace eshet