|
/*
|
|
This file contains special classes for bitwise
|
|
reading and writing of arrays
|
|
*/
|
|
|
|
#include "bitops.h"
|
|
|
|
#include <algorithm>
|
|
#include <array>
|
|
#include <cstdio>
|
|
#include <cstdlib>
|
|
#include <filesystem>
|
|
#include <fstream>
|
|
#include <stdexcept>
|
|
|
|
#if defined(_WIN32) || defined(WIN32)
|
|
#include <fcntl.h>
|
|
#include <io.h>
|
|
#endif
|
|
|
|
namespace sfs = std::filesystem;
|
|
|
|
|
|
/* -----------------------------------------------
|
|
constructor for BitReader class
|
|
----------------------------------------------- */
|
|
|
|
BitReader::BitReader(unsigned char* array, int size)
|
|
{
|
|
data = array;
|
|
lbyte = size;
|
|
}
|
|
|
|
/* -----------------------------------------------
|
|
destructor for BitReader class
|
|
----------------------------------------------- */
|
|
|
|
BitReader::~BitReader() {}
|
|
|
|
/* -----------------------------------------------
|
|
reads n bits from BitReader
|
|
----------------------------------------------- */
|
|
|
|
unsigned int BitReader::read(int nbits)
|
|
{
|
|
unsigned int retval = 0;
|
|
|
|
if (eof())
|
|
{
|
|
peof_ += nbits;
|
|
return 0;
|
|
}
|
|
|
|
while (nbits >= cbit)
|
|
{
|
|
nbits -= cbit;
|
|
retval |= (RBITS(data[cbyte], cbit) << nbits);
|
|
update_curr_byte();
|
|
if (eof())
|
|
{
|
|
peof_ = nbits;
|
|
return retval;
|
|
}
|
|
}
|
|
|
|
if (nbits > 0)
|
|
{
|
|
retval |= (MBITS(data[cbyte], cbit, (cbit-nbits)));
|
|
cbit -= nbits;
|
|
}
|
|
|
|
return retval;
|
|
}
|
|
|
|
/* -----------------------------------------------
|
|
reads one bit from BitReader
|
|
----------------------------------------------- */
|
|
|
|
unsigned char BitReader::read_bit()
|
|
{
|
|
if (eof())
|
|
{
|
|
peof_++;
|
|
return 0;
|
|
}
|
|
|
|
// read one bit
|
|
unsigned char bit = BITN(data[cbyte], --cbit);
|
|
if (cbit == 0)
|
|
{
|
|
update_curr_byte();
|
|
}
|
|
|
|
return bit;
|
|
}
|
|
|
|
void BitReader::update_curr_byte()
|
|
{
|
|
cbyte++;
|
|
eof_ = cbyte == lbyte;
|
|
cbit = 8;
|
|
}
|
|
|
|
/* -----------------------------------------------
|
|
to skip padding from current byte
|
|
----------------------------------------------- */
|
|
|
|
unsigned char BitReader::unpad(unsigned char fillbit)
|
|
{
|
|
if ((cbit == 8) || eof())
|
|
{
|
|
return fillbit;
|
|
}
|
|
else
|
|
{
|
|
fillbit = read(1);
|
|
while (cbit != 8)
|
|
{
|
|
read(1);
|
|
}
|
|
}
|
|
|
|
return fillbit;
|
|
}
|
|
|
|
/* -----------------------------------------------
|
|
get current position in array
|
|
----------------------------------------------- */
|
|
|
|
int BitReader::getpos()
|
|
{
|
|
return cbyte;
|
|
}
|
|
|
|
/* -----------------------------------------------
|
|
get current bit position
|
|
----------------------------------------------- */
|
|
|
|
int BitReader::getbitp()
|
|
{
|
|
return cbit;
|
|
}
|
|
|
|
/* -----------------------------------------------
|
|
set byte and bit position
|
|
----------------------------------------------- */
|
|
|
|
void BitReader::setpos(int pbyte, int pbit)
|
|
{
|
|
if (pbyte < lbyte)
|
|
{
|
|
// reset eof
|
|
eof_ = false;
|
|
// set positions
|
|
cbyte = pbyte;
|
|
cbit = pbit;
|
|
}
|
|
else
|
|
{
|
|
// set eof
|
|
eof_ = true;
|
|
// set positions
|
|
cbyte = lbyte;
|
|
cbit = 8;
|
|
peof_ = ((pbyte - lbyte) * 8) + 8 - pbit;
|
|
}
|
|
}
|
|
|
|
/* -----------------------------------------------
|
|
rewind n bits
|
|
----------------------------------------------- */
|
|
|
|
void BitReader::rewind_bits(int nbits)
|
|
{
|
|
if (eof())
|
|
{
|
|
if (nbits > peof_)
|
|
{
|
|
nbits -= peof_;
|
|
peof_ = 0;
|
|
}
|
|
else
|
|
{
|
|
peof_ -= nbits;
|
|
return;
|
|
}
|
|
eof_ = false;
|
|
}
|
|
|
|
cbit += nbits;
|
|
cbyte -= cbit / 8;
|
|
cbit = cbit % 8;
|
|
if (cbyte < 0)
|
|
{
|
|
cbyte = 0;
|
|
cbit = 8;
|
|
}
|
|
}
|
|
|
|
bool BitReader::eof()
|
|
{
|
|
return eof_;
|
|
}
|
|
|
|
int BitReader::peof()
|
|
{
|
|
return peof_;
|
|
}
|
|
|
|
BitWriter::BitWriter(std::uint8_t padbit) : padbit_(padbit) {}
|
|
|
|
BitWriter::~BitWriter() {}
|
|
|
|
std::uint32_t rbits32(std::uint32_t val, std::size_t n)
|
|
{
|
|
return val & (0xFFFFFFFF >> (32 - n));
|
|
}
|
|
|
|
std::uint32_t mbits32(std::uint32_t val, std::size_t l, std::size_t r)
|
|
{
|
|
return rbits32(val, l) >> r;
|
|
}
|
|
|
|
void BitWriter::write_u16(std::uint16_t val, std::size_t num_bits)
|
|
{
|
|
while (num_bits >= curr_bit_)
|
|
{
|
|
curr_byte_ |= mbits32(val, num_bits, num_bits - curr_bit_);
|
|
num_bits -= curr_bit_;
|
|
write_curr_byte();
|
|
}
|
|
|
|
if (num_bits > 0)
|
|
{
|
|
curr_byte_ |= rbits32(val, num_bits) << (curr_bit_ - num_bits);
|
|
curr_bit_ -= num_bits;
|
|
}
|
|
}
|
|
|
|
void BitWriter::write_bit(std::uint8_t bit)
|
|
{
|
|
curr_bit_--;
|
|
curr_byte_ |= bit << curr_bit_;
|
|
if (curr_bit_ == 0)
|
|
{
|
|
write_curr_byte();
|
|
}
|
|
}
|
|
|
|
void BitWriter::write_curr_byte()
|
|
{
|
|
bytes_.emplace_back(curr_byte_);
|
|
curr_byte_ = 0;
|
|
curr_bit_ = 8;
|
|
}
|
|
|
|
void BitWriter::pad()
|
|
{
|
|
while (curr_bit_ < 8)
|
|
{
|
|
write_bit(padbit_);
|
|
}
|
|
}
|
|
|
|
std::vector<std::uint8_t> BitWriter::get_bytes()
|
|
{
|
|
pad(); // Pad the last bits of the current byte before returning the written bytes.
|
|
return bytes_;
|
|
}
|
|
|
|
unsigned char* BitWriter::get_c_bytes()
|
|
{
|
|
pad(); // Pad the last bits of the current byte before returning the written bytes.
|
|
unsigned char* c_bytes = new unsigned char[bytes_.size()];
|
|
std::copy(std::begin(bytes_), std::end(bytes_), c_bytes);
|
|
return c_bytes;
|
|
}
|
|
|
|
std::size_t BitWriter::num_bytes_written() const
|
|
{
|
|
return bytes_.size();
|
|
}
|
|
|
|
unsigned char* Reader::get_c_data()
|
|
{
|
|
const auto data = this->get_data();
|
|
auto c_data_copy = (unsigned char*)std::malloc(data.size() * sizeof data[0]);
|
|
if (c_data_copy == nullptr)
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
std::copy(std::begin(data), std::end(data), c_data_copy);
|
|
return c_data_copy;
|
|
}
|
|
|
|
MemoryReader::MemoryReader(const std::vector<std::uint8_t>& bytes) :
|
|
data_(bytes),
|
|
cbyte_(std::begin(data_))
|
|
{
|
|
}
|
|
|
|
MemoryReader::MemoryReader(const std::uint8_t* bytes, std::size_t size) :
|
|
data_(bytes, bytes + size),
|
|
cbyte_(std::begin(data_))
|
|
{
|
|
}
|
|
|
|
std::size_t MemoryReader::read(std::uint8_t* to, std::size_t num_to_read)
|
|
{
|
|
if (num_to_read == 0 || to == nullptr)
|
|
{
|
|
return 0;
|
|
}
|
|
auto numAvailable = std::distance(cbyte_, std::end(data_));
|
|
auto numRead = std::min(static_cast<std::size_t>(numAvailable), num_to_read);
|
|
auto end = std::next(cbyte_, numRead);
|
|
std::copy(cbyte_, end, to);
|
|
cbyte_ = end;
|
|
return numRead;
|
|
}
|
|
|
|
std::size_t MemoryReader::read(std::vector<std::uint8_t>& into, std::size_t n, std::size_t offset)
|
|
{
|
|
const std::size_t num_available = get_size() - num_bytes_read(); // The number of bytes in the reader not yet read.
|
|
const std::size_t num_to_read = std::min(n, num_available); // How many bytes will be read.
|
|
if (into.size() < num_to_read + offset)
|
|
{
|
|
into.resize(num_to_read + offset);
|
|
}
|
|
|
|
const auto end = std::next(cbyte_, num_to_read);
|
|
const auto write_start = std::next(std::begin(into), offset);
|
|
std::copy(cbyte_, end, write_start);
|
|
cbyte_ = end;
|
|
return num_to_read;
|
|
}
|
|
|
|
std::uint8_t MemoryReader::read_byte()
|
|
{
|
|
if (end_of_reader())
|
|
{
|
|
throw std::runtime_error("No bytes left to read");
|
|
}
|
|
else
|
|
{
|
|
std::uint8_t the_byte = *cbyte_;
|
|
++cbyte_;
|
|
return the_byte;
|
|
}
|
|
}
|
|
|
|
bool MemoryReader::read_byte(std::uint8_t* byte)
|
|
{
|
|
if (end_of_reader())
|
|
{
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
*byte = *cbyte_;
|
|
++cbyte_;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
void MemoryReader::skip(std::size_t n)
|
|
{
|
|
auto num_to_skip = std::min(n, std::size_t(std::distance(cbyte_, std::end(data_))));
|
|
cbyte_ += num_to_skip;
|
|
}
|
|
|
|
void MemoryReader::rewind_bytes(std::size_t n)
|
|
{
|
|
auto num_to_rewind = std::min(n, std::size_t(std::distance(std::begin(data_), cbyte_)));
|
|
auto new_pos = std::distance(std::begin(data_), cbyte_) - num_to_rewind;
|
|
cbyte_ = std::next(std::begin(data_), new_pos);
|
|
}
|
|
|
|
void MemoryReader::rewind()
|
|
{
|
|
cbyte_ = std::begin(data_);
|
|
}
|
|
|
|
std::size_t MemoryReader::num_bytes_read()
|
|
{
|
|
return std::distance(std::begin(data_), cbyte_);
|
|
}
|
|
|
|
std::size_t MemoryReader::get_size()
|
|
{
|
|
return data_.size();
|
|
}
|
|
|
|
std::vector<std::uint8_t> MemoryReader::get_data()
|
|
{
|
|
return data_;
|
|
}
|
|
|
|
bool MemoryReader::error()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
bool MemoryReader::end_of_reader()
|
|
{
|
|
return cbyte_ == std::end(data_);
|
|
}
|
|
|
|
unsigned char* Writer::get_c_data()
|
|
{
|
|
try
|
|
{
|
|
const auto data = this->get_data();
|
|
auto c_data_copy = (unsigned char*)std::malloc(data.size() * sizeof data[0]);
|
|
if (c_data_copy == nullptr)
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
std::copy(std::begin(data), std::end(data), c_data_copy);
|
|
return c_data_copy;
|
|
}
|
|
catch (const std::exception&)
|
|
{
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
MemoryWriter::MemoryWriter() {}
|
|
|
|
std::size_t MemoryWriter::write(const std::uint8_t* from, std::size_t n)
|
|
{
|
|
data_.insert(std::end(data_), from, from + n);
|
|
return n;
|
|
}
|
|
|
|
std::size_t MemoryWriter::write(const std::vector<std::uint8_t>& bytes)
|
|
{
|
|
data_.insert(std::end(data_), std::begin(bytes), std::end(bytes));
|
|
return bytes.size();
|
|
}
|
|
|
|
std::size_t MemoryWriter::write(const std::array<std::uint8_t, 2>& bytes)
|
|
{
|
|
data_.insert(std::end(data_), std::begin(bytes), std::end(bytes));
|
|
return bytes.size();
|
|
}
|
|
|
|
bool MemoryWriter::write_byte(std::uint8_t byte)
|
|
{
|
|
data_.emplace_back(byte);
|
|
return true;
|
|
}
|
|
|
|
std::vector<std::uint8_t> MemoryWriter::get_data()
|
|
{
|
|
return data_;
|
|
}
|
|
|
|
void MemoryWriter::reset()
|
|
{
|
|
data_.resize(0);
|
|
}
|
|
|
|
std::size_t MemoryWriter::num_bytes_written()
|
|
{
|
|
return data_.size();
|
|
}
|
|
|
|
bool MemoryWriter::error()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
FileWriter::FileWriter(const std::string& file_path) : file_path_(file_path)
|
|
{
|
|
fptr_ = std::fopen(file_path.c_str(), "wb");
|
|
if (fptr_ != nullptr)
|
|
{
|
|
file_buffer_.reserve(32768);
|
|
std::setvbuf(fptr_, file_buffer_.data(), _IOFBF, file_buffer_.capacity());
|
|
}
|
|
else
|
|
{
|
|
throw std::runtime_error("Unable to open " + file_path_ + " for writing.");
|
|
}
|
|
|
|
}
|
|
|
|
FileWriter::~FileWriter()
|
|
{
|
|
if (fptr_ != nullptr)
|
|
{
|
|
std::fflush(fptr_);
|
|
std::fclose(fptr_);
|
|
}
|
|
}
|
|
|
|
std::size_t FileWriter::write(const std::uint8_t* from, std::size_t n)
|
|
{
|
|
return std::fwrite(from, sizeof from[0], n, fptr_);
|
|
}
|
|
|
|
std::size_t FileWriter::write(const std::vector<std::uint8_t>& bytes)
|
|
{
|
|
return write(bytes.data(), bytes.size());
|
|
}
|
|
|
|
std::size_t FileWriter::write(const std::array<std::uint8_t, 2>& bytes)
|
|
{
|
|
return write(bytes.data(), 2);
|
|
}
|
|
|
|
bool FileWriter::write_byte(std::uint8_t byte)
|
|
{
|
|
return std::fputc(byte, fptr_) == byte;
|
|
}
|
|
|
|
std::vector<std::uint8_t> FileWriter::get_data()
|
|
{
|
|
std::fflush(fptr_);
|
|
if (std::ifstream is{ file_path_, std::ios::binary | std::ios::ate })
|
|
{
|
|
const auto size = is.tellg();
|
|
std::vector<std::uint8_t> data_copy(size);
|
|
is.seekg(0);
|
|
if (is.read(reinterpret_cast<char*>(data_copy.data()), size))
|
|
{
|
|
return data_copy;
|
|
}
|
|
else
|
|
{
|
|
throw std::runtime_error("FileWriter::get_data: unable to read bytes from file.");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
throw std::runtime_error("FileWriter::get_data: unable to open read stream for file.");
|
|
}
|
|
}
|
|
|
|
void FileWriter::reset()
|
|
{
|
|
std::fseek(fptr_, 0, SEEK_SET);
|
|
}
|
|
|
|
std::size_t FileWriter::num_bytes_written()
|
|
{
|
|
std::fflush(fptr_);
|
|
return sfs::file_size(file_path_);
|
|
}
|
|
|
|
bool FileWriter::error()
|
|
{
|
|
return fptr_ == nullptr || std::ferror(fptr_);
|
|
}
|
|
|
|
StreamWriter::StreamWriter()
|
|
{
|
|
writer_ = std::make_unique<MemoryWriter>();
|
|
}
|
|
|
|
StreamWriter::~StreamWriter()
|
|
{
|
|
#if defined(_WIN32) || defined(WIN32)
|
|
const int result = _setmode(_fileno(stdout), _O_BINARY);
|
|
if (result == -1)
|
|
{
|
|
return;
|
|
}
|
|
#endif
|
|
const auto& data = writer_->get_data();
|
|
fwrite(data.data(), sizeof data[0], data.size(), stdout);
|
|
}
|
|
|
|
std::size_t StreamWriter::write(const std::uint8_t* from, std::size_t n)
|
|
{
|
|
return writer_->write(from, n);
|
|
}
|
|
|
|
std::size_t StreamWriter::write(const std::vector<std::uint8_t>& bytes)
|
|
{
|
|
return writer_->write(bytes);
|
|
}
|
|
|
|
std::size_t StreamWriter::write(const std::array<std::uint8_t, 2>& bytes)
|
|
{
|
|
return writer_->write(bytes);
|
|
}
|
|
|
|
bool StreamWriter::write_byte(std::uint8_t byte)
|
|
{
|
|
return writer_->write_byte(byte);
|
|
}
|
|
|
|
std::vector<std::uint8_t> StreamWriter::get_data()
|
|
{
|
|
return writer_->get_data();
|
|
}
|
|
|
|
void StreamWriter::reset()
|
|
{
|
|
writer_->reset();
|
|
}
|
|
|
|
std::size_t StreamWriter::num_bytes_written()
|
|
{
|
|
return writer_->num_bytes_written();
|
|
}
|
|
|
|
bool StreamWriter::error()
|
|
{
|
|
return writer_->error();
|
|
}
|
|
|
|
|
|
|
|
FileReader::FileReader(const std::string& file_path)
|
|
{
|
|
if (std::ifstream is{ file_path, std::ios::binary | std::ios::ate })
|
|
{
|
|
const auto size = is.tellg();
|
|
std::vector<std::uint8_t> data(size);
|
|
is.seekg(0);
|
|
if (is.read(reinterpret_cast<char*>(data.data()), size))
|
|
{
|
|
reader_ = std::make_unique<MemoryReader>(data);
|
|
}
|
|
else
|
|
{
|
|
throw std::runtime_error("FileReader: unable to read bytes from " + file_path);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
throw std::runtime_error("FileReader: unable to open read stream for " + file_path);
|
|
}
|
|
}
|
|
|
|
FileReader::~FileReader() {}
|
|
|
|
std::size_t FileReader::read(std::uint8_t* to, std::size_t num_to_read)
|
|
{
|
|
return reader_->read(to, num_to_read);
|
|
}
|
|
|
|
std::size_t FileReader::read(std::vector<std::uint8_t>& into, std::size_t num_to_read, std::size_t offset)
|
|
{
|
|
return reader_->read(into, num_to_read, offset);
|
|
}
|
|
|
|
std::uint8_t FileReader::read_byte()
|
|
{
|
|
return reader_->read_byte();
|
|
}
|
|
|
|
bool FileReader::read_byte(std::uint8_t* to)
|
|
{
|
|
return reader_->read_byte(to);
|
|
}
|
|
|
|
void FileReader::skip(std::size_t n)
|
|
{
|
|
return reader_->skip(n);
|
|
}
|
|
|
|
void FileReader::rewind_bytes(std::size_t n)
|
|
{
|
|
return reader_->rewind_bytes(n);
|
|
}
|
|
|
|
void FileReader::rewind()
|
|
{
|
|
reader_->rewind();
|
|
}
|
|
|
|
std::size_t FileReader::num_bytes_read()
|
|
{
|
|
return reader_->num_bytes_read();
|
|
}
|
|
|
|
std::size_t FileReader::get_size()
|
|
{
|
|
return reader_->get_size();
|
|
}
|
|
|
|
std::vector<std::uint8_t> FileReader::get_data()
|
|
{
|
|
return reader_->get_data();
|
|
}
|
|
|
|
bool FileReader::error()
|
|
{
|
|
return reader_->error();
|
|
}
|
|
|
|
bool FileReader::end_of_reader()
|
|
{
|
|
return reader_->end_of_reader();
|
|
}
|
|
|
|
StreamReader::StreamReader()
|
|
{
|
|
#if defined(_WIN32) || defined(WIN32)
|
|
const int result = _setmode(_fileno(stdin), _O_BINARY);
|
|
if (result == -1)
|
|
{
|
|
throw std::runtime_error("Unable to set mode for stdin");
|
|
}
|
|
#endif
|
|
// read whole stream into memory buffer
|
|
std::vector<std::uint8_t> stream_data;
|
|
constexpr auto buffer_capacity = 1024 * 1024;
|
|
std::vector<std::uint8_t> buffer(buffer_capacity);
|
|
|
|
auto bytes_read = std::fread(buffer.data(), sizeof buffer[0], buffer_capacity, stdin);
|
|
while (bytes_read > 0)
|
|
{
|
|
stream_data.insert(std::end(stream_data), std::begin(buffer), std::begin(buffer) + bytes_read);
|
|
bytes_read = std::fread(buffer.data(), sizeof buffer[0], buffer_capacity, stdin);
|
|
}
|
|
|
|
reader_ = std::make_unique<MemoryReader>(stream_data);
|
|
}
|
|
|
|
std::size_t StreamReader::read(std::uint8_t* to, std::size_t num_to_read)
|
|
{
|
|
return reader_->read(to, num_to_read);
|
|
}
|
|
|
|
std::size_t StreamReader::read(std::vector<std::uint8_t>& into, std::size_t num_to_read, std::size_t offset)
|
|
{
|
|
return reader_->read(into, num_to_read, offset);
|
|
}
|
|
|
|
std::uint8_t StreamReader::read_byte()
|
|
{
|
|
return reader_->read_byte();
|
|
}
|
|
|
|
bool StreamReader::read_byte(std::uint8_t* to)
|
|
{
|
|
return reader_->read_byte(to);
|
|
}
|
|
|
|
void StreamReader::skip(std::size_t n)
|
|
{
|
|
reader_->skip(n);
|
|
}
|
|
|
|
void StreamReader::rewind_bytes(std::size_t n)
|
|
{
|
|
reader_->rewind_bytes(n);
|
|
}
|
|
|
|
void StreamReader::rewind()
|
|
{
|
|
reader_->rewind();
|
|
}
|
|
|
|
std::size_t StreamReader::num_bytes_read()
|
|
{
|
|
return reader_->num_bytes_read();
|
|
}
|
|
|
|
std::size_t StreamReader::get_size()
|
|
{
|
|
return reader_->get_size();
|
|
}
|
|
|
|
std::vector<std::uint8_t> StreamReader::get_data()
|
|
{
|
|
return reader_->get_data();
|
|
}
|
|
|
|
bool StreamReader::error()
|
|
{
|
|
return reader_->error();
|
|
}
|
|
|
|
bool StreamReader::end_of_reader()
|
|
{
|
|
return reader_->end_of_reader();
|
|
}
|