diff --git a/src/core/crypto/xts_encryption_layer.cpp b/src/core/crypto/xts_encryption_layer.cpp index 240f930c57..5cfc67847d 100644 --- a/src/core/crypto/xts_encryption_layer.cpp +++ b/src/core/crypto/xts_encryption_layer.cpp @@ -20,67 +20,49 @@ std::size_t XTSEncryptionLayer::Read(u8* data, std::size_t length, std::size_t o if (length == 0) return 0; + constexpr std::size_t PrefetchSectors = 4; + + auto* out = data; + std::size_t remaining = length; + std::size_t current_offset = offset; std::size_t total_read = 0; - // Handle initial unaligned part within a sector. - if (auto const sector_offset = offset % XTS_SECTOR_SIZE; sector_offset != 0) { - const std::size_t aligned_off = offset - sector_offset; - std::array block{}; - if (auto const got = base->Read(block.data(), XTS_SECTOR_SIZE, aligned_off); got > 0) { - if (got < XTS_SECTOR_SIZE) - std::memset(block.data() + got, 0, XTS_SECTOR_SIZE - got); - cipher.XTSTranscode(block.data(), XTS_SECTOR_SIZE, block.data(), aligned_off / XTS_SECTOR_SIZE, - XTS_SECTOR_SIZE, Op::Decrypt); - auto const to_copy = std::min(length, got > sector_offset ? got - sector_offset : 0); - if (to_copy > 0) { - std::memcpy(data, block.data() + sector_offset, to_copy); - data += to_copy; - offset += to_copy; - length -= to_copy; - total_read += to_copy; - } - } else { - return 0; - } - } + std::array sector{}; - if (length > 0) { - // Process aligned middle inplace, in sector sized multiples. - while (length >= XTS_SECTOR_SIZE) { - const std::size_t req = (length / XTS_SECTOR_SIZE) * XTS_SECTOR_SIZE; - const std::size_t got = base->Read(data, req, offset); - if (got == 0) { + while (remaining > 0) { + const std::size_t sector_index = current_offset / XTS_SECTOR_SIZE; + const std::size_t sector_offset = current_offset % XTS_SECTOR_SIZE; + + const std::size_t sectors_to_read = std::min(PrefetchSectors, + (remaining + sector_offset + + XTS_SECTOR_SIZE - 1) / + XTS_SECTOR_SIZE); + + for (std::size_t s = 0; s < sectors_to_read && remaining > 0; ++s) { + const std::size_t index = sector_index + s; + const std::size_t read_offset = index * XTS_SECTOR_SIZE; + const std::size_t got = base->Read(sector.data(), XTS_SECTOR_SIZE, read_offset); + if (got == 0) return total_read; - } - const std::size_t got_rounded = got - (got % XTS_SECTOR_SIZE); - if (got_rounded > 0) { - cipher.XTSTranscode(data, got_rounded, data, offset / XTS_SECTOR_SIZE, XTS_SECTOR_SIZE, Op::Decrypt); - data += got_rounded; - offset += got_rounded; - length -= got_rounded; - total_read += got_rounded; - } - // If we didn't get a full sector next, break to handle tail. - if (got_rounded != got) { - break; - } - } - // Handle tail within a sector, if any. - if (length > 0) { - std::array block{}; - const std::size_t got = base->Read(block.data(), XTS_SECTOR_SIZE, offset); - if (got > 0) { - if (got < XTS_SECTOR_SIZE) { - std::memset(block.data() + got, 0, XTS_SECTOR_SIZE - got); - } - cipher.XTSTranscode(block.data(), XTS_SECTOR_SIZE, block.data(), - offset / XTS_SECTOR_SIZE, XTS_SECTOR_SIZE, Op::Decrypt); - const std::size_t to_copy = std::min(length, got); - std::memcpy(data, block.data(), to_copy); - total_read += to_copy; - } + + if (got < XTS_SECTOR_SIZE) + std::memset(sector.data() + got, 0, XTS_SECTOR_SIZE - got); + + cipher.XTSTranscode(sector.data(), XTS_SECTOR_SIZE, sector.data(), index, XTS_SECTOR_SIZE, + Op::Decrypt); + + const std::size_t local_offset = (s == 0) ? sector_offset : 0; + const std::size_t available = XTS_SECTOR_SIZE - local_offset; + const std::size_t to_copy = std::min(available, remaining); + std::memcpy(out, sector.data() + local_offset, to_copy); + + out += to_copy; + current_offset += to_copy; + remaining -= to_copy; + total_read += to_copy; } } + return total_read; } } // namespace Core::Crypto