| // Copyright 2017 PDFium Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com | |
| #include "core/fxcrt/cfx_memorystream.h" | |
| #include <algorithm> | |
| #include "core/fxcrt/fx_safe_types.h" | |
| namespace { | |
| const int32_t kBlockSize = 64 * 1024; | |
| } // namespace | |
| CFX_MemoryStream::CFX_MemoryStream(bool bConsecutive) | |
| : m_nTotalSize(0), | |
| m_nCurSize(0), | |
| m_nCurPos(0), | |
| m_nGrowSize(kBlockSize), | |
| m_dwFlags(Type::kTakeOver | (bConsecutive ? Type::kConsecutive : 0)) {} | |
| CFX_MemoryStream::CFX_MemoryStream(uint8_t* pBuffer, | |
| size_t nSize, | |
| bool bTakeOver) | |
| : m_nTotalSize(nSize), | |
| m_nCurSize(nSize), | |
| m_nCurPos(0), | |
| m_nGrowSize(kBlockSize), | |
| m_dwFlags(Type::kConsecutive | (bTakeOver ? Type::kTakeOver : 0)) { | |
| m_Blocks.push_back(pBuffer); | |
| } | |
| CFX_MemoryStream::~CFX_MemoryStream() { | |
| if (m_dwFlags & Type::kTakeOver) { | |
| for (uint8_t* pBlock : m_Blocks) | |
| FX_Free(pBlock); | |
| } | |
| } | |
| FX_FILESIZE CFX_MemoryStream::GetSize() { | |
| return static_cast<FX_FILESIZE>(m_nCurSize); | |
| } | |
| bool CFX_MemoryStream::IsEOF() { | |
| return m_nCurPos >= static_cast<size_t>(GetSize()); | |
| } | |
| FX_FILESIZE CFX_MemoryStream::GetPosition() { | |
| return static_cast<FX_FILESIZE>(m_nCurPos); | |
| } | |
| bool CFX_MemoryStream::Flush() { | |
| return true; | |
| } | |
| bool CFX_MemoryStream::ReadBlock(void* buffer, | |
| FX_FILESIZE offset, | |
| size_t size) { | |
| if (!buffer || !size || offset < 0) | |
| return false; | |
| FX_SAFE_SIZE_T newPos = size; | |
| newPos += offset; | |
| if (!newPos.IsValid() || newPos.ValueOrDefault(0) == 0 || | |
| newPos.ValueOrDie() > m_nCurSize) { | |
| return false; | |
| } | |
| m_nCurPos = newPos.ValueOrDie(); | |
| if (m_dwFlags & Type::kConsecutive) { | |
| memcpy(buffer, m_Blocks[0] + static_cast<size_t>(offset), size); | |
| return true; | |
| } | |
| size_t nStartBlock = static_cast<size_t>(offset) / m_nGrowSize; | |
| offset -= static_cast<FX_FILESIZE>(nStartBlock * m_nGrowSize); | |
| while (size) { | |
| size_t nRead = std::min(size, m_nGrowSize - static_cast<size_t>(offset)); | |
| memcpy(buffer, m_Blocks[nStartBlock] + offset, nRead); | |
| buffer = static_cast<uint8_t*>(buffer) + nRead; | |
| size -= nRead; | |
| ++nStartBlock; | |
| offset = 0; | |
| } | |
| return true; | |
| } | |
| size_t CFX_MemoryStream::ReadBlock(void* buffer, size_t size) { | |
| if (m_nCurPos >= m_nCurSize) | |
| return 0; | |
| size_t nRead = std::min(size, m_nCurSize - m_nCurPos); | |
| if (!ReadBlock(buffer, static_cast<int32_t>(m_nCurPos), nRead)) | |
| return 0; | |
| return nRead; | |
| } | |
| bool CFX_MemoryStream::WriteBlock(const void* buffer, | |
| FX_FILESIZE offset, | |
| size_t size) { | |
| if (!buffer || !size) | |
| return false; | |
| if (m_dwFlags & Type::kConsecutive) { | |
| FX_SAFE_SIZE_T newPos = size; | |
| newPos += offset; | |
| if (!newPos.IsValid()) | |
| return false; | |
| m_nCurPos = newPos.ValueOrDie(); | |
| if (m_nCurPos > m_nTotalSize) { | |
| m_nTotalSize = (m_nCurPos + m_nGrowSize - 1) / m_nGrowSize * m_nGrowSize; | |
| if (m_Blocks.empty()) | |
| m_Blocks.push_back(FX_Alloc(uint8_t, m_nTotalSize)); | |
| else | |
| m_Blocks[0] = FX_Realloc(uint8_t, m_Blocks[0], m_nTotalSize); | |
| } | |
| memcpy(m_Blocks[0] + offset, buffer, size); | |
| m_nCurSize = std::max(m_nCurSize, m_nCurPos); | |
| return true; | |
| } | |
| FX_SAFE_SIZE_T newPos = size; | |
| newPos += offset; | |
| if (!newPos.IsValid()) | |
| return false; | |
| if (!ExpandBlocks(newPos.ValueOrDie())) | |
| return false; | |
| m_nCurPos = newPos.ValueOrDie(); | |
| size_t nStartBlock = static_cast<size_t>(offset) / m_nGrowSize; | |
| offset -= static_cast<FX_FILESIZE>(nStartBlock * m_nGrowSize); | |
| while (size) { | |
| size_t nWrite = std::min(size, m_nGrowSize - static_cast<size_t>(offset)); | |
| memcpy(m_Blocks[nStartBlock] + offset, buffer, nWrite); | |
| buffer = static_cast<const uint8_t*>(buffer) + nWrite; | |
| size -= nWrite; | |
| ++nStartBlock; | |
| offset = 0; | |
| } | |
| return true; | |
| } | |
| void CFX_MemoryStream::EstimateSize(size_t nInitSize, size_t nGrowSize) { | |
| if (m_dwFlags & Type::kConsecutive) { | |
| if (m_Blocks.empty()) { | |
| m_Blocks.push_back( | |
| FX_Alloc(uint8_t, std::max(nInitSize, static_cast<size_t>(4096)))); | |
| } | |
| m_nGrowSize = std::max(nGrowSize, static_cast<size_t>(4096)); | |
| } else if (m_Blocks.empty()) { | |
| m_nGrowSize = std::max(nGrowSize, static_cast<size_t>(4096)); | |
| } | |
| } | |
| void CFX_MemoryStream::AttachBuffer(uint8_t* pBuffer, size_t nSize) { | |
| if (!(m_dwFlags & Type::kConsecutive)) | |
| return; | |
| m_Blocks.clear(); | |
| m_Blocks.push_back(pBuffer); | |
| m_nTotalSize = nSize; | |
| m_nCurSize = nSize; | |
| m_nCurPos = 0; | |
| m_dwFlags = Type::kConsecutive; | |
| } | |
| void CFX_MemoryStream::DetachBuffer() { | |
| if (!(m_dwFlags & Type::kConsecutive)) | |
| return; | |
| m_Blocks.clear(); | |
| m_nTotalSize = 0; | |
| m_nCurSize = 0; | |
| m_nCurPos = 0; | |
| m_dwFlags = Type::kTakeOver; | |
| } | |
| bool CFX_MemoryStream::ExpandBlocks(size_t size) { | |
| m_nCurSize = std::max(m_nCurSize, size); | |
| if (size <= m_nTotalSize) | |
| return true; | |
| size = (size - m_nTotalSize + m_nGrowSize - 1) / m_nGrowSize; | |
| size_t iCount = m_Blocks.size(); | |
| m_Blocks.resize(iCount + size); | |
| while (size--) { | |
| m_Blocks[iCount++] = FX_Alloc(uint8_t, m_nGrowSize); | |
| m_nTotalSize += m_nGrowSize; | |
| } | |
| return true; | |
| } |