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/corefile.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/corefile.cpp')
-rw-r--r-- | src/lib/util/corefile.cpp | 441 |
1 files changed, 68 insertions, 373 deletions
diff --git a/src/lib/util/corefile.cpp b/src/lib/util/corefile.cpp index e681c0f1fac..0c72e1337be 100644 --- a/src/lib/util/corefile.cpp +++ b/src/lib/util/corefile.cpp @@ -12,18 +12,16 @@ #include "coretmpl.h" #include "osdcore.h" +#include "path.h" #include "unicode.h" #include "vecstream.h" -#include <zlib.h> - #include <algorithm> #include <cassert> +#include <cctype> #include <cstring> #include <iterator> -#include <cctype> - namespace util { @@ -43,110 +41,11 @@ namespace { TYPE DEFINITIONS ***************************************************************************/ -class zlib_data -{ -public: - typedef std::unique_ptr<zlib_data> ptr; - - static int start_compression(int level, std::uint64_t offset, ptr &data) - { - ptr result(new zlib_data(offset)); - result->reset_output(); - auto const zerr = deflateInit(&result->m_stream, level); - result->m_compress = (Z_OK == zerr); - if (result->m_compress) data = std::move(result); - return zerr; - } - static int start_decompression(std::uint64_t offset, ptr &data) - { - ptr result(new zlib_data(offset)); - auto const zerr = inflateInit(&result->m_stream); - result->m_decompress = (Z_OK == zerr); - if (result->m_decompress) data = std::move(result); - return zerr; - } - - ~zlib_data() - { - if (m_compress) deflateEnd(&m_stream); - else if (m_decompress) inflateEnd(&m_stream); - } - - std::size_t buffer_size() const { return sizeof(m_buffer); } - void const *buffer_data() const { return m_buffer; } - void *buffer_data() { return m_buffer; } - - // general-purpose output buffer manipulation - bool output_full() const { return 0 == m_stream.avail_out; } - std::size_t output_space() const { return m_stream.avail_out; } - void set_output(void *data, std::uint32_t size) - { - m_stream.next_out = reinterpret_cast<Bytef *>(data); - m_stream.avail_out = size; - } - - // working with output to the internal buffer - bool has_output() const { return m_stream.avail_out != sizeof(m_buffer); } - std::size_t output_size() const { return sizeof(m_buffer) - m_stream.avail_out; } - void reset_output() - { - m_stream.next_out = m_buffer; - m_stream.avail_out = sizeof(m_buffer); - } - - // general-purpose input buffer manipulation - bool has_input() const { return 0 != m_stream.avail_in; } - std::size_t input_size() const { return m_stream.avail_in; } - void set_input(void const *data, std::uint32_t size) - { - m_stream.next_in = const_cast<Bytef *>(reinterpret_cast<Bytef const *>(data)); - m_stream.avail_in = size; - } - - // working with input from the internal buffer - void reset_input(std::uint32_t size) - { - m_stream.next_in = m_buffer; - m_stream.avail_in = size; - } - - int compress() { assert(m_compress); return deflate(&m_stream, Z_NO_FLUSH); } - int finalise() { assert(m_compress); return deflate(&m_stream, Z_FINISH); } - int decompress() { assert(m_decompress); return inflate(&m_stream, Z_SYNC_FLUSH); } - - std::uint64_t realoffset() const { return m_realoffset; } - void add_realoffset(std::uint32_t increment) { m_realoffset += increment; } - - bool is_nextoffset(std::uint64_t value) const { return m_nextoffset == value; } - void add_nextoffset(std::uint32_t increment) { m_nextoffset += increment; } - -private: - zlib_data(std::uint64_t offset) - : m_compress(false) - , m_decompress(false) - , m_realoffset(offset) - , m_nextoffset(offset) - { - m_stream.zalloc = Z_NULL; - m_stream.zfree = Z_NULL; - m_stream.opaque = Z_NULL; - m_stream.avail_in = m_stream.avail_out = 0; - } - - bool m_compress, m_decompress; - z_stream m_stream; - std::uint8_t m_buffer[1024]; - std::uint64_t m_realoffset; - std::uint64_t m_nextoffset; -}; - - class core_proxy_file : public core_file { public: - core_proxy_file(core_file &file) : m_file(file) { } + core_proxy_file(core_file &file) noexcept : m_file(file) { } virtual ~core_proxy_file() override { } - virtual osd_file::error compress(int level) override { return m_file.compress(level); } virtual int seek(std::int64_t offset, int whence) override { return m_file.seek(offset, whence); } virtual std::uint64_t tell() const override { return m_file.tell(); } @@ -162,9 +61,9 @@ public: virtual std::uint32_t write(const void *buffer, std::uint32_t length) override { return m_file.write(buffer, length); } virtual int puts(std::string_view s) override { return m_file.puts(s); } virtual int vprintf(util::format_argument_pack<std::ostream> const &args) override { return m_file.vprintf(args); } - virtual osd_file::error truncate(std::uint64_t offset) override { return m_file.truncate(offset); } + virtual std::error_condition truncate(std::uint64_t offset) override { return m_file.truncate(offset); } - virtual osd_file::error flush() override { return m_file.flush(); } + virtual std::error_condition flush() override { return m_file.flush(); } private: core_file &m_file; @@ -230,12 +129,12 @@ public: if (copy) { void *const buf = allocate(); - if (buf) std::memcpy(buf, data, length); + if (buf) + std::memcpy(buf, data, length); } } ~core_in_memory_file() override { purge(); } - virtual osd_file::error compress(int level) override { return osd_file::error::INVALID_ACCESS; } virtual int seek(std::int64_t offset, int whence) override; virtual std::uint64_t tell() const override { return m_offset; } @@ -246,8 +145,8 @@ public: virtual void const *buffer() override { return m_data; } virtual std::uint32_t write(void const *buffer, std::uint32_t length) override { return 0; } - virtual osd_file::error truncate(std::uint64_t offset) override; - virtual osd_file::error flush() override { clear_putback(); return osd_file::error::NONE; } + virtual std::error_condition truncate(std::uint64_t offset) override; + virtual std::error_condition flush() override { clear_putback(); return std::error_condition(); } protected: core_in_memory_file(std::uint32_t openflags, std::uint64_t length) @@ -262,7 +161,8 @@ protected: bool is_loaded() const { return nullptr != m_data; } void *allocate() { - if (m_data) return nullptr; + if (m_data) + return nullptr; void *data = malloc(m_length); if (data) { @@ -273,7 +173,8 @@ protected: } void purge() { - if (m_data && m_data_allocated) free(const_cast<void *>(m_data)); + if (m_data && m_data_allocated) + free(const_cast<void *>(m_data)); m_data_allocated = false; m_data = nullptr; } @@ -301,23 +202,18 @@ public: core_osd_file(std::uint32_t openmode, osd_file::ptr &&file, std::uint64_t length) : core_in_memory_file(openmode, length) , m_file(std::move(file)) - , m_zdata() , m_bufferbase(0) , m_bufferbytes(0) { } ~core_osd_file() override; - virtual osd_file::error compress(int level) override; - - virtual int seek(std::int64_t offset, int whence) override; - virtual std::uint32_t read(void *buffer, std::uint32_t length) override; virtual void const *buffer() override; virtual std::uint32_t write(void const *buffer, std::uint32_t length) override; - virtual osd_file::error truncate(std::uint64_t offset) override; - virtual osd_file::error flush() override; + virtual std::error_condition truncate(std::uint64_t offset) override; + virtual std::error_condition flush() override; protected: @@ -326,11 +222,7 @@ protected: private: static constexpr std::size_t FILE_BUFFER_SIZE = 512; - osd_file::error osd_or_zlib_read(void *buffer, std::uint64_t offset, std::uint32_t length, std::uint32_t &actual); - osd_file::error osd_or_zlib_write(void const *buffer, std::uint64_t offset, std::uint32_t length, std::uint32_t &actual); - osd_file::ptr m_file; // OSD file handle - zlib_data::ptr m_zdata; // compression data std::uint64_t m_bufferbase; // base offset of internal buffer std::uint32_t m_bufferbytes; // bytes currently loaded into buffer std::uint8_t m_buffer[FILE_BUFFER_SIZE]; // buffer data @@ -673,14 +565,14 @@ std::uint32_t core_in_memory_file::read(void *buffer, std::uint32_t length) truncate - truncate a file -------------------------------------------------*/ -osd_file::error core_in_memory_file::truncate(std::uint64_t offset) +std::error_condition core_in_memory_file::truncate(std::uint64_t offset) { if (m_length < offset) - return osd_file::error::FAILURE; + return std::errc::io_error; // TODO: revisit this error code // adjust to new length and offset set_length(offset); - return osd_file::error::NONE; + return std::error_condition(); } @@ -719,91 +611,6 @@ std::size_t core_in_memory_file::safe_buffer_copy( core_osd_file::~core_osd_file() { // close files and free memory - if (m_zdata) - core_osd_file::compress(FCOMPRESS_NONE); -} - - -/*------------------------------------------------- - compress - enable/disable streaming file - compression via zlib; level is 0 to disable - compression, or up to 9 for max compression --------------------------------------------------*/ - -osd_file::error core_osd_file::compress(int level) -{ - osd_file::error result = osd_file::error::NONE; - - // can only do this for read-only and write-only cases - if (read_access() && write_access()) - return osd_file::error::INVALID_ACCESS; - - // if we have been compressing, flush and free the data - if (m_zdata && (level == FCOMPRESS_NONE)) - { - int zerr = Z_OK; - - // flush any remaining data if we are writing - while (write_access() && (zerr != Z_STREAM_END)) - { - // deflate some more - zerr = m_zdata->finalise(); - if ((zerr != Z_STREAM_END) && (zerr != Z_OK)) - { - result = osd_file::error::INVALID_DATA; - break; - } - - // write the data - if (m_zdata->has_output()) - { - std::uint32_t actualdata; - auto const filerr = m_file->write(m_zdata->buffer_data(), m_zdata->realoffset(), m_zdata->output_size(), actualdata); - if (filerr != osd_file::error::NONE) - break; - m_zdata->add_realoffset(actualdata); - m_zdata->reset_output(); - } - } - - // free memory - m_zdata.reset(); - } - - // if we are just starting to compress, allocate a new buffer - if (!m_zdata && (level > FCOMPRESS_NONE)) - { - int zerr; - - // initialize the stream and compressor - if (write_access()) - zerr = zlib_data::start_compression(level, offset(), m_zdata); - else - zerr = zlib_data::start_decompression(offset(), m_zdata); - - // on error, return an error - if (zerr != Z_OK) - return osd_file::error::OUT_OF_MEMORY; - - // flush buffers - m_bufferbytes = 0; - } - - return result; -} - - -/*------------------------------------------------- - seek - seek within a file --------------------------------------------------*/ - -int core_osd_file::seek(std::int64_t offset, int whence) -{ - // error if compressing - if (m_zdata) - return 1; - else - return core_in_memory_file::seek(offset, whence); } @@ -833,7 +640,7 @@ std::uint32_t core_osd_file::read(void *buffer, std::uint32_t length) // read as much as makes sense into the buffer m_bufferbase = offset() + bytes_read; m_bufferbytes = 0; - osd_or_zlib_read(m_buffer, m_bufferbase, sizeof(m_buffer), m_bufferbytes); + m_file->read(m_buffer, m_bufferbase, sizeof(m_buffer), m_bufferbytes); // do a bounded copy from the buffer to the destination bytes_read += safe_buffer_copy(m_buffer, 0, m_bufferbytes, buffer, bytes_read, length); @@ -842,7 +649,7 @@ std::uint32_t core_osd_file::read(void *buffer, std::uint32_t length) { // read the remainder directly from the file std::uint32_t new_bytes_read = 0; - osd_or_zlib_read(reinterpret_cast<std::uint8_t *>(buffer) + bytes_read, offset() + bytes_read, length - bytes_read, new_bytes_read); + m_file->read(reinterpret_cast<std::uint8_t *>(buffer) + bytes_read, offset() + bytes_read, length - bytes_read, new_bytes_read); bytes_read += new_bytes_read; } } @@ -866,18 +673,16 @@ void const *core_osd_file::buffer() { // allocate some memory void *buf = allocate(); - if (!buf) return nullptr; + if (!buf) + return nullptr; // read the file std::uint32_t read_length = 0; - auto const filerr = osd_or_zlib_read(buf, 0, length(), read_length); - if ((filerr != osd_file::error::NONE) || (read_length != length())) + auto const filerr = m_file->read(buf, 0, length(), read_length); + if (filerr || (read_length != length())) purge(); else - { - // close the file because we don't need it anymore - m_file.reset(); - } + m_file.reset(); // close the file because we don't need it anymore } return core_in_memory_file::buffer(); } @@ -901,7 +706,7 @@ std::uint32_t core_osd_file::write(void const *buffer, std::uint32_t length) // do the write std::uint32_t bytes_written = 0; - osd_or_zlib_write(buffer, offset(), length, bytes_written); + m_file->write(buffer, offset(), length, bytes_written); // return the number of bytes written add_offset(bytes_written); @@ -913,19 +718,19 @@ std::uint32_t core_osd_file::write(void const *buffer, std::uint32_t length) truncate - truncate a file -------------------------------------------------*/ -osd_file::error core_osd_file::truncate(std::uint64_t offset) +std::error_condition core_osd_file::truncate(std::uint64_t offset) { if (is_loaded()) return core_in_memory_file::truncate(offset); // truncate file auto const err = m_file->truncate(offset); - if (err != osd_file::error::NONE) + if (err) return err; // and adjust to new length and offset set_length(offset); - return osd_file::error::NONE; + return std::error_condition(); } @@ -933,7 +738,7 @@ osd_file::error core_osd_file::truncate(std::uint64_t offset) flush - flush file buffers -------------------------------------------------*/ -osd_file::error core_osd_file::flush() +std::error_condition core_osd_file::flush() { if (is_loaded()) return core_in_memory_file::flush(); @@ -947,116 +752,6 @@ osd_file::error core_osd_file::flush() return m_file->flush(); } - -/*------------------------------------------------- - osd_or_zlib_read - wrapper for osd_read that - handles zlib-compressed data --------------------------------------------------*/ - -osd_file::error core_osd_file::osd_or_zlib_read(void *buffer, std::uint64_t offset, std::uint32_t length, std::uint32_t &actual) -{ - // if no compression, just pass through - if (!m_zdata) - return m_file->read(buffer, offset, length, actual); - - // if the offset doesn't match the next offset, fail - if (!m_zdata->is_nextoffset(offset)) - return osd_file::error::INVALID_ACCESS; - - // set up the destination - osd_file::error filerr = osd_file::error::NONE; - m_zdata->set_output(buffer, length); - while (!m_zdata->output_full()) - { - // if we didn't make progress, report an error or the end - if (m_zdata->has_input()) - { - auto const zerr = m_zdata->decompress(); - if (Z_OK != zerr) - { - if (Z_STREAM_END != zerr) filerr = osd_file::error::INVALID_DATA; - break; - } - } - - // fetch more data if needed - if (!m_zdata->has_input()) - { - std::uint32_t actualdata = 0; - filerr = m_file->read(m_zdata->buffer_data(), m_zdata->realoffset(), m_zdata->buffer_size(), actualdata); - if (filerr != osd_file::error::NONE) break; - m_zdata->add_realoffset(actualdata); - m_zdata->reset_input(actualdata); - if (!m_zdata->has_input()) break; - } - } - - // adjust everything - actual = length - m_zdata->output_space(); - m_zdata->add_nextoffset(actual); - return filerr; -} - - -/*------------------------------------------------- - osd_or_zlib_write - wrapper for osd_write that - handles zlib-compressed data --------------------------------------------------*/ - -/** - * @fn osd_file::error osd_or_zlib_write(void const *buffer, std::uint64_t offset, std::uint32_t length, std::uint32_t actual) - * - * @brief OSD or zlib write. - * - * @param buffer The buffer. - * @param offset The offset. - * @param length The length. - * @param [in,out] actual The actual. - * - * @return A osd_file::error. - */ - -osd_file::error core_osd_file::osd_or_zlib_write(void const *buffer, std::uint64_t offset, std::uint32_t length, std::uint32_t &actual) -{ - // if no compression, just pass through - if (!m_zdata) - return m_file->write(buffer, offset, length, actual); - - // if the offset doesn't match the next offset, fail - if (!m_zdata->is_nextoffset(offset)) - return osd_file::error::INVALID_ACCESS; - - // set up the source - m_zdata->set_input(buffer, length); - while (m_zdata->has_input()) - { - // if we didn't make progress, report an error or the end - auto const zerr = m_zdata->compress(); - if (zerr != Z_OK) - { - actual = length - m_zdata->input_size(); - m_zdata->add_nextoffset(actual); - return osd_file::error::INVALID_DATA; - } - - // write more data if we are full up - if (m_zdata->output_full()) - { - std::uint32_t actualdata = 0; - auto const filerr = m_file->write(m_zdata->buffer_data(), m_zdata->realoffset(), m_zdata->output_size(), actualdata); - if (filerr != osd_file::error::NONE) - return filerr; - m_zdata->add_realoffset(actualdata); - m_zdata->reset_output(); - } - } - - // we wrote everything - actual = length; - m_zdata->add_nextoffset(actual); - return osd_file::error::NONE; -} - } // anonymous namespace @@ -1070,23 +765,23 @@ osd_file::error core_osd_file::osd_or_zlib_write(void const *buffer, std::uint64 return an error code -------------------------------------------------*/ -osd_file::error core_file::open(std::string const &filename, std::uint32_t openflags, ptr &file) +std::error_condition core_file::open(std::string_view filename, std::uint32_t openflags, ptr &file) { try { // attempt to open the file osd_file::ptr f; std::uint64_t length = 0; - auto const filerr = osd_file::open(filename, openflags, f, length); - if (filerr != osd_file::error::NONE) + auto const filerr = osd_file::open(std::string(filename), openflags, f, length); // FIXME: allow osd_file to accept std::string_view + if (filerr) return filerr; file = std::make_unique<core_osd_file>(openflags, std::move(f), length); - return osd_file::error::NONE; + return std::error_condition(); } catch (...) { - return osd_file::error::OUT_OF_MEMORY; + return std::errc::not_enough_memory; } } @@ -1096,20 +791,20 @@ osd_file::error core_file::open(std::string const &filename, std::uint32_t openf like access and return an error code -------------------------------------------------*/ -osd_file::error core_file::open_ram(void const *data, std::size_t length, std::uint32_t openflags, ptr &file) +std::error_condition core_file::open_ram(void const *data, std::size_t length, std::uint32_t openflags, ptr &file) { // can only do this for read access if ((openflags & OPEN_FLAG_WRITE) || (openflags & OPEN_FLAG_CREATE)) - return osd_file::error::INVALID_ACCESS; + return std::errc::invalid_argument; try { file.reset(new core_in_memory_file(openflags, data, length, false)); - return osd_file::error::NONE; + return std::error_condition(); } catch (...) { - return osd_file::error::OUT_OF_MEMORY; + return std::errc::not_enough_memory; } } @@ -1120,24 +815,24 @@ osd_file::error core_file::open_ram(void const *data, std::size_t length, std::u error code -------------------------------------------------*/ -osd_file::error core_file::open_ram_copy(void const *data, std::size_t length, std::uint32_t openflags, ptr &file) +std::error_condition core_file::open_ram_copy(void const *data, std::size_t length, std::uint32_t openflags, ptr &file) { // can only do this for read access if ((openflags & OPEN_FLAG_WRITE) || (openflags & OPEN_FLAG_CREATE)) - return osd_file::error::INVALID_ACCESS; + return std::errc::invalid_argument; try { ptr result(new core_in_memory_file(openflags, data, length, true)); if (!result->buffer()) - return osd_file::error::OUT_OF_MEMORY; + return std::errc::not_enough_memory; file = std::move(result); - return osd_file::error::NONE; + return std::error_condition(); } catch (...) { - return osd_file::error::OUT_OF_MEMORY; + return std::errc::not_enough_memory; } } @@ -1147,17 +842,14 @@ osd_file::error core_file::open_ram_copy(void const *data, std::size_t length, s object and return an error code -------------------------------------------------*/ -osd_file::error core_file::open_proxy(core_file &file, ptr &proxy) +std::error_condition core_file::open_proxy(core_file &file, ptr &proxy) { - try - { - proxy = std::make_unique<core_proxy_file>(file); - return osd_file::error::NONE; - } - catch (...) - { - return osd_file::error::OUT_OF_MEMORY; - } + ptr result(new (std::nothrow) core_proxy_file(file)); + if (!result) + return std::errc::not_enough_memory; + + proxy = std::move(result); + return std::error_condition(); } @@ -1176,61 +868,64 @@ core_file::~core_file() pointer -------------------------------------------------*/ -osd_file::error core_file::load(std::string const &filename, void **data, std::uint32_t &length) +std::error_condition core_file::load(std::string_view filename, void **data, std::uint32_t &length) { ptr file; // attempt to open the file auto const err = open(filename, OPEN_FLAG_READ, file); - if (err != osd_file::error::NONE) + if (err) return err; // get the size auto const size = file->size(); if (std::uint32_t(size) != size) - return osd_file::error::OUT_OF_MEMORY; + return std::errc::file_too_large; // allocate memory *data = malloc(size); + if (!*data) + return std::errc::not_enough_memory; length = std::uint32_t(size); // read the data if (file->read(*data, size) != size) { free(*data); - return osd_file::error::FAILURE; + return std::errc::io_error; // TODO: revisit this error code } // close the file and return data - return osd_file::error::NONE; + return std::error_condition(); } -osd_file::error core_file::load(std::string const &filename, std::vector<uint8_t> &data) +std::error_condition core_file::load(std::string_view filename, std::vector<uint8_t> &data) { ptr file; // attempt to open the file auto const err = open(filename, OPEN_FLAG_READ, file); - if (err != osd_file::error::NONE) + if (err) return err; // get the size auto const size = file->size(); if (std::uint32_t(size) != size) - return osd_file::error::OUT_OF_MEMORY; + return std::errc::file_too_large; // allocate memory - data.resize(size); + try { data.resize(size); } + catch (...) { return std::errc::not_enough_memory; } // read the data if (file->read(&data[0], size) != size) { data.clear(); - return osd_file::error::FAILURE; + return std::errc::io_error; // TODO: revisit this error code } // close the file and return data - return osd_file::error::NONE; + return std::error_condition(); } @@ -1256,7 +951,7 @@ core_file::core_file() // assumptions about path separators // ------------------------------------------------- -std::string_view core_filename_extract_base(std::string_view name, bool strip_extension) +std::string_view core_filename_extract_base(std::string_view name, bool strip_extension) noexcept { // find the start of the basename auto const start = std::find_if(name.rbegin(), name.rend(), &util::is_directory_separator); @@ -1279,7 +974,7 @@ std::string_view core_filename_extract_base(std::string_view name, bool strip_ex // core_filename_extract_extension // ------------------------------------------------- -std::string_view core_filename_extract_extension(std::string_view filename, bool strip_period) +std::string_view core_filename_extract_extension(std::string_view filename, bool strip_period) noexcept { auto loc = filename.find_last_of('.'); if (loc != std::string_view::npos) @@ -1294,7 +989,7 @@ std::string_view core_filename_extract_extension(std::string_view filename, bool // filename end with the specified extension? // ------------------------------------------------- -bool core_filename_ends_with(std::string_view filename, std::string_view extension) +bool core_filename_ends_with(std::string_view filename, std::string_view extension) noexcept { auto namelen = filename.length(); auto extlen = extension.length(); |