mijin2/source/mijin/io/stream.hpp

185 lines
4.7 KiB
C++

#pragma once
#if !defined(MIJIN_IO_STREAM_HPP_INCLUDED)
#define MIJIN_IO_STREAM_HPP_INCLUDED 1
#include <cassert>
#include <cstdint>
#include <optional>
#include <span>
#include <string>
namespace mijin
{
//
// public defines
//
//
// public constants
//
//
// public types
//
enum class SeekMode
{
ABSOLUTE,
RELATIVE,
RELATIVE_TO_END
};
struct StreamFeatures
{
bool read : 1 = false;
bool write : 1 = false;
bool tell : 1 = false;
bool seek : 1 = false;
};
enum class FileOpenMode
{
READ,
WRITE,
APPEND,
READ_WRITE
};
enum class StreamError
{
SUCCESS,
IO_ERROR,
UNKNOWN_ERROR
};
class Stream
{
public:
virtual ~Stream() = default;
public:
virtual StreamError readRaw(std::span<std::uint8_t> buffer, bool partial = false, std::size_t* outBytesRead = nullptr) = 0;
virtual StreamError writeRaw(std::span<const std::uint8_t> buffer) = 0;
virtual std::size_t tell() = 0;
virtual StreamError seek(std::intptr_t pos, SeekMode seekMode = SeekMode::ABSOLUTE) = 0;
virtual void flush();
virtual bool isAtEnd() = 0;
virtual StreamFeatures getFeatures() = 0;
inline StreamError readRaw(void* outData, std::size_t bytes, bool partial = false, std::size_t* outBytesRead = nullptr)
{
std::uint8_t* ptr = static_cast<std::uint8_t*>(outData);
return readRaw(std::span(ptr, ptr + bytes), partial, outBytesRead);
}
inline StreamError writeRaw(const void* data, std::size_t bytes)
{
const std::uint8_t* ptr = static_cast<const std::uint8_t*>(data);
return writeRaw(std::span(ptr, ptr + bytes));
}
template<typename T>
inline StreamError read(T& value)
{
return readRaw(&value, sizeof(T));
}
template<typename T>
inline StreamError readSpan(T& values)
{
auto asSpan = std::span(values);
return readRaw(asSpan.data(), asSpan.size_bytes());
}
template<typename TItBegin, typename TItEnd>
inline StreamError readSpan(TItBegin&& begin, TItEnd&& end)
{
auto asSpan = std::span(std::forward<TItBegin>(begin), std::forward<TItEnd>(end));
return readRaw(asSpan.data(), asSpan.size_bytes());
}
template<typename T>
inline StreamError write(const T& value)
{
return writeRaw(&value, sizeof(T));
}
template<typename T>
inline StreamError writeSpan(const T& values)
{
auto asSpan = std::span(values);
return writeRaw(asSpan.data(), asSpan.size_bytes());
}
template<typename TItBegin, typename TItEnd>
inline StreamError writeSpan(TItBegin&& begin, TItEnd&& end)
{
return writeSpan(std::span(std::forward<TItBegin>(begin), std::forward<TItEnd>(end)));
}
StreamError readString(std::string& outString);
StreamError writeString(std::string_view str);
};
class FileStream : public Stream
{
private:
std::FILE* handle = nullptr; // TODO: wrap in gsl::owner<>
FileOpenMode mode;
std::size_t length = 0;
public:
~FileStream() override;
StreamError open(const char* path, FileOpenMode mode_);
inline StreamError open(const std::string& path, FileOpenMode mode_) {
return open(path.c_str(), mode_);
}
void close();
[[nodiscard]] inline bool isOpen() const { return handle != nullptr; }
// Stream overrides
StreamError readRaw(std::span<std::uint8_t> buffer, bool partial = false, std::size_t* outBytesRead = nullptr) override;
StreamError writeRaw(std::span<const std::uint8_t> buffer) override;
std::size_t tell() override;
StreamError seek(std::intptr_t pos, SeekMode seekMode = SeekMode::ABSOLUTE) override;
void flush() override;
bool isAtEnd() override;
StreamFeatures getFeatures() override;
};
class MemoryStream : public Stream
{
private:
std::span<std::uint8_t> data_;
std::size_t pos_ = 0;
bool canWrite_ = false;
public:
void openRW(std::span<std::uint8_t> data);
void openRO(std::span<const std::uint8_t> data);
void close();
[[nodiscard]] inline bool isOpen() const { return data_.data() != nullptr; }
[[nodiscard]] inline std::size_t availableBytes() const {
assert(isOpen());
return data_.size() - pos_;
}
// Stream overrides
StreamError readRaw(std::span<std::uint8_t> buffer, bool partial = false, std::size_t* outBytesRead = nullptr) override;
StreamError writeRaw(std::span<const std::uint8_t> buffer) override;
std::size_t tell() override;
StreamError seek(std::intptr_t pos, SeekMode seekMode = SeekMode::ABSOLUTE) override;
bool isAtEnd() override;
StreamFeatures getFeatures() override;
};
//
// public functions
//
} // namespace mijin
#endif // !defined(MIJIN_IO_STREAM_HPP_INCLUDED)