Initial community commit

This commit is contained in:
Jef 2024-09-24 14:54:57 +02:00
parent 537bcbc862
commit fc06254474
16440 changed files with 4239995 additions and 2 deletions

View file

@ -0,0 +1,122 @@
/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
#ifndef MPT_IO_BASE_HPP
#define MPT_IO_BASE_HPP
#include "mpt/base/integer.hpp"
#include "mpt/base/namespace.hpp"
#include <cstddef>
namespace mpt {
inline namespace MPT_INLINE_NS {
namespace IO {
using Offset = int64;
inline constexpr std::size_t BUFFERSIZE_MINUSCULE = 1 * 256; // on stack usage, tuned for single word/line buffers
inline constexpr std::size_t BUFFERSIZE_TINY = 1 * 1024; // on stack usage
inline constexpr std::size_t BUFFERSIZE_SMALL = 4 * 1024; // on heap
inline constexpr std::size_t BUFFERSIZE_NORMAL = 64 * 1024; // FILE I/O
inline constexpr std::size_t BUFFERSIZE_LARGE = 1024 * 1024;
template <typename Tfile, typename Enable = void>
struct FileOperations {
};
template <typename Tfile>
inline FileOperations<Tfile> FileOps(Tfile & f) {
;
return FileOperations<Tfile>{f};
}
template <typename Tfile>
inline bool IsValid(Tfile & f) {
return FileOps(f).IsValid();
}
template <typename Tfile>
inline bool IsReadSeekable(Tfile & f) {
return FileOps(f).IsReadSeekable();
}
template <typename Tfile>
inline bool IsWriteSeekable(Tfile & f) {
return FileOps(f).IsWriteSeekable();
}
template <typename Tfile>
inline IO::Offset TellRead(Tfile & f) {
return FileOps(f).TellRead();
}
template <typename Tfile>
inline IO::Offset TellWrite(Tfile & f) {
return FileOps(f).TellWrite();
}
template <typename Tfile>
inline bool SeekBegin(Tfile & f) {
return FileOps(f).SeekBegin();
}
template <typename Tfile>
inline bool SeekEnd(Tfile & f) {
return FileOps(f).SeekEnd();
}
template <typename Tfile>
inline bool SeekAbsolute(Tfile & f, IO::Offset pos) {
return FileOps(f).SeekAbsolute(pos);
}
template <typename Tfile>
inline bool SeekRelative(Tfile & f, IO::Offset off) {
return FileOps(f).SeekRelative(off);
}
template <typename Tfile>
inline mpt::byte_span ReadRawImpl(Tfile & f, mpt::byte_span data) {
return FileOps(f).ReadRawImpl(data);
}
template <typename Tfile>
inline bool WriteRawImpl(Tfile & f, mpt::const_byte_span data) {
return FileOps(f).WriteRawImpl(data);
}
template <typename Tfile>
inline bool IsEof(Tfile & f) {
return FileOps(f).IsEof();
}
template <typename Tfile>
inline bool Flush(Tfile & f) {
return FileOps(f).Flush();
}
} // namespace IO
} // namespace MPT_INLINE_NS
} // namespace mpt
#endif // MPT_IO_BASE_HPP

View file

