scsl/Buffer.cc

378 lines
6.7 KiB
C++
Raw Normal View History

///
/// \file Buffer.cc
/// \author K. Isom <kyle@imap.cc>
/// \date 2023-10-09
/// \brief Buffer implements basic line buffers.
///
/// Copyright 2023 K. Isom <kyle@imap.cc>
///
/// Permission to use, copy, modify, and/or distribute this software for
/// any purpose with or without fee is hereby granted, provided that
/// the above copyright notice and this permission notice appear in all /// copies.
///
/// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
/// WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
/// WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
/// AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
/// DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA
/// OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
/// TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
/// PERFORMANCE OF THIS SOFTWARE.
///
2023-10-09 17:24:40 +00:00
#include <cassert>
#include <cstring>
2023-10-10 02:59:21 +00:00
#include <ios>
2023-10-09 20:16:05 +00:00
#include <iostream>
2023-10-10 02:59:21 +00:00
#include "Buffer.h"
2023-10-09 17:24:40 +00:00
2023-10-15 01:38:01 +00:00
namespace scsl {
2023-10-09 17:24:40 +00:00
/// The defaultCapacity for a new Buffer is a reasonably arbitrary starting
/// point.
2023-10-10 02:59:21 +00:00
constexpr size_t defaultCapacity = 32;
/// maxReasonableLine is the longest a reasonable line could be. It assumes
/// something like a long, unprettified JSON strong or the like.
2023-10-10 02:59:21 +00:00
constexpr size_t maxReasonableLine = 8192;
2023-10-09 17:24:40 +00:00
static size_t
nearestPower(size_t x)
{
if (x == 0) {
return 0;
}
2023-10-09 20:16:05 +00:00
2023-10-09 17:24:40 +00:00
x--;
x |= x >> 1;
x |= x >> 2;
x |= x >> 4;
x |= x >> 8;
x |= x >> 16;
x |= x >> 32;
2023-10-10 02:59:21 +00:00
return x + 1;
2023-10-09 17:24:40 +00:00
}
Buffer::Buffer()
: contents(nullptr), length(0), capacity(0), autoTrim(true)
2023-10-09 17:24:40 +00:00
{
2023-10-10 02:59:21 +00:00
this->Resize(defaultCapacity);
2023-10-09 17:24:40 +00:00
}
Buffer::Buffer(size_t initialCapacity)
: contents(nullptr), length(0), capacity(0), autoTrim(true)
2023-10-09 17:24:40 +00:00
{
2023-10-10 02:59:21 +00:00
this->Resize(initialCapacity);
2023-10-09 17:24:40 +00:00
}
Buffer::Buffer(const char *data)
: contents(nullptr), length(0), capacity(0), autoTrim(true)
2023-10-09 17:24:40 +00:00
{
2023-10-10 02:59:21 +00:00
size_t datalen = strnlen(data, maxReasonableLine);
2023-10-09 17:24:40 +00:00
2023-10-10 02:59:21 +00:00
this->Append((uint8_t *) data, datalen);
2023-10-09 17:24:40 +00:00
}
Buffer::Buffer(const std::string s)
: contents(nullptr), length(0), capacity(0), autoTrim(true)
{
this->Append(s);
}
2023-10-09 19:45:19 +00:00
bool
Buffer::Append(const char *s)
{
2023-10-10 02:59:21 +00:00
size_t slen = strnlen(s, maxReasonableLine);
2023-10-09 19:45:19 +00:00
2023-10-10 02:59:21 +00:00
return this->Append((uint8_t *) s, slen);
2023-10-09 19:45:19 +00:00
}
2023-10-09 17:24:40 +00:00
bool
Buffer::Append(const std::string s)
{
return this->Append((const uint8_t *) s.c_str(), s.size());
}
bool
Buffer::Append(const uint8_t *data, const size_t datalen)
2023-10-09 17:24:40 +00:00
{
2023-10-10 02:59:21 +00:00
auto resized = false;
auto newCap = this->mustGrow(datalen);
2023-10-09 17:24:40 +00:00
if (newCap > 0) {
this->Resize(newCap);
resized = true;
}
assert(this->contents != nullptr);
2023-10-09 17:24:40 +00:00
memcpy(this->contents + this->length, data, datalen);
this->length += datalen;
return resized;
}
2023-10-09 17:24:40 +00:00
bool
Buffer::Append(const uint8_t c)
2023-10-09 17:24:40 +00:00
{
return this->Append(&c, 1);
}
2023-10-09 19:45:19 +00:00
bool
Buffer::Insert(const size_t index, const char *s)
2023-10-09 19:45:19 +00:00
{
2023-10-10 02:59:21 +00:00
size_t slen = strnlen(s, maxReasonableLine);
2023-10-09 19:45:19 +00:00
2023-10-10 02:59:21 +00:00
return this->Insert(index, (uint8_t *) (s), slen);
2023-10-09 19:45:19 +00:00
}
2023-10-09 17:24:40 +00:00
bool
Buffer::Insert(const size_t index, const std::string s)
{
return this->Insert(index, (const uint8_t *) s.c_str(), s.size());
}
bool
Buffer::Insert(const size_t index, const uint8_t *data, const size_t datalen)
2023-10-09 17:24:40 +00:00
{
2023-10-10 02:59:21 +00:00
auto resized = this->shiftRight(index, datalen);
2023-10-09 17:24:40 +00:00
memcpy(this->contents + index, data, datalen);
2023-10-09 19:45:19 +00:00
this->length += datalen;
2023-10-09 17:24:40 +00:00
return resized;
}
2023-10-09 17:24:40 +00:00
bool
Buffer::Insert(const size_t index, const uint8_t c)
2023-10-09 17:24:40 +00:00
{
return this->Insert(index, &c, 1);
}
2023-10-09 19:45:19 +00:00
bool
Buffer::Remove(const size_t index, const size_t count)
2023-10-09 19:45:19 +00:00
{
auto resized = this->shiftLeft(index, count);
this->length -= count;
return resized;
}
bool
Buffer::Remove(size_t index)
{
return this->Remove(index, 1);
}
2023-10-09 17:24:40 +00:00
void
Buffer::Resize(size_t newCapacity)
{
if (newCapacity < this->length) {
2023-10-10 02:59:21 +00:00
newCapacity = nearestPower(this->length + newCapacity);
2023-10-09 17:24:40 +00:00
}
auto newContents = new uint8_t[newCapacity];
2023-10-09 17:24:40 +00:00
2023-10-09 19:45:19 +00:00
memset(newContents, 0, newCapacity);
2023-10-09 17:24:40 +00:00
if (this->length > 0) {
memcpy(newContents, this->contents, this->length);
}
2023-10-10 11:22:04 +00:00
if (this->length > 0) {
delete[] this->contents;
this->contents = nullptr;
}
2023-10-09 17:24:40 +00:00
this->contents = newContents;
this->capacity = newCapacity;
}
size_t
Buffer::Trim()
{
2023-10-10 02:59:21 +00:00
size_t projectedCapacity = nearestPower(this->length);
2023-10-09 17:24:40 +00:00
assert(projectedCapacity >= length);
if (projectedCapacity < this->capacity) {
this->Resize(projectedCapacity);
return this->Capacity();
}
return 0;
}
void
Buffer::Clear()
{
2023-10-10 02:59:21 +00:00
if (this->length == 0) {
return;
}
2023-10-09 17:24:40 +00:00
memset(this->contents, 0, this->length);
this->length = 0;
}
void
Buffer::Reclaim()
{
2023-10-09 19:45:19 +00:00
this->Clear();
2023-10-10 02:59:21 +00:00
if (this->contents == nullptr) {
assert(this->length == 0);
assert(this->capacity == 0);
return;
}
2023-10-10 11:22:04 +00:00
delete[] this->contents;
2023-10-10 02:59:21 +00:00
this->contents = nullptr;
2023-10-09 17:24:40 +00:00
this->capacity = 0;
}
size_t
Buffer::mustGrow(size_t delta) const
2023-10-09 17:24:40 +00:00
{
if ((delta + this->length) < this->capacity) {
return 0;
}
auto newCapacity = delta + this->length;
return nearestPower(newCapacity);
}
2023-10-10 02:59:21 +00:00
void
Buffer::HexDump(std::ostream &os)
{
#ifndef NDEBUG
size_t index = 0;
2023-10-10 02:59:21 +00:00
os << std::hex;
os << std::setfill('0');
for (index = 0; index < this->length; index++) {
bool eol = (index % 16) == 0;
2023-10-10 02:59:21 +00:00
if (eol && (index > 0)) {
os << std::endl;
}
if (eol) {
os << std::setw(8);
os << index << " ";
os << std::setw(2);
}
os << (unsigned short) this->contents[index];
2023-10-10 02:59:21 +00:00
if ((index % 15) != 0 || (index == 0)) {
os << " ";
}
}
if ((index % 16) != 0) {
os << std::endl;
}
os << std::setw(0) << std::dec;
2023-10-11 01:57:43 +00:00
#else
(void)os;
2023-10-10 02:59:21 +00:00
#endif
}
2023-10-09 17:24:40 +00:00
bool
2023-10-09 19:45:19 +00:00
Buffer::shiftRight(size_t offset, size_t delta)
2023-10-09 17:24:40 +00:00
{
2023-10-10 02:59:21 +00:00
auto resized = false;
auto newCap = this->mustGrow(delta);
2023-10-09 17:24:40 +00:00
if (newCap > 0) {
this->Resize(newCap);
resized = true;
}
2023-10-10 02:59:21 +00:00
if (this->length == 0) return 0;
2023-10-09 17:24:40 +00:00
2023-10-10 11:22:04 +00:00
memmove(this->contents + (offset + delta), this->contents + offset,
this->length);
2023-10-09 17:24:40 +00:00
return resized;
}
2023-10-09 19:45:19 +00:00
bool
Buffer::shiftLeft(size_t offset, size_t delta)
{
2023-10-10 11:22:04 +00:00
if (delta == 0) {
return false;
}
if ((offset+delta) > this->length) {
abort();
}
for (auto i = offset; i <= (this->length-delta); i++) {
this->contents[i] = this->contents[i+delta];
}
for (auto i = this->length-delta; i < this->length; i++) {
this->contents[i] = 0;
}
2023-10-09 19:45:19 +00:00
if (this->AutoTrimIsEnabled()) {
return this->Trim() != 0;
}
return false;
2023-10-09 19:45:19 +00:00
}
2023-10-10 02:59:21 +00:00
uint8_t &
2023-10-09 19:45:19 +00:00
Buffer::operator[](size_t index)
{
if (index > this->length) {
2023-10-16 00:09:31 +00:00
#if defined(SCSL_DESKTOP_BUILD) and !defined(SCSL_NOEXCEPT)
throw std::range_error("array index out of bounds");
#else
abort();
#endif
}
2023-10-09 19:45:19 +00:00
return this->contents[index];
}
bool
operator==(const Buffer &lhs, const Buffer &rhs)
{
if (lhs.length != rhs.length) {
return false;
}
return memcmp(lhs.contents, rhs.contents, rhs.length) == 0;
}
std::ostream &
operator<<(std::ostream &os, const Buffer &buf)
{
// std::string s((const char *)buf.Contents(), buf.Length());
os << const_cast<uint8_t *>(buf.Contents());
return os;
}
2023-10-15 01:38:01 +00:00
} // namespace scsl