My solution is as follows:
//Copyright © 2021 Charles Kerr. All rights reserved.
#ifndef Buffer_hpp
#define Buffer_hpp
#include <string>
#include <vector>
#include <utility>
#include <cstdint>
#include <limits>
#include <sstream>
#include <stdexcept>
using namespace std::string_literals ;
/******************************************************************************
Buffer
A buffer for binary data. Allows access (read/write) by varying data
types.
******************************************************************************/
//===============================================================
class Buffer {
public:
/*************************************************************************
Constants used by the class
*************************************************************************/
static constexpr std::size_t infinity = std::numeric_limits<std::size_t>::max();
private:
/*************************************************************************
Data used by the class
*************************************************************************/
std::vector<std::uint8_t> _data ;
std::size_t _index ;
public:
/************************************************************************
Position/Relative classs to allow indexing via streams
***********************************************************************/
struct Position {
std::size_t position ;
Position(std::size_t position) {
this->position = position;
}
};
struct Relative {
std::int64_t relative ;
Relative(std::int16_t relative) {
this->relative = relative ;
}
};
private:
/************************************************************************
protected Methods
***********************************************************************/
bool sizeCheck(std::size_t size,bool expand=true);
std::int32_t displaySize(std::int32_t radix) const;
public:
/************************************************************************
public Methods
***********************************************************************/
Buffer(std::size_t size = 0 );
Buffer(const std::vector<std::uint8_t> &data);
Buffer(const std::uint8_t *ptr,std::size_t size );
std::size_t size() const ;
void size(std::size_t size) ;
std::size_t position() const ;
Buffer& position(std::size_t position) ;
Buffer& relative(std::int64_t relative) ;
std::stringstream description(std::size_t columns=8, std::int32_t radix=16) const ;
std::size_t write(const std::string &input, std::size_t index=infinity,std::size_t numchar = infinity,bool expand = true);
std::size_t write(const bool input,std::size_t index = infinity,bool expand = true);
std::size_t write( const std::vector<std::uint8_t> &source,std::size_t index = infinity, std::size_t numchar = infinity,bool expand = true);
std::size_t write( const std::uint8_t *ptrsource,std::size_t numchar, std::size_t index = infinity,bool expand = true);
std::size_t write(const char* strvalue,std::size_t index = infinity, std::size_t numchar = infinity,bool expand = true);
std::size_t read(std::vector<std::uint8_t> &dest,std::size_t index = infinity, std::size_t numchar = infinity);
void copy(std::vector<std::uint8_t> &dest, std::size_t index, std::size_t numchar = infinity) const;
std::size_t read(std::uint8_t *ptr, std::size_t numchar, std::size_t index = infinity);
void copy(std::uint8_t *ptr, std::size_t numchar, std::size_t index) const ;
const std::uint8_t & operator[](std::size_t offset) const ;
std::uint8_t & operator[](std::size_t offset) ;
std::uint8_t * mutableBytes() ;
const std::uint8_t * bytes() const ;
/************************************************************************
Template methods
************************************************************************/
/************************************************************************
Write methods
************************************************************************/
//=====================================================================
// Writing an integer type
template <typename T>
typename std::enable_if< std::is_integral_v<T>,std::size_t>::type
write( T input,std::size_t index = infinity,bool expand = true){
if (index == infinity){
index = _index ;
}
if (_data.size() < index+sizeof(T)){
// We need to increase the size of the buffer
if (expand) {
size(index+sizeof(T));
}
else {
throw std::out_of_range("Write exceeds buffer size. Index is: "s+std::to_string(index) + " Size of value to write: "s+std::to_string(sizeof(T)) + " Buffer size: "s + std::to_string(_data.size()));
}
}
std::copy(reinterpret_cast<unsigned char*>(&input),reinterpret_cast<unsigned char*>(&input)+sizeof(T),_data.begin()+index);
_index = index + sizeof(T) ;
return _index ;
}
/************************************************************************
Read methods
************************************************************************/
//=====================================================================
// Reading an integer type
template <typename T>
typename std::enable_if<std::is_integral_v<T> && !std::is_same_v<T, bool>,T>::type
read(std::size_t index = infinity){
T rvalue ;
if (index == infinity){
index = _index ;
}
auto amount = sizeof(T) ;
if (_data.size() < index+amount){
throw std::out_of_range("Read exceeds buffer size. Index is: "s+std::to_string(index) + ". Size of value to read: "s+std::to_string(sizeof(T)) + ". Buffer size: "s + std::to_string(_data.size())+"."s);
}
std::copy(_data.data()+index,_data.data()+index+amount,reinterpret_cast<unsigned char*>(&rvalue));
_index = index+amount ;
return rvalue ;
}
//=====================================================================
// Reading an bool type (one byte in the Buffer)
template <typename T>
typename std::enable_if<std::is_integral_v<T> && std::is_same_v<T, bool>,T>::type
read(std::size_t index = infinity){
if (index == infinity){
index = _index ;
}
auto value = static_cast<unsigned char>(0) ;
auto amount = sizeof(value) ;
if (_data.size() < index+amount){
throw std::out_of_range("Read exceeds buffer size. Index is: "s+std::to_string(index) + ". Size of value to read: "s+std::to_string(amount) + ". Buffer size: "s + std::to_string(_data.size())+"."s);
}
std::copy(_data.data()+index,_data.data()+index+amount,reinterpret_cast<unsigned char*>(&value));
_index = index+amount ;
return static_cast<T>(value) ;
}
//=====================================================================
// Reading an String type
template <typename T>
typename std::enable_if<std::is_same_v<T, std::string>,std::string>::type
read(std::size_t index=infinity,std::size_t numchar=infinity){
if (index==infinity){
index = _index ;
if (index >= _data.size()){
throw std::out_of_range("Read exceeds buffer size. Index is: "s+std::to_string(index) + ". Size of value to read: [end of buffer or \\0]."s + " Buffer size: "s + std::to_string(_data.size())+"."s);
}
}
else {
if ((_index+numchar) >= _data.size()){
throw std::out_of_range("Read exceeds buffer size. Index is: "s+std::to_string(index) + ". Size of value to read: "s + std::to_string(numchar) + ". Buffer size: "s + std::to_string(_data.size())+"."s);
}
}
std::vector<unsigned char> buffer ;
auto amount = static_cast<std::string::size_type>(numchar) ;
if (numchar==infinity){
// we need to find the first null terminator
auto iter = std::find(_data.begin()+index,_data.end(),static_cast<unsigned char>(0));
amount = std::distance(_data.begin()+index, iter);
}
if (_data.size() < index+amount){
amount = _data.size()-index ;
if (amount <= 0){
return std::string();
}
}
buffer.resize(amount,0);
std::copy(_data.begin()+index,_data.begin()+index+amount,buffer.begin());
auto nullat = std::find(buffer.begin(),buffer.end(),0);
if (nullat != buffer.end()){
auto tamount = std::distance(buffer.begin(),nullat);
buffer.resize(tamount) ;
}
_index = index+amount ;
return (std::string(reinterpret_cast<char*>(buffer.data()),buffer.size()));
}
//=====================================================================
// Copying an integer type (good on const Buffers, as they don't change the index)
template <typename T>
typename std::enable_if<std::is_integral_v<T> && !std::is_same_v<T, bool>,T>::type
copy(std::size_t index ) const{
T rvalue ;
auto amount = sizeof(T) ;
if (_data.size() < index+amount){
throw std::out_of_range("Read exceeds buffer size. Index is: "s+std::to_string(index) + ". Size of value to read: "s+std::to_string(sizeof(T)) + ". Buffer size: "s + std::to_string(_data.size())+"."s);
}
std::copy(_data.data()+index,_data.data()+index+amount,reinterpret_cast<unsigned char*>(&rvalue));
return rvalue ;
}
//=====================================================================
// Copying an bool type (one byte in the Buffer) (good on const Buffers, as they don't change the index)
template <typename T>
typename std::enable_if<std::is_integral_v<T> && std::is_same_v<T, bool>,T>::type
copy(std::size_t index ) const{
if (index == infinity){
index = _index ;
}
auto value = static_cast<unsigned char>(0) ;
auto amount = sizeof(value) ;
if (_data.size() < index+amount){
throw std::out_of_range("Read exceeds buffer size. Index is: "s+std::to_string(index) + ". Size of value to read: "s+std::to_string(amount) + ". Buffer size: "s + std::to_string(_data.size())+"."s);
}
std::copy(_data.data()+index,_data.data()+index+amount,reinterpret_cast<unsigned char*>(&value));
return static_cast<T>(value) ;
}
//=====================================================================
// Copy an String type (good on const Buffers, as they don't change the index)
template <typename T>
typename std::enable_if<std::is_same_v<T, std::string>,std::string>::type
copy(std::size_t index,std::size_t numchar=infinity) const{
if (index==infinity){
index = _index ;
if (index >= _data.size()){
throw std::out_of_range("Read exceeds buffer size. Index is: "s+std::to_string(index) + ". Size of value to read: [end of buffer or \\0]."s + " Buffer size: "s + std::to_string(_data.size())+"."s);
}
}
else {
if ((_index+numchar) >= _data.size()){
throw std::out_of_range("Read exceeds buffer size. Index is: "s+std::to_string(index) + ". Size of value to read: "s + std::to_string(numchar) + ". Buffer size: "s + std::to_string(_data.size())+"."s);
}
}
std::vector<unsigned char> buffer ;
auto amount = static_cast<std::string::size_type>(numchar) ;
if (numchar==infinity){
// we need to find the first null terminator
auto iter = std::find(_data.begin()+index,_data.end(),static_cast<unsigned char>(0));
amount = std::distance(_data.begin()+index, iter);
}
if (_data.size() < index+amount){
amount = _data.size()-index ;
if (amount <= 0){
return std::string();
}
}
buffer.resize(amount,0);
std::copy(_data.begin()+index,_data.begin()+index+amount,buffer.begin());
auto nullat = std::find(buffer.begin(),buffer.end(),0);
if (nullat != buffer.end()){
auto tamount = std::distance(buffer.begin(),nullat);
buffer.resize(tamount) ;
}
return (std::string(reinterpret_cast<char*>(buffer.data()),buffer.size()));
}
};
/******************************************************************************
Positioning for streaming
******************************************************************************/
Buffer& operator>>(Buffer &buffer, Buffer::Position &value);
Buffer& operator<<(Buffer &buffer, const Buffer::Position &value);
Buffer& operator>>(Buffer &buffer, Buffer::Relative &value);
Buffer& operator<<(Buffer &buffer, const Buffer::Relative &value);
/******************************************************************************
Reading for streaming
******************************************************************************/
Buffer& operator>>(Buffer &buffer, std::string &value);
template <typename T>
typename std::enable_if<std::is_integral_v<T> && !std::is_same_v<T, bool>,Buffer&>::type
operator>>(Buffer &buffer, T &value){
value = buffer.read<T>() ;
return buffer ;
}
/******************************************************************************
Writing for streaming
******************************************************************************/
Buffer& operator<<(Buffer &buffer, const std::string &value);
Buffer& operator<<(Buffer &buffer, const char* value);
template <typename T>
typename std::enable_if<std::is_integral_v<T> && !std::is_same_v<T, bool>,Buffer&>::type
operator<<(Buffer &buffer, const T &value){
buffer.write<T>(value) ;
return buffer ;
}
#endif /* Buffer_hpp */
#ifndef Buffer_hpp
#define Buffer_hpp
#include <string>
#include <vector>
#include <utility>
#include <cstdint>
#include <limits>
#include <sstream>
#include <stdexcept>
using namespace std::string_literals ;
/******************************************************************************
Buffer
A buffer for binary data. Allows access (read/write) by varying data
types.
******************************************************************************/
//===============================================================
class Buffer {
public:
/*************************************************************************
Constants used by the class
*************************************************************************/
static constexpr std::size_t infinity = std::numeric_limits<std::size_t>::max();
private:
/*************************************************************************
Data used by the class
*************************************************************************/
std::vector<std::uint8_t> _data ;
std::size_t _index ;
public:
/************************************************************************
Position/Relative classs to allow indexing via streams
***********************************************************************/
struct Position {
std::size_t position ;
Position(std::size_t position) {
this->position = position;
}
};
struct Relative {
std::int64_t relative ;
Relative(std::int16_t relative) {
this->relative = relative ;
}
};
private:
/************************************************************************
protected Methods
***********************************************************************/
bool sizeCheck(std::size_t size,bool expand=true);
std::int32_t displaySize(std::int32_t radix) const;
public:
/************************************************************************
public Methods
***********************************************************************/
Buffer(std::size_t size = 0 );
Buffer(const std::vector<std::uint8_t> &data);
Buffer(const std::uint8_t *ptr,std::size_t size );
std::size_t size() const ;
void size(std::size_t size) ;
std::size_t position() const ;
Buffer& position(std::size_t position) ;
Buffer& relative(std::int64_t relative) ;
std::stringstream description(std::size_t columns=8, std::int32_t radix=16) const ;
std::size_t write(const std::string &input, std::size_t index=infinity,std::size_t numchar = infinity,bool expand = true);
std::size_t write(const bool input,std::size_t index = infinity,bool expand = true);
std::size_t write( const std::vector<std::uint8_t> &source,std::size_t index = infinity, std::size_t numchar = infinity,bool expand = true);
std::size_t write( const std::uint8_t *ptrsource,std::size_t numchar, std::size_t index = infinity,bool expand = true);
std::size_t write(const char* strvalue,std::size_t index = infinity, std::size_t numchar = infinity,bool expand = true);
std::size_t read(std::vector<std::uint8_t> &dest,std::size_t index = infinity, std::size_t numchar = infinity);
void copy(std::vector<std::uint8_t> &dest, std::size_t index, std::size_t numchar = infinity) const;
std::size_t read(std::uint8_t *ptr, std::size_t numchar, std::size_t index = infinity);
void copy(std::uint8_t *ptr, std::size_t numchar, std::size_t index) const ;
const std::uint8_t & operator[](std::size_t offset) const ;
std::uint8_t & operator[](std::size_t offset) ;
std::uint8_t * mutableBytes() ;
const std::uint8_t * bytes() const ;
/************************************************************************
Template methods
************************************************************************/
/************************************************************************
Write methods
************************************************************************/
//=====================================================================
// Writing an integer type
template <typename T>
typename std::enable_if< std::is_integral_v<T>,std::size_t>::type
write( T input,std::size_t index = infinity,bool expand = true){
if (index == infinity){
index = _index ;
}
if (_data.size() < index+sizeof(T)){
// We need to increase the size of the buffer
if (expand) {
size(index+sizeof(T));
}
else {
throw std::out_of_range("Write exceeds buffer size. Index is: "s+std::to_string(index) + " Size of value to write: "s+std::to_string(sizeof(T)) + " Buffer size: "s + std::to_string(_data.size()));
}
}
std::copy(reinterpret_cast<unsigned char*>(&input),reinterpret_cast<unsigned char*>(&input)+sizeof(T),_data.begin()+index);
_index = index + sizeof(T) ;
return _index ;
}
/************************************************************************
Read methods
************************************************************************/
//=====================================================================
// Reading an integer type
template <typename T>
typename std::enable_if<std::is_integral_v<T> && !std::is_same_v<T, bool>,T>::type
read(std::size_t index = infinity){
T rvalue ;
if (index == infinity){
index = _index ;
}
auto amount = sizeof(T) ;
if (_data.size() < index+amount){
throw std::out_of_range("Read exceeds buffer size. Index is: "s+std::to_string(index) + ". Size of value to read: "s+std::to_string(sizeof(T)) + ". Buffer size: "s + std::to_string(_data.size())+"."s);
}
std::copy(_data.data()+index,_data.data()+index+amount,reinterpret_cast<unsigned char*>(&rvalue));
_index = index+amount ;
return rvalue ;
}
//=====================================================================
// Reading an bool type (one byte in the Buffer)
template <typename T>
typename std::enable_if<std::is_integral_v<T> && std::is_same_v<T, bool>,T>::type
read(std::size_t index = infinity){
if (index == infinity){
index = _index ;
}
auto value = static_cast<unsigned char>(0) ;
auto amount = sizeof(value) ;
if (_data.size() < index+amount){
throw std::out_of_range("Read exceeds buffer size. Index is: "s+std::to_string(index) + ". Size of value to read: "s+std::to_string(amount) + ". Buffer size: "s + std::to_string(_data.size())+"."s);
}
std::copy(_data.data()+index,_data.data()+index+amount,reinterpret_cast<unsigned char*>(&value));
_index = index+amount ;
return static_cast<T>(value) ;
}
//=====================================================================
// Reading an String type
template <typename T>
typename std::enable_if<std::is_same_v<T, std::string>,std::string>::type
read(std::size_t index=infinity,std::size_t numchar=infinity){
if (index==infinity){
index = _index ;
if (index >= _data.size()){
throw std::out_of_range("Read exceeds buffer size. Index is: "s+std::to_string(index) + ". Size of value to read: [end of buffer or \\0]."s + " Buffer size: "s + std::to_string(_data.size())+"."s);
}
}
else {
if ((_index+numchar) >= _data.size()){
throw std::out_of_range("Read exceeds buffer size. Index is: "s+std::to_string(index) + ". Size of value to read: "s + std::to_string(numchar) + ". Buffer size: "s + std::to_string(_data.size())+"."s);
}
}
std::vector<unsigned char> buffer ;
auto amount = static_cast<std::string::size_type>(numchar) ;
if (numchar==infinity){
// we need to find the first null terminator
auto iter = std::find(_data.begin()+index,_data.end(),static_cast<unsigned char>(0));
amount = std::distance(_data.begin()+index, iter);
}
if (_data.size() < index+amount){
amount = _data.size()-index ;
if (amount <= 0){
return std::string();
}
}
buffer.resize(amount,0);
std::copy(_data.begin()+index,_data.begin()+index+amount,buffer.begin());
auto nullat = std::find(buffer.begin(),buffer.end(),0);
if (nullat != buffer.end()){
auto tamount = std::distance(buffer.begin(),nullat);
buffer.resize(tamount) ;
}
_index = index+amount ;
return (std::string(reinterpret_cast<char*>(buffer.data()),buffer.size()));
}
//=====================================================================
// Copying an integer type (good on const Buffers, as they don't change the index)
template <typename T>
typename std::enable_if<std::is_integral_v<T> && !std::is_same_v<T, bool>,T>::type
copy(std::size_t index ) const{
T rvalue ;
auto amount = sizeof(T) ;
if (_data.size() < index+amount){
throw std::out_of_range("Read exceeds buffer size. Index is: "s+std::to_string(index) + ". Size of value to read: "s+std::to_string(sizeof(T)) + ". Buffer size: "s + std::to_string(_data.size())+"."s);
}
std::copy(_data.data()+index,_data.data()+index+amount,reinterpret_cast<unsigned char*>(&rvalue));
return rvalue ;
}
//=====================================================================
// Copying an bool type (one byte in the Buffer) (good on const Buffers, as they don't change the index)
template <typename T>
typename std::enable_if<std::is_integral_v<T> && std::is_same_v<T, bool>,T>::type
copy(std::size_t index ) const{
if (index == infinity){
index = _index ;
}
auto value = static_cast<unsigned char>(0) ;
auto amount = sizeof(value) ;
if (_data.size() < index+amount){
throw std::out_of_range("Read exceeds buffer size. Index is: "s+std::to_string(index) + ". Size of value to read: "s+std::to_string(amount) + ". Buffer size: "s + std::to_string(_data.size())+"."s);
}
std::copy(_data.data()+index,_data.data()+index+amount,reinterpret_cast<unsigned char*>(&value));
return static_cast<T>(value) ;
}
//=====================================================================
// Copy an String type (good on const Buffers, as they don't change the index)
template <typename T>
typename std::enable_if<std::is_same_v<T, std::string>,std::string>::type
copy(std::size_t index,std::size_t numchar=infinity) const{
if (index==infinity){
index = _index ;
if (index >= _data.size()){
throw std::out_of_range("Read exceeds buffer size. Index is: "s+std::to_string(index) + ". Size of value to read: [end of buffer or \\0]."s + " Buffer size: "s + std::to_string(_data.size())+"."s);
}
}
else {
if ((_index+numchar) >= _data.size()){
throw std::out_of_range("Read exceeds buffer size. Index is: "s+std::to_string(index) + ". Size of value to read: "s + std::to_string(numchar) + ". Buffer size: "s + std::to_string(_data.size())+"."s);
}
}
std::vector<unsigned char> buffer ;
auto amount = static_cast<std::string::size_type>(numchar) ;
if (numchar==infinity){
// we need to find the first null terminator
auto iter = std::find(_data.begin()+index,_data.end(),static_cast<unsigned char>(0));
amount = std::distance(_data.begin()+index, iter);
}
if (_data.size() < index+amount){
amount = _data.size()-index ;
if (amount <= 0){
return std::string();
}
}
buffer.resize(amount,0);
std::copy(_data.begin()+index,_data.begin()+index+amount,buffer.begin());
auto nullat = std::find(buffer.begin(),buffer.end(),0);
if (nullat != buffer.end()){
auto tamount = std::distance(buffer.begin(),nullat);
buffer.resize(tamount) ;
}
return (std::string(reinterpret_cast<char*>(buffer.data()),buffer.size()));
}
};
/******************************************************************************
Positioning for streaming
******************************************************************************/
Buffer& operator>>(Buffer &buffer, Buffer::Position &value);
Buffer& operator<<(Buffer &buffer, const Buffer::Position &value);
Buffer& operator>>(Buffer &buffer, Buffer::Relative &value);
Buffer& operator<<(Buffer &buffer, const Buffer::Relative &value);
/******************************************************************************
Reading for streaming
******************************************************************************/
Buffer& operator>>(Buffer &buffer, std::string &value);
template <typename T>
typename std::enable_if<std::is_integral_v<T> && !std::is_same_v<T, bool>,Buffer&>::type
operator>>(Buffer &buffer, T &value){
value = buffer.read<T>() ;
return buffer ;
}
/******************************************************************************
Writing for streaming
******************************************************************************/
Buffer& operator<<(Buffer &buffer, const std::string &value);
Buffer& operator<<(Buffer &buffer, const char* value);
template <typename T>
typename std::enable_if<std::is_integral_v<T> && !std::is_same_v<T, bool>,Buffer&>::type
operator<<(Buffer &buffer, const T &value){
buffer.write<T>(value) ;
return buffer ;
}
#endif /* Buffer_hpp */
//Copyright © 2021 Charles Kerr. All rights reserved.
#include "Buffer.hpp"
#include <algorithm>
#include <cstring>
#include "StringUtility.hpp"
/*******************************************************************************
Constructors
*******************************************************************************/
//===============================================================
Buffer::Buffer(std::size_t size) {
_data = std::vector<std::uint8_t>(size,0);
_index = 0 ;
}
//===============================================================
Buffer::Buffer(const std::vector<std::uint8_t> &data): Buffer(data.size()){
std::copy(data.cbegin(),data.cend(),_data.begin());
}
//===============================================================
Buffer::Buffer(const std::uint8_t *ptr,std::size_t size ):Buffer(size){
std::copy(ptr,ptr+size,_data.begin());
}
/*******************************************************************************
Sizing
*******************************************************************************/
//===============================================================
std::size_t Buffer::size() const {
return _data.size();
}
//===============================================================
void Buffer::size(std::size_t size) {
_data.resize(size,0);
if (_index > _data.size()){
_index = _data.size();
}
}
//===============================================================
bool Buffer::sizeCheck(std::size_t size,bool expand){
if (size > _data.size()){
if (!expand){
return false ;
}
this->size(size);
}
return true ;
}
/*******************************************************************************
Positioning
*******************************************************************************/
//===============================================================
std::size_t Buffer::position() const {
return _index ;
}
//===============================================================
Buffer& Buffer::position(std::size_t position) {
_index = position ;
return *this;
}
//===============================================================
Buffer& Buffer::relative(std::int64_t relative){
if (std::abs(relative)> _data.size()){
throw std::out_of_range("Buffer Positioned beyond beginning. Current :"s+std::to_string(_index) + ". Relative: "s+std::to_string(relative)+"."s);
}
_index = _index + relative ;
return *this;
}
//===============================================================
Buffer& operator>>(Buffer &buffer, Buffer::Position &value){
buffer.position(value.position) ;
return buffer ;
}
//===============================================================
Buffer& operator<<(Buffer &buffer, const Buffer::Position &value){
buffer.position(value.position) ;
return buffer ;
}
//===============================================================
Buffer& operator>>(Buffer &buffer, Buffer::Relative &value){
buffer.relative(value.relative) ;
return buffer ;
}
//===============================================================
Buffer& operator<<(Buffer &buffer, const Buffer::Relative &value){
buffer.relative(value.relative) ;
return buffer ;
}
/******************************************************************************
Display formatting
******************************************************************************/
//=====================================================================
std::int32_t Buffer::displaySize(std::int32_t radix) const {
switch (radix) {
case 10:
return 3 ;
case 16:
return 2;
case 2:
return 8;
case 8:
return 3;
default:
return 4 ;
}
}
//=====================================================================
std::stringstream Buffer::description(std::size_t columns, std::int32_t radix) const {
std::stringstream output ;
std::stringstream ascii ;
std::stringstream header ;
auto count = 0 ;
auto valuesize = displaySize(radix);
auto withpad = valuesize+1 ;
auto indexsize = 10 ; // We allocate 10 columns for display the index into buffer
auto indexdivider = ": "s;
header << std::string(indexsize+indexdivider.size(),' ') ;
for (auto i = 0 ; i < std::min(columns,_data.size()) ;i++){
header<<strutil::numtostr(i,10,false,valuesize+1,' ');
}
header<<"\n";
output << header.str() ;
for (auto & entry : _data){
if ((count % columns)==0) {
if (count != 0){
output <<" "<<ascii.str();
ascii.str(std::string());
output <<"\n";
}
auto label = strutil::numtostr(count) ;
auto pad = std::string(10-label.size(),' ');
output<<pad<<label<<": ";
}
output << strutil::numtostr(static_cast<unsigned char>(entry),radix,false,valuesize) << " ";
if (std::isalpha(static_cast<unsigned char>(entry))!=0){
ascii<<std::string(1,entry);
}
else {
ascii<<".";
}
count++ ;
}
if (count != 0){
auto tcount = (count%columns);
auto tempsize = (columns-tcount) * withpad + 1 ;
output << std::string(tempsize,' ') <<ascii.str() <<"\n";
}
return output ;
}
/******************************************************************************
Writing
******************************************************************************/
//=====================================================================
Buffer& operator<<(Buffer &buffer, const std::string &value){
buffer.write(value) ;
return buffer ;
}
//=====================================================================
Buffer& operator<<(Buffer &buffer, const char *value){
buffer.write(value) ;
return buffer ;
}
//=====================================================================
// Write a string
std::size_t Buffer::write(const std::string &input,std::size_t index ,std::size_t numchar,bool expand ){
if (index == infinity){
index = _index ;
}
std::string::size_type amount = static_cast<std::string::size_type>(numchar) ;
if (numchar == infinity ){
amount = input.size()+1 ;
}
if (_data.size() < index+amount){
// We need to increase the size of the buffer
if (expand) {
this->size(index+amount) ;
}
else {
throw std::out_of_range("Write exceeds buffer size. Index is: "s+std::to_string(index) + ". Size of value to write: "s+std::to_string(amount) + ". Buffer size: "s + std::to_string(_data.size())+"."s);
}
}
std::vector<char> temp ;
temp.resize(amount,0) ;
auto intermediate = amount ;
if (amount > (input.size()+1)) {
intermediate = input.size()+1;
}
std::copy(input.c_str(),input.c_str()+intermediate,temp.data());
std::copy(temp.begin(),temp.end(),_data.begin()+index);
_index = index+amount ;
return _index ;
}
//=====================================================================
// Write a boolean
std::size_t Buffer::write(const bool input,std::size_t index ,bool expand){
char value = 0 ;
if (index == infinity){
index = _index ;
}
if (_data.size() < index+sizeof(value)){
// We need to increase the size of the buffer
if (expand) {
this->size(index+sizeof(value)) ;
}
else {
throw std::out_of_range("Write exceeds buffer size. Index is: "s+std::to_string(index) + ". Size of value to write: "s+std::to_string(sizeof(value)) + ". Buffer size: "s + std::to_string(_data.size())+"."s);
}
}
if (input) {
value = 1 ;
}
std::copy(reinterpret_cast<char*>(&value),reinterpret_cast<char*>(&value)+sizeof(value),_data.begin()+index);
_index = index + sizeof(value) ;
return _index ;
}
//=====================================================================
// Write a vector
std::size_t Buffer::write( const std::vector<std::uint8_t> &source,std::size_t index , std::size_t numchar ,bool expand){
if (index == infinity){
index = _index ;
}
if (numchar == infinity){
numchar = source.size() ;
}
if (source.size() < numchar){
throw std::out_of_range("Source has insufficent data ("s + std::to_string(source.size())+") for requested size: "s+std::to_string(numchar)+"."s);
}
if (_data.size() < (index+numchar)){
if (expand){
size(index+numchar);
}
else {
throw std::out_of_range("Write exceeds buffer size. Index is: "s+std::to_string(index) + ". Size of value to write: "s+std::to_string(numchar) + ". Buffer size: "s + std::to_string(_data.size())+"."s);
}
}
std::copy(source.cbegin(),source.cbegin()+numchar,_data.begin()+index);
_index = index+numchar;
return _index ;
}
//=====================================================================
// Write a ptr
std::size_t Buffer::write( const std::uint8_t *ptrsource,std::size_t numchar, std::size_t index ,bool expand ){
if (index == infinity){
index = _index ;
}
if (_data.size() < (index+numchar)){
if (expand){
size(index+numchar);
}
else {
throw std::out_of_range("Write exceeds buffer size. Index is: "s+std::to_string(index) + ". Size of value to write: "s+std::to_string(numchar) + ". Buffer size: "s + std::to_string(_data.size())+"."s);
}
}
std::copy(ptrsource,ptrsource+numchar,_data.begin()+index);
_index = index+numchar;
return _index ;
}
//=====================================================================
// Write a C string
std::size_t Buffer::write(const char* strvalue,std::size_t index , std::size_t numchar,bool expand){
if (index == infinity){
index = _index ;
}
if (numchar == infinity){
numchar = std::strlen(strvalue);
}
if (std::strlen(strvalue) < numchar) {
throw std::out_of_range("Source has insufficent data ("s + std::to_string(std::strlen(strvalue))+") for requested size: "s+std::to_string(numchar)+"."s);
}
if (_data.size() < (index+numchar)){
if (expand){
size(index+numchar);
}
else {
throw std::out_of_range("Write exceeds buffer size. Index is: "s+std::to_string(index) + ". Size of value to write: "s+std::to_string(numchar) + ". Buffer size: "s + std::to_string(_data.size())+"."s);
}
}
std::copy(reinterpret_cast<const std::uint8_t*>(strvalue),reinterpret_cast<const std::uint8_t*>(strvalue)+numchar,_data.begin()+index);
_index = index + numchar ;
return _index ;
}
/******************************************************************************
Reading
******************************************************************************/
//=====================================================================
Buffer& operator>>(Buffer &buffer, std::string &value){
value = buffer.read<std::string>() ;
return buffer ;
}
//=====================================================================
std::size_t Buffer::read(std::vector<std::uint8_t> &dest,std::size_t index , std::size_t numchar ){
if (index == infinity){
index = _index ;
}
if (numchar == infinity){
numchar = dest.size();
}
if (dest.size() < numchar){
throw std::out_of_range("Destination has insufficent data ("s + std::to_string(dest.size())+") for requested size: "s+std::to_string(numchar)+"."s);
}
if (_data.size() < (index+numchar)){
throw std::out_of_range("Read exceeds buffer size. Index is: "s+std::to_string(index) + ". Size of value to read: "s+std::to_string(numchar) + ". Buffer size: "s + std::to_string(_data.size())+"."s);
}
std::copy(_data.begin()+index,_data.begin()+index+numchar,dest.begin());
_index = index+numchar ;
return _index ;
}
//=====================================================================
std::size_t Buffer::read(std::uint8_t *ptr, std::size_t numchar, std::size_t index ){
if (index == infinity){
index = _index ;
}
if (_data.size() < (index+numchar)){
throw std::out_of_range("Read exceeds buffer size. Index is: "s+std::to_string(index) + ". Size of value to read: "s+std::to_string(numchar) + ". Buffer size: "s + std::to_string(_data.size())+"."s);
}
std::copy(_data.cbegin()+index,_data.cbegin()+index+numchar,ptr);
_index = index+numchar ;
return _index ;
}
/******************************************************************************
Copying (doesn't impact Buffer index)
******************************************************************************/
//=====================================================================
void Buffer::copy(std::vector<std::uint8_t> &dest, std::size_t index, std::size_t numchar) const{
if (index == infinity){
index = _index ;
}
if (numchar == infinity){
numchar = dest.size();
}
if (dest.size() < numchar){
throw std::out_of_range("Destination has insufficent data ("s + std::to_string(dest.size())+") for requested size: "s+std::to_string(numchar)+"."s);
}
if (_data.size() < (index+numchar)){
throw std::out_of_range("Read exceeds buffer size. Index is: "s+std::to_string(index) + ". Size of value to read: "s+std::to_string(numchar) + ". Buffer size: "s + std::to_string(_data.size())+"."s);
}
std::copy(_data.cbegin()+index,_data.cbegin()+index+numchar,dest.begin());
}
//=====================================================================
void Buffer::copy(std::uint8_t *ptr, std::size_t numchar, std::size_t index) const{
if (_data.size() < (index+numchar)){
throw std::out_of_range("Read exceeds buffer size. Index is: "s+std::to_string(index) + ". Size of value to read: "s+std::to_string(numchar) + ". Buffer size: "s + std::to_string(_data.size())+"."s);
}
std::copy(_data.cbegin()+index,_data.cbegin()+index+numchar,ptr);
}
/******************************************************************************
Access
******************************************************************************/
//=====================================================================
const std::uint8_t & Buffer::operator[](std::size_t offset) const {
return _data[offset];
}
//=====================================================================
std::uint8_t & Buffer::operator[](std::size_t offset) {
return _data[offset] ;
}
//=====================================================================
std::uint8_t * Buffer::mutableBytes() {
return _data.data();
}
//=====================================================================
const std::uint8_t * Buffer::bytes() const {
return const_cast<std::uint8_t *>(_data.data());
}
#include "Buffer.hpp"
#include <algorithm>
#include <cstring>
#include "StringUtility.hpp"
/*******************************************************************************
Constructors
*******************************************************************************/
//===============================================================
Buffer::Buffer(std::size_t size) {
_data = std::vector<std::uint8_t>(size,0);
_index = 0 ;
}
//===============================================================
Buffer::Buffer(const std::vector<std::uint8_t> &data): Buffer(data.size()){
std::copy(data.cbegin(),data.cend(),_data.begin());
}
//===============================================================
Buffer::Buffer(const std::uint8_t *ptr,std::size_t size ):Buffer(size){
std::copy(ptr,ptr+size,_data.begin());
}
/*******************************************************************************
Sizing
*******************************************************************************/
//===============================================================
std::size_t Buffer::size() const {
return _data.size();
}
//===============================================================
void Buffer::size(std::size_t size) {
_data.resize(size,0);
if (_index > _data.size()){
_index = _data.size();
}
}
//===============================================================
bool Buffer::sizeCheck(std::size_t size,bool expand){
if (size > _data.size()){
if (!expand){
return false ;
}
this->size(size);
}
return true ;
}
/*******************************************************************************
Positioning
*******************************************************************************/
//===============================================================
std::size_t Buffer::position() const {
return _index ;
}
//===============================================================
Buffer& Buffer::position(std::size_t position) {
_index = position ;
return *this;
}
//===============================================================
Buffer& Buffer::relative(std::int64_t relative){
if (std::abs(relative)> _data.size()){
throw std::out_of_range("Buffer Positioned beyond beginning. Current :"s+std::to_string(_index) + ". Relative: "s+std::to_string(relative)+"."s);
}
_index = _index + relative ;
return *this;
}
//===============================================================
Buffer& operator>>(Buffer &buffer, Buffer::Position &value){
buffer.position(value.position) ;
return buffer ;
}
//===============================================================
Buffer& operator<<(Buffer &buffer, const Buffer::Position &value){
buffer.position(value.position) ;
return buffer ;
}
//===============================================================
Buffer& operator>>(Buffer &buffer, Buffer::Relative &value){
buffer.relative(value.relative) ;
return buffer ;
}
//===============================================================
Buffer& operator<<(Buffer &buffer, const Buffer::Relative &value){
buffer.relative(value.relative) ;
return buffer ;
}
/******************************************************************************
Display formatting
******************************************************************************/
//=====================================================================
std::int32_t Buffer::displaySize(std::int32_t radix) const {
switch (radix) {
case 10:
return 3 ;
case 16:
return 2;
case 2:
return 8;
case 8:
return 3;
default:
return 4 ;
}
}
//=====================================================================
std::stringstream Buffer::description(std::size_t columns, std::int32_t radix) const {
std::stringstream output ;
std::stringstream ascii ;
std::stringstream header ;
auto count = 0 ;
auto valuesize = displaySize(radix);
auto withpad = valuesize+1 ;
auto indexsize = 10 ; // We allocate 10 columns for display the index into buffer
auto indexdivider = ": "s;
header << std::string(indexsize+indexdivider.size(),' ') ;
for (auto i = 0 ; i < std::min(columns,_data.size()) ;i++){
header<<strutil::numtostr(i,10,false,valuesize+1,' ');
}
header<<"\n";
output << header.str() ;
for (auto & entry : _data){
if ((count % columns)==0) {
if (count != 0){
output <<" "<<ascii.str();
ascii.str(std::string());
output <<"\n";
}
auto label = strutil::numtostr(count) ;
auto pad = std::string(10-label.size(),' ');
output<<pad<<label<<": ";
}
output << strutil::numtostr(static_cast<unsigned char>(entry),radix,false,valuesize) << " ";
if (std::isalpha(static_cast<unsigned char>(entry))!=0){
ascii<<std::string(1,entry);
}
else {
ascii<<".";
}
count++ ;
}
if (count != 0){
auto tcount = (count%columns);
auto tempsize = (columns-tcount) * withpad + 1 ;
output << std::string(tempsize,' ') <<ascii.str() <<"\n";
}
return output ;
}
/******************************************************************************
Writing
******************************************************************************/
//=====================================================================
Buffer& operator<<(Buffer &buffer, const std::string &value){
buffer.write(value) ;
return buffer ;
}
//=====================================================================
Buffer& operator<<(Buffer &buffer, const char *value){
buffer.write(value) ;
return buffer ;
}
//=====================================================================
// Write a string
std::size_t Buffer::write(const std::string &input,std::size_t index ,std::size_t numchar,bool expand ){
if (index == infinity){
index = _index ;
}
std::string::size_type amount = static_cast<std::string::size_type>(numchar) ;
if (numchar == infinity ){
amount = input.size()+1 ;
}
if (_data.size() < index+amount){
// We need to increase the size of the buffer
if (expand) {
this->size(index+amount) ;
}
else {
throw std::out_of_range("Write exceeds buffer size. Index is: "s+std::to_string(index) + ". Size of value to write: "s+std::to_string(amount) + ". Buffer size: "s + std::to_string(_data.size())+"."s);
}
}
std::vector<char> temp ;
temp.resize(amount,0) ;
auto intermediate = amount ;
if (amount > (input.size()+1)) {
intermediate = input.size()+1;
}
std::copy(input.c_str(),input.c_str()+intermediate,temp.data());
std::copy(temp.begin(),temp.end(),_data.begin()+index);
_index = index+amount ;
return _index ;
}
//=====================================================================
// Write a boolean
std::size_t Buffer::write(const bool input,std::size_t index ,bool expand){
char value = 0 ;
if (index == infinity){
index = _index ;
}
if (_data.size() < index+sizeof(value)){
// We need to increase the size of the buffer
if (expand) {
this->size(index+sizeof(value)) ;
}
else {
throw std::out_of_range("Write exceeds buffer size. Index is: "s+std::to_string(index) + ". Size of value to write: "s+std::to_string(sizeof(value)) + ". Buffer size: "s + std::to_string(_data.size())+"."s);
}
}
if (input) {
value = 1 ;
}
std::copy(reinterpret_cast<char*>(&value),reinterpret_cast<char*>(&value)+sizeof(value),_data.begin()+index);
_index = index + sizeof(value) ;
return _index ;
}
//=====================================================================
// Write a vector
std::size_t Buffer::write( const std::vector<std::uint8_t> &source,std::size_t index , std::size_t numchar ,bool expand){
if (index == infinity){
index = _index ;
}
if (numchar == infinity){
numchar = source.size() ;
}
if (source.size() < numchar){
throw std::out_of_range("Source has insufficent data ("s + std::to_string(source.size())+") for requested size: "s+std::to_string(numchar)+"."s);
}
if (_data.size() < (index+numchar)){
if (expand){
size(index+numchar);
}
else {
throw std::out_of_range("Write exceeds buffer size. Index is: "s+std::to_string(index) + ". Size of value to write: "s+std::to_string(numchar) + ". Buffer size: "s + std::to_string(_data.size())+"."s);
}
}
std::copy(source.cbegin(),source.cbegin()+numchar,_data.begin()+index);
_index = index+numchar;
return _index ;
}
//=====================================================================
// Write a ptr
std::size_t Buffer::write( const std::uint8_t *ptrsource,std::size_t numchar, std::size_t index ,bool expand ){
if (index == infinity){
index = _index ;
}
if (_data.size() < (index+numchar)){
if (expand){
size(index+numchar);
}
else {
throw std::out_of_range("Write exceeds buffer size. Index is: "s+std::to_string(index) + ". Size of value to write: "s+std::to_string(numchar) + ". Buffer size: "s + std::to_string(_data.size())+"."s);
}
}
std::copy(ptrsource,ptrsource+numchar,_data.begin()+index);
_index = index+numchar;
return _index ;
}
//=====================================================================
// Write a C string
std::size_t Buffer::write(const char* strvalue,std::size_t index , std::size_t numchar,bool expand){
if (index == infinity){
index = _index ;
}
if (numchar == infinity){
numchar = std::strlen(strvalue);
}
if (std::strlen(strvalue) < numchar) {
throw std::out_of_range("Source has insufficent data ("s + std::to_string(std::strlen(strvalue))+") for requested size: "s+std::to_string(numchar)+"."s);
}
if (_data.size() < (index+numchar)){
if (expand){
size(index+numchar);
}
else {
throw std::out_of_range("Write exceeds buffer size. Index is: "s+std::to_string(index) + ". Size of value to write: "s+std::to_string(numchar) + ". Buffer size: "s + std::to_string(_data.size())+"."s);
}
}
std::copy(reinterpret_cast<const std::uint8_t*>(strvalue),reinterpret_cast<const std::uint8_t*>(strvalue)+numchar,_data.begin()+index);
_index = index + numchar ;
return _index ;
}
/******************************************************************************
Reading
******************************************************************************/
//=====================================================================
Buffer& operator>>(Buffer &buffer, std::string &value){
value = buffer.read<std::string>() ;
return buffer ;
}
//=====================================================================
std::size_t Buffer::read(std::vector<std::uint8_t> &dest,std::size_t index , std::size_t numchar ){
if (index == infinity){
index = _index ;
}
if (numchar == infinity){
numchar = dest.size();
}
if (dest.size() < numchar){
throw std::out_of_range("Destination has insufficent data ("s + std::to_string(dest.size())+") for requested size: "s+std::to_string(numchar)+"."s);
}
if (_data.size() < (index+numchar)){
throw std::out_of_range("Read exceeds buffer size. Index is: "s+std::to_string(index) + ". Size of value to read: "s+std::to_string(numchar) + ". Buffer size: "s + std::to_string(_data.size())+"."s);
}
std::copy(_data.begin()+index,_data.begin()+index+numchar,dest.begin());
_index = index+numchar ;
return _index ;
}
//=====================================================================
std::size_t Buffer::read(std::uint8_t *ptr, std::size_t numchar, std::size_t index ){
if (index == infinity){
index = _index ;
}
if (_data.size() < (index+numchar)){
throw std::out_of_range("Read exceeds buffer size. Index is: "s+std::to_string(index) + ". Size of value to read: "s+std::to_string(numchar) + ". Buffer size: "s + std::to_string(_data.size())+"."s);
}
std::copy(_data.cbegin()+index,_data.cbegin()+index+numchar,ptr);
_index = index+numchar ;
return _index ;
}
/******************************************************************************
Copying (doesn't impact Buffer index)
******************************************************************************/
//=====================================================================
void Buffer::copy(std::vector<std::uint8_t> &dest, std::size_t index, std::size_t numchar) const{
if (index == infinity){
index = _index ;
}
if (numchar == infinity){
numchar = dest.size();
}
if (dest.size() < numchar){
throw std::out_of_range("Destination has insufficent data ("s + std::to_string(dest.size())+") for requested size: "s+std::to_string(numchar)+"."s);
}
if (_data.size() < (index+numchar)){
throw std::out_of_range("Read exceeds buffer size. Index is: "s+std::to_string(index) + ". Size of value to read: "s+std::to_string(numchar) + ". Buffer size: "s + std::to_string(_data.size())+"."s);
}
std::copy(_data.cbegin()+index,_data.cbegin()+index+numchar,dest.begin());
}
//=====================================================================
void Buffer::copy(std::uint8_t *ptr, std::size_t numchar, std::size_t index) const{
if (_data.size() < (index+numchar)){
throw std::out_of_range("Read exceeds buffer size. Index is: "s+std::to_string(index) + ". Size of value to read: "s+std::to_string(numchar) + ". Buffer size: "s + std::to_string(_data.size())+"."s);
}
std::copy(_data.cbegin()+index,_data.cbegin()+index+numchar,ptr);
}
/******************************************************************************
Access
******************************************************************************/
//=====================================================================
const std::uint8_t & Buffer::operator[](std::size_t offset) const {
return _data[offset];
}
//=====================================================================
std::uint8_t & Buffer::operator[](std::size_t offset) {
return _data[offset] ;
}
//=====================================================================
std::uint8_t * Buffer::mutableBytes() {
return _data.data();
}
//=====================================================================
const std::uint8_t * Buffer::bytes() const {
return const_cast<std::uint8_t *>(_data.data());
}