@ -0,0 +1,367 @@
/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
#ifndef MPT_IO_IO_HPP
#define MPT_IO_IO_HPP
#include "mpt/base/array.hpp"
#include "mpt/base/bit.hpp"
#include "mpt/base/integer.hpp"
#include "mpt/base/memory.hpp"
#include "mpt/base/namespace.hpp"
#include "mpt/base/span.hpp"
#include "mpt/endian/integer.hpp"
#include "mpt/io/base.hpp"
#include <algorithm>
#include <limits>
#include <string>
#include <vector>
#include <cassert>
#include <cstddef>
namespace mpt {
inline namespace MPT_INLINE_NS {
namespace IO {
template <typename Tbyte, typename Tfile>
inline mpt::byte_span ReadRaw(Tfile & f, Tbyte * data, std::size_t size) {
return mpt::IO::ReadRawImpl(f, mpt::as_span(mpt::byte_cast<std::byte *>(data), size));
}
template <typename Tbyte, typename Tfile>
inline mpt::byte_span ReadRaw(Tfile & f, mpt::span<Tbyte> data) {
return mpt::IO::ReadRawImpl(f, mpt::byte_cast<mpt::byte_span>(data));
}
template <typename Tbyte, typename Tfile>
inline bool WriteRaw(Tfile & f, const Tbyte * data, std::size_t size) {
return mpt::IO::WriteRawImpl(f, mpt::as_span(mpt::byte_cast<const std::byte *>(data), size));
}
template <typename Tbyte, typename Tfile>
inline bool WriteRaw(Tfile & f, mpt::span<Tbyte> data) {
return mpt::IO::WriteRawImpl(f, mpt::byte_cast<mpt::const_byte_span>(data));
}
template <typename Tbinary, typename Tfile>
inline bool Read(Tfile & f, Tbinary & v) {
return mpt::IO::ReadRaw(f, mpt::as_raw_memory(v)).size() == mpt::as_raw_memory(v).size();
}
template <typename Tbinary, typename Tfile>
inline bool Write(Tfile & f, const Tbinary & v) {
return mpt::IO::WriteRaw(f, mpt::as_raw_memory(v));
}
template <typename Tbinary, typename Tfile>
inline bool Write(Tfile & f, const std::vector<Tbinary> & v) {
static_assert(mpt::is_binary_safe<Tbinary>::value);
return mpt::IO::WriteRaw(f, mpt::as_raw_memory(v));
}
template <typename T, typename Tfile>
inline bool WritePartial(Tfile & f, const T & v, std::size_t size = sizeof(T)) {
assert(size <= sizeof(T));
return mpt::IO::WriteRaw(f, mpt::as_span(mpt::as_raw_memory(v).data(), size));
}
template <typename Tfile>
inline bool ReadByte(Tfile & f, std::byte & v) {
bool result = false;
std::byte byte = mpt::as_byte(0);
const std::size_t readResult = mpt::IO::ReadRaw(f, &byte, sizeof(std::byte)).size();
result = (readResult == sizeof(std::byte));
v = byte;
return result;
}
template <typename T, typename Tfile>
inline bool ReadBinaryTruncatedLE(Tfile & f, T & v, std::size_t size) {
bool result = false;
static_assert(std::numeric_limits<T>::is_integer);
std::array<uint8, sizeof(T)> bytes = mpt::init_array<uint8, sizeof(T)>(uint8{0});
const std::size_t readResult = mpt::IO::ReadRaw(f, bytes.data(), std::min(size, sizeof(T))).size();
result = (readResult == std::min(size, sizeof(T)));
v = mpt::bit_cast<typename mpt::make_le<T>::type>(bytes);
return result;
}
template <typename T, typename Tfile>
inline bool ReadIntLE(Tfile & f, T & v) {
bool result = false;
static_assert(std::numeric_limits<T>::is_integer);
std::array<uint8, sizeof(T)> bytes = mpt::init_array<uint8, sizeof(T)>(uint8{0});
const std::size_t readResult = mpt::IO::ReadRaw(f, mpt::as_span(bytes)).size();
result = (readResult == sizeof(T));
v = mpt::bit_cast<typename mpt::make_le<T>::type>(bytes);
return result;
}
template <typename T, typename Tfile>
inline bool ReadIntBE(Tfile & f, T & v) {
bool result = false;
static_assert(std::numeric_limits<T>::is_integer);
std::array<uint8, sizeof(T)> bytes = mpt::init_array<uint8, sizeof(T)>(uint8{0});
const std::size_t readResult = mpt::IO::ReadRaw(f, mpt::as_span(bytes)).size();
result = (readResult == sizeof(T));
v = mpt::bit_cast<typename mpt::make_be<T>::type>(bytes);
return result;
}
template <typename Tfile>
inline bool ReadAdaptiveInt16LE(Tfile & f, uint16 & v) {
bool result = true;
uint8 byte = 0;
std::size_t additionalBytes = 0;
v = 0;
byte = 0;
if (!mpt::IO::ReadIntLE<uint8>(f, byte)) {
result = false;
}
additionalBytes = (byte & 0x01);
v = byte >> 1;
for (std::size_t i = 0; i < additionalBytes; ++i) {
byte = 0;
if (!mpt::IO::ReadIntLE<uint8>(f, byte)) {
result = false;
}
v |= (static_cast<uint16>(byte) << (((i + 1) * 8) - 1));
}
return result;
}
template <typename Tfile>
inline bool ReadAdaptiveInt32LE(Tfile & f, uint32 & v) {
bool result = true;
uint8 byte = 0;
std::size_t additionalBytes = 0;
v = 0;
byte = 0;
if (!mpt::IO::ReadIntLE<uint8>(f, byte)) {
result = false;
}
additionalBytes = (byte & 0x03);
v = byte >> 2;
for (std::size_t i = 0; i < additionalBytes; ++i) {
byte = 0;
if (!mpt::IO::ReadIntLE<uint8>(f, byte)) {
result = false;
}
v |= (static_cast<uint32>(byte) << (((i + 1) * 8) - 2));
}
return result;
}
template <typename Tfile>
inline bool ReadAdaptiveInt64LE(Tfile & f, uint64 & v) {
bool result = true;
uint8 byte = 0;
std::size_t additionalBytes = 0;
v = 0;
byte = 0;
if (!mpt::IO::ReadIntLE<uint8>(f, byte)) {
result = false;
}
additionalBytes = (1 << (byte & 0x03)) - 1;
v = byte >> 2;
for (std::size_t i = 0; i < additionalBytes; ++i) {
byte = 0;
if (!mpt::IO::ReadIntLE<uint8>(f, byte)) {
result = false;
}
v |= (static_cast<uint64>(byte) << (((i + 1) * 8) - 2));
}
return result;
}
template <typename Tsize, typename Tfile>
inline bool ReadSizedStringLE(Tfile & f, std::string & str, Tsize maxSize = std::numeric_limits<Tsize>::max()) {
static_assert(std::numeric_limits<Tsize>::is_integer);
str.clear();
Tsize size = 0;
if (!mpt::IO::ReadIntLE(f, size)) {
return false;
}
if (size > maxSize) {
return false;
}
for (Tsize i = 0; i != size; ++i) {
char c = '\0';
if (!mpt::IO::ReadIntLE(f, c)) {
return false;
}
str.push_back(c);
}
return true;
}
template <typename T, typename Tfile>
inline bool WriteIntLE(Tfile & f, const T v) {
static_assert(std::numeric_limits<T>::is_integer);
return mpt::IO::Write(f, mpt::as_le(v));
}
template <typename T, typename Tfile>
inline bool WriteIntBE(Tfile & f, const T v) {
static_assert(std::numeric_limits<T>::is_integer);
return mpt::IO::Write(f, mpt::as_be(v));
}
template <typename Tfile>
inline bool WriteAdaptiveInt16LE(Tfile & f, const uint16 v, std::size_t fixedSize = 0) {
std::size_t minSize = fixedSize;
std::size_t maxSize = fixedSize;
assert(minSize == 0 || minSize == 1 || minSize == 2);
assert(maxSize == 0 || maxSize == 1 || maxSize == 2);
assert(maxSize == 0 || maxSize >= minSize);
if (maxSize == 0) {
maxSize = 2;
}
if (v < 0x80 && minSize <= 1 && 1 <= maxSize) {
return mpt::IO::WriteIntLE<uint8>(f, static_cast<uint8>(v << 1) | 0x00);
} else if (v < 0x8000 && minSize <= 2 && 2 <= maxSize) {
return mpt::IO::WriteIntLE<uint16>(f, static_cast<uint16>(v << 1) | 0x01);
} else {
assert(false);
return false;
}
}
template <typename Tfile>
inline bool WriteAdaptiveInt32LE(Tfile & f, const uint32 v, std::size_t fixedSize = 0) {
std::size_t minSize = fixedSize;
std::size_t maxSize = fixedSize;
assert(minSize == 0 || minSize == 1 || minSize == 2 || minSize == 3 || minSize == 4);
assert(maxSize == 0 || maxSize == 1 || maxSize == 2 || maxSize == 3 || maxSize == 4);
assert(maxSize == 0 || maxSize >= minSize);
if (maxSize == 0) {
maxSize = 4;
}
if (v < 0x40 && minSize <= 1 && 1 <= maxSize) {
return mpt::IO::WriteIntLE<uint8>(f, static_cast<uint8>(v << 2) | 0x00);
} else if (v < 0x4000 && minSize <= 2 && 2 <= maxSize) {
return mpt::IO::WriteIntLE<uint16>(f, static_cast<uint16>(v << 2) | 0x01);
} else if (v < 0x400000 && minSize <= 3 && 3 <= maxSize) {
uint32 value = static_cast<uint32>(v << 2) | 0x02;
std::byte bytes[3];
bytes[0] = static_cast<std::byte>(value >> 0);
bytes[1] = static_cast<std::byte>(value >> 8);
bytes[2] = static_cast<std::byte>(value >> 16);
return mpt::IO::WriteRaw(f, bytes, 3);
} else if (v < 0x40000000 && minSize <= 4 && 4 <= maxSize) {
return mpt::IO::WriteIntLE<uint32>(f, static_cast<uint32>(v << 2) | 0x03);
} else {
assert(false);
return false;
}
}
template <typename Tfile>
inline bool WriteAdaptiveInt64LE(Tfile & f, const uint64 v, std::size_t fixedSize = 0) {
std::size_t minSize = fixedSize;
std::size_t maxSize = fixedSize;
assert(minSize == 0 || minSize == 1 || minSize == 2 || minSize == 4 || minSize == 8);
assert(maxSize == 0 || maxSize == 1 || maxSize == 2 || maxSize == 4 || maxSize == 8);
assert(maxSize == 0 || maxSize >= minSize);
if (maxSize == 0) {
maxSize = 8;
}
if (v < 0x40 && minSize <= 1 && 1 <= maxSize) {
return mpt::IO::WriteIntLE<uint8>(f, static_cast<uint8>(v << 2) | 0x00);
} else if (v < 0x4000 && minSize <= 2 && 2 <= maxSize) {
return mpt::IO::WriteIntLE<uint16>(f, static_cast<uint16>(v << 2) | 0x01);
} else if (v < 0x40000000 && minSize <= 4 && 4 <= maxSize) {
return mpt::IO::WriteIntLE<uint32>(f, static_cast<uint32>(v << 2) | 0x02);
} else if (v < 0x4000000000000000ull && minSize <= 8 && 8 <= maxSize) {
return mpt::IO::WriteIntLE<uint64>(f, static_cast<uint64>(v << 2) | 0x03);
} else {
assert(false);
return false;
}
}
// Write a variable-length integer, as found in MIDI files. The number of written bytes is placed in the bytesWritten parameter.
template <typename Tfile, typename T>
bool WriteVarInt(Tfile & f, const T v, std::size_t * bytesWritten = nullptr) {
static_assert(std::numeric_limits<T>::is_integer);
static_assert(!std::numeric_limits<T>::is_signed);
std::byte out[(sizeof(T) * 8 + 6) / 7];
std::size_t numBytes = 0;
for (uint32 n = (sizeof(T) * 8) / 7; n > 0; n--) {
if (v >= (static_cast<T>(1) << (n * 7u))) {
out[numBytes++] = static_cast<std::byte>(((v >> (n * 7u)) & 0x7F) | 0x80);
}
}
out[numBytes++] = static_cast<std::byte>(v & 0x7F);
assert(numBytes <= std::size(out));
if (bytesWritten != nullptr) {
*bytesWritten = numBytes;
}
return mpt::IO::WriteRaw(f, out, numBytes);
}
template <typename Tsize, typename Tfile>
inline bool WriteSizedStringLE(Tfile & f, const std::string & str) {
static_assert(std::numeric_limits<Tsize>::is_integer);
if (str.size() > std::numeric_limits<Tsize>::max()) {
return false;
}
Tsize size = static_cast<Tsize>(str.size());
if (!mpt::IO::WriteIntLE(f, size)) {
return false;
}
if (!mpt::IO::WriteRaw(f, str.data(), str.size())) {
return false;
}
return true;
}
template <typename Tfile>
inline bool WriteText(Tfile & f, const std::string & s) {
return mpt::IO::WriteRaw(f, s.data(), s.size());
}
template <typename Tfile>
inline bool WriteTextCRLF(Tfile & f) {
return mpt::IO::WriteText(f, "\r\n");
}
template <typename Tfile>
inline bool WriteTextLF(Tfile & f) {
return mpt::IO::WriteText(f, "\n");
}
template <typename Tfile>
inline bool WriteTextCRLF(Tfile & f, const std::string & s) {
return mpt::IO::WriteText(f, s) && mpt::IO::WriteTextCRLF(f);
}
template <typename Tfile>
inline bool WriteTextLF(Tfile & f, const std::string & s) {
return mpt::IO::WriteText(f, s) && mpt::IO::WriteTextLF(f);
}
} // namespace IO
} // namespace MPT_INLINE_NS
} // namespace mpt
#endif // MPT_IO_IO_HPP

