summaryrefslogtreecommitdiffstats
path: root/src/lib/util/ioprocsvec.h
diff options
context:
space:
mode:
author Vas Crabb <cuavas@users.noreply.github.com>2021-08-22 09:06:15 +1000
committer GitHub <noreply@github.com>2021-08-22 09:06:15 +1000
commite8bbea1fc6e94e14768509d322f6c624403ffb36 (patch)
tree74dd1606a900d83de8aecff17a6737af4113308d /src/lib/util/ioprocsvec.h
parente319bde5fc3696d7f48f62b15b6366c4377fe5d1 (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/ioprocsvec.h')
-rw-r--r--src/lib/util/ioprocsvec.h211
1 files changed, 211 insertions, 0 deletions
diff --git a/src/lib/util/ioprocsvec.h b/src/lib/util/ioprocsvec.h
new file mode 100644
index 00000000000..27dda095ad3
--- /dev/null
+++ b/src/lib/util/ioprocsvec.h
@@ -0,0 +1,211 @@
+// license:BSD-3-Clause
+// copyright-holders:Vas Crabb
+/***************************************************************************
+
+ ioprocsvec.h
+
+ Helper for using a vector as an in-memory file
+
+***************************************************************************/
+#ifndef MAME_LIB_UTIL_IOPROCSVEC_H
+#define MAME_LIB_UTIL_IOPROCSVEC_H
+
+#include "ioprocs.h"
+
+#include <cstdint>
+#include <cstdlib>
+#include <cstring>
+#include <limits>
+#include <system_error>
+#include <type_traits>
+#include <vector>
+
+
+namespace util {
+
+template <typename T = std::uint8_t>
+class vector_read_write_adapter : public random_read_write
+{
+public:
+ vector_read_write_adapter(std::vector<T> &storage) noexcept : m_storage(storage)
+ {
+ check_resized();
+ }
+
+ 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:
+ check_resized();
+ 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
+ {
+ check_resized();
+ if (std::numeric_limits<uint64_t>::max() < m_size)
+ return std::errc::file_too_large;
+
+ result = m_size;
+ return std::error_condition();
+ }
+
+ virtual std::error_condition read(void *buffer, std::size_t length, std::size_t &actual) noexcept override
+ {
+ do_read(m_pointer, buffer, length, actual);
+ 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();
+ }
+
+ virtual std::error_condition finalize() noexcept override
+ {
+ return std::error_condition();
+ }
+
+ virtual std::error_condition flush() noexcept override
+ {
+ return std::error_condition();
+ }
+
+ virtual std::error_condition write(void const *buffer, std::size_t length, std::size_t &actual) noexcept override
+ {
+ std::error_condition result = do_write(m_pointer, buffer, length, actual);
+ m_pointer += actual;
+ return result;
+ }
+
+ virtual std::error_condition write_at(std::uint64_t offset, void const *buffer, std::size_t length, std::size_t &actual) noexcept override
+ {
+ return do_write(offset, buffer, length, actual);
+ }
+
+private:
+ using elements_type = typename std::vector<T>::size_type;
+ using common_size_type = std::common_type_t<std::uint64_t, std::size_t>;
+
+ void check_resized() noexcept
+ {
+ if (m_storage.size() != m_elements)
+ {
+ m_elements = m_storage.size();
+ m_size = m_elements * sizeof(T);
+ }
+ }
+
+ void do_read(std::uint64_t offset, void *buffer, std::size_t length, std::size_t &actual) noexcept
+ {
+ check_resized();
+ if ((offset < m_size) && length)
+ {
+ actual = std::min(std::size_t(m_size - offset), length);
+ std::memmove(buffer, reinterpret_cast<std::uint8_t *>(m_storage.data()) + offset, actual);
+ }
+ else
+ {
+ actual = 0U;
+ }
+ }
+
+ std::error_condition do_write(std::uint64_t offset, void const *buffer, std::size_t length, std::size_t &actual) noexcept
+ {
+ check_resized();
+ std::size_t allocated = m_elements * sizeof(T);
+ std::uint64_t const target = ((std::numeric_limits<std::uint64_t>::max() - offset) >= length)
+ ? (offset + length)
+ : std::numeric_limits<std::uint64_t>::max();
+
+ std::error_condition result;
+ if (target > allocated)
+ {
+ elements_type const required = std::min<std::common_type_t<elements_type, std::uint64_t> >(
+ std::numeric_limits<elements_type>::max(),
+ (target + sizeof(T) - 1) / sizeof(T)); // technically can overflow but unlikely
+ try { m_storage.resize(required); } // unpredictable if constructing/assigning T can throw
+ catch (...) { result = std::errc::not_enough_memory; }
+ m_elements = m_storage.size();
+ allocated = m_elements * sizeof(T);
+ assert(allocated >= m_size);
+
+ if (offset > m_size)
+ {
+ std::size_t const new_size = std::size_t(std::min<common_size_type>(allocated, offset));
+ std::memset(
+ reinterpret_cast<std::uint8_t *>(m_storage.data()) + m_size,
+ 0x00, // POSIX says unwritten areas of files should read as zero
+ new_size - m_size);
+ m_size = new_size;
+ }
+ }
+
+ std::size_t const limit = std::min<common_size_type>(target, allocated);
+ if (limit > offset)
+ {
+ actual = std::size_t(limit - offset);
+ std::memmove(reinterpret_cast<std::uint8_t *>(m_storage.data()) + offset, buffer, actual);
+ m_size = limit;
+ }
+ else
+ {
+ actual = 0U;
+ }
+ if (!result && (actual < length))
+ result = std::errc::no_space_on_device;
+
+ return result;
+ }
+
+ std::vector<T> &m_storage;
+ std::uint64_t m_pointer = 0U;
+ elements_type m_elements = 0U;
+ std::size_t m_size = 0U;
+};
+
+} // namespace util
+
+#endif // MAME_LIB_UTIL_IOPROCSVEC_H