From b45569d964aa8fae416d2049ba70699f5b2d7837 Mon Sep 17 00:00:00 2001 From: AJR Date: Tue, 19 Apr 2022 14:22:04 -0400 Subject: File handling cleanup - Remove fgetc, fgets and ptr methods from device_image_interface. - Remove the core_file::buffer method to read an entire file into memory and rewrite emu_file::hashes to not depend on it. - Make core_in_memory_file a final class; now that buffering is gone, core_osd_file no longer subclasses it but a new superclass that retains some common methods. - Rename the offset and length methods used internally in core_file implementations to index and size due to frequent clashes with parameter names. - Convert comments in util/corefile.cpp to C++ style. - Add a new overload of the hash_collection::compute method which hashes data from a random_read stream, reading it into memory one chunk at a time. As a result, the hash_collection::begin and hash_collection::end methods have been removed as obsolete (similar methods are now used internally only). - Enhance error messages for the frontend media identifier when it encounters file errors. --- src/emu/diimage.cpp | 44 ++--- src/emu/diimage.h | 25 +-- src/emu/fileio.cpp | 19 +- src/frontend/mame/media_ident.cpp | 30 ++- src/lib/util/corefile.cpp | 398 +++++++++++++++++--------------------- src/lib/util/corefile.h | 4 - src/lib/util/hash.cpp | 160 ++++++++------- src/lib/util/hash.h | 29 ++- src/tools/romcmp.cpp | 4 +- 9 files changed, 318 insertions(+), 395 deletions(-) diff --git a/src/emu/diimage.cpp b/src/emu/diimage.cpp index 2d192836ddd..4e5ca05e002 100644 --- a/src/emu/diimage.cpp +++ b/src/emu/diimage.cpp @@ -351,43 +351,25 @@ bool device_image_interface::load_software_region(std::string_view tag, std::uni // to be loaded // **************************************************************************** -bool device_image_interface::run_hash(util::core_file &file, u32 skip_bytes, util::hash_collection &hashes, const char *types) +std::error_condition device_image_interface::run_hash(util::random_read &file, u32 skip_bytes, util::hash_collection &hashes, const char *types) { // reset the hash; we want to override existing data hashes.reset(); // figure out the size, and "cap" the skip bytes u64 size; - if (file.length(size)) - return false; + std::error_condition filerr = file.length(size); + if (filerr) + return filerr; skip_bytes = u32(std::min(skip_bytes, size)); - // seek to the beginning - file.seek(skip_bytes, SEEK_SET); // TODO: check error return - u64 position = skip_bytes; - - // keep on reading hashes - hashes.begin(types); - while (position < size) - { - uint8_t buffer[8192]; - - // read bytes - const size_t count = size_t(std::min(size - position, sizeof(buffer))); - size_t actual_count; - const std::error_condition filerr = file.read(buffer, count, actual_count); - if (filerr || !actual_count) - return false; - position += actual_count; - - // and compute the hashes - hashes.buffer(buffer, actual_count); - } - hashes.end(); + // and compute the hashes + size_t actual_count; + filerr = hashes.compute(file, skip_bytes, size - skip_bytes, actual_count, types); + if (filerr) + return filerr; - // cleanup - file.seek(0, SEEK_SET); // TODO: check error return - return true; + return std::error_condition(); } @@ -408,18 +390,18 @@ bool device_image_interface::image_checkhash() return true; // run the hash - if (!run_hash(*m_file, unhashed_header_length(), m_hash, util::hash_collection::HASH_TYPES_ALL)) + if (run_hash(*m_file, unhashed_header_length(), m_hash, util::hash_collection::HASH_TYPES_ALL)) return false; } return true; } -util::hash_collection device_image_interface::calculate_hash_on_file(util::core_file &file) const +util::hash_collection device_image_interface::calculate_hash_on_file(util::random_read &file) const { // calculate the hash util::hash_collection hash; - if (!run_hash(file, unhashed_header_length(), hash, util::hash_collection::HASH_TYPES_ALL)) + if (run_hash(file, unhashed_header_length(), hash, util::hash_collection::HASH_TYPES_ALL)) hash.reset(); return hash; } diff --git a/src/emu/diimage.h b/src/emu/diimage.h index 4d97e4cd717..08d48020cc3 100644 --- a/src/emu/diimage.h +++ b/src/emu/diimage.h @@ -113,7 +113,7 @@ public: const util::option_guide &device_get_creation_option_guide() const { return create_option_guide(); } std::string_view error(); - void seterror(std::error_condition err, const char *message); + void seterror(std::error_condition err, const char *message = nullptr); void message(const char *format, ...) ATTR_PRINTF(2,3); bool exists() const noexcept { return !m_image_name.empty(); } @@ -126,7 +126,7 @@ public: bool is_filetype(std::string_view candidate_filetype) const; bool is_open() const noexcept { return bool(m_file); } - util::core_file &image_core_file() const noexcept { return *m_file; } + util::core_file &image_core_file() const noexcept { assert(is_open()); return *m_file; } bool is_readonly() const noexcept { return m_readonly; } // image file I/O wrappers @@ -165,28 +165,11 @@ public: m_file->tell(result); return result; } - int fgetc() - { - char ch; - if (fread(&ch, 1) != 1) - ch = '\0'; - return ch; - } - char *fgets(char *buffer, u32 length) - { - check_for_file(); - return m_file->gets(buffer, length); - } bool image_feof() { check_for_file(); return m_file->eof(); } - const void *ptr() - { - check_for_file(); - return m_file->buffer(); - } // allocate and read into buffers u32 fread(std::unique_ptr &ptr, u32 length) { ptr = std::make_unique(length); return fread(ptr.get(), length); } @@ -211,7 +194,7 @@ public: u32 crc(); util::hash_collection& hash() { return m_hash; } - util::hash_collection calculate_hash_on_file(util::core_file &file) const; + util::hash_collection calculate_hash_on_file(util::random_read &file) const; void battery_load(void *buffer, int length, int fill); void battery_load(void *buffer, int length, const void *def_buffer); @@ -294,7 +277,7 @@ private: bool load_software_part(std::string_view identifier); bool init_phase() const; - static bool run_hash(util::core_file &file, u32 skip_bytes, util::hash_collection &hashes, const char *types); + static std::error_condition run_hash(util::random_read &file, u32 skip_bytes, util::hash_collection &hashes, const char *types); // loads an image or software items and resets - called internally when we // load an is_reset_on_load() item diff --git a/src/emu/fileio.cpp b/src/emu/fileio.cpp index 21634d8f211..a1bf4adbcb4 100644 --- a/src/emu/fileio.cpp +++ b/src/emu/fileio.cpp @@ -259,7 +259,7 @@ util::hash_collection &emu_file::hashes(std::string_view types) // determine which hashes we need std::string needed; for (char scan : types) - if (already_have.find_first_of(scan) == -1) + if (already_have.find_first_of(scan) == std::string::npos) needed.push_back(scan); // if we need nothing, skip it @@ -279,15 +279,14 @@ util::hash_collection &emu_file::hashes(std::string_view types) return m_hashes; } - // read the data if we can - const u8 *filedata = (const u8 *)m_file->buffer(); - if (filedata == nullptr) + std::uint64_t length; + if (m_file->length(length)) return m_hashes; - // compute the hash - std::uint64_t length; - if (!m_file->length(length)) - m_hashes.compute(filedata, length, needed.c_str()); + // hash the data + std::size_t actual; + (void)m_hashes.compute(*m_file, 0U, length, actual, needed.c_str()); // FIXME: need better interface to report errors + return m_hashes; } @@ -805,7 +804,7 @@ std::error_condition emu_file::load_zipped_file() m_zipdata.resize(m_ziplength); // read the data into our buffer and return - auto const ziperr = m_zipfile->decompress(&m_zipdata[0], m_zipdata.size()); + auto const ziperr = m_zipfile->decompress(m_zipdata.data(), m_zipdata.size()); if (ziperr) { m_zipdata.clear(); @@ -813,7 +812,7 @@ std::error_condition emu_file::load_zipped_file() } // convert to RAM file - std::error_condition const filerr = util::core_file::open_ram(&m_zipdata[0], m_zipdata.size(), m_openflags, m_file); + std::error_condition const filerr = util::core_file::open_ram(m_zipdata.data(), m_zipdata.size(), m_openflags, m_file); if (filerr) { m_zipdata.clear(); diff --git a/src/frontend/mame/media_ident.cpp b/src/frontend/mame/media_ident.cpp index 549420192c1..7e6665bc765 100644 --- a/src/frontend/mame/media_ident.cpp +++ b/src/frontend/mame/media_ident.cpp @@ -2,7 +2,7 @@ // copyright-holders:Aaron Giles /*************************************************************************** - media_ident.c + media_ident.cpp Media identify. @@ -260,33 +260,27 @@ void media_identifier::digest_file(std::vector &info, char const *pat // load the file and process if it opens and has a valid length util::core_file::ptr file; - if (util::core_file::open(path, OPEN_FLAG_READ, file) || !file) + std::error_condition err = util::core_file::open(path, OPEN_FLAG_READ, file); + if (err || !file) { - osd_printf_error("%s: error opening file\n", path); + osd_printf_error("%s: error opening file (%s)\n", path, err ? err.message() : std::string("could not allocate pointer")); return; } std::uint64_t length; - if (file->length(length)) + err = file->length(length); + if (err) { - osd_printf_error("%s: error getting file length\n", path); + osd_printf_error("%s: error getting file length (%s)\n", path, err.message()); return; } util::hash_collection hashes; - hashes.begin(util::hash_collection::HASH_TYPES_CRC_SHA1); - std::uint8_t buf[1024]; - for (std::uint64_t remaining = length; remaining; ) + std::size_t actual; + err = hashes.compute(*file, 0U, length, actual, util::hash_collection::HASH_TYPES_CRC_SHA1); + if (err) { - std::size_t const block = std::min(remaining, sizeof(buf)); - std::size_t actual; - if (file->read(buf, block, actual) || !actual) - { - osd_printf_error("%s: error reading file\n", path); - return; - } - remaining -= actual; - hashes.buffer(buf, actual); + osd_printf_error("%s: error reading file (%s)\n", path, err.message()); + return; } - hashes.end(); info.emplace_back(path, length, std::move(hashes), file_flavour::RAW); m_total++; } diff --git a/src/lib/util/corefile.cpp b/src/lib/util/corefile.cpp index b53f68a713d..fd26ddaf40e 100644 --- a/src/lib/util/corefile.cpp +++ b/src/lib/util/corefile.cpp @@ -2,7 +2,7 @@ // copyright-holders:Aaron Giles, Vas Crabb /*************************************************************************** - corefile.c + corefile.cpp File access functions. @@ -64,7 +64,6 @@ public: virtual int getc() override { return m_file.getc(); } virtual int ungetc(int c) override { return m_file.ungetc(c); } virtual char *gets(char *s, int n) override { return m_file.gets(s, n); } - virtual void const *buffer() override { return m_file.buffer(); } virtual int puts(std::string_view s) override { return m_file.puts(s); } virtual int vprintf(util::format_argument_pack const &args) override { return m_file.vprintf(args); } @@ -121,15 +120,45 @@ private: }; -class core_in_memory_file : public core_text_file +class core_basic_file : public core_text_file { public: - core_in_memory_file(std::uint32_t openflags, void const *data, std::size_t length, bool copy) noexcept + virtual std::error_condition seek(std::int64_t offset, int whence) noexcept override; + virtual std::error_condition tell(std::uint64_t &result) noexcept override { result = m_index; return std::error_condition(); } + virtual std::error_condition length(std::uint64_t &result) noexcept override { result = m_size; return std::error_condition(); } + + virtual bool eof() const override; + +protected: + core_basic_file(std::uint32_t openflags, std::uint64_t length) noexcept : core_text_file(openflags) + , m_index(0) + , m_size(length) + { + } + + std::uint64_t index() const noexcept { return m_index; } + void add_offset(std::size_t increment) noexcept { m_index += increment; m_size = (std::max)(m_size, m_index); } + std::uint64_t size() const noexcept { return m_size; } + void set_size(std::uint64_t value) noexcept { m_size = value; } + + static std::size_t safe_buffer_copy( + void const *source, std::size_t sourceoffs, std::size_t sourcelen, + void *dest, std::size_t destoffs, std::size_t destlen) noexcept; + + +private: + std::uint64_t m_index; // current file offset + std::uint64_t m_size; // total file length +}; + +class core_in_memory_file final : public core_basic_file +{ +public: + core_in_memory_file(std::uint32_t openflags, void const *data, std::size_t length, bool copy) noexcept + : core_basic_file(openflags, length) , m_data_allocated(false) , m_data(copy ? nullptr : data) - , m_offset(0) - , m_length(length) { if (copy) { @@ -141,10 +170,6 @@ public: ~core_in_memory_file() override { purge(); } - virtual std::error_condition seek(std::int64_t offset, int whence) noexcept override; - virtual std::error_condition tell(std::uint64_t &result) noexcept override { result = m_offset; return std::error_condition(); } - virtual std::error_condition length(std::uint64_t &result) noexcept override { result = m_length; return std::error_condition(); } - virtual std::error_condition read(void *buffer, std::size_t length, std::size_t &actual) noexcept override; virtual std::error_condition read_at(std::uint64_t offset, void *buffer, std::size_t length, std::size_t &actual) noexcept override; @@ -153,29 +178,16 @@ public: virtual std::error_condition write(void const *buffer, std::size_t length, std::size_t &actual) noexcept override { actual = 0; return std::errc::bad_file_descriptor; } virtual std::error_condition write_at(std::uint64_t offset, void const *buffer, std::size_t length, std::size_t &actual) noexcept override { actual = 0; return std::errc::bad_file_descriptor; } - virtual bool eof() const override; - - virtual void const *buffer() override { return m_data; } + void const *buffer() const { return m_data; } virtual std::error_condition truncate(std::uint64_t offset) override; protected: - core_in_memory_file(std::uint32_t openflags, std::uint64_t length) noexcept - : core_text_file(openflags) - , m_data_allocated(false) - , m_data(nullptr) - , m_offset(0) - , m_length(length) - { - } - - bool is_loaded() const noexcept { return nullptr != m_data; } - void *allocate() noexcept { - if (m_data || (std::numeric_limits::max() < m_length)) + if (m_data || (std::numeric_limits::max() < size())) return nullptr; - void *data = malloc(m_length); + void *data = malloc(size()); if (data) { m_data_allocated = true; @@ -192,28 +204,17 @@ protected: m_data = nullptr; } - std::uint64_t offset() const noexcept { return m_offset; } - void add_offset(std::size_t increment) noexcept { m_offset += increment; m_length = (std::max)(m_length, m_offset); } - std::uint64_t length() const noexcept { return m_length; } - void set_length(std::uint64_t value) noexcept { m_length = value; } - - static std::size_t safe_buffer_copy( - void const *source, std::size_t sourceoffs, std::size_t sourcelen, - void *dest, std::size_t destoffs, std::size_t destlen) noexcept; - private: bool m_data_allocated; // was the data allocated by us? void const * m_data; // file data, if RAM-based - std::uint64_t m_offset; // current file offset - std::uint64_t m_length; // total file length }; -class core_osd_file final : public core_in_memory_file +class core_osd_file final : public core_basic_file { public: core_osd_file(std::uint32_t openmode, osd_file::ptr &&file, std::uint64_t length) - : core_in_memory_file(openmode, length) + : core_basic_file(openmode, length) , m_file(std::move(file)) { } @@ -227,12 +228,9 @@ public: virtual std::error_condition write(void const *buffer, std::size_t length, std::size_t &actual) noexcept override; virtual std::error_condition write_at(std::uint64_t offset, void const *buffer, std::size_t length, std::size_t &actual) noexcept override; - virtual void const *buffer() override; - virtual std::error_condition truncate(std::uint64_t offset) override; protected: - bool is_buffered(std::uint64_t offset) const noexcept { return (offset >= m_bufferbase) && (offset < (m_bufferbase + m_bufferbytes)); } private: @@ -246,13 +244,13 @@ private: -/*************************************************************************** - core_text_file -***************************************************************************/ +//************************************************************************** +// core_text_file +//************************************************************************** -/*------------------------------------------------- - getc - read a character from a file --------------------------------------------------*/ +//------------------------------------------------- +// getc - read a character from a file +//------------------------------------------------- int core_text_file::getc() { @@ -406,10 +404,10 @@ int core_text_file::getc() } -/*------------------------------------------------- - ungetc - put back a character read from a - file --------------------------------------------------*/ +//------------------------------------------------- +// ungetc - put back a character read from a +// file +//------------------------------------------------- int core_text_file::ungetc(int c) { @@ -419,9 +417,9 @@ int core_text_file::ungetc(int c) } -/*------------------------------------------------- - gets - read a line from a text file --------------------------------------------------*/ +//------------------------------------------------- +// gets - read a line from a text file +//------------------------------------------------- char *core_text_file::gets(char *s, int n) { @@ -466,9 +464,9 @@ char *core_text_file::gets(char *s, int n) } -/*------------------------------------------------- - puts - write a string to a text file --------------------------------------------------*/ +//------------------------------------------------- +// puts - write a string to a text file +//------------------------------------------------- int core_text_file::puts(std::string_view s) { @@ -535,9 +533,9 @@ int core_text_file::puts(std::string_view s) } -/*------------------------------------------------- - vprintf - vfprintf to a text file --------------------------------------------------*/ +//------------------------------------------------- +// vprintf - vfprintf to a text file +//------------------------------------------------- int core_text_file::vprintf(util::format_argument_pack const &args) { @@ -550,15 +548,15 @@ int core_text_file::vprintf(util::format_argument_pack const &args -/*************************************************************************** - core_in_memory_file -***************************************************************************/ +//************************************************************************** +// core_basic_file +//************************************************************************** -/*------------------------------------------------- - seek - seek within a file --------------------------------------------------*/ +//------------------------------------------------- +// seek - seek within a file +//------------------------------------------------- -std::error_condition core_in_memory_file::seek(std::int64_t offset, int whence) noexcept +std::error_condition core_basic_file::seek(std::int64_t offset, int whence) noexcept { // flush any buffered char clear_putback(); // TODO: report errors; also, should the argument check happen before this? @@ -569,33 +567,33 @@ std::error_condition core_in_memory_file::seek(std::int64_t offset, int whence) case SEEK_SET: if (0 > offset) return std::errc::invalid_argument; - m_offset = offset; + m_index = offset; return std::error_condition(); case SEEK_CUR: if (0 > offset) { - if (std::uint64_t(-offset) > m_offset) + if (std::uint64_t(-offset) > m_index) return std::errc::invalid_argument; } - else if ((std::numeric_limits::max() - offset) < m_offset) + else if ((std::numeric_limits::max() - offset) < m_index) { return std::errc::invalid_argument; } - m_offset += offset; + m_index += offset; return std::error_condition(); case SEEK_END: if (0 > offset) { - if (std::uint64_t(-offset) > m_length) + if (std::uint64_t(-offset) > m_size) return std::errc::invalid_argument; } - else if ((std::numeric_limits::max() - offset) < m_length) + else if ((std::numeric_limits::max() - offset) < m_size) { return std::errc::invalid_argument; } - m_offset = m_length + offset; + m_index = m_size + offset; return std::error_condition(); default: @@ -604,35 +602,63 @@ std::error_condition core_in_memory_file::seek(std::int64_t offset, int whence) } -/*------------------------------------------------- - eof - return 1 if we're at the end of file --------------------------------------------------*/ +//------------------------------------------------- +// eof - return true if we're at the end of file +//------------------------------------------------- -bool core_in_memory_file::eof() const +bool core_basic_file::eof() const { // check for buffered chars if (has_putback()) return false; // if the offset == length, we're at EOF - return (m_offset >= m_length); + return (m_index >= m_size); } -/*------------------------------------------------- - read - read from a file --------------------------------------------------*/ +//------------------------------------------------- +// safe_buffer_copy - copy safely from one +// bounded buffer to another +//------------------------------------------------- + +std::size_t core_basic_file::safe_buffer_copy( + void const *source, std::size_t sourceoffs, std::size_t sourcelen, + void *dest, std::size_t destoffs, std::size_t destlen) noexcept +{ + auto const sourceavail = sourcelen - sourceoffs; + auto const destavail = destlen - destoffs; + auto const bytes_to_copy = (std::min)(sourceavail, destavail); + if (bytes_to_copy > 0) + { + std::memcpy( + reinterpret_cast(dest) + destoffs, + reinterpret_cast(source) + sourceoffs, + bytes_to_copy); + } + return bytes_to_copy; +} + + + +//************************************************************************** +// core_in_memory_file +//************************************************************************** + +//------------------------------------------------- +// read - read from a file +//------------------------------------------------- std::error_condition core_in_memory_file::read(void *buffer, std::size_t length, std::size_t &actual) noexcept { clear_putback(); // handle RAM-based files - if (m_offset < m_length) - actual = safe_buffer_copy(m_data, std::size_t(m_offset), std::size_t(m_length), buffer, 0, length); + if (index() < size()) + actual = safe_buffer_copy(m_data, std::size_t(index()), std::size_t(size()), buffer, 0, length); else actual = 0U; - m_offset += actual; + add_offset(actual); return std::error_condition(); } @@ -641,60 +667,36 @@ std::error_condition core_in_memory_file::read_at(std::uint64_t offset, void *bu clear_putback(); // handle RAM-based files - if (offset < m_length) - actual = safe_buffer_copy(m_data, std::size_t(offset), std::size_t(m_length), buffer, 0, length); + if (offset < size()) + actual = safe_buffer_copy(m_data, std::size_t(offset), std::size_t(size()), buffer, 0, length); else actual = 0U; return std::error_condition(); } -/*------------------------------------------------- - truncate - truncate a file --------------------------------------------------*/ +//------------------------------------------------- +// truncate - truncate a file +//------------------------------------------------- std::error_condition core_in_memory_file::truncate(std::uint64_t offset) { - if (m_length < offset) + if (size() < offset) return std::errc::io_error; // TODO: revisit this error code // adjust to new length and offset - set_length(offset); + set_size(offset); return std::error_condition(); } -/*------------------------------------------------- - safe_buffer_copy - copy safely from one - bounded buffer to another --------------------------------------------------*/ - -std::size_t core_in_memory_file::safe_buffer_copy( - void const *source, std::size_t sourceoffs, std::size_t sourcelen, - void *dest, std::size_t destoffs, std::size_t destlen) noexcept -{ - auto const sourceavail = sourcelen - sourceoffs; - auto const destavail = destlen - destoffs; - auto const bytes_to_copy = (std::min)(sourceavail, destavail); - if (bytes_to_copy > 0) - { - std::memcpy( - reinterpret_cast(dest) + destoffs, - reinterpret_cast(source) + sourceoffs, - bytes_to_copy); - } - return bytes_to_copy; -} - - - -/*************************************************************************** - core_osd_file -***************************************************************************/ +//************************************************************************** +// core_osd_file +//************************************************************************** -/*------------------------------------------------- - closes a file --------------------------------------------------*/ +//------------------------------------------------- +// closes a file +//------------------------------------------------- core_osd_file::~core_osd_file() { @@ -702,15 +704,15 @@ core_osd_file::~core_osd_file() } -/*------------------------------------------------- - read - read from a file --------------------------------------------------*/ +//------------------------------------------------- +// read - read from a file +//------------------------------------------------- std::error_condition core_osd_file::read(void *buffer, std::size_t length, std::size_t &actual) noexcept { // since osd_file works like pread/pwrite, implement in terms of read_at // core_osd_file is declared final, so a derived class can't interfere - std::error_condition err = read_at(offset(), buffer, length, actual); + std::error_condition err = read_at(index(), buffer, length, actual); add_offset(actual); return err; } @@ -718,8 +720,11 @@ std::error_condition core_osd_file::read(void *buffer, std::size_t length, std:: std::error_condition core_osd_file::read_at(std::uint64_t offset, void *buffer, std::size_t length, std::size_t &actual) noexcept { - if (!m_file || is_loaded()) - return core_in_memory_file::read_at(offset, buffer, length, actual); + if (!m_file) + { + actual = 0U; + return std::errc::bad_file_descriptor; + } // flush any buffered char clear_putback(); @@ -768,55 +773,15 @@ std::error_condition core_osd_file::read_at(std::uint64_t offset, void *buffer, } -/*------------------------------------------------- - buffer - return a pointer to the file buffer; - if it doesn't yet exist, load the file into - RAM first --------------------------------------------------*/ - -void const *core_osd_file::buffer() -{ - // if we already have data, just return it - if (!is_loaded() && length()) - { - // allocate some memory - void *const buf = allocate(); - if (!buf) - return nullptr; - - // read the file - std::uint64_t bytes_read = 0; - std::uint64_t remaining = length(); - std::uint8_t *ptr = reinterpret_cast(buf); - while (remaining) - { - std::uint32_t const chunk = std::min >(std::numeric_limits::max(), remaining); - std::uint32_t read_length; - std::error_condition const filerr = m_file->read(ptr, bytes_read, chunk, read_length); - if (filerr || !read_length) - { - purge(); - return core_in_memory_file::buffer(); - } - bytes_read += read_length; - remaining -= read_length; - ptr += read_length; - } - m_file.reset(); // close the file because we don't need it anymore - } - return core_in_memory_file::buffer(); -} - - -/*------------------------------------------------- - write - write to a file --------------------------------------------------*/ +//------------------------------------------------- +// write - write to a file +//------------------------------------------------- std::error_condition core_osd_file::write(void const *buffer, std::size_t length, std::size_t &actual) noexcept { // since osd_file works like pread/pwrite, implement in terms of write_at // core_osd_file is declared final, so a derived class can't interfere - std::error_condition err = write_at(offset(), buffer, length, actual); + std::error_condition err = write_at(index(), buffer, length, actual); add_offset(actual); return err; } @@ -824,10 +789,6 @@ std::error_condition core_osd_file::write(void const *buffer, std::size_t length std::error_condition core_osd_file::write_at(std::uint64_t offset, void const *buffer, std::size_t length, std::size_t &actual) noexcept { - // can't write to RAM-based stuff - if (is_loaded()) - return core_in_memory_file::write_at(offset, buffer, length, actual); - // flush any buffered char clear_putback(); @@ -849,50 +810,41 @@ std::error_condition core_osd_file::write_at(std::uint64_t offset, void const *b buffer = reinterpret_cast(buffer) + bytes_written; length -= bytes_written; actual += bytes_written; - set_length((std::max)(this->length(), offset)); + set_size((std::max)(size(), offset)); } return std::error_condition(); } -/*------------------------------------------------- - truncate - truncate a file --------------------------------------------------*/ +//------------------------------------------------- +// truncate - truncate a file +//------------------------------------------------- std::error_condition core_osd_file::truncate(std::uint64_t offset) { - if (is_loaded()) - return core_in_memory_file::truncate(offset); - // truncate file std::error_condition err = m_file->truncate(offset); if (err) return err; // and adjust to new length and offset - set_length(offset); + set_size(offset); return std::error_condition(); } -/*------------------------------------------------- - flush - flush file buffers --------------------------------------------------*/ +//------------------------------------------------- +// flush - flush file buffers +//------------------------------------------------- std::error_condition core_osd_file::finalize() noexcept { - if (is_loaded()) - return core_in_memory_file::finalize(); - return std::error_condition(); } std::error_condition core_osd_file::flush() noexcept { - if (is_loaded()) - return core_in_memory_file::flush(); - // flush any buffered char clear_putback(); @@ -906,14 +858,14 @@ std::error_condition core_osd_file::flush() noexcept -/*************************************************************************** - core_file -***************************************************************************/ +//************************************************************************** +// core_file +//************************************************************************** -/*------------------------------------------------- - open - open a file for access and - return an error code --------------------------------------------------*/ +//------------------------------------------------- +// open - open a file for access and +// return an error code +//------------------------------------------------- std::error_condition core_file::open(std::string_view filename, std::uint32_t openflags, ptr &file) noexcept { @@ -931,10 +883,10 @@ std::error_condition core_file::open(std::string_view filename, std::uint32_t op } -/*------------------------------------------------- - open_ram - open a RAM-based buffer for file- - like access and return an error code --------------------------------------------------*/ +//------------------------------------------------- +// open_ram - open a RAM-based buffer for file- +// like access and return an error code +//------------------------------------------------- std::error_condition core_file::open_ram(void const *data, std::size_t length, std::uint32_t openflags, ptr &file) noexcept { @@ -959,11 +911,11 @@ std::error_condition core_file::open_ram(void const *data, std::size_t length, s } -/*------------------------------------------------- - open_ram_copy - open a copy of a RAM-based - buffer for file-like access and return an - error code --------------------------------------------------*/ +//------------------------------------------------- +// open_ram_copy - open a copy of a RAM-based +// buffer for file-like access and return an +// error code +//------------------------------------------------- std::error_condition core_file::open_ram_copy(void const *data, std::size_t length, std::uint32_t openflags, ptr &file) noexcept { @@ -979,7 +931,7 @@ std::error_condition core_file::open_ram_copy(void const *data, std::size_t leng if (std::uint64_t(length) != length) return std::errc::file_too_large; - ptr result(new (std::nothrow) core_in_memory_file(openflags, data, length, true)); + std::unique_ptr result(new (std::nothrow) core_in_memory_file(openflags, data, length, true)); if (!result || !result->buffer()) return std::errc::not_enough_memory; @@ -988,10 +940,10 @@ std::error_condition core_file::open_ram_copy(void const *data, std::size_t leng } -/*------------------------------------------------- - open_proxy - open a proxy to an existing file - object and return an error code --------------------------------------------------*/ +//------------------------------------------------- +// open_proxy - open a proxy to an existing file +// object and return an error code +//------------------------------------------------- std::error_condition core_file::open_proxy(core_file &file, ptr &proxy) noexcept { @@ -1004,20 +956,20 @@ std::error_condition core_file::open_proxy(core_file &file, ptr &proxy) noexcept } -/*------------------------------------------------- - closes a file --------------------------------------------------*/ +//------------------------------------------------- +// closes a file +//------------------------------------------------- core_file::~core_file() { } -/*------------------------------------------------- - load - open a file with the specified - filename, read it into memory, and return a - pointer --------------------------------------------------*/ +//------------------------------------------------- +// load - open a file with the specified +// filename, read it into memory, and return a +// pointer +//------------------------------------------------- std::error_condition core_file::load(std::string_view filename, void **data, std::uint32_t &length) noexcept { diff --git a/src/lib/util/corefile.h b/src/lib/util/corefile.h index 802dd1348a1..c2731a1c4ed 100644 --- a/src/lib/util/corefile.h +++ b/src/lib/util/corefile.h @@ -76,10 +76,6 @@ public: // read a full line of text from the file virtual char *gets(char *s, int n) = 0; - // get a pointer to a buffer that holds the full file data in RAM - // this function may cause the full file data to be read - virtual const void *buffer() = 0; - // open a file with the specified filename, read it into memory, and return a pointer static std::error_condition load(std::string_view filename, void **data, std::uint32_t &length) noexcept; static std::error_condition load(std::string_view filename, std::vector &data) noexcept; diff --git a/src/lib/util/hash.cpp b/src/lib/util/hash.cpp index a521e737192..8cdf41ba0e9 100644 --- a/src/lib/util/hash.cpp +++ b/src/lib/util/hash.cpp @@ -12,8 +12,11 @@ #include "hash.h" +#include "ioprocs.h" + #include #include +#include namespace util { @@ -27,48 +30,68 @@ char const *const hash_collection::HASH_TYPES_CRC_SHA1 = "RS"; char const *const hash_collection::HASH_TYPES_ALL = "RS"; - //************************************************************************** -// HASH COLLECTION +// HASH CREATOR //************************************************************************** -//------------------------------------------------- -// hash_collection - constructor -//------------------------------------------------- - -hash_collection::hash_collection() - : m_has_crc32(false), - m_has_sha1(false), - m_creator(nullptr) +class hash_collection::hash_creator { -} +public: + // constructor + hash_creator(bool doing_crc32, bool doing_sha1) + { + if (doing_crc32) + m_crc32_creator.emplace(); + if (doing_sha1) + m_sha1_creator.emplace(); + } + // add the given buffer to the hash + void append(void const *buffer, std::size_t length) + { + // append to each active hash + if (m_crc32_creator) + m_crc32_creator->append(buffer, length); + if (m_sha1_creator) + m_sha1_creator->append(buffer, length); + } -hash_collection::hash_collection(std::string_view string) - : m_has_crc32(false), - m_has_sha1(false), - m_creator(nullptr) -{ - from_internal_string(string); -} + // stop hashing + void finish(hash_collection &hashes) + { + // finish up the CRC32 + if (m_crc32_creator) + { + hashes.add_crc(m_crc32_creator->finish()); + m_crc32_creator.reset(); + } + // finish up the SHA1 + if (m_sha1_creator) + { + hashes.add_sha1(m_sha1_creator->finish()); + m_sha1_creator.reset(); + } + } -hash_collection::hash_collection(const hash_collection &src) - : m_has_crc32(false), - m_has_sha1(false), - m_creator(nullptr) -{ - copyfrom(src); -} +private: + std::optional m_crc32_creator; + std::optional m_sha1_creator; +}; +//************************************************************************** +// HASH COLLECTION +//************************************************************************** + //------------------------------------------------- -// ~hash_collection - destructor +// hash_collection - constructor //------------------------------------------------- -hash_collection::~hash_collection() +hash_collection::hash_collection() + : m_has_crc32(false) + , m_has_sha1(false) { - delete m_creator; } @@ -138,8 +161,6 @@ void hash_collection::reset() { m_flags.clear(); m_has_crc32 = m_has_sha1 = false; - delete m_creator; - m_creator = nullptr; } @@ -336,69 +357,75 @@ bool hash_collection::from_internal_string(std::string_view string) //------------------------------------------------- -// begin - begin hashing +// create - begin hashing //------------------------------------------------- -void hash_collection::begin(const char *types) +std::unique_ptr hash_collection::create(const char *types) { - // nuke previous creator and make a new one - delete m_creator; - m_creator = new hash_creator; - // by default use all types if (types == nullptr) - m_creator->m_doing_crc32 = m_creator->m_doing_sha1 = true; + return std::make_unique(true, true); // otherwise, just allocate the ones that are specified else - { - m_creator->m_doing_crc32 = (strchr(types, HASH_CRC) != nullptr); - m_creator->m_doing_sha1 = (strchr(types, HASH_SHA1) != nullptr); - } + return std::make_unique(strchr(types, HASH_CRC) != nullptr, strchr(types, HASH_SHA1) != nullptr); } //------------------------------------------------- -// buffer - add the given buffer to the hash +// compute - hash a block of data //------------------------------------------------- -void hash_collection::buffer(const uint8_t *data, uint32_t length) +void hash_collection::compute(const uint8_t *data, uint32_t length, const char *types) { - assert(m_creator != nullptr); + // begin + std::unique_ptr creator = create(types); - // append to each active hash - if (m_creator->m_doing_crc32) - m_creator->m_crc32_creator.append(data, length); - if (m_creator->m_doing_sha1) - m_creator->m_sha1_creator.append(data, length); + // run the hashes + creator->append(data, length); + + // end + creator->finish(*this); } //------------------------------------------------- -// end - stop hashing +// compute - hash data from a stream //------------------------------------------------- -void hash_collection::end() +std::error_condition hash_collection::compute(random_read &stream, uint64_t offset, size_t length, size_t &actual, const char *types) { - assert(m_creator != nullptr); + // begin + std::unique_ptr creator = create(types); - // finish up the CRC32 - if (m_creator->m_doing_crc32) - { - m_has_crc32 = true; - m_crc32 = m_creator->m_crc32_creator.finish(); - } + // local buffer of arbitrary size + uint8_t buffer[2048]; - // finish up the SHA1 - if (m_creator->m_doing_sha1) + // run the hashes + actual = 0U; + while (length) { - m_has_sha1 = true; - m_sha1 = m_creator->m_sha1_creator.finish(); + // determine the size of the next chunk + unsigned const chunk_length = std::min(length, sizeof(buffer)); + + // read one chunk + std::size_t bytes_read; + std::error_condition err = stream.read_at(offset, buffer, chunk_length, bytes_read); + if (err) + return err; + if (!bytes_read) // EOF? + break; + offset += bytes_read; + length -= chunk_length; + + // append the chunk + creator->append(buffer, bytes_read); + actual += bytes_read; } - // nuke the creator - delete m_creator; - m_creator = nullptr; + // end + creator->finish(*this); + return std::error_condition(); } @@ -417,9 +444,6 @@ void hash_collection::copyfrom(const hash_collection &src) m_crc32 = src.m_crc32; m_has_sha1 = src.m_has_sha1; m_sha1 = src.m_sha1; - - // don't copy creators - m_creator = nullptr; } } // namespace util diff --git a/src/lib/util/hash.h b/src/lib/util/hash.h index b13ac5556ae..5fa4f787097 100644 --- a/src/lib/util/hash.h +++ b/src/lib/util/hash.h @@ -16,6 +16,10 @@ #pragma once #include "hashing.h" +#include "utilfwd.h" + +#include +#include //************************************************************************** @@ -40,6 +44,9 @@ namespace util { // a collection of the various supported hashes and flags class hash_collection { + // forward declaration + class hash_creator; + public: // hash types are identified by non-hex alpha values (G-Z) static constexpr char HASH_CRC = 'R'; @@ -56,9 +63,8 @@ public: // construction/destruction hash_collection(); - hash_collection(std::string_view string); - hash_collection(const hash_collection &src); - ~hash_collection(); + hash_collection(std::string_view string) : hash_collection() { from_internal_string(string); } + hash_collection(const hash_collection &src) : hash_collection() { copyfrom(src); } // operators hash_collection &operator=(const hash_collection &src); @@ -89,13 +95,12 @@ public: bool from_internal_string(std::string_view string); // creation - void begin(const char *types = nullptr); - void buffer(const uint8_t *data, uint32_t length); - void end(); - void compute(const uint8_t *data, uint32_t length, const char *types = nullptr) { begin(types); buffer(data, length); end(); } + void compute(const uint8_t *data, uint32_t length, const char *types = nullptr); + std::error_condition compute(random_read &stream, uint64_t offset, size_t length, size_t &actual, const char *types = nullptr); private: // internal helpers + std::unique_ptr create(const char *types = nullptr); void copyfrom(const hash_collection &src); // internal state @@ -104,16 +109,6 @@ private: crc32_t m_crc32; bool m_has_sha1; sha1_t m_sha1; - - // creators - struct hash_creator - { - bool m_doing_crc32; - crc32_creator m_crc32_creator; - bool m_doing_sha1; - sha1_creator m_sha1_creator; - }; - hash_creator * m_creator; }; diff --git a/src/tools/romcmp.cpp b/src/tools/romcmp.cpp index b65889c70a1..01a234b9736 100644 --- a/src/tools/romcmp.cpp +++ b/src/tools/romcmp.cpp @@ -211,9 +211,7 @@ struct fileinfo { printf("%-23s %-23s 1ST AND 2ND HALF IDENTICAL\n", (side & 1) ? name.c_str() : "", (side & 2) ? name.c_str() : ""); util::hash_collection hash; - hash.begin(); - hash.buffer(buf.get(), size / 2); - hash.end(); + hash.compute(buf.get(), size / 2); printf("%-23s %-23s %s\n", (side & 1) ? name.c_str() : "", (side & 2) ? name.c_str() : "", hash.attribute_string().c_str()); } else -- cgit v1.2.3