Skip to content

Instantly share code, notes, and snippets.

@reiver-dev
Created September 7, 2014 18:49
Show Gist options
  • Save reiver-dev/45057793a99d1506131e to your computer and use it in GitHub Desktop.
Save reiver-dev/45057793a99d1506131e to your computer and use it in GitHub Desktop.
#ifndef DECODER_HPP_
#define DECODER_HPP_
#include "streambuf.hpp"
#include "client_messages.hpp"
class Decoder {
public:
Decoder(size_t capacity)
{
m_data.reset(new uint8_t[capacity]);
m_buf.reset_data(m_data.get(), capacity);
m_payload = nullptr;
}
bool advance(size_t len)
{
if (m_state.is_completed())
return true;
m_buf.in_commit(len);
while (m_state.step(this)) {
if (m_state.is_completed()) {
return true;
}
}
return false;
}
void reset()
{
m_payload = nullptr;
m_state.next(&Decoder::initial_state);
m_buf.reset();
}
const ClientMessages::Header& get_header() const
{
return m_header;
}
const uint8_t* get_payload() const
{
return m_payload;
}
void get_buffer(uint8_t **data, size_t *len)
{
*data = m_buf.in_begin();
*len = m_buf.in_len();
}
private:
class State {
public:
using MemPtr = bool (Decoder::*)();
State() : m_state(&Decoder::initial_state)
{
//
}
bool step(Decoder *decoder)
{
return (decoder->*m_state)();
}
bool is_completed()
{
return !m_state;
}
void complete()
{
m_state = nullptr;
}
void next(MemPtr ptr)
{
m_state = ptr;
}
private:
MemPtr m_state;
};
bool header_ready()
{
if (m_buf.out_len() >= m_header.len)
return false;
m_payload = m_buf.out_begin();
m_state.complete();
return true;
}
bool signature_ready()
{
if (m_buf.out_len() < sizeof(ClientMessages::Header))
return false;
ClientMessages::Header header, *hdata;
hdata = reinterpret_cast<ClientMessages::Header *>(m_buf.out_begin());
header.sig = hdata->sig;
header.len = ntohl(hdata->len);
header.type = ntohl(hdata->type);
if (header.len <= m_buf.in_len()) {
m_header = header;
m_buf.out_commit(sizeof(m_header));
m_state.next(&Decoder::header_ready);
} else {
reset();
}
return true;
}
bool initial_state()
{
m_payload = nullptr;
if (m_buf.out_len() < 2)
return false;
if (!memcmp(m_buf.out_begin(), &ClientMessages::SIGNATURE, sizeof(ClientMessages::SIGNATURE))) {
m_state.next(&Decoder::signature_ready);
} else {
reset();
}
return true;
}
std::unique_ptr<uint8_t> m_data;
State m_state;
StreamBuf m_buf;
ClientMessages::Header m_header;
uint8_t *m_payload;
};
#endif /* DECODER_HPP_ */
#ifndef STREAMBUF_HPP
#define STREAMBUF_HPP
#include <cstring>
using std::size_t;
class StreamBuf
{
public:
typedef uint8_t val_t;
StreamBuf() = default;
StreamBuf(void *data, size_t len)
: m_begin(static_cast<uint8_t *>(data)),
m_end(m_begin + len),
m_in(m_begin),
m_out(m_begin)
{
//
}
StreamBuf(const StreamBuf&) = delete;
StreamBuf& operator=(const StreamBuf&) = delete;
StreamBuf(StreamBuf&&) = default;
StreamBuf& operator=(StreamBuf&&) = default;
// read operations
const val_t* out_begin() const {
return m_out;
}
val_t* out_begin() {
return m_out;
}
const val_t* out_end() const {
return m_in;
}
size_t out_len() const {
return m_in - m_out;
}
void out_commit(size_t bytes) {
m_out += bytes;
}
// write operations
val_t* in_begin() {
return m_in;
}
val_t* in_end() {
return m_end;
}
size_t in_len() const {
return m_end - m_in;
}
void in_commit(size_t bytes) {
m_in += bytes;
}
// state operations
void reset() {
m_out = m_in = m_begin;
}
void reset_data(val_t *data, size_t len) {
m_begin = static_cast<uint8_t *>(data);
m_end = m_begin + len;
reset();
}
void rearrange() {
size_t rd = out_len();
if (rd > 0) {
memmove(m_begin, m_out, rd);
}
m_out = m_begin;
m_in = m_begin + rd;
}
private:
val_t* m_begin;
val_t* m_end;
val_t* m_in;
val_t* m_out;
};
#endif // STREAMBUF_HPP
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment