// Copyright 2011-2022, Molecular Matters GmbH // See LICENSE.txt for licensing details (2-clause BSD License: https://opensource.org/licenses/BSD-2-Clause) #include "PDB_PCH.h" #include "PDB_CoalescedMSFStream.h" #include "PDB_Util.h" #include "PDB_DirectMSFStream.h" #include "Foundation/PDB_PointerUtil.h" #include "Foundation/PDB_Memory.h" #include "Foundation/PDB_DisableWarningsPush.h" #include #include "Foundation/PDB_DisableWarningsPop.h" namespace { // ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------ PDB_NO_DISCARD static bool AreBlockIndicesContiguous(const uint32_t* blockIndices, uint32_t blockSize, uint32_t streamSize) PDB_NO_EXCEPT { const uint32_t blockCount = PDB::ConvertSizeToBlockCount(streamSize, blockSize); // start with the first index, checking if all following indices are contiguous (N, N+1, N+2, ...) uint32_t expectedIndex = blockIndices[0]; for (uint32_t i = 1u; i < blockCount; ++i) { ++expectedIndex; if (blockIndices[i] != expectedIndex) { return false; } } return true; } } // ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------ PDB::CoalescedMSFStream::CoalescedMSFStream(void) PDB_NO_EXCEPT : m_ownedData(nullptr) , m_data(nullptr) , m_size(0u) { } // ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------ PDB::CoalescedMSFStream::CoalescedMSFStream(CoalescedMSFStream&& other) PDB_NO_EXCEPT : m_ownedData(PDB_MOVE(other.m_ownedData)) , m_data(PDB_MOVE(other.m_data)) , m_size(PDB_MOVE(other.m_size)) { other.m_ownedData = nullptr; other.m_data = nullptr; other.m_size = 0u; } // ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------ PDB::CoalescedMSFStream& PDB::CoalescedMSFStream::operator=(CoalescedMSFStream&& other) PDB_NO_EXCEPT { if (this != &other) { PDB_DELETE_ARRAY(m_ownedData); m_ownedData = PDB_MOVE(other.m_ownedData); m_data = PDB_MOVE(other.m_data); m_size = PDB_MOVE(other.m_size); other.m_ownedData = nullptr; other.m_data = nullptr; other.m_size = 0u; } return *this; } // ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------ PDB::CoalescedMSFStream::CoalescedMSFStream(const void* data, uint32_t blockSize, const uint32_t* blockIndices, uint32_t streamSize) PDB_NO_EXCEPT : m_ownedData(nullptr) , m_data(nullptr) , m_size(streamSize) { if (AreBlockIndicesContiguous(blockIndices, blockSize, streamSize)) { // fast path, all block indices are contiguous, so we don't have to copy any data at all. // instead, we directly point into the memory-mapped file at the correct offset. const uint32_t index = blockIndices[0]; const size_t fileOffset = PDB::ConvertBlockIndexToFileOffset(index, blockSize); m_data = Pointer::Offset(data, fileOffset); } else { // slower path, we need to copy disjunct blocks into our own data array, block by block m_ownedData = PDB_NEW_ARRAY(Byte, streamSize); m_data = m_ownedData; Byte* destination = m_ownedData; // copy full blocks first const uint32_t fullBlockCount = streamSize / blockSize; for (uint32_t i = 0u; i < fullBlockCount; ++i) { const uint32_t index = blockIndices[i]; // read one single block at the correct offset in the stream const size_t fileOffset = PDB::ConvertBlockIndexToFileOffset(index, blockSize); const void* sourceData = Pointer::Offset(data, fileOffset); std::memcpy(destination, sourceData, blockSize); destination += blockSize; } // account for non-full blocks const uint32_t remainingBytes = streamSize - (fullBlockCount * blockSize); if (remainingBytes != 0u) { const uint32_t index = blockIndices[fullBlockCount]; // read remaining bytes at correct offset in the stream const size_t fileOffset = PDB::ConvertBlockIndexToFileOffset(index, blockSize); const void* sourceData = Pointer::Offset(data, fileOffset); std::memcpy(destination, sourceData, remainingBytes); } } } // ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------ PDB::CoalescedMSFStream::CoalescedMSFStream(const DirectMSFStream& directStream, uint32_t size, uint32_t offset) PDB_NO_EXCEPT : m_ownedData(nullptr) , m_data(nullptr) , m_size(size) { const uint32_t* const blockIndicesForOffset = directStream.GetBlockIndicesForOffset(offset); if (AreBlockIndicesContiguous(blockIndicesForOffset, directStream.GetBlockSize(), size)) { // fast path, all block indices inside the direct stream from (data + offset) to (data + offset + size) are contiguous const size_t offsetWithinData = directStream.GetDataOffsetForOffset(offset); m_data = Pointer::Offset(directStream.GetData(), offsetWithinData); } else { // slower path, we need to copy from disjunct blocks, which is performed by the direct stream m_ownedData = PDB_NEW_ARRAY(Byte, size); m_data = m_ownedData; directStream.ReadAtOffset(m_ownedData, size, offset); } } // ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------ PDB::CoalescedMSFStream::~CoalescedMSFStream(void) PDB_NO_EXCEPT { PDB_DELETE_ARRAY(m_ownedData); }