View file

@ -0,0 +1,146 @@
/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
#ifndef MPT_IO_IO_SPAN_HPP
#define MPT_IO_IO_SPAN_HPP
#include "mpt/base/macros.hpp"
#include "mpt/base/memory.hpp"
#include "mpt/base/namespace.hpp"
#include "mpt/base/saturate_cast.hpp"
#include "mpt/base/span.hpp"
#include "mpt/io/base.hpp"
#include <algorithm>
#include <utility>
#include <cstddef>
namespace mpt {
inline namespace MPT_INLINE_NS {
namespace IO {
template <typename Tbyte>
struct FileOperations<std::pair<mpt::span<Tbyte>, IO::Offset>> {
private:
std::pair<mpt::span<Tbyte>, IO::Offset> & f;
public:
FileOperations(std::pair<mpt::span<Tbyte>, IO::Offset> & f_)
: f(f_) {
return;
}
public:
inline bool IsValid() {
return (f.second >= 0);
}
inline bool IsReadSeekable() {
MPT_UNUSED(f);
return true;
}
inline bool IsWriteSeekable() {
MPT_UNUSED(f);
return true;
}
inline IO::Offset TellRead() {
return f.second;
}
inline IO::Offset TellWrite() {
return f.second;
}
inline bool SeekBegin() {
f.second = 0;
return true;
}
inline bool SeekEnd() {
f.second = f.first.size();
return true;
}
inline bool SeekAbsolute(IO::Offset pos) {
f.second = pos;
return true;
}
inline bool SeekRelative(IO::Offset off) {
if (f.second < 0)
{
return false;
}
f.second += off;
return true;
}
inline mpt::byte_span ReadRawImpl(mpt::byte_span data) {
if (f.second < 0)
{
return data.first(0);
}
if (f.second >= static_cast<IO::Offset>(f.first.size()))
{
return data.first(0);
}
std::size_t num = mpt::saturate_cast<std::size_t>(std::min(static_cast<IO::Offset>(f.first.size()) - f.second, static_cast<IO::Offset>(data.size())));
std::copy(mpt::byte_cast<const std::byte *>(f.first.data() + f.second), mpt::byte_cast<const std::byte *>(f.first.data() + f.second + num), data.data());
f.second += num;
return data.first(num);
}
inline bool WriteRawImpl(mpt::const_byte_span data) {
if (f.second < 0)
{
return false;
}
if (f.second > static_cast<IO::Offset>(f.first.size()))
{
return false;
}
std::size_t num = mpt::saturate_cast<std::size_t>(std::min(static_cast<IO::Offset>(f.first.size()) - f.second, static_cast<IO::Offset>(data.size())));
if (num != data.size())
{
return false;
}
std::copy(data.data(), data.data() + num, mpt::byte_cast<std::byte *>(f.first.data() + f.second));
f.second += num;
return true;
}
inline bool IsEof() {
return (f.second >= static_cast<IO::Offset>(f.first.size()));
}
inline bool Flush() {
MPT_UNUSED(f);
return true;
}
};
} // namespace IO
} // namespace MPT_INLINE_NS
} // namespace mpt
#endif // MPT_IO_IO_SPAN_HPP

View file

@ -0,0 +1,332 @@
/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
#ifndef MPT_IO_IO_STDSTREAM_HPP
#define MPT_IO_IO_STDSTREAM_HPP
#include "mpt/base/macros.hpp"
#include "mpt/base/memory.hpp"
#include "mpt/base/namespace.hpp"
#include "mpt/base/utility.hpp"
#include "mpt/io/base.hpp"
#include <ios>
#include <istream>
#include <ostream>
#include <type_traits>
namespace mpt {
inline namespace MPT_INLINE_NS {
namespace IO {
//static_assert(sizeof(std::streamoff) == 8); // Assert 64bit file support.
struct FileOperationsStdIos {
private:
std::ios & f;
public:
FileOperationsStdIos(std::ios & f_)
: f(f_) {
return;
}
public:
inline bool IsValid() {
return !f.fail();
}
};
struct FileOperationsStdIstream
: public FileOperationsStdIos {
private:
std::istream & f;
public:
FileOperationsStdIstream(std::istream & f_)
: FileOperationsStdIos(f_)
, f(f_) {
return;
}
public:
inline bool IsReadSeekable() {
f.clear();
std::streampos oldpos = f.tellg();
if (f.fail() || oldpos == std::streampos(-1))
{
f.clear();
return false;
}
f.seekg(0, std::ios::beg);
if (f.fail())
{
f.clear();
f.seekg(oldpos);
f.clear();
return false;
}
f.seekg(0, std::ios::end);
if (f.fail())
{
f.clear();
f.seekg(oldpos);
f.clear();
return false;
}
std::streampos length = f.tellg();
if (f.fail() || length == std::streampos(-1))
{
f.clear();
f.seekg(oldpos);
f.clear();
return false;
}
f.seekg(oldpos);
f.clear();
return true;
}
inline IO::Offset TellRead() {
return f.tellg();
}
inline bool SeekBegin() {
f.seekg(0);
return !f.fail();
}
inline bool SeekEnd() {
f.seekg(0, std::ios::end);
return !f.fail();
}
inline bool SeekAbsolute(IO::Offset pos) {
if (!mpt::in_range<std::streamoff>(pos))
{
return false;
}
f.seekg(static_cast<std::streamoff>(pos), std::ios::beg);
return !f.fail();
}
inline bool SeekRelative(IO::Offset off) {
if (!mpt::in_range<std::streamoff>(off))
{
return false;
}
f.seekg(static_cast<std::streamoff>(off), std::ios::cur);
return !f.fail();
}
inline mpt::byte_span ReadRawImpl(mpt::byte_span data) {
f.read(mpt::byte_cast<char *>(data.data()), data.size());
return data.first(mpt::saturate_cast<std::size_t>(f.gcount()));
}
inline bool IsEof() {
return f.eof();
}
};
struct FileOperationsStdOstream
: public FileOperationsStdIos {
private:
std::ostream & f;
public:
FileOperationsStdOstream(std::ostream & f_)
: FileOperationsStdIos(f_)
, f(f_) {
return;
}
public:
inline bool IsWriteSeekable() {
f.clear();
std::streampos oldpos = f.tellp();
if (f.fail() || oldpos == std::streampos(-1))
{
f.clear();
return false;
}
f.seekp(0, std::ios::beg);
if (f.fail())
{
f.clear();
f.seekp(oldpos);
f.clear();
return false;
}
f.seekp(0, std::ios::end);
if (f.fail())
{
f.clear();
f.seekp(oldpos);
f.clear();
return false;
}
std::streampos length = f.tellp();
if (f.fail() || length == std::streampos(-1))
{
f.clear();
f.seekp(oldpos);
f.clear();
return false;
}
f.seekp(oldpos);
f.clear();
return true;
}
inline IO::Offset TellWrite() {
return f.tellp();
}
inline bool SeekBegin() {
f.seekp(0);
return !f.fail();
}
inline bool SeekEnd() {
f.seekp(0, std::ios::end);
return !f.fail();
}
inline bool SeekAbsolute(IO::Offset pos) {
if (!mpt::in_range<std::streamoff>(pos))
{
return false;
}
f.seekp(static_cast<std::streamoff>(pos), std::ios::beg);
return !f.fail();
}
inline bool SeekRelative(IO::Offset off) {
if (!mpt::in_range<std::streamoff>(off))
{
return false;
}
f.seekp(static_cast<std::streamoff>(off), std::ios::cur);
return !f.fail();
}
inline bool WriteRawImpl(mpt::const_byte_span data) {
f.write(mpt::byte_cast<const char *>(data.data()), data.size());
return !f.fail();
}
inline bool Flush() {
f.flush();
return !f.fail();
}
};
struct FileOperationsStdIOstream
: public FileOperationsStdIstream
, public FileOperationsStdOstream {
private:
std::iostream & f;
public:
FileOperationsStdIOstream(std::iostream & f_)
: FileOperationsStdIstream(f_)
, FileOperationsStdOstream(f_)
, f(f_) {
return;
}
public:
inline bool SeekBegin() {
FileOperationsStdIstream::SeekBegin();
FileOperationsStdOstream::SeekBegin();
return !f.fail();
}
inline bool SeekEnd() {
FileOperationsStdIstream::SeekEnd();
FileOperationsStdOstream::SeekEnd();
return !f.fail();
}
inline bool SeekAbsolute(IO::Offset pos) {
if (!mpt::in_range<std::streamoff>(pos))
{
return false;
}
FileOperationsStdIstream::SeekAbsolute(pos);
FileOperationsStdOstream::SeekAbsolute(pos);
return !f.fail();
}
inline bool SeekRelative(IO::Offset off) {
if (!mpt::in_range<std::streamoff>(off))
{
return false;
}
FileOperationsStdIstream::SeekRelative(off);
FileOperationsStdOstream::SeekRelative(off);
return !f.fail();
}
};
template <typename Tstream>
struct FileOperations<Tstream, typename std::enable_if_t<std::is_base_of<std::iostream, Tstream>::value>>
: public FileOperationsStdIOstream {
public:
FileOperations(Tstream & f)
: FileOperationsStdIOstream(f) {
return;
}
};
template <typename Tstream>
struct FileOperations<Tstream, typename std::enable_if_t<std::is_base_of<std::istream, Tstream>::value && !std::is_base_of<std::iostream, Tstream>::value>>
: public FileOperationsStdIstream {
public:
FileOperations(Tstream & f)
: FileOperationsStdIstream(f) {
return;
}
};
template <typename Tstream>
struct FileOperations<Tstream, typename std::enable_if_t<std::is_base_of<std::ostream, Tstream>::value && !std::is_base_of<std::iostream, Tstream>::value>>
: public FileOperationsStdOstream {
public:
FileOperations(Tstream & f)
: FileOperationsStdOstream(f) {
return;
}
};
} // namespace IO
} // namespace MPT_INLINE_NS
} // namespace mpt
#endif // MPT_IO_IO_STDSTREAM_HPP

View file

