diff options
author | 2021-08-22 09:06:15 +1000 | |
---|---|---|
committer | 2021-08-22 09:06:15 +1000 | |
commit | e8bbea1fc6e94e14768509d322f6c624403ffb36 (patch) | |
tree | 74dd1606a900d83de8aecff17a6737af4113308d /src/lib/util/ioprocs.cpp | |
parent | e319bde5fc3696d7f48f62b15b6366c4377fe5d1 (diff) |
formats, osd, util: Started refactoring file I/O stuff. (#8456)
Added more modern generic I/O interfaces with implementation backed by stdio, osd_file and core_file, replacing io_generic. Also replaced core_file's build-in zlib compression with a filter.
unzip.cpp, un7z.cpp: Added option to supply abstract I/O interface rather than filename.
Converted osd_file, core_file, archive_file, chd_file and device_image_interface to use std::error_condition rather than their own error enums.
Allow mounting TI-99 RPK from inside archives.
Diffstat (limited to 'src/lib/util/ioprocs.cpp')
-rw-r--r-- | src/lib/util/ioprocs.cpp | 1248 |
1 files changed, 1248 insertions, 0 deletions
diff --git a/src/lib/util/ioprocs.cpp b/src/lib/util/ioprocs.cpp new file mode 100644 index 00000000000..1f52a3daf97 --- /dev/null +++ b/src/lib/util/ioprocs.cpp @@ -0,0 +1,1248 @@ +// license:BSD-3-Clause +// copyright-holders:Vas Crabb +/*************************************************************************** + + ioprocs.h + + I/O interfaces + +***************************************************************************/ + +#include "ioprocs.h" + +#include "corefile.h" +#include "ioprocsfill.h" + +#include "osdfile.h" + +#include <algorithm> +#include <cassert> +#include <cerrno> +#include <cstring> +#include <iterator> +#include <limits> +#include <type_traits> + + +namespace util { + +namespace { + +// helper for holding a block of memory and deallocating it (or not) as necessary + +template <typename T, bool Owned> +class ram_adapter_base : public virtual random_access +{ +public: + virtual ~ram_adapter_base() + { + if constexpr (Owned) + std::free(m_data); + } + + virtual std::error_condition seek(std::int64_t offset, int whence) noexcept override + { + switch (whence) + { + case SEEK_SET: + if (0 > offset) + return std::errc::invalid_argument; + m_pointer = std::uint64_t(offset); + return std::error_condition(); + + case SEEK_CUR: + if (0 > offset) + { + if (std::uint64_t(-offset) > m_pointer) + return std::errc::invalid_argument; + } + else if ((std::numeric_limits<std::uint64_t>::max() - offset) < m_pointer) + { + return std::errc::invalid_argument; + } + m_pointer += offset; + return std::error_condition(); + + case SEEK_END: + if (0 > offset) + { + if (std::uint64_t(-offset) > m_size) + return std::errc::invalid_argument; + } + else if ((std::numeric_limits<std::uint64_t>::max() - offset) < m_size) + { + return std::errc::invalid_argument; + } + m_pointer = std::uint64_t(m_size) + offset; + return std::error_condition(); + + default: + return std::errc::invalid_argument; + } + } + + virtual std::error_condition tell(std::uint64_t &result) noexcept override + { + result = m_pointer; + return std::error_condition(); + } + + virtual std::error_condition length(std::uint64_t &result) noexcept override + { + if (std::numeric_limits<uint64_t>::max() < m_size) + return std::errc::file_too_large; + + result = m_size; + return std::error_condition(); + } + +protected: + template <typename U> + ram_adapter_base(U *data, std::size_t size) noexcept : m_data(reinterpret_cast<T>(data)), m_size(size) + { + static_assert(sizeof(*m_data) == 1U, "Element type must be byte-sized"); + assert(m_data || !m_size); + } + + T m_data; + std::uint64_t m_pointer = 0U; + std::size_t m_size; +}; + + +// RAM read implementation + +template <typename T, bool Owned> +class ram_read_adapter : public ram_adapter_base<T, Owned>, public virtual random_read +{ +public: + template <typename U> + ram_read_adapter(U *data, std::size_t size) noexcept : ram_adapter_base<T, Owned>(data, size) + { + } + + virtual std::error_condition read(void *buffer, std::size_t length, std::size_t &actual) noexcept override + { + do_read(this->m_pointer, buffer, length, actual); + this->m_pointer += actual; + return std::error_condition(); + } + + virtual std::error_condition read_at(std::uint64_t offset, void *buffer, std::size_t length, std::size_t &actual) noexcept override + { + do_read(offset, buffer, length, actual); + return std::error_condition(); + } + +private: + void do_read(std::uint64_t offset, void *buffer, std::size_t length, std::size_t &actual) const noexcept + { + if ((offset < this->m_size) && length) + { + actual = std::min(std::size_t(this->m_size - offset), length); + if constexpr (Owned) + std::memcpy(buffer, this->m_data + offset, actual); + else + std::memmove(buffer, this->m_data + offset, actual); + } + else + { + actual = 0U; + } + } +}; + + +// helper for holding a stdio FILE and closing it (or not) as necessary + +class stdio_adapter_base : public virtual random_access +{ +public: + virtual ~stdio_adapter_base() + { + if (m_close) + std::fclose(m_file); + } + + virtual std::error_condition seek(std::int64_t offset, int whence) noexcept override + { + if ((std::numeric_limits<long>::max() < offset) || (std::numeric_limits<long>::min() > offset)) + return std::errc::invalid_argument; + else if (!std::fseek(m_file, long(offset), whence)) + return std::error_condition(); + else + return std::error_condition(errno, std::generic_category()); + } + + virtual std::error_condition tell(std::uint64_t &result) noexcept override + { + long const pos = std::ftell(m_file); + if (0 > pos) + return std::error_condition(errno, std::generic_category()); + result = static_cast<unsigned long>(pos); + return std::error_condition(); + } + + virtual std::error_condition length(std::uint64_t &result) noexcept override + { + std::fpos_t oldpos; + if (std::fgetpos(m_file, &oldpos)) + return std::error_condition(errno, std::generic_category()); + + long endpos = -1; + if (!std::fseek(m_file, 0, SEEK_END)) + { + m_dangling_read = m_dangling_write = false; + endpos = std::ftell(m_file); + } + std::error_condition err; + if (0 > endpos) + err.assign(errno, std::generic_category()); + else if (std::numeric_limits<std::uint64_t>::max() < static_cast<unsigned long>(endpos)) + err = std::errc::file_too_large; + else + result = static_cast<unsigned long>(endpos); + + if (!std::fsetpos(m_file, &oldpos)) + m_dangling_read = m_dangling_write = false; + else if (!err) + err.assign(errno, std::generic_category()); + + return err; + } + +protected: + stdio_adapter_base(FILE *file, bool close) noexcept : m_file(file), m_close(close) + { + assert(m_file); + } + + FILE *file() noexcept + { + return m_file; + } + +private: + FILE *const m_file; + bool const m_close; + +protected: + bool m_dangling_read = true, m_dangling_write = true; +}; + + +// stdio read implementation + +class stdio_read_adapter : public stdio_adapter_base, public virtual random_read +{ +public: + stdio_read_adapter(FILE *file, bool close) noexcept : stdio_adapter_base(file, close) + { + } + + virtual std::error_condition read(void *buffer, std::size_t length, std::size_t &actual) noexcept override + { + if (m_dangling_write) + { + if (std::fflush(file())) + { + std::clearerr(file()); + actual = 0U; + return std::error_condition(errno, std::generic_category()); + } + m_dangling_write = false; + } + + actual = std::fread(buffer, sizeof(std::uint8_t), length, file()); + m_dangling_read = true; + if (length != actual) + { + if (std::ferror(file())) + { + std::clearerr(file()); + return std::error_condition(errno, std::generic_category()); + } + else if (std::feof(file())) + { + m_dangling_read = false; + } + } + + return std::error_condition(); + } + + virtual std::error_condition read_at(std::uint64_t offset, void *buffer, std::size_t length, std::size_t &actual) noexcept override + { + actual = 0U; + + if (static_cast<unsigned long>(std::numeric_limits<long>::max()) < offset) + return std::errc::invalid_argument; + + std::fpos_t oldpos; + if (std::fgetpos(file(), &oldpos)) + return std::error_condition(errno, std::generic_category()); + + std::error_condition err; + if (std::fseek(file(), long(static_cast<unsigned long>(offset)), SEEK_SET)) + { + err.assign(errno, std::generic_category()); + } + else + { + m_dangling_write = false; + actual = std::fread(buffer, sizeof(std::uint8_t), length, file()); + m_dangling_read = true; + if (length != actual) + { + if (std::ferror(file())) + { + err.assign(errno, std::generic_category()); + std::clearerr(file()); + } + else if (std::feof(file())) + { + m_dangling_read = false; + } + } + } + + if (!std::fsetpos(file(), &oldpos)) + m_dangling_read = m_dangling_write = false; + else if (!err) + err.assign(errno, std::generic_category()); + + return err; + } +}; + + +// stdio read/write implementation + +class stdio_read_write_adapter : public stdio_read_adapter, public random_read_write +{ +public: + using stdio_read_adapter::stdio_read_adapter; + + virtual std::error_condition finalize() noexcept override + { + return std::error_condition(); + } + + virtual std::error_condition flush() noexcept override + { + if (std::fflush(file())) + { + std::clearerr(file()); + return std::error_condition(errno, std::generic_category()); + } + m_dangling_write = false; + return std::error_condition(); + } + + virtual std::error_condition write(void const *buffer, std::size_t length, std::size_t &actual) noexcept override + { + if (m_dangling_read) + { + if (std::fseek(file(), 0, SEEK_CUR)) + { + actual = 0U; + return std::error_condition(errno, std::generic_category()); + } + m_dangling_read = false; + } + + actual = std::fwrite(buffer, sizeof(std::uint8_t), length, file()); + m_dangling_write = true; + if (length != actual) + { + std::clearerr(file()); + return std::error_condition(errno, std::generic_category()); + } + + return std::error_condition(); + } + + virtual std::error_condition write_at(std::uint64_t offset, void const *buffer, std::size_t length, std::size_t &actual) noexcept override + { + actual = 0U; + + if (static_cast<unsigned long>(std::numeric_limits<long>::max()) < offset) + return std::errc::invalid_argument; + + std::fpos_t oldpos; + if (std::fgetpos(file(), &oldpos)) + return std::error_condition(errno, std::generic_category()); + + std::error_condition err; + if (std::fseek(file(), long(static_cast<unsigned long>(offset)), SEEK_SET)) + { + err.assign(errno, std::generic_category()); + } + else + { + m_dangling_read = false; + actual = std::fwrite(buffer, sizeof(std::uint8_t), length, file()); + m_dangling_write = true; + if (length != actual) + { + err.assign(errno, std::generic_category()); + std::clearerr(file()); + } + } + + if (!std::fsetpos(file(), &oldpos)) + m_dangling_read = m_dangling_write = false; + else if (!err) + err.assign(errno, std::generic_category()); + + return err; + } +}; + + +// stdio helper that fills space when writing past the end-of-file + +class stdio_read_write_filler : public random_read_fill_wrapper<stdio_read_write_adapter> +{ +public: + stdio_read_write_filler(FILE *file, bool close, std::uint8_t fill) noexcept : random_read_fill_wrapper<stdio_read_write_adapter>(file, close) + { + set_filler(fill); + } + + virtual std::error_condition write(void const *buffer, std::size_t length, std::size_t &actual) noexcept override + { + actual = 0U; + + long const offset = std::ftell(file()); + if (0 > offset) + return std::error_condition(errno, std::generic_category()); + + if (std::fseek(file(), 0, SEEK_END)) + { + std::error_condition err(errno, std::generic_category()); + if (!std::fseek(file(), offset, SEEK_SET)) + m_dangling_read = m_dangling_write = false; + return err; + } + m_dangling_read = m_dangling_write = false; + long endpos = std::ftell(file()); + if (0 > endpos) + { + std::error_condition err(errno, std::generic_category()); + std::fseek(file(), offset, SEEK_SET); + return err; + } + + if (offset > endpos) + { + std::uint8_t block[1024]; + std::fill_n( + block, + std::min<std::common_type_t<std::size_t, std::uint64_t, long> >(std::size(block), offset - endpos), + get_filler()); + do + { + std::size_t const chunk = std::min<std::common_type_t<std::size_t, std::uint64_t, long> >(std::size(block), offset - endpos); + std::size_t const filled = std::fwrite(block, sizeof(block[0]), chunk, file()); + endpos += filled; + m_dangling_write = true; + if (chunk != filled) + { + std::error_condition err(errno, std::generic_category()); + std::clearerr(file()); + if (!std::fseek(file(), offset, SEEK_SET)) + m_dangling_write = false; + return err; + } + } + while (static_cast<unsigned long>(endpos) < offset); + } + else if ((offset < endpos) && std::fseek(file(), offset, SEEK_SET)) + { + return std::error_condition(errno, std::generic_category()); + } + + actual = std::fwrite(buffer, sizeof(std::uint8_t), length, file()); + m_dangling_write = true; + if (length != actual) + { + std::clearerr(file()); + return std::error_condition(errno, std::generic_category()); + } + + return std::error_condition(); + } + + virtual std::error_condition write_at(std::uint64_t offset, void const *buffer, std::size_t length, std::size_t &actual) noexcept override + { + actual = 0U; + + if (static_cast<unsigned long>(std::numeric_limits<long>::max()) < offset) + return std::errc::invalid_argument; + + std::fpos_t oldpos; + if (std::fgetpos(file(), &oldpos)) + return std::error_condition(errno, std::generic_category()); + + std::error_condition err; + if (std::fseek(file(), 0, SEEK_END)) + { + err.assign(errno, std::generic_category()); + } + else + { + m_dangling_read = m_dangling_write = false; + long endpos = std::ftell(file()); + if (0 > endpos) + { + err.assign(errno, std::generic_category()); + } + else if (static_cast<unsigned long>(endpos) > offset) + { + if (std::fseek(file(), long(static_cast<unsigned long>(offset)), SEEK_SET)) + err.assign(errno, std::generic_category()); + } + else if (static_cast<unsigned long>(endpos) < offset) + { + std::uint8_t block[1024]; + std::fill_n( + block, + std::min<std::common_type_t<std::size_t, std::uint64_t, long> >(std::size(block), offset - endpos), + get_filler()); + do + { + std::size_t const chunk = std::min<std::common_type_t<std::size_t, std::uint64_t, long> >(std::size(block), offset - endpos); + std::size_t const filled = std::fwrite(block, sizeof(block[0]), chunk, file()); + endpos += filled; + m_dangling_write = true; + if (chunk != filled) + { + err.assign(errno, std::generic_category()); + std::clearerr(file()); + } + } + while (!err && (static_cast<unsigned long>(endpos) < offset)); + } + } + + if (!err) + { + actual = std::fwrite(buffer, sizeof(std::uint8_t), length, file()); + m_dangling_write = true; + if (length != actual) + { + err.assign(errno, std::generic_category()); + std::clearerr(file()); + } + } + + if (!std::fsetpos(file(), &oldpos)) + m_dangling_read = m_dangling_write = false; + else if (!err) + err.assign(errno, std::generic_category()); + + return err; + } +}; + + +// helper class for holding an osd_file and closing it (or not) as necessary + +class osd_file_adapter_base : public virtual random_access +{ +public: + virtual ~osd_file_adapter_base() + { + if (m_close) + delete m_file; + } + + virtual std::error_condition seek(std::int64_t offset, int whence) noexcept override + { + switch (whence) + { + case SEEK_SET: + if (0 > offset) + return std::errc::invalid_argument; + m_pointer = std::uint64_t(offset); + return std::error_condition(); + + case SEEK_CUR: + if (0 > offset) + { + if (std::uint64_t(-offset) > m_pointer) + return std::errc::invalid_argument; + } + else if ((std::numeric_limits<std::uint64_t>::max() - offset) < m_pointer) + { + return std::errc::invalid_argument; + } + m_pointer += offset; + return std::error_condition(); + + // TODO: add SEEK_END when osd_file can support it - should it return a different error? + default: + return std::errc::invalid_argument; + } + } + + virtual std::error_condition tell(std::uint64_t &result) noexcept override + { + result = m_pointer; + return std::error_condition(); + } + + virtual std::error_condition length(std::uint64_t &result) noexcept override + { + // not supported by osd_file + return std::errc::not_supported; // TODO: revisit this error code + } + +protected: + osd_file_adapter_base(osd_file::ptr &&file) noexcept : m_file(file.release()), m_close(true) + { + assert(m_file); + } + + osd_file_adapter_base(osd_file &file) noexcept : m_file(&file), m_close(false) + { + } + + osd_file &file() noexcept + { + return *m_file; + } + + std::uint64_t m_pointer = 0U; + +private: + osd_file *const m_file; + bool const m_close; +}; + + +// osd_file read implementation + +class osd_file_read_adapter : public osd_file_adapter_base, public virtual random_read +{ +public: + osd_file_read_adapter(osd_file::ptr &&file) noexcept : osd_file_adapter_base(std::move(file)) + { + } + + osd_file_read_adapter(osd_file &file) noexcept : osd_file_adapter_base(file) + { + } + + virtual std::error_condition read(void *buffer, std::size_t length, std::size_t &actual) noexcept override + { + // TODO: should the client have to deal with reading less than expected even if EOF isn't hit? + if (std::numeric_limits<std::uint32_t>::max() < length) + { + actual = 0U; + return std::errc::invalid_argument; + } + + // actual length not valid on error + std::uint32_t count; + std::error_condition err = file().read(buffer, m_pointer, std::uint32_t(length), count); + if (!err) + { + m_pointer += count; + actual = std::size_t(count); + } + else + { + actual = 0U; + } + return err; + } + + virtual std::error_condition read_at(std::uint64_t offset, void *buffer, std::size_t length, std::size_t &actual) noexcept override + { + // TODO: should the client have to deal with reading less than expected even if EOF isn't hit? + if (std::numeric_limits<std::uint32_t>::max() < length) + { + actual = 0U; + return std::errc::invalid_argument; + } + + // actual length not valid on error + std::uint32_t count; + std::error_condition err = file().read(buffer, offset, std::uint32_t(length), count); + if (!err) + actual = std::size_t(count); + else + actual = 0U; + return err; + } +}; + + +// osd_file read/write implementation + +class osd_file_read_write_adapter : public osd_file_read_adapter, public random_read_write +{ +public: + using osd_file_read_adapter::osd_file_read_adapter; + + virtual std::error_condition finalize() noexcept override + { + return std::error_condition(); + } + + virtual std::error_condition flush() noexcept override + { + return file().flush(); + } + + virtual std::error_condition write(void const *buffer, std::size_t length, std::size_t &actual) noexcept override + { + actual = 0U; + while (length) + { + // actual length not valid on error + std::uint32_t const chunk = std::min<std::common_type_t<std::uint32_t, std::size_t> >(std::numeric_limits<std::uint32_t>::max(), length); + std::uint32_t written; + std::error_condition err = file().write(buffer, m_pointer, chunk, written); + if (err) + return err; + m_pointer += written; + buffer = reinterpret_cast<std::uint8_t const *>(buffer) + written; + length -= written; + actual += written; + } + return std::error_condition(); + } + + virtual std::error_condition write_at(std::uint64_t offset, void const *buffer, std::size_t length, std::size_t &actual) noexcept override + { + actual = 0U; + while (length) + { + // actual length not valid on error + std::uint32_t const chunk = std::min<std::common_type_t<std::uint32_t, std::size_t> >(std::numeric_limits<std::uint32_t>::max(), length); + std::uint32_t written; + std::error_condition err = file().write(buffer, offset, chunk, written); + if (err) + return err; + offset += written; + buffer = reinterpret_cast<std::uint8_t const *>(buffer) + written; + length -= written; + actual += written; + } + return std::error_condition(); + } +}; + + +// helper class for holding a core_file and closing it (or not) as necessary + +class core_file_adapter_base : public virtual random_access +{ +public: + virtual ~core_file_adapter_base() + { + if (m_close) + delete m_file; + } + + virtual std::error_condition seek(std::int64_t offset, int whence) noexcept override + { + if (!m_file->seek(offset, whence)) + return std::error_condition(); + else + return std::errc::invalid_argument; // TODO: better error reporting for core_file + } + + virtual std::error_condition tell(std::uint64_t &result) noexcept override + { + // no error reporting + result = m_file->tell(); + return std::error_condition(); + } + + virtual std::error_condition length(std::uint64_t &result) noexcept override + { + // no error reporting + result = m_file->size(); + return std::error_condition(); + } + +protected: + core_file_adapter_base(core_file::ptr &&file) noexcept : m_file(file.release()), m_close(true) + { + assert(m_file); + } + + core_file_adapter_base(core_file &file) noexcept : m_file(&file), m_close(false) + { + } + + core_file &file() noexcept + { + return *m_file; + } + +private: + core_file *const m_file; + bool const m_close; +}; + + +// core_file read implementation + +class core_file_read_adapter : public core_file_adapter_base, public virtual random_read +{ +public: + core_file_read_adapter(core_file::ptr &&file) noexcept : core_file_adapter_base(std::move(file)) + { + } + + core_file_read_adapter(core_file &file) noexcept : core_file_adapter_base(file) + { + } + + virtual std::error_condition read(void *buffer, std::size_t length, std::size_t &actual) noexcept override + { + // TODO: should the client have to deal with reading less than expected even if EOF isn't hit? + if (std::numeric_limits<std::uint32_t>::max() < length) + { + actual = 0U; + return std::errc::invalid_argument; + } + + // no way to distinguish between EOF and error + actual = file().read(buffer, std::uint32_t(length)); + return std::error_condition(); + } + + virtual std::error_condition read_at(std::uint64_t offset, void *buffer, std::size_t length, std::size_t &actual) noexcept override + { + actual = 0U; + + // TODO: should the client have to deal with reading less than expected even if EOF isn't hit? + if (std::numeric_limits<std::uint32_t>::max() < length) + return std::errc::invalid_argument; + + // no error reporting for tell + std::uint64_t const oldpos = file().tell(); + if (file().seek(offset, SEEK_SET)) + { + file().seek(oldpos, SEEK_SET); + return std::errc::invalid_argument; // TODO: better error reporting for core_file + } + + // no way to distinguish between EOF and error + actual = file().read(buffer, std::uint32_t(length)); + + if (!file().seek(oldpos, SEEK_SET)) + return std::error_condition(); + else + return std::errc::invalid_argument; // TODO: better error reporting for core_file + } +}; + + +// core_file read/write implementation + +class core_file_read_write_adapter : public core_file_read_adapter, public random_read_write +{ +public: + using core_file_read_adapter::core_file_read_adapter; + + virtual std::error_condition finalize() noexcept override + { + return std::error_condition(); + } + + virtual std::error_condition flush() noexcept override + { + return file().flush(); + } + + virtual std::error_condition write(void const *buffer, std::size_t length, std::size_t &actual) noexcept override + { + actual = 0U; + while (length) + { + std::uint32_t const chunk = std::min<std::common_type_t<std::uint32_t, std::size_t> >(std::numeric_limits<std::uint32_t>::max(), length); + std::uint32_t const written = file().write(buffer, chunk); + actual += written; + if (written < chunk) + return std::errc::io_error; // TODO: better error reporting for core_file + buffer = reinterpret_cast<std::uint8_t const *>(buffer) + written; + length -= written; + } + return std::error_condition(); + } + + virtual std::error_condition write_at(std::uint64_t offset, void const *buffer, std::size_t length, std::size_t &actual) noexcept override + { + actual = 0U; + + // no error reporting for tell + std::uint64_t const oldpos = file().tell(); + if (file().seek(offset, SEEK_SET)) + { + file().seek(oldpos, SEEK_SET); + return std::errc::invalid_argument; // TODO: better error reporting for core_file + } + + std::error_condition err; + while (!err && length) + { + std::uint32_t const chunk = std::min<std::common_type_t<std::uint32_t, std::size_t> >(std::numeric_limits<std::uint32_t>::max(), length); + std::uint32_t const written = file().write(buffer, chunk); + actual += written; + if (written < chunk) + { + err = std::errc::io_error; // TODO: better error reporting for core_file + } + else + { + buffer = reinterpret_cast<std::uint8_t const *>(buffer) + written; + length -= written; + } + } + + if (!file().seek(oldpos, SEEK_SET) || err) + return err; + else + return std::errc::invalid_argument; // TODO: better error reporting for core_file + } +}; + + +// core_file helper that fills space when writing past the end-of-file + +class core_file_read_write_filler : public random_read_fill_wrapper<core_file_read_write_adapter> +{ +public: + core_file_read_write_filler(core_file::ptr &&file, std::uint8_t fill) noexcept : random_read_fill_wrapper<core_file_read_write_adapter>(std::move(file)) + { + set_filler(fill); + } + + core_file_read_write_filler(core_file &file, std::uint8_t fill) noexcept : random_read_fill_wrapper<core_file_read_write_adapter>(file) + { + set_filler(fill); + } + + virtual std::error_condition write(void const *buffer, std::size_t length, std::size_t &actual) noexcept override + { + // no error reporting for tell or size + std::uint64_t const offset = file().tell(); + std::uint64_t endpos = file().size(); + if (endpos < offset) + { + if (file().seek(endpos, SEEK_SET)) + { + file().seek(offset, SEEK_SET); + actual = 0U; + return std::errc::invalid_argument; // TODO: better error reporting for core_file + } + std::uint8_t block[1024]; + std::fill_n( + block, + std::min<std::common_type_t<std::size_t, std::uint64_t> >(std::size(block), offset - endpos), + get_filler()); + do + { + std::uint32_t const chunk = std::min<std::common_type_t<std::size_t, std::uint64_t> >(sizeof(block), offset - endpos); + std::uint32_t const filled = file().write(block, chunk); + endpos += filled; + if (chunk != filled) + { + file().seek(offset, SEEK_SET); + actual = 0U; + return std::errc::io_error; // TODO: better error reporting for core_file + } + } + while (endpos < offset); + } + + return core_file_read_write_adapter::write(buffer, length, actual); + } + + virtual std::error_condition write_at(std::uint64_t offset, void const *buffer, std::size_t length, std::size_t &actual) noexcept override + { + actual = 0U; + + // no error reporting for tell + std::uint64_t const oldpos = file().tell(); + if (file().seek(0, SEEK_END)) + { + file().seek(oldpos, SEEK_SET); + return std::errc::invalid_argument; // TODO: better error reporting for core_file + } + std::uint64_t endpos = file().tell(); + + std::error_condition err; + if (endpos > offset) + { + if (file().seek(offset, SEEK_SET)) + err = std::errc::invalid_argument; // TODO: better error reporting for core_file + } + else if (endpos < offset) + { + std::uint8_t block[1024]; + std::fill_n( + block, + std::min<std::common_type_t<std::size_t, std::uint64_t> >(std::size(block), offset - endpos), + get_filler()); + do + { + std::uint32_t const chunk = std::min<std::common_type_t<std::size_t, std::uint64_t> >(sizeof(block), offset - endpos); + std::uint32_t const filled = file().write(block, chunk); + endpos += filled; + if (chunk != filled) + err = std::errc::io_error; // TODO: better error reporting for core_file + } + while (!err && (endpos < offset)); + } + + while (!err && length) + { + std::uint32_t const chunk = std::min<std::common_type_t<std::uint32_t, std::size_t> >(std::numeric_limits<std::uint32_t>::max(), length); + std::uint32_t const written = file().write(buffer, chunk); + actual += written; + if (written < chunk) + { + err = std::errc::io_error; // TODO: better error reporting for core_file + } + else + { + buffer = reinterpret_cast<std::uint8_t const *>(buffer) + written; + length -= written; + } + } + + if (!file().seek(oldpos, SEEK_SET) || err) + return err; + else + return std::errc::invalid_argument; // TODO: better error reporting for core_file + } +}; + +} // anonymous namespace + + +// creating RAM read adapters + +random_read::ptr ram_read(void const *data, std::size_t size) noexcept +{ + random_read::ptr result; + if (data || !size) + result.reset(new (std::nothrow) ram_read_adapter<std::uint8_t const *const, false>(data, size)); + return result; +} + +random_read::ptr ram_read(void const *data, std::size_t size, std::uint8_t filler) noexcept +{ + std::unique_ptr<random_read_fill_wrapper<ram_read_adapter<std::uint8_t const *const, false> > > result; + if (data || !size) + result.reset(new (std::nothrow) decltype(result)::element_type(data, size)); + if (result) + result->set_filler(filler); + return result; +} + +random_read::ptr ram_read_copy(void const *data, std::size_t size) noexcept +{ + random_read::ptr result; + void *const copy = size ? std::malloc(size) : nullptr; + if (copy) + std::memcpy(copy, data, size); + if (copy || !size) + result.reset(new (std::nothrow) ram_read_adapter<std::uint8_t *const, true>(copy, size)); + if (!result) + std::free(copy); + return result; +} + +random_read::ptr ram_read_copy(void const *data, std::size_t size, std::uint8_t filler) noexcept +{ + std::unique_ptr<random_read_fill_wrapper<ram_read_adapter<std::uint8_t *const, true> > > result; + void *const copy = size ? std::malloc(size) : nullptr; + if (copy) + std::memcpy(copy, data, size); + if (copy || !size) + result.reset(new (std::nothrow) decltype(result)::element_type(copy, size)); + if (!result) + std::free(copy); + return result; +} + + +// creating stdio read adapters + +random_read::ptr stdio_read(FILE *file) noexcept +{ + random_read::ptr result; + if (file) + result.reset(new (std::nothrow) stdio_read_adapter(file, true)); + return result; +} + +random_read::ptr stdio_read(FILE *file, std::uint8_t filler) noexcept +{ + std::unique_ptr<random_read_fill_wrapper<stdio_read_adapter> > result; + if (file) + result.reset(new (std::nothrow) decltype(result)::element_type(file, true)); + if (result) + result->set_filler(filler); + return result; +} + +random_read::ptr stdio_read_noclose(FILE *file) noexcept +{ + random_read::ptr result; + if (file) + result.reset(new (std::nothrow) stdio_read_adapter(file, false)); + return result; +} + +random_read::ptr stdio_read_noclose(FILE *file, std::uint8_t filler) noexcept +{ + std::unique_ptr<random_read_fill_wrapper<stdio_read_adapter> > result; + if (file) + result.reset(new (std::nothrow) decltype(result)::element_type(file, false)); + if (result) + result->set_filler(filler); + return result; +} + + +// creating stdio read/write adapters + +random_read_write::ptr stdio_read_write(FILE *file) noexcept +{ + random_read_write::ptr result; + if (file) + result.reset(new (std::nothrow) stdio_read_write_adapter(file, true)); + return result; +} + +random_read_write::ptr stdio_read_write(FILE *file, std::uint8_t filler) noexcept +{ + random_read_write::ptr result; + if (file) + result.reset(new (std::nothrow) stdio_read_write_filler(file, true, filler)); + return result; +} + +random_read_write::ptr stdio_read_write_noclose(FILE *file) noexcept +{ + random_read_write::ptr result; + if (file) + result.reset(new (std::nothrow) stdio_read_write_adapter(file, false)); + return result; +} + +random_read_write::ptr stdio_read_write_noclose(FILE *file, std::uint8_t filler) noexcept +{ + random_read_write::ptr result; + if (file) + result.reset(new (std::nothrow) stdio_read_write_filler(file, false, filler)); + return result; +} + + +// creating osd_file read adapters + +random_read::ptr osd_file_read(osd_file::ptr &&file) noexcept +{ + random_read::ptr result; + if (file) + result.reset(new (std::nothrow) osd_file_read_adapter(std::move(file))); + return result; +} + +random_read::ptr osd_file_read(osd_file &file) noexcept +{ + return random_read::ptr(new (std::nothrow) osd_file_read_adapter(file)); +} + + +// creating osd_file read/write adapters + +random_read_write::ptr osd_file_read_write(osd_file::ptr &&file) noexcept +{ + random_read_write::ptr result; + if (file) + result.reset(new (std::nothrow) osd_file_read_write_adapter(std::move(file))); + return result; +} + +random_read_write::ptr osd_file_read_write(osd_file &file) noexcept +{ + return random_read_write::ptr(new (std::nothrow) osd_file_read_write_adapter(file)); +} + + +// creating core_file read adapters + +random_read::ptr core_file_read(core_file::ptr &&file) noexcept +{ + random_read::ptr result; + if (file) + result.reset(new (std::nothrow) core_file_read_adapter(std::move(file))); + return result; +} + +random_read::ptr core_file_read(core_file::ptr &&file, std::uint8_t filler) noexcept +{ + std::unique_ptr<random_read_fill_wrapper<core_file_read_adapter> > result; + if (file) + result.reset(new (std::nothrow) decltype(result)::element_type(std::move(file))); + if (result) + result->set_filler(filler); + return result; +} + +random_read::ptr core_file_read(core_file &file) noexcept +{ + return random_read::ptr(new (std::nothrow) core_file_read_adapter(file)); +} + +random_read::ptr core_file_read(core_file &file, std::uint8_t filler) noexcept +{ + std::unique_ptr<random_read_fill_wrapper<core_file_read_adapter> > result; + result.reset(new (std::nothrow) decltype(result)::element_type(file)); + if (result) + result->set_filler(filler); + return result; +} + + +// creating core_file read/write adapters + +random_read_write::ptr core_file_read_write(core_file::ptr &&file) noexcept +{ + random_read_write::ptr result; + if (file) + result.reset(new (std::nothrow) core_file_read_write_adapter(std::move(file))); + return result; +} + +random_read_write::ptr core_file_read_write(core_file::ptr &&file, std::uint8_t filler) noexcept +{ + random_read_write::ptr result; + if (file) + result.reset(new (std::nothrow) core_file_read_write_filler(std::move(file), filler)); + return result; +} + +random_read_write::ptr core_file_read_write(core_file &file) noexcept +{ + return random_read_write::ptr(new (std::nothrow) core_file_read_write_adapter(file)); +} + +random_read_write::ptr core_file_read_write(core_file &file, std::uint8_t filler) noexcept +{ + return random_read_write::ptr(new (std::nothrow) core_file_read_write_filler(file, filler)); +} + +} // namespace util |