mijin2/source/mijin/io/stream.cpp
2023-05-29 14:51:44 +02:00

309 lines
7.1 KiB
C++

#include "./stream.hpp"
#include <algorithm>
#include <cassert>
#include <limits>
namespace mijin
{
//
// internal defines
//
//
// internal constants
//
//
// internal types
//
//
// internal variables
//
//
// internal functions
//
//
// public functions
//
void Stream::flush() {}
StreamError Stream::readString(std::string& outString)
{
std::uint32_t length; // NOLINT(cppcoreguidelines-init-variables)
StreamError error = read(length);
if (error != StreamError::SUCCESS) {
return error;
}
std::string result;
result.resize(length);
error = readSpan(result.begin(), result.end());
if (error != StreamError::SUCCESS) {
return error;
}
outString = std::move(result);
return StreamError::SUCCESS;
}
StreamError Stream::writeString(std::string_view str)
{
assert(str.length() <= std::numeric_limits<std::uint32_t>::max());
const std::uint32_t length = static_cast<std::uint32_t>(str.length());
StreamError error = write(length);
if (error != StreamError::SUCCESS) {
return error;
}
return writeSpan(str.begin(), str.end());
}
FileStream::~FileStream()
{
if (handle) {
close();
}
}
StreamError FileStream::open(const char* path, FileOpenMode mode_)
{
mode = mode_;
const char* modeStr; // NOLINT(cppcoreguidelines-init-variables)
switch (mode_)
{
case FileOpenMode::READ:
modeStr = "rb";
break;
case FileOpenMode::WRITE:
modeStr = "wb";
break;
case FileOpenMode::APPEND:
modeStr = "ab";
break;
case FileOpenMode::READ_WRITE:
modeStr = "r+b";
break;
}
handle = std::fopen(path, modeStr); // NOLINT(cppcoreguidelines-owning-memory)
if (!handle && mode_ == FileOpenMode::READ_WRITE) {
handle = std::fopen(path, "w+b"); // NOLINT(cppcoreguidelines-owning-memory)
}
if (!handle) {
return StreamError::IO_ERROR;
}
int result = std::fseek(handle, 0, SEEK_END);
assert(result == 0);
length = std::ftell(handle);
result = std::fseek(handle, 0, SEEK_SET);
assert(result == 0);
return StreamError::SUCCESS;
}
void FileStream::close()
{
assert(handle);
const int result = std::fclose(handle); // NOLINT(cppcoreguidelines-owning-memory)
assert(result == 0);
}
StreamError FileStream::readRaw(std::span<std::uint8_t> buffer, bool partial, std::size_t* outBytesRead)
{
assert(handle);
assert(mode == FileOpenMode::READ || mode == FileOpenMode::READ_WRITE);
const std::size_t readBytes = std::fread(buffer.data(), 1, buffer.size(), handle);
if (std::ferror(handle)) {
return StreamError::IO_ERROR;
}
if (!partial && readBytes < buffer.size()) {
return StreamError::IO_ERROR;
}
if (outBytesRead != nullptr) {
*outBytesRead = readBytes;
}
return StreamError::SUCCESS;
}
StreamError FileStream::writeRaw(std::span<const std::uint8_t> buffer)
{
assert(handle);
assert(mode == FileOpenMode::WRITE || mode == FileOpenMode::APPEND || mode == FileOpenMode::READ_WRITE);
const std::size_t written = std::fwrite(buffer.data(), 1, buffer.size(), handle);
if (written != buffer.size() || std::ferror(handle)) {
return StreamError::IO_ERROR;
}
length = std::max<std::size_t>(length, std::ftell(handle));
return StreamError::SUCCESS;
}
std::size_t FileStream::tell()
{
assert(handle);
return std::ftell(handle);
}
StreamError FileStream::seek(std::intptr_t pos, SeekMode seekMode)
{
assert(handle);
int origin; // NOLINT(cppcoreguidelines-init-variables)
switch (seekMode)
{
case SeekMode::ABSOLUTE:
origin = SEEK_SET;
break;
case SeekMode::RELATIVE:
origin = SEEK_CUR;
break;
case SeekMode::RELATIVE_TO_END:
origin = SEEK_END;
break;
}
const int result = std::fseek(handle, static_cast<long>(pos), origin);
if (result != 0 || std::ferror(handle)) {
return StreamError::IO_ERROR;
}
return StreamError::SUCCESS;
}
void FileStream::flush()
{
const int result = std::fflush(handle);
assert(result == 0);
}
bool FileStream::isAtEnd()
{
assert(handle);
(void) std::fgetc(handle);
if (std::feof(handle)) {
return true;
}
const int result = std::fseek(handle, -1, SEEK_CUR);
assert(result == 0);
return false;
}
StreamFeatures FileStream::getFeatures()
{
if (handle)
{
return {
.read = (mode == FileOpenMode::READ),
.write = (mode == FileOpenMode::WRITE || mode == FileOpenMode::APPEND || mode == FileOpenMode::READ_WRITE),
.tell = true,
.seek = true
};
}
return {};
}
void MemoryStream::openRW(std::span<std::uint8_t> data)
{
assert(!isOpen());
data_ = data;
pos_ = 0;
canWrite_ = true;
}
void MemoryStream::openRO(std::span<const std::uint8_t> data)
{
assert(!isOpen());
data_ = std::span<std::uint8_t>(const_cast<std::uint8_t*>(data.data()), data.size()); // NOLINT(cppcoreguidelines-pro-type-const-cast) we'll be fine
pos_ = 0;
canWrite_ = false;
}
void MemoryStream::close()
{
assert(isOpen());
data_ = {};
}
StreamError MemoryStream::readRaw(std::span<std::uint8_t> buffer, bool partial, std::size_t* outBytesRead)
{
assert(isOpen());
if (!partial && availableBytes() < buffer.size()) {
return StreamError::IO_ERROR; // TODO: need more errors?
}
const std::size_t numBytes = std::min(buffer.size(), availableBytes());
std::copy_n(data_.begin() + static_cast<long>(pos_), numBytes, buffer.begin());
if (outBytesRead) {
*outBytesRead = numBytes;
}
pos_ += numBytes;
return StreamError::SUCCESS;
}
StreamError MemoryStream::writeRaw(std::span<const std::uint8_t> buffer)
{
assert(isOpen());
assert(canWrite_);
if (availableBytes() < buffer.size()) {
return StreamError::IO_ERROR;
}
std::copy(buffer.begin(), buffer.end(), data_.begin() + static_cast<long>(pos_));
pos_ += buffer.size();
return StreamError::SUCCESS;
}
std::size_t MemoryStream::tell()
{
assert(isOpen());
return pos_;
}
StreamError MemoryStream::seek(std::intptr_t pos, SeekMode seekMode)
{
assert(isOpen());
std::intptr_t newPos = -1;
switch (seekMode)
{
case SeekMode::ABSOLUTE:
newPos = pos;
break;
case SeekMode::RELATIVE:
newPos = static_cast<std::intptr_t>(pos_) + pos;
break;
case SeekMode::RELATIVE_TO_END:
newPos = static_cast<std::intptr_t>(data_.size()) + pos;
break;
}
if (newPos < 0 || newPos > data_.size()) {
return StreamError::IO_ERROR;
}
pos_ = newPos;
return StreamError::SUCCESS;
}
bool MemoryStream::isAtEnd()
{
assert(isOpen());
return pos_ == data_.size();
}
StreamFeatures MemoryStream::getFeatures()
{
return {
.read = true,
.write = canWrite_,
.tell = true,
.seek = true
};
}
} // namespace mijin