nostalgia/deps/ox/src/ox/std/buffer.hpp

240 lines
5.4 KiB
C++

/*
* Copyright 2015 - 2024 gary@drinkingtea.net
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
#pragma once
#include "error.hpp"
#include "reader.hpp"
#include "span.hpp"
#include "vector.hpp"
#include "writer.hpp"
OX_CLANG_NOWARN_BEGIN(-Wunsafe-buffer-usage)
namespace ox {
extern template class Vector<char>;
using Buffer = Vector<char>;
using BufferView = Span<const char>;
class BufferWriter {
private:
std::size_t m_it = 0;
ox::Buffer &m_buff;
public:
explicit constexpr BufferWriter(Buffer *buff) noexcept: m_it(buff->size()), m_buff(*buff) {
}
explicit constexpr BufferWriter(Buffer *buff, std::size_t it) noexcept: m_it(it), m_buff(*buff) {
}
constexpr ox::Error seekp(std::size_t p) noexcept {
m_it = p;
return {};
}
constexpr ox::Error seekp(ox::Signed<std::size_t> off, ox::ios_base::seekdir dir) noexcept {
ox::Signed<std::size_t> base = 0;
switch (dir) {
case ox::ios_base::beg:
base = 0;
break;
case ox::ios_base::end:
base = static_cast<ox::Signed<std::size_t>>(m_buff.size());
break;
case ox::ios_base::cur:
base = static_cast<ox::Signed<std::size_t>>(m_it);
break;
default:
return OxError(1, "Invalid seekdir");
}
m_it = static_cast<std::size_t>(base + off);
return {};
}
[[nodiscard]]
constexpr auto tellp() const noexcept {
return m_it;
}
constexpr ox::Error put(char val) noexcept {
if (m_it >= m_buff.size()) {
m_buff.resize(m_buff.size() + 1);
}
m_buff[m_it] = val;
++m_it;
return {};
}
constexpr ox::Error write(const char *inBuff, std::size_t cnt) noexcept {
const auto end = m_it + cnt;
if (end >= m_buff.size()) {
m_buff.resize(end);
}
if (inBuff) {
const auto buff = m_buff.data() + m_it;
for (auto i = 0u; i < cnt; ++i) {
buff[i] = inBuff[i];
}
}
m_it += cnt;
return {};
}
[[nodiscard]]
constexpr const auto &buff() const noexcept {
return m_buff;
}
};
class CharBuffWriter {
private:
std::size_t m_it = 0;
std::size_t m_cap = 0;
std::size_t m_size = 0;
char *m_buff = nullptr;
public:
explicit constexpr CharBuffWriter(ox::Span<char> buff) noexcept:
m_cap(buff.size()),
m_buff(buff.data()) {
}
constexpr ox::Error seekp(std::size_t p) noexcept {
m_it = p;
return {};
}
constexpr ox::Error seekp(ox::Signed<std::size_t> off, ox::ios_base::seekdir dir) noexcept {
ox::Signed<std::size_t> base = 0;
switch (dir) {
case ox::ios_base::beg:
base = 0;
break;
case ox::ios_base::end:
base = static_cast<ox::Signed<std::size_t>>(m_size);
break;
case ox::ios_base::cur:
base = static_cast<ox::Signed<std::size_t>>(m_it);
break;
default:
return OxError(1, "Invalid seekdir");
}
m_it = static_cast<std::size_t>(base + off);
return {};
}
[[nodiscard]]
constexpr auto tellp() const noexcept {
return m_it;
}
constexpr ox::Error put(char val) noexcept {
if (m_it >= m_cap) [[unlikely]] {
return OxError(1, "Buffer overrun");
}
m_buff[m_it] = val;
++m_it;
m_size = ox::max(m_it, m_size);
return {};
}
constexpr ox::Error write(const char *buff, std::size_t cnt) noexcept {
const auto end = m_it + cnt;
if (end > m_cap) [[unlikely]] {
return OxError(1, "Buffer overrun");
}
if (buff) {
for (auto i = 0u; i < cnt; ++i) {
m_buff[m_it + i] = buff[i];
}
}
m_it += cnt;
m_size = ox::max(m_it, m_size);
return {};
}
[[nodiscard]]
constexpr auto data() const noexcept {
return m_buff;
}
};
class BufferReader {
private:
std::size_t m_it = 0;
std::size_t m_size = 0;
char const* m_buff = nullptr;
public:
constexpr explicit BufferReader(ox::BufferView buffer) noexcept:
m_size(buffer.size()), m_buff(buffer.data()) {}
constexpr ox::Result<char> peek() const noexcept {
if (m_it >= m_size) [[unlikely]] {
return OxError(1, "Peek failed: buffer overrun");
}
return m_buff[m_it];
}
constexpr ox::Result<std::size_t> read(void *v, std::size_t sz) noexcept {
sz = ox::min(sz, m_size - m_it);
if (m_it + sz > m_size) [[unlikely]] {
return OxError(1, "Read failed: Buffer overrun");
}
ox::memcpy(v, &m_buff[m_it], sz);
m_it += sz;
return sz;
}
constexpr ox::Error seekg(std::size_t p) noexcept {
if (p > m_size) [[unlikely]] {
return OxError(1, "Seek failed: Buffer overrun");
}
m_it = p;
return {};
}
constexpr ox::Error seekg(int64_t off, ios_base::seekdir dir) noexcept {
ox::Signed<std::size_t> base = 0;
switch (dir) {
case ox::ios_base::beg:
base = 0;
break;
case ox::ios_base::end:
base = static_cast<ox::Signed<std::size_t>>(m_size);
break;
case ox::ios_base::cur:
base = static_cast<ox::Signed<std::size_t>>(m_it);
break;
default:
return OxError(1, "Invalid seekdir");
}
auto const newIt = static_cast<std::size_t>(base + off);
if (newIt > m_size) [[unlikely]] {
return OxError(1, "Seek failed: Buffer overrun");
}
m_it = newIt;
return {};
}
constexpr ox::Result<std::size_t> tellg() const noexcept {
return m_it;
}
};
extern template class ReaderT<BufferReader>;
extern template class WriterT<BufferWriter>;
extern template class WriterT<CharBuffWriter>;
}
OX_CLANG_NOWARN_END