#pragma once
#ifndef __binbuf_h__
#define __binbuf_h__
#include <cstdint>
#include <cctype>
#include <iostream>
#include <string>
#include <array>
using std::string;
/*
* Binary data buffer. Mainly for binary network protocols, to build messages
* for sending and extract data from received messages. Allows general access
* to the whole buffer for network transmission, as well as building or reading
* from front to to back. The data itself is held in a C++ template array.
* This object does not try to remember how much (if any) of its data is
* meaningful, just how much space it contains and where the reading/writing
* process is currently.
*/
class BinaryBuffer {
public:
static const int SIZE = 1024;
BinaryBuffer(): m_rwloc(0) { m_data.fill(0); }
// Direct access to the data contents. Resets the reading or writing
// pointer because it is intended to be used to receive data to be
// extracted, or send data which has been inserted.
auto & arr() { reset_loc(); return m_data; }
unsigned char *buf() { return arr().data(); }
// Gets the full physical size. Same as SIZE.
auto bufsize() const { return m_data.size(); }
// The read/write location. When writing the buffer, this is the
// amount of meaningful data added, and is probably the size needed
// for any send operation. When extracting, this is the amount
// of data extracted. Extraction would usually continue until this
// equals the size of the message received.
unsigned int loc() const { return m_rwloc; }
void reset_loc() { m_rwloc = 0; }
// Skip a portion of the data. May be used on reading.
void skip(int n) { m_rwloc += n; }
// Add a single byte.
void put(unsigned char byte) { m_data[m_rwloc++] = byte; }
// Add arbitrary bytes. Adds all of the argument array, or
// a portion of an argument size is specified.
void put(const unsigned char *start, int amt) {
while(amt-- > 0) { put(*start++); }
}
template <unsigned int ADDSIZE>
void put(const std::array<unsigned char, ADDSIZE> &data,
unsigned int amt = ADDSIZE) {
put(data.data(),amt);
}
// Get the next byte.
unsigned char get() { return m_data[m_rwloc++]; }
// Peek the next byte without updating the location pointer.
unsigned char peek() const { return m_data[m_rwloc]; }
// Extract arbitrary bytes of a
void get(unsigned char *start, int amt) {
while(amt-- > 0) { *start++ = get(); }
}
template <unsigned int GETSIZE>
void get(std::array<unsigned char, GETSIZE> &data,
unsigned int amt = GETSIZE) {
get(data.data(),amt);
}
// Put a numeric value in big-endian order (largest-order byte first)
// T should be a numeric type. Unless you're being carful about the
// size of x, you might want to use one of the specific put8, put16
// or put32.
template<typename T>
void put_be(T x)
{
// How to get the largest byte in the smallest place.
unsigned char shift = 8*((sizeof x)-1);
// Place each byte in descending
for(int i = sizeof x; i--; shift -= 8) {
put(static_cast<unsigned char>((x>>shift)&0x0ff));
}
}
void put8(uint8_t s) { put(s); }
void put16(uint16_t s) { put_be(s); }
void put32(uint32_t i) { put_be(i); }
// Get a numeric value in big-endian order (largest-order byte first)
// T should be a numeric type. Since there's no parameter for the
// the system to figure out the type parameter T, you will need to
// provide it with <> on the call. So it might just be easier to
// use one of the specific get8, get16 or get32.
template<typename T>
T get_be()
{
// Place each byte in descending
T ret = 0;
for(int i = sizeof(T); i--; ) {
ret = (ret << 8) | (get() & 0x0ff);
}
return ret;
}
uint8_t get8() { return get(); }
uint16_t get16() { return get_be<uint16_t>(); }
uint32_t get32() { return get_be<uint32_t>(); }
// Insert a string using a length byte followed by it's characters.
// For DNS requests, the limit is 63 characters, for the basic format
// 255 character limit. This method performs no check on all that,
// so don't abuse it.
void puts(std::string s);
// Get a string by reading a length byte then that many characters.
std::string gets();
// Get or put a host name. Put produces no compression, but get
// recognizes it.
void puthost(string host);
string gethost();
// Dump the contents up to the location, or the indicated size.
void dump(int limit = -1, std::ostream &strm = std::cout) const;
private:
// Helper for gethost() that appends one segment and returns the
// following location.
unsigned int segment(unsigned int start, string &accum);
// Detect the first byte of a hostname back reference.
bool is_jump(unsigned char ch) {
return (ch & 0xc0) == 0xc0;
}
// The data is here.
std::array<unsigned char, SIZE> m_data;
// This is the current location for the next insert or extract.
unsigned int m_rwloc;
};
#endif