VolumeVerifier: Handle overlapping blocks more efficiently
The performance gains of doing this aren't too important since you normally wouldn't run into any disc image that has overlapping blocks (which by extension means overlapping partitions), but this change also lets us get rid of things like VolumeVerifier's mutex that used to exist just for the sake of handling overlapping blocks.
This commit is contained in:
parent
15ebb1d9e4
commit
10e1acf25c
|
@ -9,7 +9,6 @@
|
||||||
#include <future>
|
#include <future>
|
||||||
#include <limits>
|
#include <limits>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <mutex>
|
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
|
@ -1057,11 +1056,20 @@ void VolumeVerifier::WaitForAsyncOperations() const
|
||||||
bool VolumeVerifier::ReadChunkAndWaitForAsyncOperations(u64 bytes_to_read)
|
bool VolumeVerifier::ReadChunkAndWaitForAsyncOperations(u64 bytes_to_read)
|
||||||
{
|
{
|
||||||
std::vector<u8> data(bytes_to_read);
|
std::vector<u8> data(bytes_to_read);
|
||||||
|
|
||||||
|
const u64 bytes_to_copy = std::min(m_excess_bytes, bytes_to_read);
|
||||||
|
if (bytes_to_copy > 0)
|
||||||
|
std::memcpy(data.data(), m_data.data() + m_data.size() - m_excess_bytes, bytes_to_copy);
|
||||||
|
bytes_to_read -= bytes_to_copy;
|
||||||
|
|
||||||
|
if (bytes_to_read > 0)
|
||||||
|
{
|
||||||
|
if (!m_volume.Read(m_progress + bytes_to_copy, bytes_to_read, data.data() + bytes_to_copy,
|
||||||
|
PARTITION_NONE))
|
||||||
{
|
{
|
||||||
std::lock_guard lk(m_volume_mutex);
|
|
||||||
if (!m_volume.Read(m_progress, bytes_to_read, data.data(), PARTITION_NONE))
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
WaitForAsyncOperations();
|
WaitForAsyncOperations();
|
||||||
m_data = std::move(data);
|
m_data = std::move(data);
|
||||||
|
@ -1080,6 +1088,7 @@ void VolumeVerifier::Process()
|
||||||
bool content_read = false;
|
bool content_read = false;
|
||||||
bool block_read = false;
|
bool block_read = false;
|
||||||
u64 bytes_to_read = BLOCK_SIZE;
|
u64 bytes_to_read = BLOCK_SIZE;
|
||||||
|
u64 excess_bytes = 0;
|
||||||
if (m_content_index < m_content_offsets.size() &&
|
if (m_content_index < m_content_offsets.size() &&
|
||||||
m_content_offsets[m_content_index] == m_progress)
|
m_content_offsets[m_content_index] == m_progress)
|
||||||
{
|
{
|
||||||
|
@ -1096,12 +1105,27 @@ void VolumeVerifier::Process()
|
||||||
{
|
{
|
||||||
bytes_to_read = VolumeWii::BLOCK_TOTAL_SIZE;
|
bytes_to_read = VolumeWii::BLOCK_TOTAL_SIZE;
|
||||||
block_read = true;
|
block_read = true;
|
||||||
|
|
||||||
|
if (m_block_index + 1 < m_blocks.size() &&
|
||||||
|
m_blocks[m_block_index + 1].offset < m_progress + bytes_to_read)
|
||||||
|
{
|
||||||
|
excess_bytes = m_progress + bytes_to_read - m_blocks[m_block_index + 1].offset;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (m_block_index < m_blocks.size() && m_blocks[m_block_index].offset > m_progress)
|
else if (m_block_index < m_blocks.size() && m_blocks[m_block_index].offset > m_progress)
|
||||||
{
|
{
|
||||||
bytes_to_read = std::min(bytes_to_read, m_blocks[m_block_index].offset - m_progress);
|
bytes_to_read = std::min(bytes_to_read, m_blocks[m_block_index].offset - m_progress);
|
||||||
}
|
}
|
||||||
bytes_to_read = std::min(bytes_to_read, m_max_progress - m_progress);
|
|
||||||
|
if (m_progress + bytes_to_read > m_max_progress)
|
||||||
|
{
|
||||||
|
const u64 bytes_over_max = m_progress + bytes_to_read - m_max_progress;
|
||||||
|
bytes_to_read -= bytes_over_max;
|
||||||
|
if (excess_bytes < bytes_over_max)
|
||||||
|
excess_bytes = 0;
|
||||||
|
else
|
||||||
|
excess_bytes -= bytes_over_max;
|
||||||
|
}
|
||||||
|
|
||||||
const bool is_data_needed = m_calculating_any_hash || content_read || block_read;
|
const bool is_data_needed = m_calculating_any_hash || content_read || block_read;
|
||||||
const bool read_succeeded = is_data_needed && ReadChunkAndWaitForAsyncOperations(bytes_to_read);
|
const bool read_succeeded = is_data_needed && ReadChunkAndWaitForAsyncOperations(bytes_to_read);
|
||||||
|
@ -1114,28 +1138,31 @@ void VolumeVerifier::Process()
|
||||||
m_calculating_any_hash = false;
|
m_calculating_any_hash = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
m_excess_bytes = excess_bytes;
|
||||||
|
const u64 byte_increment = bytes_to_read - excess_bytes;
|
||||||
|
|
||||||
if (m_calculating_any_hash)
|
if (m_calculating_any_hash)
|
||||||
{
|
{
|
||||||
if (m_hashes_to_calculate.crc32)
|
if (m_hashes_to_calculate.crc32)
|
||||||
{
|
{
|
||||||
m_crc32_future = std::async(std::launch::async, [this] {
|
m_crc32_future = std::async(std::launch::async, [this, byte_increment] {
|
||||||
// It would be nice to use crc32_z here instead of crc32, but it isn't available on Android
|
// It would be nice to use crc32_z here instead of crc32, but it isn't available on Android
|
||||||
m_crc32_context =
|
m_crc32_context =
|
||||||
crc32(m_crc32_context, m_data.data(), static_cast<unsigned int>(m_data.size()));
|
crc32(m_crc32_context, m_data.data(), static_cast<unsigned int>(byte_increment));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_hashes_to_calculate.md5)
|
if (m_hashes_to_calculate.md5)
|
||||||
{
|
{
|
||||||
m_md5_future = std::async(std::launch::async, [this] {
|
m_md5_future = std::async(std::launch::async, [this, byte_increment] {
|
||||||
mbedtls_md5_update_ret(&m_md5_context, m_data.data(), m_data.size());
|
mbedtls_md5_update_ret(&m_md5_context, m_data.data(), byte_increment);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_hashes_to_calculate.sha1)
|
if (m_hashes_to_calculate.sha1)
|
||||||
{
|
{
|
||||||
m_sha1_future = std::async(std::launch::async, [this] {
|
m_sha1_future = std::async(std::launch::async, [this, byte_increment] {
|
||||||
mbedtls_sha1_update_ret(&m_sha1_context, m_data.data(), m_data.size());
|
mbedtls_sha1_update_ret(&m_sha1_context, m_data.data(), byte_increment);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1152,61 +1179,36 @@ void VolumeVerifier::Process()
|
||||||
m_content_index++;
|
m_content_index++;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_block_index < m_blocks.size() &&
|
if (block_read)
|
||||||
m_blocks[m_block_index].offset < m_progress + bytes_to_read)
|
|
||||||
{
|
{
|
||||||
m_block_future = std::async(
|
m_block_future = std::async(std::launch::async, [this, read_succeeded,
|
||||||
std::launch::async,
|
block_index = m_block_index] {
|
||||||
[this, read_succeeded, bytes_to_read](size_t block_index, u64 progress) {
|
const BlockToVerify& block = m_blocks[block_index];
|
||||||
while (block_index < m_blocks.size() &&
|
if (read_succeeded &&
|
||||||
m_blocks[block_index].offset < progress + bytes_to_read)
|
m_volume.CheckBlockIntegrity(block.block_index, m_data, block.partition))
|
||||||
{
|
|
||||||
bool success;
|
|
||||||
if (m_blocks[block_index].offset == progress)
|
|
||||||
{
|
|
||||||
success = read_succeeded &&
|
|
||||||
m_volume.CheckBlockIntegrity(m_blocks[block_index].block_index, m_data,
|
|
||||||
m_blocks[block_index].partition);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
std::lock_guard lk(m_volume_mutex);
|
|
||||||
success = m_volume.CheckBlockIntegrity(m_blocks[block_index].block_index,
|
|
||||||
m_blocks[block_index].partition);
|
|
||||||
}
|
|
||||||
|
|
||||||
const u64 offset = m_blocks[block_index].offset;
|
|
||||||
if (success)
|
|
||||||
{
|
{
|
||||||
m_biggest_verified_offset =
|
m_biggest_verified_offset =
|
||||||
std::max(m_biggest_verified_offset, offset + VolumeWii::BLOCK_TOTAL_SIZE);
|
std::max(m_biggest_verified_offset, block.offset + VolumeWii::BLOCK_TOTAL_SIZE);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (m_scrubber.CanBlockBeScrubbed(offset))
|
if (m_scrubber.CanBlockBeScrubbed(block.offset))
|
||||||
{
|
{
|
||||||
WARN_LOG_FMT(DISCIO, "Integrity check failed for unused block at {:#x}", offset);
|
WARN_LOG_FMT(DISCIO, "Integrity check failed for unused block at {:#x}", block.offset);
|
||||||
m_unused_block_errors[m_blocks[block_index].partition]++;
|
m_unused_block_errors[block.partition]++;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
WARN_LOG_FMT(DISCIO, "Integrity check failed for block at {:#x}", offset);
|
WARN_LOG_FMT(DISCIO, "Integrity check failed for block at {:#x}", block.offset);
|
||||||
m_block_errors[m_blocks[block_index].partition]++;
|
m_block_errors[block.partition]++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
block_index++;
|
});
|
||||||
}
|
|
||||||
},
|
|
||||||
m_block_index, m_progress);
|
|
||||||
|
|
||||||
while (m_block_index < m_blocks.size() &&
|
|
||||||
m_blocks[m_block_index].offset < m_progress + bytes_to_read)
|
|
||||||
{
|
|
||||||
m_block_index++;
|
m_block_index++;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
m_progress += bytes_to_read;
|
m_progress += byte_increment;
|
||||||
}
|
}
|
||||||
|
|
||||||
u64 VolumeVerifier::GetBytesProcessed() const
|
u64 VolumeVerifier::GetBytesProcessed() const
|
||||||
|
|
|
@ -6,7 +6,6 @@
|
||||||
|
|
||||||
#include <future>
|
#include <future>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <mutex>
|
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
@ -177,8 +176,8 @@ private:
|
||||||
mbedtls_md5_context m_md5_context;
|
mbedtls_md5_context m_md5_context;
|
||||||
mbedtls_sha1_context m_sha1_context;
|
mbedtls_sha1_context m_sha1_context;
|
||||||
|
|
||||||
|
u64 m_excess_bytes = 0;
|
||||||
std::vector<u8> m_data;
|
std::vector<u8> m_data;
|
||||||
std::mutex m_volume_mutex;
|
|
||||||
std::future<void> m_crc32_future;
|
std::future<void> m_crc32_future;
|
||||||
std::future<void> m_md5_future;
|
std::future<void> m_md5_future;
|
||||||
std::future<void> m_sha1_future;
|
std::future<void> m_sha1_future;
|
||||||
|
|
Loading…
Reference in New Issue