@ -0,0 +1,405 @@
/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
#ifndef MPT_IO_IO_VIRTUAL_WRAPPER_HPP
#define MPT_IO_IO_VIRTUAL_WRAPPER_HPP
#include "mpt/base/memory.hpp"
#include "mpt/base/namespace.hpp"
#include "mpt/io/base.hpp"
#include <type_traits>
namespace mpt {
inline namespace MPT_INLINE_NS {
namespace IO {
class IFileBase {
protected:
IFileBase() = default;
virtual ~IFileBase() = default;
public:
virtual bool IsValid() = 0;
virtual bool IsReadSeekable() = 0;
virtual IO::Offset TellRead() = 0;
virtual bool SeekBegin() = 0;
virtual bool SeekEnd() = 0;
virtual bool SeekAbsolute(IO::Offset pos) = 0;
virtual bool SeekRelative(IO::Offset off) = 0;
virtual mpt::byte_span ReadRawImpl(mpt::byte_span data) = 0;
virtual bool IsEof() = 0;
};
template <typename Tfile>
class IFile
: public IFileBase {
private:
Tfile & f;
public:
IFile(Tfile & f_)
: f(f_) {
}
~IFile() override = default;
public:
bool IsValid() override {
return mpt::IO::IsValid(f);
}
bool IsReadSeekable() override {
return mpt::IO::IsReadSeekable(f);
}
IO::Offset TellRead() override {
return mpt::IO::TellRead(f);
}
bool SeekBegin() override {
return mpt::IO::SeekBegin(f);
}
bool SeekEnd() override {
return mpt::IO::SeekEnd(f);
}
bool SeekAbsolute(IO::Offset pos) override {
return mpt::IO::SeekAbsolute(f, pos);
}
bool SeekRelative(IO::Offset off) override {
return mpt::IO::SeekRelative(f, off);
}
mpt::byte_span ReadRawImpl(mpt::byte_span data) override {
return mpt::IO::ReadRawImpl(f, data);
}
bool IsEof() override {
return mpt::IO::IsEof(f);
}
};
class OFileBase {
protected:
OFileBase() = default;
virtual ~OFileBase() = default;
public:
virtual bool IsValid() = 0;
virtual bool IsWriteSeekable() = 0;
virtual IO::Offset TellWrite() = 0;
virtual bool SeekBegin() = 0;
virtual bool SeekEnd() = 0;
virtual bool SeekAbsolute(IO::Offset pos) = 0;
virtual bool SeekRelative(IO::Offset off) = 0;
virtual bool WriteRawImpl(mpt::const_byte_span data) = 0;
virtual bool Flush() = 0;
};
template <typename Tfile>
class OFile
: public OFileBase {
private:
Tfile & f;
public:
OFile(Tfile & f_)
: f(f_) {
}
~OFile() override = default;
public:
bool IsValid() override {
return mpt::IO::IsValid(f);
}
bool IsWriteSeekable() override {
return mpt::IO::IsWriteSeekable(f);
}
IO::Offset TellWrite() override {
return mpt::IO::TellWrite(f);
}
bool SeekBegin() override {
return mpt::IO::SeekBegin(f);
}
bool SeekEnd() override {
return mpt::IO::SeekEnd(f);
}
bool SeekAbsolute(IO::Offset pos) override {
return mpt::IO::SeekAbsolute(f, pos);
}
bool SeekRelative(IO::Offset off) override {
return mpt::IO::SeekRelative(f, off);
}
bool WriteRawImpl(mpt::const_byte_span data) override {
return mpt::IO::WriteRawImpl(f, data);
}
bool Flush() override {
return mpt::IO::Flush(f);
}
};
class IOFileBase {
protected:
IOFileBase() = default;
virtual ~IOFileBase() = default;
public:
virtual bool IsValid() = 0;
virtual bool IsReadSeekable() = 0;
virtual bool IsWriteSeekable() = 0;
virtual IO::Offset TellRead() = 0;
virtual IO::Offset TellWrite() = 0;
virtual bool SeekBegin() = 0;
virtual bool SeekEnd() = 0;
virtual bool SeekAbsolute(IO::Offset pos) = 0;
virtual bool SeekRelative(IO::Offset off) = 0;
virtual mpt::byte_span ReadRawImpl(mpt::byte_span data) = 0;
virtual bool WriteRawImpl(mpt::const_byte_span data) = 0;
virtual bool IsEof() = 0;
virtual bool Flush() = 0;
};
template <typename Tfile>
class IOFile
: public IOFileBase {
private:
Tfile & f;
public:
IOFile(Tfile & f_)
: f(f_) {
}
~IOFile() override = default;
public:
bool IsValid() override {
return mpt::IO::IsValid(f);
}
bool IsReadSeekable() override {
return mpt::IO::IsReadSeekable(f);
}
bool IsWriteSeekable() override {
return mpt::IO::IsWriteSeekable(f);
}
IO::Offset TellRead() override {
return mpt::IO::TellRead(f);
}
IO::Offset TellWrite() override {
return mpt::IO::TellWrite(f);
}
bool SeekBegin() override {
return mpt::IO::SeekBegin(f);
}
bool SeekEnd() override {
return mpt::IO::SeekEnd(f);
}
bool SeekAbsolute(IO::Offset pos) override {
return mpt::IO::SeekAbsolute(f, pos);
}
bool SeekRelative(IO::Offset off) override {
return mpt::IO::SeekRelative(f, off);
}
mpt::byte_span ReadRawImpl(mpt::byte_span data) override {
return mpt::IO::ReadRawImpl(f, data);
}
bool WriteRawImpl(mpt::const_byte_span data) override {
return mpt::IO::WriteRawImpl(f, data);
}
bool IsEof() override {
return mpt::IO::IsEof(f);
}
bool Flush() override {
return mpt::IO::Flush(f);
}
};
template <typename Tfile>
struct FileOperations<Tfile, typename std::enable_if_t<std::is_base_of<IFileBase, Tfile>::value>> {
private:
IFileBase & f;
public:
FileOperations(IFileBase & f_)
: f(f_) {
return;
}
public:
inline bool IsValid() {
return f.IsValid();
}
inline bool IsReadSeekable() {
return f.IsReadSeekable();
}
inline IO::Offset TellRead() {
return f.TellRead();
}
inline bool SeekBegin() {
return f.SeekBegin();
}
inline bool SeekEnd() {
return f.SeekEnd();
}
inline bool SeekAbsolute(IO::Offset pos) {
return f.SeekAbsolute(pos);
}
inline bool SeekRelative(IO::Offset off) {
return f.SeekRelative(off);
}
inline mpt::byte_span ReadRawImpl(mpt::byte_span data) {
return f.ReadRawImpl(data);
}
inline bool IsEof() {
return f.IsEof();
}
};
template <typename Tfile>
struct FileOperations<Tfile, typename std::enable_if_t<std::is_base_of<OFileBase, Tfile>::value>> {
private:
OFileBase & f;
public:
FileOperations(OFileBase & f_)
: f(f_) {
return;
}
public:
inline bool IsValid() {
return f.IsValid();
}
inline bool IsWriteSeekable() {
return f.IsWriteSeekable();
}
inline IO::Offset TellWrite() {
return f.TellWrite();
}
inline bool SeekBegin() {
return f.SeekBegin();
}
inline bool SeekEnd() {
return f.SeekEnd();
}
inline bool SeekAbsolute(IO::Offset pos) {
return f.SeekAbsolute(pos);
}
inline bool SeekRelative(IO::Offset off) {
return f.SeekRelative(off);
}
inline bool WriteRawImpl(mpt::const_byte_span data) {
return f.WriteRawImpl(data);
}
inline bool Flush() {
return f.Flush();
}
};
template <typename Tfile>
struct FileOperations<Tfile, typename std::enable_if_t<std::is_base_of<IOFileBase, Tfile>::value>> {
private:
IOFileBase & f;
public:
FileOperations(IOFileBase & f_)
: f(f_) {
return;
}
public:
inline bool IsValid() {
return f.IsValid();
}
inline bool IsReadSeekable() {
return f.IsReadSeekable();
}
inline bool IsWriteSeekable() {
return f.IsWriteSeekable();
}
inline IO::Offset TellRead() {
return f.TellRead();
}
inline IO::Offset TellWrite() {
return f.TellWrite();
}
inline bool SeekBegin() {
return f.SeekBegin();
}
inline bool SeekEnd() {
return f.SeekEnd();
}
inline bool SeekAbsolute(IO::Offset pos) {
return f.SeekAbsolute(pos);
}
inline bool SeekRelative(IO::Offset off) {
return f.SeekRelative(off);
}
inline mpt::byte_span ReadRawImpl(mpt::byte_span data) {
return f.ReadRawImpl(data);
}
inline bool WriteRawImpl(mpt::const_byte_span data) {
return f.WriteRawImpl(data);
}
inline bool IsEof() {
return f.IsEof();
}
inline bool Flush() {
return f.Flush();
}
};
} // namespace IO
} // namespace MPT_INLINE_NS
} // namespace mpt
#endif // MPT_IO_IO_VIRTUAL_WRAPPER_HPP

View file

