240 lines
5.4 KiB
C++
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
|