From 46521af5592925ee8bae47bb64ee6cc7f9b886d8 Mon Sep 17 00:00:00 2001 From: Vas Crabb Date: Sun, 13 Oct 2024 01:55:43 +1100 Subject: util/chd.cpp: Made a few more member functions return error conditions. Also mark error paths as unexpected to optimise against them. --- src/lib/util/chd.cpp | 963 ++++++++++++++++++++++++++------------------------- src/lib/util/chd.h | 12 +- 2 files changed, 493 insertions(+), 482 deletions(-) diff --git a/src/lib/util/chd.cpp b/src/lib/util/chd.cpp index 1f663fedd2a..14484575e7f 100644 --- a/src/lib/util/chd.cpp +++ b/src/lib/util/chd.cpp @@ -2,8 +2,6 @@ // copyright-holders:Aaron Giles /*************************************************************************** - chd.c - MAME Compressed Hunks of Data file format ***************************************************************************/ @@ -22,6 +20,7 @@ #include +#include #include #include #include @@ -156,48 +155,45 @@ inline void chd_file::be_write_sha1(uint8_t *base, util::sha1_t value) noexcept //------------------------------------------------- // file_read - read from the file at the given -// offset; on failure throw an error +// offset. //------------------------------------------------- -inline void chd_file::file_read(uint64_t offset, void *dest, uint32_t length) const +inline std::error_condition chd_file::file_read(uint64_t offset, void *dest, uint32_t length) const noexcept { // no file = failure - if (!m_file) - throw std::error_condition(error::NOT_OPEN); + if (UNEXPECTED(!m_file)) + return std::error_condition(error::NOT_OPEN); // seek and read std::error_condition err; err = m_file->seek(offset, SEEK_SET); - if (err) - throw err; + if (UNEXPECTED(err)) + return err; size_t count; std::tie(err, count) = read(*m_file, dest, length); - if (err) - throw err; - else if (count != length) - throw std::error_condition(std::errc::io_error); // TODO: revisit this error code (happens if file is truncated) + if (UNEXPECTED(!err && (count != length))) + return std::error_condition(std::errc::io_error); // TODO: revisit this error code (happens if file is truncated) + return err; } //------------------------------------------------- // file_write - write to the file at the given -// offset; on failure throw an error +// offset. //------------------------------------------------- -inline void chd_file::file_write(uint64_t offset, const void *source, uint32_t length) +inline std::error_condition chd_file::file_write(uint64_t offset, const void *source, uint32_t length) noexcept { // no file = failure - if (!m_file) - throw std::error_condition(error::NOT_OPEN); + if (UNEXPECTED(!m_file)) + return std::error_condition(error::NOT_OPEN); // seek and write std::error_condition err; err = m_file->seek(offset, SEEK_SET); - if (err) - throw err; - std::tie(err, std::ignore) = write(*m_file, source, length); - if (err) - throw err; + if (UNEXPECTED(err)) + return err; + return write(*m_file, source, length).first; } @@ -209,21 +205,20 @@ inline void chd_file::file_write(uint64_t offset, const void *source, uint32_t l inline uint64_t chd_file::file_append(const void *source, uint32_t length, uint32_t alignment) { - std::error_condition err; - // no file = failure - if (!m_file) + if (UNEXPECTED(!m_file)) throw std::error_condition(error::NOT_OPEN); // seek to the end and align if necessary + std::error_condition err; err = m_file->seek(0, SEEK_END); - if (err) + if (UNEXPECTED(err)) throw err; if (alignment != 0) { uint64_t offset; err = m_file->tell(offset); - if (err) + if (UNEXPECTED(err)) throw err; uint32_t delta = offset % alignment; if (delta != 0) @@ -237,7 +232,7 @@ inline uint64_t chd_file::file_append(const void *source, uint32_t length, uint3 uint32_t bytes_to_write = std::min(sizeof(buffer), delta); size_t count; std::tie(err, count) = write(*m_file, buffer, bytes_to_write); - if (err) + if (UNEXPECTED(err)) throw err; delta -= count; } @@ -247,10 +242,10 @@ inline uint64_t chd_file::file_append(const void *source, uint32_t length, uint3 // write the real data uint64_t offset; err = m_file->tell(offset); - if (err) + if (UNEXPECTED(err)) throw err; std::tie(err, std::ignore) = write(*m_file, source, length); - if (err) + if (UNEXPECTED(err)) throw err; return offset; } @@ -261,11 +256,14 @@ inline uint64_t chd_file::file_append(const void *source, uint32_t length, uint3 // necessary to represent all numbers 0..value //------------------------------------------------- -inline uint8_t chd_file::bits_for_value(uint64_t value) +inline uint8_t chd_file::bits_for_value(uint64_t value) noexcept { uint8_t result = 0; while (value != 0) - value >>= 1, result++; + { + value >>= 1; + result++; + } return result; } @@ -341,18 +339,12 @@ bool chd_file::parent_missing() const noexcept util::sha1_t chd_file::sha1() const noexcept { - try - { - // read the big-endian version - uint8_t rawbuf[sizeof(util::sha1_t)]; - file_read(m_sha1_offset, rawbuf, sizeof(rawbuf)); - return be_read_sha1(rawbuf); - } - catch (std::error_condition const &) - { - // on failure, return null - return util::sha1_t::null; - } + // read the big-endian version + uint8_t rawbuf[sizeof(util::sha1_t)]; + std::error_condition err = file_read(m_sha1_offset, rawbuf, sizeof(rawbuf)); + if (UNEXPECTED(err)) + return util::sha1_t::null; // on failure, return null + return be_read_sha1(rawbuf); } /** @@ -373,12 +365,14 @@ util::sha1_t chd_file::raw_sha1() const noexcept try { // determine offset within the file for data-only - if (!m_rawsha1_offset) + if (UNEXPECTED(!m_rawsha1_offset)) throw std::error_condition(error::UNSUPPORTED_VERSION); // read the big-endian version uint8_t rawbuf[sizeof(util::sha1_t)]; - file_read(m_rawsha1_offset, rawbuf, sizeof(rawbuf)); + std::error_condition err = file_read(m_rawsha1_offset, rawbuf, sizeof(rawbuf)); + if (UNEXPECTED(err)) + throw err; return be_read_sha1(rawbuf); } catch (std::error_condition const &) @@ -406,12 +400,14 @@ util::sha1_t chd_file::parent_sha1() const noexcept try { // determine offset within the file - if (!m_parentsha1_offset) + if (UNEXPECTED(!m_parentsha1_offset)) throw std::error_condition(error::UNSUPPORTED_VERSION); // read the big-endian version uint8_t rawbuf[sizeof(util::sha1_t)]; - file_read(m_parentsha1_offset, rawbuf, sizeof(rawbuf)); + std::error_condition err = file_read(m_parentsha1_offset, rawbuf, sizeof(rawbuf)); + if (UNEXPECTED(err)) + throw err; return be_read_sha1(rawbuf); } catch (std::error_condition const &) @@ -442,49 +438,51 @@ std::error_condition chd_file::hunk_info(uint32_t hunknum, chd_codec_type &compr return std::error_condition(error::HUNK_OUT_OF_RANGE); // get the map pointer - uint8_t *rawmap; switch (m_version) { - // v3/v4 map entries - case 3: - case 4: - rawmap = &m_rawmap[16 * hunknum]; + // v3/v4 map entries + case 3: + case 4: + { + uint8_t const *const rawmap = &m_rawmap[16 * hunknum]; switch (rawmap[15] & V34_MAP_ENTRY_FLAG_TYPE_MASK) { - case V34_MAP_ENTRY_TYPE_COMPRESSED: - compressor = CHD_CODEC_ZLIB; - compbytes = get_u16be(&rawmap[12]) + (rawmap[14] << 16); - break; + case V34_MAP_ENTRY_TYPE_COMPRESSED: + compressor = CHD_CODEC_ZLIB; + compbytes = get_u16be(&rawmap[12]) + (rawmap[14] << 16); + break; - case V34_MAP_ENTRY_TYPE_UNCOMPRESSED: - compressor = CHD_CODEC_NONE; - compbytes = m_hunkbytes; - break; + case V34_MAP_ENTRY_TYPE_UNCOMPRESSED: + compressor = CHD_CODEC_NONE; + compbytes = m_hunkbytes; + break; - case V34_MAP_ENTRY_TYPE_MINI: - compressor = CHD_CODEC_MINI; - compbytes = 0; - break; + case V34_MAP_ENTRY_TYPE_MINI: + compressor = CHD_CODEC_MINI; + compbytes = 0; + break; - case V34_MAP_ENTRY_TYPE_SELF_HUNK: - compressor = CHD_CODEC_SELF; - compbytes = 0; - break; + case V34_MAP_ENTRY_TYPE_SELF_HUNK: + compressor = CHD_CODEC_SELF; + compbytes = 0; + break; - case V34_MAP_ENTRY_TYPE_PARENT_HUNK: - compressor = CHD_CODEC_PARENT; - compbytes = 0; - break; + case V34_MAP_ENTRY_TYPE_PARENT_HUNK: + compressor = CHD_CODEC_PARENT; + compbytes = 0; + break; } - break; + } + break; - // v5 map entries - case 5: - rawmap = &m_rawmap[m_mapentrybytes * hunknum]; + // v5 map entries + case 5: + { + uint8_t const *const rawmap = &m_rawmap[m_mapentrybytes * hunknum]; - // uncompressed case if (!compressed()) { + // uncompressed case if (get_u32be(&rawmap[0]) == 0) { compressor = CHD_CODEC_PARENT; @@ -495,12 +493,12 @@ std::error_condition chd_file::hunk_info(uint32_t hunknum, chd_codec_type &compr compressor = CHD_CODEC_NONE; compbytes = m_hunkbytes; } - break; } - - // compressed case - switch (rawmap[0]) + else { + // compressed case + switch (rawmap[0]) + { case COMPRESSION_TYPE_0: case COMPRESSION_TYPE_1: case COMPRESSION_TYPE_2: @@ -526,9 +524,12 @@ std::error_condition chd_file::hunk_info(uint32_t hunknum, chd_codec_type &compr default: return error::UNKNOWN_COMPRESSION; + } } - break; + } + break; } + return std::error_condition(); } @@ -551,11 +552,13 @@ std::error_condition chd_file::set_raw_sha1(util::sha1_t rawdata) noexcept uint8_t rawbuf[sizeof(util::sha1_t)]; be_write_sha1(rawbuf, rawdata); + // write to the header + std::error_condition err = file_write(offset, rawbuf, sizeof(rawbuf)); + if (UNEXPECTED(err)) + return err; + try { - // write to the header - file_write(offset, rawbuf, sizeof(rawbuf)); - // if we have a separate rawsha1_offset, update the full sha1 as well if (m_rawsha1_offset != 0) metadata_update_hash(); @@ -587,7 +590,7 @@ std::error_condition chd_file::set_raw_sha1(util::sha1_t rawdata) noexcept std::error_condition chd_file::set_parent_sha1(util::sha1_t parent) noexcept { // if no file, fail - if (!m_file) + if (UNEXPECTED(!m_file)) return std::error_condition(error::INVALID_FILE); assert(m_parentsha1_offset != 0); @@ -597,10 +600,7 @@ std::error_condition chd_file::set_parent_sha1(util::sha1_t parent) noexcept be_write_sha1(rawbuf, parent); // write to the header - try { file_write(m_parentsha1_offset, rawbuf, sizeof(rawbuf)); } - catch (std::error_condition const &err) { return err; } - - return std::error_condition(); + return file_write(m_parentsha1_offset, rawbuf, sizeof(rawbuf)); } /** @@ -627,9 +627,9 @@ std::error_condition chd_file::create( const chd_codec_type (&compression)[4]) { // make sure we don't already have a file open - if (m_file) + if (UNEXPECTED(m_file)) return error::ALREADY_OPEN; - else if (!file) + else if (UNEXPECTED(!file)) return std::errc::invalid_argument; // set the header parameters @@ -668,9 +668,9 @@ std::error_condition chd_file::create( chd_file &parent) { // make sure we don't already have a file open - if (m_file) + if (UNEXPECTED(m_file)) return error::ALREADY_OPEN; - else if (!file) + else if (UNEXPECTED(!file)) return std::errc::invalid_argument; // set the header parameters @@ -709,20 +709,20 @@ std::error_condition chd_file::create( const chd_codec_type (&compression)[4]) { // make sure we don't already have a file open - if (m_file) + if (UNEXPECTED(m_file)) return error::ALREADY_OPEN; // create the new file util::core_file::ptr file; std::error_condition filerr = util::core_file::open(filename, OPEN_FLAG_READ | OPEN_FLAG_WRITE | OPEN_FLAG_CREATE, file); - if (filerr) + if (UNEXPECTED(filerr)) return filerr; // create the file normally, then claim the file std::error_condition chderr = create(std::move(file), logicalbytes, hunkbytes, unitbytes, compression); // if an error happened, close and delete the file - if (chderr) + if (UNEXPECTED(chderr)) { file.reset(); osd_file::remove(std::string(filename)); // FIXME: allow osd_file to use std::string_view @@ -754,20 +754,20 @@ std::error_condition chd_file::create( chd_file &parent) { // make sure we don't already have a file open - if (m_file) + if (UNEXPECTED(m_file)) return error::ALREADY_OPEN; // create the new file util::core_file::ptr file; std::error_condition filerr = util::core_file::open(filename, OPEN_FLAG_READ | OPEN_FLAG_WRITE | OPEN_FLAG_CREATE, file); - if (filerr) + if (UNEXPECTED(filerr)) return filerr; // create the file normally, then claim the file std::error_condition chderr = create(std::move(file), logicalbytes, hunkbytes, compression, parent); // if an error happened, close and delete the file - if (chderr) + if (UNEXPECTED(chderr)) { file.reset(); osd_file::remove(std::string(filename)); // FIXME: allow osd_file to use std::string_view @@ -796,14 +796,14 @@ std::error_condition chd_file::open( const open_parent_func &open_parent) { // make sure we don't already have a file open - if (m_file) + if (UNEXPECTED(m_file)) return error::ALREADY_OPEN; // open the file const uint32_t openflags = writeable ? (OPEN_FLAG_READ | OPEN_FLAG_WRITE) : OPEN_FLAG_READ; util::core_file::ptr file; std::error_condition filerr = util::core_file::open(filename, openflags, file); - if (filerr) + if (UNEXPECTED(filerr)) return filerr; // now open the CHD @@ -831,9 +831,9 @@ std::error_condition chd_file::open( const open_parent_func &open_parent) { // make sure we don't already have a file open - if (m_file) + if (UNEXPECTED(m_file)) return error::ALREADY_OPEN; - else if (!file) + else if (UNEXPECTED(!file)) return std::errc::invalid_argument; // open the file @@ -867,7 +867,7 @@ void chd_file::close() m_hunkcount = 0; m_unitbytes = 0; m_unitcount = 0; - memset(m_compression, 0, sizeof(m_compression)); + std::fill(std::begin(m_compression), std::end(m_compression), 0); m_parent.reset(); m_parent_missing = false; @@ -912,81 +912,90 @@ void chd_file::close() * @param hunknum The hunknum. * @param [in,out] buffer If non-null, the buffer. * - * @return The hunk. + * @return An error condition. */ std::error_condition chd_file::read_hunk(uint32_t hunknum, void *buffer) { + // punt if no file + if (UNEXPECTED(!m_file)) + return std::error_condition(error::NOT_OPEN); + + // return an error if out of range + if (UNEXPECTED(hunknum >= m_hunkcount)) + return std::error_condition(error::HUNK_OUT_OF_RANGE); + + auto *const dest = reinterpret_cast(buffer); + // wrap this for clean reporting try { - // punt if no file - if (!m_file) - throw std::error_condition(error::NOT_OPEN); - - // return an error if out of range - if (hunknum >= m_hunkcount) - throw std::error_condition(error::HUNK_OUT_OF_RANGE); - // get a pointer to the map entry - uint64_t blockoffs; - uint32_t blocklen; - util::crc32_t blockcrc; - uint8_t *rawmap; - auto *dest = reinterpret_cast(buffer); switch (m_version) { - // v3/v4 map entries - case 3: - case 4: - rawmap = &m_rawmap[16 * hunknum]; - blockoffs = get_u64be(&rawmap[0]); - blockcrc = get_u32be(&rawmap[8]); + // v3/v4 map entries + case 3: + case 4: + { + uint8_t const *const rawmap = &m_rawmap[16 * hunknum]; + uint64_t const blockoffs = get_u64be(&rawmap[0]); + util::crc32_t const blockcrc = get_u32be(&rawmap[8]); + bool const nocrc = rawmap[15] & V34_MAP_ENTRY_FLAG_NO_CRC; switch (rawmap[15] & V34_MAP_ENTRY_FLAG_TYPE_MASK) { - case V34_MAP_ENTRY_TYPE_COMPRESSED: - blocklen = get_u16be(&rawmap[12]) + (rawmap[14] << 16); - file_read(blockoffs, &m_compressed[0], blocklen); + case V34_MAP_ENTRY_TYPE_COMPRESSED: + { + uint32_t const blocklen = get_u16be(&rawmap[12]) | (uint32_t(rawmap[14]) << 16); + std::error_condition err = file_read(blockoffs, &m_compressed[0], blocklen); + if (UNEXPECTED(err)) + return err; m_decompressor[0]->decompress(&m_compressed[0], blocklen, dest, m_hunkbytes); - if (!(rawmap[15] & V34_MAP_ENTRY_FLAG_NO_CRC) && dest != nullptr && util::crc32_creator::simple(dest, m_hunkbytes) != blockcrc) - throw std::error_condition(error::DECOMPRESSION_ERROR); + if (UNEXPECTED(!nocrc && (util::crc32_creator::simple(dest, m_hunkbytes) != blockcrc))) + return std::error_condition(error::DECOMPRESSION_ERROR); return std::error_condition(); + } - case V34_MAP_ENTRY_TYPE_UNCOMPRESSED: - file_read(blockoffs, dest, m_hunkbytes); - if (!(rawmap[15] & V34_MAP_ENTRY_FLAG_NO_CRC) && util::crc32_creator::simple(dest, m_hunkbytes) != blockcrc) - throw std::error_condition(error::DECOMPRESSION_ERROR); + case V34_MAP_ENTRY_TYPE_UNCOMPRESSED: + { + std::error_condition err = file_read(blockoffs, dest, m_hunkbytes); + if (UNEXPECTED(err)) + return err; + if (UNEXPECTED(!nocrc && (util::crc32_creator::simple(dest, m_hunkbytes) != blockcrc))) + return std::error_condition(error::DECOMPRESSION_ERROR); return std::error_condition(); + } - case V34_MAP_ENTRY_TYPE_MINI: - put_u64be(dest, blockoffs); - for (uint32_t bytes = 8; bytes < m_hunkbytes; bytes++) - dest[bytes] = dest[bytes - 8]; - if (!(rawmap[15] & V34_MAP_ENTRY_FLAG_NO_CRC) && util::crc32_creator::simple(dest, m_hunkbytes) != blockcrc) - throw std::error_condition(error::DECOMPRESSION_ERROR); - return std::error_condition(); + case V34_MAP_ENTRY_TYPE_MINI: + put_u64be(dest, blockoffs); + for (uint32_t bytes = 8; bytes < m_hunkbytes; bytes++) + dest[bytes] = dest[bytes - 8]; + if (UNEXPECTED(!nocrc && (util::crc32_creator::simple(dest, m_hunkbytes) != blockcrc))) + return std::error_condition(error::DECOMPRESSION_ERROR); + return std::error_condition(); - case V34_MAP_ENTRY_TYPE_SELF_HUNK: - return read_hunk(blockoffs, dest); + case V34_MAP_ENTRY_TYPE_SELF_HUNK: + return read_hunk(blockoffs, dest); - case V34_MAP_ENTRY_TYPE_PARENT_HUNK: - if (m_parent_missing) - throw std::error_condition(error::REQUIRES_PARENT); - return m_parent->read_hunk(blockoffs, dest); + case V34_MAP_ENTRY_TYPE_PARENT_HUNK: + if (UNEXPECTED(m_parent_missing)) + return std::error_condition(error::REQUIRES_PARENT); + return m_parent->read_hunk(blockoffs, dest); } - break; + } + break; - // v5 map entries - case 5: - rawmap = &m_rawmap[m_mapentrybytes * hunknum]; + // v5 map entries + case 5: + { + uint8_t const *const rawmap = &m_rawmap[m_mapentrybytes * hunknum]; - // uncompressed case if (!compressed()) { - blockoffs = mulu_32x32(get_u32be(rawmap), m_hunkbytes); + // uncompressed case + uint64_t const blockoffs = mulu_32x32(get_u32be(rawmap), m_hunkbytes); if (blockoffs != 0) - file_read(blockoffs, dest, m_hunkbytes); - else if (m_parent_missing) + return file_read(blockoffs, dest, m_hunkbytes); + else if (UNEXPECTED(m_parent_missing)) return std::error_condition(error::REQUIRES_PARENT); else if (m_parent) return m_parent->read_hunk(hunknum, dest); @@ -994,44 +1003,57 @@ std::error_condition chd_file::read_hunk(uint32_t hunknum, void *buffer) memset(dest, 0, m_hunkbytes); return std::error_condition(); } - - // compressed case - blocklen = get_u24be(&rawmap[1]); - blockoffs = get_u48be(&rawmap[4]); - blockcrc = get_u16be(&rawmap[10]); - switch (rawmap[0]) + else { + // compressed case + uint32_t const blocklen = get_u24be(&rawmap[1]); + uint64_t const blockoffs = get_u48be(&rawmap[4]); + util::crc16_t const blockcrc = get_u16be(&rawmap[10]); + switch (rawmap[0]) + { case COMPRESSION_TYPE_0: case COMPRESSION_TYPE_1: case COMPRESSION_TYPE_2: case COMPRESSION_TYPE_3: - file_read(blockoffs, &m_compressed[0], blocklen); - m_decompressor[rawmap[0]]->decompress(&m_compressed[0], blocklen, dest, m_hunkbytes); - if (!m_decompressor[rawmap[0]]->lossy() && dest != nullptr && util::crc16_creator::simple(dest, m_hunkbytes) != blockcrc) - throw std::error_condition(error::DECOMPRESSION_ERROR); - if (m_decompressor[rawmap[0]]->lossy() && util::crc16_creator::simple(&m_compressed[0], blocklen) != blockcrc) - throw std::error_condition(error::DECOMPRESSION_ERROR); - return std::error_condition(); + { + std::error_condition err = file_read(blockoffs, &m_compressed[0], blocklen); + if (UNEXPECTED(err)) + return err; + auto &decompressor = *m_decompressor[rawmap[0]]; + decompressor.decompress(&m_compressed[0], blocklen, dest, m_hunkbytes); + util::crc16_t const calculated = !decompressor.lossy() + ? util::crc16_creator::simple(dest, m_hunkbytes) + : util::crc16_creator::simple(&m_compressed[0], blocklen); + if (UNEXPECTED(calculated != blockcrc)) + return std::error_condition(error::DECOMPRESSION_ERROR); + return std::error_condition(); + } case COMPRESSION_NONE: - file_read(blockoffs, dest, m_hunkbytes); - if (util::crc16_creator::simple(dest, m_hunkbytes) != blockcrc) - throw std::error_condition(error::DECOMPRESSION_ERROR); - return std::error_condition(); + { + std::error_condition err = file_read(blockoffs, dest, m_hunkbytes); + if (UNEXPECTED(err)) + return err; + if (UNEXPECTED(util::crc16_creator::simple(dest, m_hunkbytes) != blockcrc)) + return std::error_condition(error::DECOMPRESSION_ERROR); + return std::error_condition(); + } case COMPRESSION_SELF: return read_hunk(blockoffs, dest); case COMPRESSION_PARENT: - if (m_parent_missing) - throw std::error_condition(error::REQUIRES_PARENT); + if (UNEXPECTED(m_parent_missing)) + return std::error_condition(error::REQUIRES_PARENT); return m_parent->read_bytes(uint64_t(blockoffs) * uint64_t(m_parent->unit_bytes()), dest, m_hunkbytes); + } } break; + } } - // if we get here, something was wrong - throw std::error_condition(std::errc::io_error); + // if we get here, the map contained an unsupported block type + return std::error_condition(error::INVALID_DATA); } catch (std::error_condition const &err) { @@ -1061,68 +1083,73 @@ std::error_condition chd_file::read_hunk(uint32_t hunknum, void *buffer) std::error_condition chd_file::write_hunk(uint32_t hunknum, const void *buffer) { - // wrap this for clean reporting - try - { - // punt if no file - if (!m_file) - throw std::error_condition(error::NOT_OPEN); + // punt if no file + if (UNEXPECTED(!m_file)) + return std::error_condition(error::NOT_OPEN); - // return an error if out of range - if (hunknum >= m_hunkcount) - throw std::error_condition(error::HUNK_OUT_OF_RANGE); + // return an error if out of range + if (UNEXPECTED(hunknum >= m_hunkcount)) + return std::error_condition(error::HUNK_OUT_OF_RANGE); - // if not writeable, fail - if (!m_allow_writes) - throw std::error_condition(error::FILE_NOT_WRITEABLE); + // if not writeable, fail + if (UNEXPECTED(!m_allow_writes)) + return std::error_condition(error::FILE_NOT_WRITEABLE); - // uncompressed writes only via this interface - if (compressed()) - throw std::error_condition(error::FILE_NOT_WRITEABLE); + // uncompressed writes only via this interface + if (UNEXPECTED(compressed())) + return std::error_condition(error::FILE_NOT_WRITEABLE); - // see if we have allocated the space on disk for this hunk - uint8_t *rawmap = &m_rawmap[hunknum * 4]; - uint32_t rawentry = get_u32be(rawmap); + // see if we have allocated the space on disk for this hunk + uint8_t *const rawmap = &m_rawmap[hunknum * 4]; + uint32_t rawentry = get_u32be(rawmap); - // if not, allocate one now - if (rawentry == 0) + // if not, allocate one now + if (rawentry == 0) + { + // first make sure we need to allocate it + bool all_zeros = true; + const auto *scan = reinterpret_cast(buffer); + for (uint32_t index = 0; index < m_hunkbytes / 4; index++) { - // first make sure we need to allocate it - bool all_zeros = true; - const auto *scan = reinterpret_cast(buffer); - for (uint32_t index = 0; index < m_hunkbytes / 4; index++) - if (scan[index] != 0) - { - all_zeros = false; - break; - } + if (scan[index] != 0) + { + all_zeros = false; + break; + } + } - // if it's all zeros, do nothing more - if (all_zeros) - return std::error_condition(); + // if it's all zeros, do nothing more + if (all_zeros) + return std::error_condition(); + // wrap this for clean reporting + try + { // append new data to the end of the file, aligning the first chunk rawentry = file_append(buffer, m_hunkbytes, m_hunkbytes) / m_hunkbytes; - - // write the map entry back - put_u32be(rawmap, rawentry); - file_write(m_mapoffset + hunknum * 4, rawmap, 4); - - // update the cached hunk if we just wrote it - if (hunknum == m_cachehunk && buffer != &m_cache[0]) - memcpy(&m_cache[0], buffer, m_hunkbytes); } - else + catch (std::error_condition const &err) { - // otherwise, just overwrite - file_write(uint64_t(rawentry) * uint64_t(m_hunkbytes), buffer, m_hunkbytes); + // just return errors + return err; } + + // write the map entry back + put_u32be(rawmap, rawentry); + std::error_condition err = file_write(m_mapoffset + hunknum * 4, rawmap, 4); + if (UNEXPECTED(err)) + return err; + + // update the cached hunk if we just wrote it + if (hunknum == m_cachehunk && buffer != &m_cache[0]) + memcpy(&m_cache[0], buffer, m_hunkbytes); + return std::error_condition(); } - catch (std::error_condition const &err) + else { - // just return errors - return err; + // otherwise, just overwrite + return file_write(uint64_t(rawentry) * uint64_t(m_hunkbytes), buffer, m_hunkbytes); } } @@ -1182,36 +1209,36 @@ std::error_condition chd_file::write_units(uint64_t unitnum, const void *buffer, std::error_condition chd_file::read_bytes(uint64_t offset, void *buffer, uint32_t bytes) { // iterate over hunks - uint32_t first_hunk = offset / m_hunkbytes; - uint32_t last_hunk = (offset + bytes - 1) / m_hunkbytes; + uint32_t const first_hunk = offset / m_hunkbytes; + uint32_t const last_hunk = (offset + bytes - 1) / m_hunkbytes; auto *dest = reinterpret_cast(buffer); for (uint32_t curhunk = first_hunk; curhunk <= last_hunk; curhunk++) { // determine start/end boundaries - uint32_t startoffs = (curhunk == first_hunk) ? (offset % m_hunkbytes) : 0; - uint32_t endoffs = (curhunk == last_hunk) ? ((offset + bytes - 1) % m_hunkbytes) : (m_hunkbytes - 1); + uint32_t const startoffs = (curhunk == first_hunk) ? (offset % m_hunkbytes) : 0; + uint32_t const endoffs = (curhunk == last_hunk) ? ((offset + bytes - 1) % m_hunkbytes) : (m_hunkbytes - 1); - // if it's a full block, just read directly from disk unless it's the cached hunk - std::error_condition err; - if (startoffs == 0 && endoffs == m_hunkbytes - 1 && curhunk != m_cachehunk) - err = read_hunk(curhunk, dest); - - // otherwise, read from the cache + if ((startoffs == 0) && (endoffs == m_hunkbytes - 1) && (curhunk != m_cachehunk)) + { + // if it's a full block, just read directly from disk unless it's the cached hunk + std::error_condition err = read_hunk(curhunk, dest); + if (UNEXPECTED(err)) + return err; + } else { + // otherwise, read from the cache if (curhunk != m_cachehunk) { - err = read_hunk(curhunk, &m_cache[0]); - if (err) + std::error_condition err = read_hunk(curhunk, &m_cache[0]); + if (UNEXPECTED(err)) return err; m_cachehunk = curhunk; } memcpy(dest, &m_cache[startoffs], endoffs + 1 - startoffs); } - // handle errors and advance - if (err) - return err; + // advance dest += endoffs + 1 - startoffs; } return std::error_condition(); @@ -1235,27 +1262,28 @@ std::error_condition chd_file::read_bytes(uint64_t offset, void *buffer, uint32_ std::error_condition chd_file::write_bytes(uint64_t offset, const void *buffer, uint32_t bytes) { // iterate over hunks - uint32_t first_hunk = offset / m_hunkbytes; - uint32_t last_hunk = (offset + bytes - 1) / m_hunkbytes; - const auto *source = reinterpret_cast(buffer); + uint32_t const first_hunk = offset / m_hunkbytes; + uint32_t const last_hunk = (offset + bytes - 1) / m_hunkbytes; + auto const *source = reinterpret_cast(buffer); for (uint32_t curhunk = first_hunk; curhunk <= last_hunk; curhunk++) { // determine start/end boundaries - uint32_t startoffs = (curhunk == first_hunk) ? (offset % m_hunkbytes) : 0; - uint32_t endoffs = (curhunk == last_hunk) ? ((offset + bytes - 1) % m_hunkbytes) : (m_hunkbytes - 1); + uint32_t const startoffs = (curhunk == first_hunk) ? (offset % m_hunkbytes) : 0; + uint32_t const endoffs = (curhunk == last_hunk) ? ((offset + bytes - 1) % m_hunkbytes) : (m_hunkbytes - 1); - // if it's a full block, just write directly to disk unless it's the cached hunk std::error_condition err; - if (startoffs == 0 && endoffs == m_hunkbytes - 1 && curhunk != m_cachehunk) + if ((startoffs == 0) && (endoffs == m_hunkbytes - 1) && (curhunk != m_cachehunk)) + { + // if it's a full block, just write directly to disk unless it's the cached hunk err = write_hunk(curhunk, source); - - // otherwise, write from the cache + } else { + // otherwise, write from the cache if (curhunk != m_cachehunk) { err = read_hunk(curhunk, &m_cache[0]); - if (err) + if (UNEXPECTED(err)) return err; m_cachehunk = curhunk; } @@ -1264,7 +1292,7 @@ std::error_condition chd_file::write_bytes(uint64_t offset, const void *buffer, } // handle errors and advance - if (err) + if (UNEXPECTED(err)) return err; source += endoffs + 1 - startoffs; } @@ -1285,7 +1313,7 @@ std::error_condition chd_file::write_bytes(uint64_t offset, const void *buffer, * @param searchindex The searchindex. * @param [in,out] output The output. * - * @return The metadata. + * @return An error condition. */ std::error_condition chd_file::read_metadata(chd_metadata_tag searchtag, uint32_t searchindex, std::string &output) @@ -1295,19 +1323,10 @@ std::error_condition chd_file::read_metadata(chd_metadata_tag searchtag, uint32_ if (std::error_condition err = metadata_find(searchtag, searchindex, metaentry)) return err; - // wrap this for clean reporting - try - { - // read the metadata - output.assign(metaentry.length, '\0'); - file_read(metaentry.offset + METADATA_HEADER_SIZE, &output[0], metaentry.length); - return std::error_condition(); - } - catch (std::error_condition const &err) - { - // just return errors - return err; - } + // read the metadata + try { output.assign(metaentry.length, '\0'); } + catch (std::bad_alloc const &) { return std::errc::not_enough_memory; } + return file_read(metaentry.offset + METADATA_HEADER_SIZE, &output[0], metaentry.length); } /** @@ -1322,7 +1341,7 @@ std::error_condition chd_file::read_metadata(chd_metadata_tag searchtag, uint32_ * @param searchindex The searchindex. * @param [in,out] output The output. * - * @return The metadata. + * @return An error condition. */ std::error_condition chd_file::read_metadata(chd_metadata_tag searchtag, uint32_t searchindex, std::vector &output) @@ -1332,19 +1351,10 @@ std::error_condition chd_file::read_metadata(chd_metadata_tag searchtag, uint32_ if (std::error_condition err = metadata_find(searchtag, searchindex, metaentry)) return err; - // wrap this for clean reporting - try - { - // read the metadata - output.resize(metaentry.length); - file_read(metaentry.offset + METADATA_HEADER_SIZE, &output[0], metaentry.length); - return std::error_condition(); - } - catch (std::error_condition const &err) - { - // just return errors - return err; - } + // read the metadata + try { output.resize(metaentry.length); } + catch (std::bad_alloc const &) { return std::errc::not_enough_memory; } + return file_read(metaentry.offset + METADATA_HEADER_SIZE, &output[0], metaentry.length); } /** @@ -1361,7 +1371,7 @@ std::error_condition chd_file::read_metadata(chd_metadata_tag searchtag, uint32_ * @param outputlen The outputlen. * @param [in,out] resultlen The resultlen. * - * @return The metadata. + * @return An error condition. */ std::error_condition chd_file::read_metadata(chd_metadata_tag searchtag, uint32_t searchindex, void *output, uint32_t outputlen, uint32_t &resultlen) @@ -1371,19 +1381,9 @@ std::error_condition chd_file::read_metadata(chd_metadata_tag searchtag, uint32_ if (std::error_condition err = metadata_find(searchtag, searchindex, metaentry)) return err; - // wrap this for clean reporting - try - { - // read the metadata - resultlen = metaentry.length; - file_read(metaentry.offset + METADATA_HEADER_SIZE, output, std::min(outputlen, resultlen)); - return std::error_condition(); - } - catch (std::error_condition const &err) - { - // just return errors - return err; - } + // read the metadata + resultlen = metaentry.length; + return file_read(metaentry.offset + METADATA_HEADER_SIZE, output, std::min(outputlen, resultlen)); } /** @@ -1400,31 +1400,28 @@ std::error_condition chd_file::read_metadata(chd_metadata_tag searchtag, uint32_ * @param [in,out] resulttag The resulttag. * @param [in,out] resultflags The resultflags. * - * @return The metadata. + * @return An error condition. */ std::error_condition chd_file::read_metadata(chd_metadata_tag searchtag, uint32_t searchindex, std::vector &output, chd_metadata_tag &resulttag, uint8_t &resultflags) { + std::error_condition err; + // if we didn't find it, just return metadata_entry metaentry; - if (std::error_condition err = metadata_find(searchtag, searchindex, metaentry)) + err = metadata_find(searchtag, searchindex, metaentry); + if (err) return err; - // wrap this for clean reporting - try - { - // read the metadata - output.resize(metaentry.length); - file_read(metaentry.offset + METADATA_HEADER_SIZE, &output[0], metaentry.length); - resulttag = metaentry.metatag; - resultflags = metaentry.flags; - return std::error_condition(); - } - catch (std::error_condition const &err) - { - // just return errors + // read the metadata + try { output.resize(metaentry.length); } + catch (std::bad_alloc const &) { return std::errc::not_enough_memory; } + err = file_read(metaentry.offset + METADATA_HEADER_SIZE, &output[0], metaentry.length); + if (UNEXPECTED(err)) return err; - } + resulttag = metaentry.metatag; + resultflags = metaentry.flags; + return std::error_condition(); } /** @@ -1445,43 +1442,52 @@ std::error_condition chd_file::read_metadata(chd_metadata_tag searchtag, uint32_ std::error_condition chd_file::write_metadata(chd_metadata_tag metatag, uint32_t metaindex, const void *inputbuf, uint32_t inputlen, uint8_t flags) { - // wrap this for clean reporting - try + // must write at least 1 byte and no more than 16MB + if (UNEXPECTED((inputlen < 1) || (inputlen >= 16 * 1024 * 1024))) + return std::error_condition(std::errc::invalid_argument); + + // find the entry if it already exists + metadata_entry metaentry; + bool finished = false; + std::error_condition err = metadata_find(metatag, metaindex, metaentry); + if (!err) { - // must write at least 1 byte and no more than 16MB - if (inputlen < 1 || inputlen >= 16 * 1024 * 1024) - return std::error_condition(std::errc::invalid_argument); - - // find the entry if it already exists - metadata_entry metaentry; - bool finished = false; - std::error_condition err = metadata_find(metatag, metaindex, metaentry); - if (err && err != error::METADATA_NOT_FOUND) - throw err; - else if (!err) + if (inputlen <= metaentry.length) { // if the new data fits over the old data, just overwrite - if (inputlen <= metaentry.length) - { - file_write(metaentry.offset + METADATA_HEADER_SIZE, inputbuf, inputlen); - - // if the lengths don't match, we need to update the length in our header - if (inputlen != metaentry.length) - { - uint8_t length[3]; - put_u24be(length, inputlen); - file_write(metaentry.offset + 5, length, sizeof(length)); - } + err = file_write(metaentry.offset + METADATA_HEADER_SIZE, inputbuf, inputlen); + if (UNEXPECTED(err)) + return err; - // indicate we did everything - finished = true; + // if the lengths don't match, we need to update the length in our header + if (inputlen != metaentry.length) + { + uint8_t length[3]; + put_u24be(length, inputlen); + err = file_write(metaentry.offset + 5, length, sizeof(length)); + if (UNEXPECTED(err)) + return err; } + // indicate we did everything + finished = true; + } + else + { // if it doesn't fit, unlink the current entry - else - metadata_set_previous_next(metaentry.prev, metaentry.next); + err = metadata_set_previous_next(metaentry.prev, metaentry.next); + if (UNEXPECTED(err)) + return err; } + } + else if (UNEXPECTED(err != error::METADATA_NOT_FOUND)) + { + return err; + } + // wrap this for clean reporting + try + { // if not yet done, create a new entry and append if (!finished) { @@ -1497,7 +1503,9 @@ std::error_condition chd_file::write_metadata(chd_metadata_tag metatag, uint32_t file_append(inputbuf, inputlen); // set the previous entry to point to us - metadata_set_previous_next(metaentry.prev, offset); + err = metadata_set_previous_next(metaentry.prev, offset); + if (UNEXPECTED(err)) + return err; } // update the hash @@ -1509,6 +1517,10 @@ std::error_condition chd_file::write_metadata(chd_metadata_tag metatag, uint32_t // return any errors return err; } + catch (std::bad_alloc const &) + { + return std::errc::not_enough_memory; + } } /** @@ -1534,18 +1546,8 @@ std::error_condition chd_file::delete_metadata(chd_metadata_tag metatag, uint32_ if (std::error_condition err = metadata_find(metatag, metaindex, metaentry)) return err; - // wrap this for clean reporting - try - { - // point the previous to the next, unlinking us - metadata_set_previous_next(metaentry.prev, metaentry.next); - return std::error_condition(); - } - catch (std::error_condition const &err) - { - // return any errors - return err; - } + // point the previous to the next, unlinking us + return metadata_set_previous_next(metaentry.prev, metaentry.next); } /** @@ -1574,21 +1576,16 @@ std::error_condition chd_file::clone_all_metadata(chd_file &source) std::error_condition err; for (err = source.metadata_find(CHDMETATAG_WILDCARD, 0, metaentry); !err; err = source.metadata_find(CHDMETATAG_WILDCARD, 0, metaentry, true)) { - // wrap this for clean reporting - try - { - // read the metadata item - filedata.resize(metaentry.length); - source.file_read(metaentry.offset + METADATA_HEADER_SIZE, &filedata[0], metaentry.length); - } - catch (std::error_condition const &filerr) - { - return filerr; - } + // read the metadata item + try { filedata.resize(metaentry.length); } + catch (std::bad_alloc const &) { return std::errc::not_enough_memory; } + err = source.file_read(metaentry.offset + METADATA_HEADER_SIZE, &filedata[0], metaentry.length); + if (UNEXPECTED(err)) + return err; // write it to the destination err = write_metadata(metaentry.metatag, (uint32_t)-1, &filedata[0], metaentry.length, metaentry.flags); - if (err) + if (UNEXPECTED(err)) return err; } if (err == error::METADATA_NOT_FOUND) @@ -1624,12 +1621,14 @@ util::sha1_t chd_file::compute_overall_sha1(util::sha1_t rawsha1) for (err = metadata_find(CHDMETATAG_WILDCARD, 0, metaentry); !err; err = metadata_find(CHDMETATAG_WILDCARD, 0, metaentry, true)) { // if not checksumming, continue - if ((metaentry.flags & CHD_MDFLAGS_CHECKSUM) == 0) + if (!(metaentry.flags & CHD_MDFLAGS_CHECKSUM)) continue; // allocate memory and read the data filedata.resize(metaentry.length); - file_read(metaentry.offset + METADATA_HEADER_SIZE, &filedata[0], metaentry.length); + err = file_read(metaentry.offset + METADATA_HEADER_SIZE, &filedata[0], metaentry.length); + if (UNEXPECTED(err)) + throw err; // create an entry for this metadata and add it metadata_hash hashentry; @@ -1800,7 +1799,7 @@ uint32_t chd_file::guess_unitbytes() void chd_file::parse_v3_header(uint8_t *rawheader, util::sha1_t &parentsha1) { // verify header length - if (get_u32be(&rawheader[8]) != V3_HEADER_SIZE) + if (UNEXPECTED(get_u32be(&rawheader[8]) != V3_HEADER_SIZE)) throw std::error_condition(error::INVALID_FILE); // extract core info @@ -1863,7 +1862,7 @@ void chd_file::parse_v3_header(uint8_t *rawheader, util::sha1_t &parentsha1) void chd_file::parse_v4_header(uint8_t *rawheader, util::sha1_t &parentsha1) { // verify header length - if (get_u32be(&rawheader[8]) != V4_HEADER_SIZE) + if (UNEXPECTED(get_u32be(&rawheader[8]) != V4_HEADER_SIZE)) throw std::error_condition(error::INVALID_FILE); // extract core info @@ -1923,7 +1922,7 @@ void chd_file::parse_v4_header(uint8_t *rawheader, util::sha1_t &parentsha1) void chd_file::parse_v5_header(uint8_t *rawheader, util::sha1_t &parentsha1) { // verify header length - if (get_u32be(&rawheader[8]) != V5_HEADER_SIZE) + if (UNEXPECTED(get_u32be(&rawheader[8]) != V5_HEADER_SIZE)) throw std::error_condition(error::INVALID_FILE); // extract core info @@ -2080,10 +2079,10 @@ std::error_condition chd_file::compress_v5_map() // compute a tree and export it to the buffer huffman_error err = encoder.compute_tree_from_histo(); - if (err != HUFFERR_NONE) + if (UNEXPECTED(err != HUFFERR_NONE)) throw std::error_condition(error::COMPRESSION_ERROR); err = encoder.export_tree_rle(bitbuf); - if (err != HUFFERR_NONE) + if (UNEXPECTED(err != HUFFERR_NONE)) throw std::error_condition(error::COMPRESSION_ERROR); // encode the data @@ -2172,13 +2171,16 @@ std::error_condition chd_file::compress_v5_map() // then write the map offset uint8_t rawbuf[sizeof(uint64_t)]; put_u64be(rawbuf, m_mapoffset); - file_write(m_mapoffset_offset, rawbuf, sizeof(rawbuf)); - return std::error_condition(); + return file_write(m_mapoffset_offset, rawbuf, sizeof(rawbuf)); } catch (std::error_condition const &err) { return err; } + catch (std::bad_alloc const &) + { + return std::errc::not_enough_memory; + } } /** @@ -2203,7 +2205,10 @@ void chd_file::decompress_v5_map() // read the reader uint8_t rawbuf[16]; - file_read(m_mapoffset, rawbuf, sizeof(rawbuf)); + std::error_condition ioerr; + ioerr = file_read(m_mapoffset, rawbuf, sizeof(rawbuf)); + if (UNEXPECTED(ioerr)) + throw ioerr; uint32_t const mapbytes = get_u32be(&rawbuf[0]); uint64_t const firstoffs = get_u48be(&rawbuf[4]); util::crc16_t const mapcrc = get_u16be(&rawbuf[10]); @@ -2213,13 +2218,15 @@ void chd_file::decompress_v5_map() // now read the map std::vector compressed(mapbytes); - file_read(m_mapoffset + 16, &compressed[0], mapbytes); + ioerr = file_read(m_mapoffset + 16, &compressed[0], mapbytes); + if (UNEXPECTED(ioerr)) + throw ioerr; bitstream_in bitbuf(&compressed[0], compressed.size()); // first decode the compression types huffman_decoder<16, 8> decoder; - huffman_error err = decoder.import_tree_rle(bitbuf); - if (err != HUFFERR_NONE) + huffman_error const huferr = decoder.import_tree_rle(bitbuf); + if (UNEXPECTED(huferr != HUFFERR_NONE)) throw std::error_condition(error::DECOMPRESSION_ERROR); uint8_t lastcomp = 0; int repcount = 0; @@ -2303,7 +2310,7 @@ void chd_file::decompress_v5_map() } // verify the final CRC - if (util::crc16_creator::simple(&m_rawmap[0], m_hunkcount * 12) != mapcrc) + if (UNEXPECTED(util::crc16_creator::simple(&m_rawmap[0], m_hunkcount * 12) != mapcrc)) throw std::error_condition(error::DECOMPRESSION_ERROR); } @@ -2333,13 +2340,13 @@ std::error_condition chd_file::create_common() m_metaoffset = 0; // if we have a parent, it must be V3 or later - if (m_parent && m_parent->version() < 3) + if (UNEXPECTED(m_parent && m_parent->version() < 3)) throw std::error_condition(error::UNSUPPORTED_VERSION); // must be an even number of units per hunk - if (m_hunkbytes % m_unitbytes != 0) + if (UNEXPECTED(m_hunkbytes % m_unitbytes != 0)) throw std::error_condition(std::errc::invalid_argument); - if (m_parent && m_unitbytes != m_parent->unit_bytes()) + if (UNEXPECTED(m_parent && m_unitbytes != m_parent->unit_bytes())) throw std::error_condition(std::errc::invalid_argument); // verify the compression types @@ -2374,7 +2381,9 @@ std::error_condition chd_file::create_common() be_write_sha1(&rawheader[104], m_parent ? m_parent->sha1() : util::sha1_t::null); // write the resulting header - file_write(0, rawheader, sizeof(rawheader)); + std::error_condition err = file_write(0, rawheader, sizeof(rawheader)); + if (UNEXPECTED(err)) + throw err; // parse it back out to set up fields appropriately util::sha1_t parentsha1; @@ -2392,8 +2401,10 @@ std::error_condition chd_file::create_common() uint64_t offset = m_mapoffset; while (mapsize != 0) { - uint32_t bytes_to_write = (std::min)(mapsize, sizeof(buffer)); - file_write(offset, buffer, bytes_to_write); + uint32_t const bytes_to_write = std::min(mapsize, sizeof(buffer)); + err = file_write(offset, buffer, bytes_to_write); + if (UNEXPECTED(err)) + throw err; offset += bytes_to_write; mapsize -= bytes_to_write; } @@ -2449,10 +2460,12 @@ std::error_condition chd_file::open_common(bool writeable, const open_parent_fun // read the raw header uint8_t rawheader[MAX_HEADER_SIZE]; - file_read(0, rawheader, sizeof(rawheader)); + std::error_condition err = file_read(0, rawheader, sizeof(rawheader)); + if (UNEXPECTED(err)) + throw err; // verify the signature - if (memcmp(rawheader, "MComprHD", 8) != 0) + if (UNEXPECTED(memcmp(rawheader, "MComprHD", 8) != 0)) throw std::error_condition(error::INVALID_FILE); m_version = get_u32be(&rawheader[12]); @@ -2471,7 +2484,7 @@ std::error_condition chd_file::open_common(bool writeable, const open_parent_fun if (m_version < HEADER_VERSION) m_allow_writes = false; - if (writeable && !m_allow_writes) + if (UNEXPECTED(writeable && !m_allow_writes)) throw std::error_condition(error::FILE_NOT_WRITEABLE); // make sure we have a parent if we need one (and don't if we don't) @@ -2485,7 +2498,7 @@ std::error_condition chd_file::open_common(bool writeable, const open_parent_fun else if (m_parent->sha1() != parentsha1) throw std::error_condition(error::INVALID_PARENT); } - else if (m_parent) + else if (UNEXPECTED(m_parent)) { throw std::error_condition(std::errc::invalid_argument); } @@ -2519,16 +2532,22 @@ void chd_file::create_open_common() for (int decompnum = 0; decompnum < std::size(m_compression); decompnum++) { m_decompressor[decompnum] = chd_codec_list::new_decompressor(m_compression[decompnum], *this); - if (m_decompressor[decompnum] == nullptr && m_compression[decompnum] != 0) + if (UNEXPECTED(!m_decompressor[decompnum] && (m_compression[decompnum] != 0))) throw std::error_condition(error::UNKNOWN_COMPRESSION); } // read the map; v5+ compressed drives need to read and decompress their map m_rawmap.resize(m_hunkcount * m_mapentrybytes); if (m_version >= 5 && compressed()) + { decompress_v5_map(); + } else - file_read(m_mapoffset, &m_rawmap[0], m_rawmap.size()); + { + std::error_condition err = file_read(m_mapoffset, &m_rawmap[0], m_rawmap.size()); + if (UNEXPECTED(err)) + throw err; + } // allocate the temporary compressed buffer and a buffer for caching m_compressed.resize(m_hunkbytes); @@ -2543,44 +2562,37 @@ void chd_file::create_open_common() * for appending to a compressed CHD * -------------------------------------------------. * - * @exception CHDERR_NOT_OPEN Thrown when a chderr not open error condition occurs. - * @exception CHDERR_HUNK_OUT_OF_RANGE Thrown when a chderr hunk out of range error - * condition occurs. - * @exception CHDERR_FILE_NOT_WRITEABLE Thrown when a chderr file not writeable error - * condition occurs. - * @exception CHDERR_COMPRESSION_ERROR Thrown when a chderr compression error error - * condition occurs. - * * @param hunknum The hunknum. */ -void chd_file::verify_proper_compression_append(uint32_t hunknum) +std::error_condition chd_file::verify_proper_compression_append(uint32_t hunknum) const noexcept { // punt if no file - if (!m_file) - throw std::error_condition(error::NOT_OPEN); + if (UNEXPECTED(!m_file)) + return std::error_condition(error::NOT_OPEN); // return an error if out of range - if (hunknum >= m_hunkcount) - throw std::error_condition(error::HUNK_OUT_OF_RANGE); + if (UNEXPECTED(hunknum >= m_hunkcount)) + return std::error_condition(error::HUNK_OUT_OF_RANGE); // if not writeable, fail - if (!m_allow_writes) - throw std::error_condition(error::FILE_NOT_WRITEABLE); + if (UNEXPECTED(!m_allow_writes)) + return std::error_condition(error::FILE_NOT_WRITEABLE); // compressed writes only via this interface - if (!compressed()) - throw std::error_condition(error::FILE_NOT_WRITEABLE); + if (UNEXPECTED(!compressed())) + return std::error_condition(error::FILE_NOT_WRITEABLE); // only permitted to write new blocks - uint8_t *rawmap = &m_rawmap[hunknum * 12]; - if (rawmap[0] != 0xff) - throw std::error_condition(error::COMPRESSION_ERROR); + uint8_t const *const rawmap = &m_rawmap[hunknum * 12]; + if (UNEXPECTED(rawmap[0] != 0xff)) + return std::error_condition(error::COMPRESSION_ERROR); + + // if this isn't the first block, only permitted to write immediately after the previous one + if (UNEXPECTED((hunknum != 0) && (rawmap[-12] == 0xff))) + return std::error_condition(error::COMPRESSION_ERROR); - // if this isn't the first block, only permitted to write immediately - // after the previous one - if (hunknum != 0 && rawmap[-12] == 0xff) - throw std::error_condition(error::COMPRESSION_ERROR); + return std::error_condition(); } /** @@ -2601,7 +2613,9 @@ void chd_file::verify_proper_compression_append(uint32_t hunknum) void chd_file::hunk_write_compressed(uint32_t hunknum, int8_t compression, const uint8_t *compressed, uint32_t complength, util::crc16_t crc16) { // verify that we are appending properly to a compressed file - verify_proper_compression_append(hunknum); + std::error_condition err = verify_proper_compression_append(hunknum); + if (UNEXPECTED(err)) + throw err; // write the final result uint64_t offset = file_append(compressed, complength); @@ -2631,10 +2645,12 @@ void chd_file::hunk_write_compressed(uint32_t hunknum, int8_t compression, const void chd_file::hunk_copy_from_self(uint32_t hunknum, uint32_t otherhunk) { // verify that we are appending properly to a compressed file - verify_proper_compression_append(hunknum); + std::error_condition err = verify_proper_compression_append(hunknum); + if (UNEXPECTED(err)) + throw err; // only permitted to reference prior hunks - if (otherhunk >= hunknum) + if (UNEXPECTED(otherhunk >= hunknum)) throw std::error_condition(error::HUNK_OUT_OF_RANGE); // update the map entry @@ -2659,10 +2675,12 @@ void chd_file::hunk_copy_from_self(uint32_t hunknum, uint32_t otherhunk) void chd_file::hunk_copy_from_parent(uint32_t hunknum, uint64_t parentunit) { // verify that we are appending properly to a compressed file - verify_proper_compression_append(hunknum); + std::error_condition err = verify_proper_compression_append(hunknum); + if (UNEXPECTED(err)) + throw err; // update the map entry - uint8_t *rawmap = &m_rawmap[hunknum * 12]; + uint8_t *const rawmap = &m_rawmap[hunknum * 12]; rawmap[0] = COMPRESSION_PARENT; put_u24be(&rawmap[1], 0); put_u48be(&rawmap[4], parentunit); @@ -2698,40 +2716,33 @@ std::error_condition chd_file::metadata_find(chd_metadata_tag metatag, int32_t m metaentry.offset = metaentry.next; } - // wrap this for clean reporting - try + // loop until we run out of options + while (metaentry.offset != 0) { - // loop until we run out of options - while (metaentry.offset != 0) - { - // read the raw header - uint8_t raw_meta_header[METADATA_HEADER_SIZE]; - file_read(metaentry.offset, raw_meta_header, sizeof(raw_meta_header)); - - // extract the data - metaentry.metatag = get_u32be(&raw_meta_header[0]); - metaentry.flags = raw_meta_header[4]; - metaentry.length = get_u24be(&raw_meta_header[5]); - metaentry.next = get_u64be(&raw_meta_header[8]); + // read the raw header + uint8_t raw_meta_header[METADATA_HEADER_SIZE]; + std::error_condition err = file_read(metaentry.offset, raw_meta_header, sizeof(raw_meta_header)); + if (UNEXPECTED(err)) + return err; - // if we got a match, proceed - if (metatag == CHDMETATAG_WILDCARD || metaentry.metatag == metatag) - if (metaindex-- == 0) - return std::error_condition(); + // extract the data + metaentry.metatag = get_u32be(&raw_meta_header[0]); + metaentry.flags = raw_meta_header[4]; + metaentry.length = get_u24be(&raw_meta_header[5]); + metaentry.next = get_u64be(&raw_meta_header[8]); - // no match, fetch the next link - metaentry.prev = metaentry.offset; - metaentry.offset = metaentry.next; - } + // if we got a match, proceed + if (metatag == CHDMETATAG_WILDCARD || metaentry.metatag == metatag) + if (metaindex-- == 0) + return std::error_condition(); - // if we get here, we didn't find it - return error::METADATA_NOT_FOUND; - } - catch (std::error_condition const &err) - { - // return any errors - return err; + // no match, fetch the next link + metaentry.prev = metaentry.offset; + metaentry.offset = metaentry.next; } + + // if we get here, we didn't find it + return error::METADATA_NOT_FOUND; } /** @@ -2745,27 +2756,28 @@ std::error_condition chd_file::metadata_find(chd_metadata_tag metatag, int32_t m * @param nextoffset The nextoffset. */ -void chd_file::metadata_set_previous_next(uint64_t prevoffset, uint64_t nextoffset) +std::error_condition chd_file::metadata_set_previous_next(uint64_t prevoffset, uint64_t nextoffset) noexcept { uint64_t offset = 0; - // if we were the first entry, make the next entry the first if (prevoffset == 0) { + // if we were the first entry, make the next entry the first offset = m_metaoffset_offset; m_metaoffset = nextoffset; } - - // otherwise, update the link in the previous header else + { + // otherwise, update the link in the previous header offset = prevoffset + 8; + } // create a big-endian version uint8_t rawbuf[sizeof(uint64_t)]; put_u64be(rawbuf, nextoffset); // write to the header and update our local copy - file_write(offset, rawbuf, sizeof(rawbuf)); + return file_write(offset, rawbuf, sizeof(rawbuf)); } /** @@ -2779,7 +2791,7 @@ void chd_file::metadata_set_previous_next(uint64_t prevoffset, uint64_t nextoffs void chd_file::metadata_update_hash() { // only works for V4 and above, and only for compressed CHDs - if (m_version < 4 || !compressed()) + if ((m_version < 4) || !compressed()) return; // compute the new overall hash @@ -2790,7 +2802,9 @@ void chd_file::metadata_update_hash() be_write_sha1(&rawbuf[0], fullsha1); // write to the header - file_write(m_sha1_offset, rawbuf, sizeof(rawbuf)); + std::error_condition err = file_write(m_sha1_offset, rawbuf, sizeof(rawbuf)); + if (UNEXPECTED(err)) + throw err; } /** @@ -2825,18 +2839,18 @@ int CLIB_DECL chd_file::metadata_hash_compare(const void *elem1, const void *ele * -------------------------------------------------. */ -chd_file_compressor::chd_file_compressor() - : m_walking_parent(false), - m_total_in(0), - m_total_out(0), - m_read_queue(nullptr), - m_read_queue_offset(0), - m_read_done_offset(0), - m_work_queue(nullptr), - m_write_hunk(0) +chd_file_compressor::chd_file_compressor() : + m_walking_parent(false), + m_total_in(0), + m_total_out(0), + m_read_queue(nullptr), + m_read_queue_offset(0), + m_read_done_offset(0), + m_work_queue(nullptr), + m_write_hunk(0) { // zap arrays - memset(m_codecs, 0, sizeof(m_codecs)); + std::fill(std::begin(m_codecs), std::end(m_codecs), nullptr); // allocate work queues m_read_queue = osd_work_queue_alloc(WORK_QUEUE_FLAG_IO); @@ -2927,7 +2941,7 @@ void chd_file_compressor::compress_begin() std::error_condition chd_file_compressor::compress_continue(double &progress, double &ratio) { // if we got an error, return the error - if (m_read_error) + if (UNEXPECTED(m_read_error)) return m_read_error; // if done reading, queue some more @@ -2982,14 +2996,14 @@ std::error_condition chd_file_compressor::compress_continue(double &progress, do { // if we're uncompressed, use regular writes std::error_condition err = write_hunk(item.m_hunknum, item.m_data); - if (err) + if (UNEXPECTED(err)) return err; // writes of all-0 data don't actually take space, so see if we count this chd_codec_type codec = CHD_CODEC_NONE; uint32_t complen; err = hunk_info(item.m_hunknum, codec, complen); - if (!err && codec == CHD_CODEC_NONE) + if (!err && codec == CHD_CODEC_NONE) // TODO: report error? m_total_out += m_hunkbytes; } else do @@ -3045,7 +3059,7 @@ std::error_condition chd_file_compressor::compress_continue(double &progress, do if (!compressed()) return std::error_condition(); std::error_condition err = set_raw_sha1(m_compsha1.finish()); - if (err) + if (UNEXPECTED(err)) return err; return compress_v5_map(); } @@ -3061,9 +3075,9 @@ std::error_condition chd_file_compressor::compress_continue(double &progress, do // if we're waiting for work, wait // sometimes code can get here with .m_status == WS_READY and .m_osd != nullptr, TODO find out why this happens - while (m_work_item[m_write_hunk % WORK_BUFFER_HUNKS].m_status != WS_READY && - m_work_item[m_write_hunk % WORK_BUFFER_HUNKS].m_status != WS_COMPLETE && - m_work_item[m_write_hunk % WORK_BUFFER_HUNKS].m_osd != nullptr) + while ((m_work_item[m_write_hunk % WORK_BUFFER_HUNKS].m_status != WS_READY) && + (m_work_item[m_write_hunk % WORK_BUFFER_HUNKS].m_status != WS_COMPLETE) && + m_work_item[m_write_hunk % WORK_BUFFER_HUNKS].m_osd) osd_work_item_wait(m_work_item[m_write_hunk % WORK_BUFFER_HUNKS].m_osd, osd_ticks_per_second()); return m_walking_parent ? error::WALKING_PARENT : error::COMPRESSING; @@ -3084,7 +3098,7 @@ std::error_condition chd_file_compressor::compress_continue(double &progress, do void *chd_file_compressor::async_walk_parent_static(void *param, int threadid) { - auto *item = reinterpret_cast(param); + auto *const item = reinterpret_cast(param); item->m_compressor->async_walk_parent(*item); return nullptr; } @@ -3153,8 +3167,8 @@ void chd_file_compressor::async_compress_hunk(work_item &item, int threadid) // find the best compression scheme, unless we already have a self or parent match // (note we may miss a self match from blocks not yet added, but this just results in extra work) // TODO: data race - if (m_current_map.find(item.m_hash[0].m_crc16, item.m_hash[0].m_sha1) == hashmap::NOT_FOUND && - m_parent_map.find(item.m_hash[0].m_crc16, item.m_hash[0].m_sha1) == hashmap::NOT_FOUND) + if ((m_current_map.find(item.m_hash[0].m_crc16, item.m_hash[0].m_sha1) == hashmap::NOT_FOUND) && + (m_parent_map.find(item.m_hash[0].m_crc16, item.m_hash[0].m_sha1) == hashmap::NOT_FOUND)) item.m_compression = item.m_codecs->find_best_compressor(item.m_data, item.m_compressed, item.m_complen); // mark us complete @@ -3189,7 +3203,7 @@ void *chd_file_compressor::async_read_static(void *param, int threadid) void chd_file_compressor::async_read() { // if in the error or complete state, stop - if (m_read_error) + if (UNEXPECTED(m_read_error)) return; // determine parameters for the read @@ -3272,8 +3286,8 @@ void chd_file_compressor::async_read() * -------------------------------------------------. */ -chd_file_compressor::hashmap::hashmap() - : m_block_list(new entry_block(nullptr)) +chd_file_compressor::hashmap::hashmap() : + m_block_list(new entry_block(nullptr)) { // initialize the map to empty memset(m_map, 0, sizeof(m_map)); @@ -3332,7 +3346,7 @@ void chd_file_compressor::hashmap::reset() uint64_t chd_file_compressor::hashmap::find(util::crc16_t crc16, util::sha1_t sha1) const noexcept { // look up the entry in the map - for (entry_t *entry = m_map[crc16]; entry != nullptr; entry = entry->m_next) + for (entry_t *entry = m_map[crc16]; entry; entry = entry->m_next) if (entry->m_sha1 == sha1) return entry->m_itemnum; return NOT_FOUND; @@ -3399,4 +3413,3 @@ std::error_condition chd_file::check_is_av() const noexcept metadata_entry metaentry; return metadata_find(AV_METADATA_TAG, 0, metaentry); } - diff --git a/src/lib/util/chd.h b/src/lib/util/chd.h index ecf9ae6fb7d..9ea40bb9136 100644 --- a/src/lib/util/chd.h +++ b/src/lib/util/chd.h @@ -2,8 +2,6 @@ // copyright-holders:Aaron Giles /*************************************************************************** - chd.h - MAME Compressed Hunks of Data file format ***************************************************************************/ @@ -374,10 +372,10 @@ private: // inline helpers util::sha1_t be_read_sha1(const uint8_t *base) const noexcept; void be_write_sha1(uint8_t *base, util::sha1_t value) noexcept; - void file_read(uint64_t offset, void *dest, uint32_t length) const; - void file_write(uint64_t offset, const void *source, uint32_t length); + std::error_condition file_read(uint64_t offset, void *dest, uint32_t length) const noexcept; + std::error_condition file_write(uint64_t offset, const void *source, uint32_t length) noexcept; uint64_t file_append(const void *source, uint32_t length, uint32_t alignment = 0); - uint8_t bits_for_value(uint64_t value); + static uint8_t bits_for_value(uint64_t value) noexcept; // internal helpers uint32_t guess_unitbytes(); @@ -389,12 +387,12 @@ private: std::error_condition create_common(); std::error_condition open_common(bool writeable, const open_parent_func &open_parent); void create_open_common(); - void verify_proper_compression_append(uint32_t hunknum); + std::error_condition verify_proper_compression_append(uint32_t hunknum) const noexcept; void hunk_write_compressed(uint32_t hunknum, int8_t compression, const uint8_t *compressed, uint32_t complength, util::crc16_t crc16); void hunk_copy_from_self(uint32_t hunknum, uint32_t otherhunk); void hunk_copy_from_parent(uint32_t hunknum, uint64_t parentunit); std::error_condition metadata_find(chd_metadata_tag metatag, int32_t metaindex, metadata_entry &metaentry, bool resume = false) const noexcept; - void metadata_set_previous_next(uint64_t prevoffset, uint64_t nextoffset); + std::error_condition metadata_set_previous_next(uint64_t prevoffset, uint64_t nextoffset) noexcept; void metadata_update_hash(); static int CLIB_DECL metadata_hash_compare(const void *elem1, const void *elem2); -- cgit v1.2.3