@ -0,0 +1,573 @@
/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */
#ifndef MPT_IO_TESTS_IO_HPP
#define MPT_IO_TESTS_IO_HPP
#include "mpt/base/integer.hpp"
#include "mpt/base/namespace.hpp"
#include "mpt/base/utility.hpp"
#include "mpt/endian/integer.hpp"
#include "mpt/io/base.hpp"
#include "mpt/io/io.hpp"
#include "mpt/io/io_stdstream.hpp"
#include "mpt/test/test.hpp"
#include "mpt/test/test_macros.hpp"
#include <ios>
#include <sstream>
#include <string>
#include <vector>
#include <cstddef>
namespace mpt {
inline namespace MPT_INLINE_NS {
namespace tests {
namespace io {
#if MPT_COMPILER_CLANG
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wglobal-constructors"
#endif
MPT_TEST_GROUP_INLINE("mpt/io")
#if MPT_COMPILER_CLANG
#pragma clang diagnostic pop
#endif
{
// check that empty stringstream behaves correctly with our MSVC workarounds when using iostream interface directly
{
std::ostringstream ss;
MPT_TEST_EXPECT_EQUAL(ss.tellp(), std::streampos(0));
}
{
std::ostringstream ss;
ss.seekp(0);
MPT_TEST_EXPECT_EQUAL(mpt::IO::SeekAbsolute(ss, 0), true);
}
{
std::ostringstream ss;
ss.seekp(0, std::ios_base::beg);
MPT_TEST_EXPECT_EQUAL(!ss.fail(), true);
}
{
std::ostringstream ss;
ss.seekp(0, std::ios_base::cur);
MPT_TEST_EXPECT_EQUAL(!ss.fail(), true);
}
{
std::istringstream ss;
MPT_TEST_EXPECT_EQUAL(ss.tellg(), std::streampos(0));
}
{
std::istringstream ss;
ss.seekg(0);
MPT_TEST_EXPECT_EQUAL(mpt::IO::SeekAbsolute(ss, 0), true);
}
{
std::istringstream ss;
ss.seekg(0, std::ios_base::beg);
MPT_TEST_EXPECT_EQUAL(!ss.fail(), true);
}
{
std::istringstream ss;
ss.seekg(0, std::ios_base::cur);
MPT_TEST_EXPECT_EQUAL(!ss.fail(), true);
}
{
std::ostringstream s;
char b = 23;
MPT_TEST_EXPECT_EQUAL(!s.fail(), true);
MPT_TEST_EXPECT_EQUAL(s.tellp(), std::streampos(0));
MPT_TEST_EXPECT_EQUAL(!s.fail(), true);
s.seekp(0, std::ios_base::beg);
MPT_TEST_EXPECT_EQUAL(!s.fail(), true);
MPT_TEST_EXPECT_EQUAL(s.tellp(), std::streampos(0));
MPT_TEST_EXPECT_EQUAL(!s.fail(), true);
s.write(&b, 1);
MPT_TEST_EXPECT_EQUAL(!s.fail(), true);
MPT_TEST_EXPECT_EQUAL(s.tellp(), std::streampos(1));
MPT_TEST_EXPECT_EQUAL(!s.fail(), true);
s.seekp(0, std::ios_base::beg);
MPT_TEST_EXPECT_EQUAL(!s.fail(), true);
MPT_TEST_EXPECT_EQUAL(s.tellp(), std::streampos(0));
MPT_TEST_EXPECT_EQUAL(!s.fail(), true);
s.seekp(0, std::ios_base::end);
MPT_TEST_EXPECT_EQUAL(!s.fail(), true);
MPT_TEST_EXPECT_EQUAL(s.tellp(), std::streampos(1));
MPT_TEST_EXPECT_EQUAL(!s.fail(), true);
MPT_TEST_EXPECT_EQUAL(s.str(), std::string(1, b));
}
{
std::istringstream s;
MPT_TEST_EXPECT_EQUAL(!s.fail(), true);
MPT_TEST_EXPECT_EQUAL(s.tellg(), std::streampos(0));
MPT_TEST_EXPECT_EQUAL(!s.fail(), true);
s.seekg(0, std::ios_base::beg);
MPT_TEST_EXPECT_EQUAL(!s.fail(), true);
MPT_TEST_EXPECT_EQUAL(s.tellg(), std::streampos(0));
MPT_TEST_EXPECT_EQUAL(!s.fail(), true);
s.seekg(0, std::ios_base::end);
MPT_TEST_EXPECT_EQUAL(!s.fail(), true);
MPT_TEST_EXPECT_EQUAL(s.tellg(), std::streampos(0));
MPT_TEST_EXPECT_EQUAL(!s.fail(), true);
}
{
std::istringstream s("a");
char a = 0;
MPT_TEST_EXPECT_EQUAL(!s.fail(), true);
MPT_TEST_EXPECT_EQUAL(s.tellg(), std::streampos(0));
MPT_TEST_EXPECT_EQUAL(!s.fail(), true);
s.seekg(0, std::ios_base::beg);
MPT_TEST_EXPECT_EQUAL(!s.fail(), true);
MPT_TEST_EXPECT_EQUAL(s.tellg(), std::streampos(0));
MPT_TEST_EXPECT_EQUAL(!s.fail(), true);
s.read(&a, 1);
MPT_TEST_EXPECT_EQUAL(a, 'a');
MPT_TEST_EXPECT_EQUAL(!s.fail(), true);
MPT_TEST_EXPECT_EQUAL(s.tellg(), std::streampos(1));
MPT_TEST_EXPECT_EQUAL(!s.fail(), true);
s.seekg(0, std::ios_base::beg);
MPT_TEST_EXPECT_EQUAL(!s.fail(), true);
MPT_TEST_EXPECT_EQUAL(s.tellg(), std::streampos(0));
MPT_TEST_EXPECT_EQUAL(!s.fail(), true);
s.seekg(0, std::ios_base::end);
MPT_TEST_EXPECT_EQUAL(!s.fail(), true);
MPT_TEST_EXPECT_EQUAL(s.tellg(), std::streampos(1));
MPT_TEST_EXPECT_EQUAL(!s.fail(), true);
MPT_TEST_EXPECT_EQUAL(std::string(1, a), std::string(1, 'a'));
}
// check that empty native and fixed stringstream both behaves correctly with out IO functions
{
std::ostringstream ss;
MPT_TEST_EXPECT_EQUAL(mpt::IO::TellWrite(ss), 0);
}
{
std::ostringstream ss;
MPT_TEST_EXPECT_EQUAL(mpt::IO::SeekBegin(ss), true);
}
{
std::ostringstream ss;
MPT_TEST_EXPECT_EQUAL(mpt::IO::SeekAbsolute(ss, 0), true);
}
{
std::ostringstream ss;
MPT_TEST_EXPECT_EQUAL(mpt::IO::SeekRelative(ss, 0), true);
}
{
std::istringstream ss;
MPT_TEST_EXPECT_EQUAL(mpt::IO::TellRead(ss), 0);
}
{
std::istringstream ss;
MPT_TEST_EXPECT_EQUAL(mpt::IO::SeekBegin(ss), true);
}
{
std::istringstream ss;
MPT_TEST_EXPECT_EQUAL(mpt::IO::SeekAbsolute(ss, 0), true);
}
{
std::istringstream ss;
MPT_TEST_EXPECT_EQUAL(mpt::IO::SeekRelative(ss, 0), true);
}
{
std::ostringstream ss;
MPT_TEST_EXPECT_EQUAL(mpt::IO::TellWrite(ss), 0);
}
{
std::ostringstream ss;
MPT_TEST_EXPECT_EQUAL(mpt::IO::SeekBegin(ss), true);
}
{
std::ostringstream ss;
MPT_TEST_EXPECT_EQUAL(mpt::IO::SeekAbsolute(ss, 0), true);
}
{
std::ostringstream ss;
MPT_TEST_EXPECT_EQUAL(mpt::IO::SeekRelative(ss, 0), true);
}
{
std::istringstream ss;
MPT_TEST_EXPECT_EQUAL(mpt::IO::TellRead(ss), 0);
}
{
std::istringstream ss;
MPT_TEST_EXPECT_EQUAL(mpt::IO::SeekBegin(ss), true);
}
{
std::istringstream ss;
MPT_TEST_EXPECT_EQUAL(mpt::IO::SeekAbsolute(ss, 0), true);
}
{
std::istringstream ss;
MPT_TEST_EXPECT_EQUAL(mpt::IO::SeekRelative(ss, 0), true);
}
{
std::ostringstream s;
char b = 23;
MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true);
MPT_TEST_EXPECT_EQUAL(mpt::IO::TellWrite(s), 0);
MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true);
MPT_TEST_EXPECT_EQUAL(mpt::IO::SeekBegin(s), true);
MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true);
MPT_TEST_EXPECT_EQUAL(mpt::IO::TellWrite(s), 0);
MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true);
MPT_TEST_EXPECT_EQUAL(mpt::IO::WriteRaw(s, &b, 1), true);
MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true);
MPT_TEST_EXPECT_EQUAL(mpt::IO::TellWrite(s), 1);
MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true);
MPT_TEST_EXPECT_EQUAL(mpt::IO::SeekBegin(s), true);
MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true);
MPT_TEST_EXPECT_EQUAL(mpt::IO::TellWrite(s), 0);
MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true);
MPT_TEST_EXPECT_EQUAL(mpt::IO::SeekEnd(s), true);
MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true);
MPT_TEST_EXPECT_EQUAL(mpt::IO::TellWrite(s), 1);
MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true);
MPT_TEST_EXPECT_EQUAL(s.str(), std::string(1, b));
}
{
std::istringstream s;
MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true);
MPT_TEST_EXPECT_EQUAL(mpt::IO::TellRead(s), 0);
MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true);
MPT_TEST_EXPECT_EQUAL(mpt::IO::SeekBegin(s), true);
MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true);
MPT_TEST_EXPECT_EQUAL(mpt::IO::TellRead(s), 0);
MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true);
MPT_TEST_EXPECT_EQUAL(mpt::IO::SeekEnd(s), true);
MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true);
MPT_TEST_EXPECT_EQUAL(mpt::IO::TellRead(s), 0);
MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true);
}
{
std::istringstream s("a");
char a = 0;
MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true);
MPT_TEST_EXPECT_EQUAL(mpt::IO::TellRead(s), 0);
MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true);
MPT_TEST_EXPECT_EQUAL(mpt::IO::SeekBegin(s), true);
MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true);
MPT_TEST_EXPECT_EQUAL(mpt::IO::TellRead(s), 0);
MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true);
MPT_TEST_EXPECT_EQUAL(mpt::IO::ReadRaw(s, &a, 1).size(), 1u);
MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true);
MPT_TEST_EXPECT_EQUAL(mpt::IO::TellRead(s), 1);
MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true);
MPT_TEST_EXPECT_EQUAL(mpt::IO::SeekBegin(s), true);
MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true);
MPT_TEST_EXPECT_EQUAL(mpt::IO::TellRead(s), 0);
MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true);
MPT_TEST_EXPECT_EQUAL(mpt::IO::SeekEnd(s), true);
MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true);
MPT_TEST_EXPECT_EQUAL(mpt::IO::TellRead(s), 1);
MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true);
MPT_TEST_EXPECT_EQUAL(std::string(1, a), std::string(1, 'a'));
}
{
std::ostringstream s;
char b = 23;
MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true);
MPT_TEST_EXPECT_EQUAL(mpt::IO::TellWrite(s), 0);
MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true);
MPT_TEST_EXPECT_EQUAL(mpt::IO::SeekBegin(s), true);
MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true);
MPT_TEST_EXPECT_EQUAL(mpt::IO::TellWrite(s), 0);
MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true);
MPT_TEST_EXPECT_EQUAL(mpt::IO::WriteRaw(s, &b, 1), true);
MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true);
MPT_TEST_EXPECT_EQUAL(mpt::IO::TellWrite(s), 1);
MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true);
MPT_TEST_EXPECT_EQUAL(mpt::IO::SeekBegin(s), true);
MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true);
MPT_TEST_EXPECT_EQUAL(mpt::IO::TellWrite(s), 0);
MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true);
MPT_TEST_EXPECT_EQUAL(mpt::IO::SeekEnd(s), true);
MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true);
MPT_TEST_EXPECT_EQUAL(mpt::IO::TellWrite(s), 1);
MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true);
MPT_TEST_EXPECT_EQUAL(s.str(), std::string(1, b));
}
{
std::istringstream s;
MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true);
MPT_TEST_EXPECT_EQUAL(mpt::IO::TellRead(s), 0);
MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true);
MPT_TEST_EXPECT_EQUAL(mpt::IO::SeekBegin(s), true);
MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true);
MPT_TEST_EXPECT_EQUAL(mpt::IO::TellRead(s), 0);
MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true);
MPT_TEST_EXPECT_EQUAL(mpt::IO::SeekEnd(s), true);
MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true);
MPT_TEST_EXPECT_EQUAL(mpt::IO::TellRead(s), 0);
MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true);
}
{
std::istringstream s("a");
char a = 0;
MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true);
MPT_TEST_EXPECT_EQUAL(mpt::IO::TellRead(s), 0);
MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true);
MPT_TEST_EXPECT_EQUAL(mpt::IO::SeekBegin(s), true);
MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true);
MPT_TEST_EXPECT_EQUAL(mpt::IO::TellRead(s), 0);
MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true);
MPT_TEST_EXPECT_EQUAL(mpt::IO::ReadRaw(s, &a, 1).size(), 1u);
MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true);
MPT_TEST_EXPECT_EQUAL(mpt::IO::TellRead(s), 1);
MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true);
MPT_TEST_EXPECT_EQUAL(mpt::IO::SeekBegin(s), true);
MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true);
MPT_TEST_EXPECT_EQUAL(mpt::IO::TellRead(s), 0);
MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true);
MPT_TEST_EXPECT_EQUAL(mpt::IO::SeekEnd(s), true);
MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true);
MPT_TEST_EXPECT_EQUAL(mpt::IO::TellRead(s), 1);
MPT_TEST_EXPECT_EQUAL(mpt::IO::IsValid(s), true);
MPT_TEST_EXPECT_EQUAL(std::string(1, a), std::string(1, 'a'));
}
// General file I/O tests
{
// Verify that writing arrays does not confuse the compiler.
// This is both, compile-time and run-time cheking.
// Run-time in case some weird compiler gets confused by our templates
// and only writes the first array element.
std::ostringstream f;
uint16be data[2];
mpt::reset(data);
data[0] = 0x1234;
data[1] = 0x5678;
mpt::IO::Write(f, data);
MPT_TEST_EXPECT_EQUAL(f.str(), std::string("\x12\x34\x56\x78"));
}
{
std::ostringstream f;
std::vector<int16be> data;
data.resize(3);
data[0] = 0x1234;
data[1] = 0x5678;
data[2] = 0x1234;
mpt::IO::Write(f, data);
MPT_TEST_EXPECT_EQUAL(f.str(), std::string("\x12\x34\x56\x78\x12\x34"));
}
{
std::ostringstream f;
int16be data[3];
mpt::reset(data);
data[0] = 0x1234;
data[1] = 0x5678;
data[2] = 0x1234;
mpt::IO::Write(f, data);
MPT_TEST_EXPECT_EQUAL(f.str(), std::string("\x12\x34\x56\x78\x12\x34"));
}
{
auto TestAdaptive16 = [&](uint16 value, mpt::IO::Offset expected_size, std::size_t fixedSize, const char * bytes) {
std::stringstream f;
MPT_TEST_EXPECT_EQUAL(mpt::IO::WriteAdaptiveInt16LE(f, value, fixedSize), true);
MPT_TEST_EXPECT_EQUAL(mpt::IO::TellWrite(f), expected_size);
if (bytes) {
mpt::IO::SeekBegin(f);
for (mpt::IO::Offset i = 0; i < expected_size; ++i) {
uint8 val = 0;
mpt::IO::ReadIntLE<uint8>(f, val);
MPT_TEST_EXPECT_EQUAL(val, static_cast<uint8>(bytes[i]));
}
}
mpt::IO::SeekBegin(f);
uint16 result = 0;
MPT_TEST_EXPECT_EQUAL(mpt::IO::ReadAdaptiveInt16LE(f, result), true);
MPT_TEST_EXPECT_EQUAL(result, value);
};
auto TestAdaptive32 = [&](uint32 value, mpt::IO::Offset expected_size, std::size_t fixedSize, const char * bytes) {
std::stringstream f;
MPT_TEST_EXPECT_EQUAL(mpt::IO::WriteAdaptiveInt32LE(f, value, fixedSize), true);
MPT_TEST_EXPECT_EQUAL(mpt::IO::TellWrite(f), expected_size);
if (bytes) {
mpt::IO::SeekBegin(f);
for (mpt::IO::Offset i = 0; i < expected_size; ++i) {
uint8 val = 0;
mpt::IO::ReadIntLE<uint8>(f, val);
MPT_TEST_EXPECT_EQUAL(val, static_cast<uint8>(bytes[i]));
}
}
mpt::IO::SeekBegin(f);
uint32 result = 0;
MPT_TEST_EXPECT_EQUAL(mpt::IO::ReadAdaptiveInt32LE(f, result), true);
MPT_TEST_EXPECT_EQUAL(result, value);
};
auto TestAdaptive64 = [&](uint64 value, mpt::IO::Offset expected_size, std::size_t fixedSize, const char * bytes) {
std::stringstream f;
MPT_TEST_EXPECT_EQUAL(mpt::IO::WriteAdaptiveInt64LE(f, value, fixedSize), true);
MPT_TEST_EXPECT_EQUAL(mpt::IO::TellWrite(f), expected_size);
if (bytes) {
mpt::IO::SeekBegin(f);
for (mpt::IO::Offset i = 0; i < expected_size; ++i) {
uint8 val = 0;
mpt::IO::ReadIntLE<uint8>(f, val);
MPT_TEST_EXPECT_EQUAL(val, static_cast<uint8>(bytes[i]));
}
}
mpt::IO::SeekBegin(f);
uint64 result = 0;
MPT_TEST_EXPECT_EQUAL(mpt::IO::ReadAdaptiveInt64LE(f, result), true);
MPT_TEST_EXPECT_EQUAL(result, value);
};
TestAdaptive16(0, 1, 0, "\x00");
TestAdaptive16(1, 1, 0, "\x02");
TestAdaptive16(2, 1, 0, nullptr);
TestAdaptive16(0x7f, 1, 0, nullptr);
TestAdaptive16(0x80, 2, 0, "\x01\x01");
TestAdaptive16(0x81, 2, 0, "\x03\x01");
TestAdaptive16(0x7fff, 2, 0, "\xff\xff");
TestAdaptive16(0, 1, 1, nullptr);
TestAdaptive16(1, 1, 1, nullptr);
TestAdaptive16(2, 1, 1, nullptr);
TestAdaptive16(0x7f, 1, 1, nullptr);
TestAdaptive16(0x80, 2, 0, nullptr);
TestAdaptive16(0x81, 2, 0, nullptr);
TestAdaptive16(0x7fff, 2, 0, nullptr);
TestAdaptive16(0, 2, 2, "\x01\x00");
TestAdaptive16(1, 2, 2, "\x03\x00");
TestAdaptive16(2, 2, 2, nullptr);
TestAdaptive16(0x7f, 2, 2, nullptr);
TestAdaptive16(0x80, 2, 2, nullptr);
TestAdaptive16(0x81, 2, 2, nullptr);
TestAdaptive16(0x7fff, 2, 2, nullptr);
TestAdaptive32(0, 1, 0, "\x00");
TestAdaptive32(1, 1, 0, nullptr);
TestAdaptive32(2, 1, 0, nullptr);
TestAdaptive32(0x3f, 1, 0, nullptr);
TestAdaptive32(0x40, 2, 0, "\x01\x01");
TestAdaptive32(0x41, 2, 0, "\x05\x01");
TestAdaptive32(0x7f, 2, 0, nullptr);
TestAdaptive32(0x80, 2, 0, nullptr);
TestAdaptive32(0x3fff, 2, 0, nullptr);
TestAdaptive32(0x4000, 3, 0, "\x02\x00\x01");
TestAdaptive32(0x4001, 3, 0, nullptr);
TestAdaptive32(0x3fffff, 3, 0, nullptr);
TestAdaptive32(0x400000, 4, 0, "\x03\x00\x00\x01");
TestAdaptive32(0x400001, 4, 0, nullptr);
TestAdaptive32(0x3fffffff, 4, 0, "\xff\xff\xff\xff");
TestAdaptive32(0, 2, 2, nullptr);
TestAdaptive32(1, 2, 2, nullptr);
TestAdaptive32(2, 2, 2, nullptr);
TestAdaptive32(0x3f, 2, 2, nullptr);
TestAdaptive32(0x40, 2, 2, nullptr);
TestAdaptive32(0x41, 2, 2, nullptr);
TestAdaptive32(0x7f, 2, 2, nullptr);
TestAdaptive32(0x80, 2, 2, nullptr);
TestAdaptive32(0x3fff, 2, 2, nullptr);
TestAdaptive32(0, 3, 3, nullptr);
TestAdaptive32(1, 3, 3, nullptr);
TestAdaptive32(2, 3, 3, nullptr);
TestAdaptive32(0x3f, 3, 3, nullptr);
TestAdaptive32(0x40, 3, 3, nullptr);
TestAdaptive32(0x41, 3, 3, nullptr);
TestAdaptive32(0x7f, 3, 3, nullptr);
TestAdaptive32(0x80, 3, 3, nullptr);
TestAdaptive32(0x3fff, 3, 3, nullptr);
TestAdaptive32(0x4000, 3, 3, nullptr);
TestAdaptive32(0x4001, 3, 3, nullptr);
TestAdaptive32(0x3fffff, 3, 3, nullptr);
TestAdaptive32(0, 4, 4, nullptr);
TestAdaptive32(1, 4, 4, nullptr);
TestAdaptive32(2, 4, 4, nullptr);
TestAdaptive32(0x3f, 4, 4, nullptr);
TestAdaptive32(0x40, 4, 4, nullptr);
TestAdaptive32(0x41, 4, 4, nullptr);
TestAdaptive32(0x7f, 4, 4, nullptr);
TestAdaptive32(0x80, 4, 4, nullptr);
TestAdaptive32(0x3fff, 4, 4, nullptr);
TestAdaptive32(0x4000, 4, 4, nullptr);
TestAdaptive32(0x4001, 4, 4, nullptr);
TestAdaptive32(0x3fffff, 4, 4, nullptr);
TestAdaptive32(0x400000, 4, 4, nullptr);
TestAdaptive32(0x400001, 4, 4, nullptr);
TestAdaptive32(0x3fffffff, 4, 4, nullptr);
TestAdaptive64(0, 1, 0, nullptr);
TestAdaptive64(1, 1, 0, nullptr);
TestAdaptive64(2, 1, 0, nullptr);
TestAdaptive64(0x3f, 1, 0, nullptr);
TestAdaptive64(0x40, 2, 0, nullptr);
TestAdaptive64(0x41, 2, 0, nullptr);
TestAdaptive64(0x7f, 2, 0, nullptr);
TestAdaptive64(0x80, 2, 0, nullptr);
TestAdaptive64(0x3fff, 2, 0, nullptr);
TestAdaptive64(0x4000, 4, 0, nullptr);
TestAdaptive64(0x4001, 4, 0, nullptr);
TestAdaptive64(0x3fffff, 4, 0, nullptr);
TestAdaptive64(0x400000, 4, 0, nullptr);
TestAdaptive64(0x400001, 4, 0, nullptr);
TestAdaptive64(0x3fffffff, 4, 0, nullptr);
TestAdaptive64(0x40000000, 8, 0, nullptr);
TestAdaptive64(0x40000001, 8, 0, nullptr);
TestAdaptive64(0x3fffffffffffffffull, 8, 0, nullptr);
TestAdaptive64(0, 2, 2, nullptr);
TestAdaptive64(1, 2, 2, nullptr);
TestAdaptive64(2, 2, 2, nullptr);
TestAdaptive64(0x3f, 2, 2, nullptr);
TestAdaptive64(0, 4, 4, nullptr);
TestAdaptive64(1, 4, 4, nullptr);
TestAdaptive64(2, 4, 4, nullptr);
TestAdaptive64(0x3f, 4, 4, nullptr);
TestAdaptive64(0x40, 4, 4, nullptr);
TestAdaptive64(0x41, 4, 4, nullptr);
TestAdaptive64(0x7f, 4, 4, nullptr);
TestAdaptive64(0x80, 4, 4, nullptr);
TestAdaptive64(0x3fff, 4, 4, nullptr);
TestAdaptive64(0, 8, 8, nullptr);
TestAdaptive64(1, 8, 8, nullptr);
TestAdaptive64(2, 8, 8, nullptr);
TestAdaptive64(0x3f, 8, 8, nullptr);
TestAdaptive64(0x40, 8, 8, nullptr);
TestAdaptive64(0x41, 8, 8, nullptr);
TestAdaptive64(0x7f, 8, 8, nullptr);
TestAdaptive64(0x80, 8, 8, nullptr);
TestAdaptive64(0x3fff, 8, 8, nullptr);
TestAdaptive64(0x4000, 8, 8, nullptr);
TestAdaptive64(0x4001, 8, 8, nullptr);
TestAdaptive64(0x3fffff, 8, 8, nullptr);
TestAdaptive64(0x400000, 8, 8, nullptr);
TestAdaptive64(0x400001, 8, 8, nullptr);
TestAdaptive64(0x3fffffff, 8, 8, nullptr);
TestAdaptive64(0x40000000, 8, 8, nullptr);
TestAdaptive64(0x40000001, 8, 8, nullptr);
TestAdaptive64(0x3fffffffffffffffull, 8, 8, nullptr);
}
}
} // namespace io
} // namespace tests
} // namespace MPT_INLINE_NS
} // namespace mpt
#endif // MPT_IO_TESTS_IO_HPP