WIA/RVZ: Skip some memory allocations when reusing chunks
This commit is contained in:
parent
f5ef70fc76
commit
1e92b54bf5
|
@ -13,6 +13,7 @@
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
#include <type_traits>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
#include <bzlib.h>
|
#include <bzlib.h>
|
||||||
|
@ -1589,6 +1590,24 @@ void WIAFileReader::SetUpCompressor(std::unique_ptr<Compressor>* compressor,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool WIAFileReader::TryReuse(std::map<ReuseID, GroupEntry>* reusable_groups,
|
||||||
|
std::mutex* reusable_groups_mutex, OutputParametersEntry* entry)
|
||||||
|
{
|
||||||
|
if (entry->reused_group)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (!entry->reuse_id)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
std::lock_guard guard(*reusable_groups_mutex);
|
||||||
|
const auto it = reusable_groups->find(*entry->reuse_id);
|
||||||
|
if (it == reusable_groups->end())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
entry->reused_group = it->second;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
static bool AllAre(const std::vector<u8>& data, u8 x)
|
static bool AllAre(const std::vector<u8>& data, u8 x)
|
||||||
{
|
{
|
||||||
return std::all_of(data.begin(), data.end(), [x](u8 y) { return x == y; });
|
return std::all_of(data.begin(), data.end(), [x](u8 y) { return x == y; });
|
||||||
|
@ -1622,16 +1641,6 @@ WIAFileReader::ProcessAndCompress(CompressThreadState* state, CompressParameters
|
||||||
std::mutex* reusable_groups_mutex, u64 chunks_per_wii_group,
|
std::mutex* reusable_groups_mutex, u64 chunks_per_wii_group,
|
||||||
u64 exception_lists_per_chunk, bool compressed_exception_lists)
|
u64 exception_lists_per_chunk, bool compressed_exception_lists)
|
||||||
{
|
{
|
||||||
const auto reuse_id_exists = [reusable_groups,
|
|
||||||
reusable_groups_mutex](const std::optional<ReuseID>& reuse_id) {
|
|
||||||
if (!reuse_id)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
std::lock_guard guard(*reusable_groups_mutex);
|
|
||||||
const auto it = reusable_groups->find(*reuse_id);
|
|
||||||
return it != reusable_groups->end();
|
|
||||||
};
|
|
||||||
|
|
||||||
std::vector<OutputParametersEntry> output_entries;
|
std::vector<OutputParametersEntry> output_entries;
|
||||||
|
|
||||||
if (!parameters.data_entry->is_partition)
|
if (!parameters.data_entry->is_partition)
|
||||||
|
@ -1664,6 +1673,8 @@ WIAFileReader::ProcessAndCompress(CompressThreadState* state, CompressParameters
|
||||||
const u64 in_data_per_chunk = blocks_per_chunk * VolumeWii::BLOCK_TOTAL_SIZE;
|
const u64 in_data_per_chunk = blocks_per_chunk * VolumeWii::BLOCK_TOTAL_SIZE;
|
||||||
const u64 out_data_per_chunk = blocks_per_chunk * VolumeWii::BLOCK_DATA_SIZE;
|
const u64 out_data_per_chunk = blocks_per_chunk * VolumeWii::BLOCK_DATA_SIZE;
|
||||||
|
|
||||||
|
const size_t first_chunk = output_entries.size();
|
||||||
|
|
||||||
const auto create_reuse_id = [&partition_entry, blocks,
|
const auto create_reuse_id = [&partition_entry, blocks,
|
||||||
blocks_per_chunk](u8 value, bool decrypted, u64 block) {
|
blocks_per_chunk](u8 value, bool decrypted, u64 block) {
|
||||||
const u64 size = std::min(blocks - block, blocks_per_chunk) * VolumeWii::BLOCK_DATA_SIZE;
|
const u64 size = std::min(blocks - block, blocks_per_chunk) * VolumeWii::BLOCK_DATA_SIZE;
|
||||||
|
@ -1683,17 +1694,18 @@ WIAFileReader::ProcessAndCompress(CompressThreadState* state, CompressParameters
|
||||||
if (AllSame(data, std::min(parameters_data_end, data + in_data_per_chunk)))
|
if (AllSame(data, std::min(parameters_data_end, data + in_data_per_chunk)))
|
||||||
reuse_id = create_reuse_id(parameters.data.front(), false, i * blocks_per_chunk);
|
reuse_id = create_reuse_id(parameters.data.front(), false, i * blocks_per_chunk);
|
||||||
|
|
||||||
if (!reuse_id_exists(reuse_id) &&
|
TryReuse(reusable_groups, reusable_groups_mutex, &entry);
|
||||||
!(reuse_id && std::any_of(output_entries.begin(), output_entries.begin() + i,
|
if (!entry.reused_group && reuse_id)
|
||||||
[reuse_id](const auto& e) { return e.reuse_id == reuse_id; })))
|
|
||||||
{
|
{
|
||||||
const u64 bytes_left = (blocks - block_index) * VolumeWii::BLOCK_DATA_SIZE;
|
const auto it = std::find_if(output_entries.begin(), output_entries.begin() + i,
|
||||||
entry.main_data.resize(std::min(out_data_per_chunk, bytes_left));
|
[reuse_id](const auto& e) { return e.reuse_id == reuse_id; });
|
||||||
|
if (it != output_entries.begin() + i)
|
||||||
|
entry.reused_group = it->reused_group;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!std::all_of(output_entries.begin(), output_entries.end(),
|
if (!std::all_of(output_entries.begin(), output_entries.end(),
|
||||||
[](const OutputParametersEntry& entry) { return entry.main_data.empty(); }))
|
[](const OutputParametersEntry& entry) { return entry.reused_group; }))
|
||||||
{
|
{
|
||||||
const u64 number_of_exception_lists =
|
const u64 number_of_exception_lists =
|
||||||
chunks_per_wii_group == 1 ? exception_lists_per_chunk : chunks;
|
chunks_per_wii_group == 1 ? exception_lists_per_chunk : chunks;
|
||||||
|
@ -1728,7 +1740,7 @@ WIAFileReader::ProcessAndCompress(CompressThreadState* state, CompressParameters
|
||||||
const u64 chunk_index = j / blocks_per_chunk;
|
const u64 chunk_index = j / blocks_per_chunk;
|
||||||
const u64 block_index_in_chunk = j % blocks_per_chunk;
|
const u64 block_index_in_chunk = j % blocks_per_chunk;
|
||||||
|
|
||||||
if (output_entries[chunk_index].main_data.empty())
|
if (output_entries[chunk_index].reused_group)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
const u64 exception_list_index = chunks_per_wii_group == 1 ? i : chunk_index;
|
const u64 exception_list_index = chunks_per_wii_group == 1 ? i : chunk_index;
|
||||||
|
@ -1780,27 +1792,50 @@ WIAFileReader::ProcessAndCompress(CompressThreadState* state, CompressParameters
|
||||||
compare_hashes(offsetof(HashBlock, padding_2), sizeof(HashBlock::padding_2));
|
compare_hashes(offsetof(HashBlock, padding_2), sizeof(HashBlock::padding_2));
|
||||||
}
|
}
|
||||||
|
|
||||||
for (u64 j = 0; j < blocks_in_this_group; ++j)
|
static_assert(std::is_trivially_copyable_v<decltype(
|
||||||
|
CompressThreadState::decryption_buffer)::value_type>);
|
||||||
|
const u8* in_ptr = state->decryption_buffer[0].data();
|
||||||
|
for (u64 j = 0; j < chunks; ++j)
|
||||||
{
|
{
|
||||||
const u64 chunk_index = j / blocks_per_chunk;
|
OutputParametersEntry& entry = output_entries[first_chunk + j];
|
||||||
const u64 block_index_in_chunk = j % blocks_per_chunk;
|
|
||||||
|
|
||||||
OutputParametersEntry& entry = output_entries[chunk_index];
|
if (!entry.reused_group)
|
||||||
if (entry.main_data.empty())
|
{
|
||||||
continue;
|
const u64 bytes_left = (blocks - j * blocks_per_chunk) * VolumeWii::BLOCK_DATA_SIZE;
|
||||||
|
const u64 bytes_to_write_total = std::min(out_data_per_chunk, bytes_left);
|
||||||
|
|
||||||
const u64 write_offset_of_block =
|
if (i == 0)
|
||||||
write_offset_of_group + block_index_in_chunk * VolumeWii::BLOCK_DATA_SIZE;
|
entry.main_data.resize(bytes_to_write_total);
|
||||||
|
|
||||||
std::memcpy(entry.main_data.data() + write_offset_of_block,
|
const u64 bytes_to_write = std::min(bytes_to_write_total, VolumeWii::GROUP_DATA_SIZE);
|
||||||
state->decryption_buffer[j].data(), VolumeWii::BLOCK_DATA_SIZE);
|
|
||||||
|
std::memcpy(entry.main_data.data() + write_offset_of_group, in_ptr, bytes_to_write);
|
||||||
|
|
||||||
|
// Set this chunk as reusable if the decrypted data is AllSame.
|
||||||
|
// There is also a requirement that it lacks exceptions, but this is checked later
|
||||||
|
if (i == 0 && !entry.reuse_id)
|
||||||
|
{
|
||||||
|
if (AllSame(in_ptr, in_ptr + bytes_to_write))
|
||||||
|
entry.reuse_id = create_reuse_id(*in_ptr, true, j * blocks_per_chunk);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (entry.reuse_id && entry.reuse_id->decrypted &&
|
||||||
|
(!AllSame(in_ptr, in_ptr + bytes_to_write) || entry.reuse_id->value != *in_ptr))
|
||||||
|
{
|
||||||
|
entry.reuse_id.reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
in_ptr += out_data_per_chunk;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (size_t i = 0; i < exception_lists.size(); ++i)
|
for (size_t i = 0; i < exception_lists.size(); ++i)
|
||||||
{
|
{
|
||||||
OutputParametersEntry& entry = output_entries[chunks_per_wii_group == 1 ? 0 : i];
|
OutputParametersEntry& entry = output_entries[chunks_per_wii_group == 1 ? 0 : i];
|
||||||
if (entry.main_data.empty())
|
if (entry.reused_group)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
const std::vector<HashExceptionEntry>& in = exception_lists[i];
|
const std::vector<HashExceptionEntry>& in = exception_lists[i];
|
||||||
|
@ -1815,25 +1850,23 @@ WIAFileReader::ProcessAndCompress(CompressThreadState* state, CompressParameters
|
||||||
for (u64 i = 0; i < output_entries.size(); ++i)
|
for (u64 i = 0; i < output_entries.size(); ++i)
|
||||||
{
|
{
|
||||||
OutputParametersEntry& entry = output_entries[i];
|
OutputParametersEntry& entry = output_entries[i];
|
||||||
if (entry.main_data.empty() || entry.reuse_id)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// Set this chunk as reusable if it lacks exceptions and the decrypted data is AllSame
|
// If this chunk was set as reusable because the decrypted data is AllSame,
|
||||||
if (AllZero(entry.exception_lists) && AllSame(parameters.data))
|
// but it has exceptions, unmark it as reusable
|
||||||
entry.reuse_id = create_reuse_id(parameters.data.front(), true, i * blocks_per_chunk);
|
if (entry.reuse_id && entry.reuse_id->decrypted && !AllZero(entry.exception_lists))
|
||||||
|
entry.reuse_id.reset();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (OutputParametersEntry& entry : output_entries)
|
for (OutputParametersEntry& entry : output_entries)
|
||||||
{
|
{
|
||||||
if (entry.main_data.empty())
|
TryReuse(reusable_groups, reusable_groups_mutex, &entry);
|
||||||
|
if (entry.reused_group)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// Special case - a compressed size of zero is treated by WIA as meaning the data is all zeroes
|
// Special case - a compressed size of zero is treated by WIA as meaning the data is all zeroes
|
||||||
const bool all_zero = AllZero(entry.exception_lists) && AllZero(entry.main_data);
|
if (AllZero(entry.exception_lists) && AllZero(entry.main_data))
|
||||||
|
|
||||||
if (all_zero || reuse_id_exists(entry.reuse_id))
|
|
||||||
{
|
{
|
||||||
entry.exception_lists.clear();
|
entry.exception_lists.clear();
|
||||||
entry.main_data.clear();
|
entry.main_data.clear();
|
||||||
|
@ -1898,25 +1931,21 @@ WIAFileReader::ProcessAndCompress(CompressThreadState* state, CompressParameters
|
||||||
return OutputParameters{std::move(output_entries), parameters.bytes_read, parameters.group_index};
|
return OutputParameters{std::move(output_entries), parameters.bytes_read, parameters.group_index};
|
||||||
}
|
}
|
||||||
|
|
||||||
ConversionResultCode WIAFileReader::Output(const OutputParameters& parameters,
|
ConversionResultCode WIAFileReader::Output(std::vector<OutputParametersEntry>* entries,
|
||||||
File::IOFile* outfile,
|
File::IOFile* outfile,
|
||||||
std::map<ReuseID, GroupEntry>* reusable_groups,
|
std::map<ReuseID, GroupEntry>* reusable_groups,
|
||||||
std::mutex* reusable_groups_mutex,
|
std::mutex* reusable_groups_mutex,
|
||||||
GroupEntry* group_entry, u64* bytes_written)
|
GroupEntry* group_entry, u64* bytes_written)
|
||||||
{
|
{
|
||||||
for (const OutputParametersEntry& entry : parameters.entries)
|
for (OutputParametersEntry& entry : *entries)
|
||||||
{
|
{
|
||||||
if (entry.reuse_id)
|
TryReuse(reusable_groups, reusable_groups_mutex, &entry);
|
||||||
|
if (entry.reused_group)
|
||||||
{
|
{
|
||||||
std::lock_guard guard(*reusable_groups_mutex);
|
*group_entry = *entry.reused_group;
|
||||||
const auto it = reusable_groups->find(*entry.reuse_id);
|
|
||||||
if (it != reusable_groups->end())
|
|
||||||
{
|
|
||||||
*group_entry = it->second;
|
|
||||||
++group_entry;
|
++group_entry;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
const size_t data_size = entry.exception_lists.size() + entry.main_data.size();
|
const size_t data_size = entry.exception_lists.size() + entry.main_data.size();
|
||||||
|
|
||||||
|
@ -2060,7 +2089,7 @@ WIAFileReader::ConvertToWIA(BlobReader* infile, const VolumeDisc* infile_volume,
|
||||||
|
|
||||||
const auto output = [&](OutputParameters parameters) {
|
const auto output = [&](OutputParameters parameters) {
|
||||||
const ConversionResultCode result =
|
const ConversionResultCode result =
|
||||||
Output(parameters, outfile, &reusable_groups, &reusable_groups_mutex,
|
Output(¶meters.entries, outfile, &reusable_groups, &reusable_groups_mutex,
|
||||||
&group_entries[parameters.group_index], &bytes_written);
|
&group_entries[parameters.group_index], &bytes_written);
|
||||||
|
|
||||||
if (result != ConversionResultCode::Success)
|
if (result != ConversionResultCode::Success)
|
||||||
|
|
|
@ -479,6 +479,7 @@ private:
|
||||||
std::vector<u8> exception_lists;
|
std::vector<u8> exception_lists;
|
||||||
std::vector<u8> main_data;
|
std::vector<u8> main_data;
|
||||||
std::optional<ReuseID> reuse_id;
|
std::optional<ReuseID> reuse_id;
|
||||||
|
std::optional<GroupEntry> reused_group;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct OutputParameters
|
struct OutputParameters
|
||||||
|
@ -509,6 +510,8 @@ private:
|
||||||
static void SetUpCompressor(std::unique_ptr<Compressor>* compressor,
|
static void SetUpCompressor(std::unique_ptr<Compressor>* compressor,
|
||||||
WIACompressionType compression_type, int compression_level,
|
WIACompressionType compression_type, int compression_level,
|
||||||
WIAHeader2* header_2);
|
WIAHeader2* header_2);
|
||||||
|
static bool TryReuse(std::map<ReuseID, GroupEntry>* reusable_groups,
|
||||||
|
std::mutex* reusable_groups_mutex, OutputParametersEntry* entry);
|
||||||
static ConversionResult<OutputParameters>
|
static ConversionResult<OutputParameters>
|
||||||
ProcessAndCompress(CompressThreadState* state, CompressParameters parameters,
|
ProcessAndCompress(CompressThreadState* state, CompressParameters parameters,
|
||||||
const std::vector<PartitionEntry>& partition_entries,
|
const std::vector<PartitionEntry>& partition_entries,
|
||||||
|
@ -516,7 +519,8 @@ private:
|
||||||
std::map<ReuseID, GroupEntry>* reusable_groups,
|
std::map<ReuseID, GroupEntry>* reusable_groups,
|
||||||
std::mutex* reusable_groups_mutex, u64 chunks_per_wii_group,
|
std::mutex* reusable_groups_mutex, u64 chunks_per_wii_group,
|
||||||
u64 exception_lists_per_chunk, bool compressed_exception_lists);
|
u64 exception_lists_per_chunk, bool compressed_exception_lists);
|
||||||
static ConversionResultCode Output(const OutputParameters& parameters, File::IOFile* outfile,
|
static ConversionResultCode Output(std::vector<OutputParametersEntry>* entries,
|
||||||
|
File::IOFile* outfile,
|
||||||
std::map<ReuseID, GroupEntry>* reusable_groups,
|
std::map<ReuseID, GroupEntry>* reusable_groups,
|
||||||
std::mutex* reusable_groups_mutex, GroupEntry* group_entry,
|
std::mutex* reusable_groups_mutex, GroupEntry* group_entry,
|
||||||
u64* bytes_written);
|
u64* bytes_written);
|
||||||
|
|
Loading…
Reference in New Issue