Buffer class
Posted: Tue Nov 02, 2021 12:06 pm
I have often found the need to manipulate binary byte buffers. The alignment issues and tacking offsets for converting the data to different data types is always an interesting prospect. There one has strings to deal with, if a fixed amount, or null terminated. Ideally one should be able to stream the data as well.
My solution is as follows:
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());
}