// Copyright 2014 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 "xfa/fde/cfde_txtedtbuf.h" #include #include #include "third_party/base/ptr_util.h" #include "third_party/base/stl_util.h" namespace { const int kDefaultChunkSize = 1024; } // namespace CFDE_TxtEdtBuf::CFDE_TxtEdtBuf() : m_chunkSize(kDefaultChunkSize), m_nTotal(0) { m_chunks.push_back(NewChunk()); } CFDE_TxtEdtBuf::~CFDE_TxtEdtBuf() {} int32_t CFDE_TxtEdtBuf::GetChunkSize() const { return m_chunkSize; } int32_t CFDE_TxtEdtBuf::GetTextLength() const { return m_nTotal; } void CFDE_TxtEdtBuf::SetText(const CFX_WideString& wsText) { ASSERT(!wsText.IsEmpty()); Clear(false); int32_t nTextLength = wsText.GetLength(); int32_t nNeedCount = ((nTextLength - 1) / GetChunkSize() + 1) - m_chunks.size(); int32_t i = 0; for (i = 0; i < nNeedCount; i++) m_chunks.push_back(NewChunk()); int32_t nTotalCount = m_chunks.size(); const wchar_t* lpSrcBuf = wsText.c_str(); int32_t nLeave = nTextLength; int32_t nCopyedLength = GetChunkSize(); for (i = 0; i < nTotalCount && nLeave > 0; i++) { if (nLeave < nCopyedLength) { nCopyedLength = nLeave; } ChunkHeader* chunk = m_chunks[i].get(); FXSYS_memcpy(chunk->wChars.get(), lpSrcBuf, nCopyedLength * sizeof(wchar_t)); nLeave -= nCopyedLength; lpSrcBuf += nCopyedLength; chunk->nUsed = nCopyedLength; } m_nTotal = nTextLength; } CFX_WideString CFDE_TxtEdtBuf::GetText() const { return GetRange(0, m_nTotal); } wchar_t CFDE_TxtEdtBuf::GetCharByIndex(int32_t nIndex) const { ASSERT(nIndex >= 0 && nIndex < GetTextLength()); ChunkHeader* pChunkHeader = nullptr; int32_t nTotal = 0; for (const auto& chunk : m_chunks) { pChunkHeader = chunk.get(); nTotal += pChunkHeader->nUsed; if (nTotal > nIndex) break; } ASSERT(pChunkHeader); wchar_t* buf = pChunkHeader->wChars.get(); return buf[pChunkHeader->nUsed - (nTotal - nIndex)]; } CFX_WideString CFDE_TxtEdtBuf::GetRange(int32_t nBegin, int32_t nLength) const { if (nLength == 0 || GetTextLength() == 0) return CFX_WideString(); ASSERT(nBegin >= 0 && nLength > 0 && nBegin < GetTextLength() && nBegin + nLength <= GetTextLength()); int32_t chunkIndex = 0; int32_t charIndex = 0; std::tie(chunkIndex, charIndex) = Index2CP(nBegin); int32_t nLeave = nLength; int32_t nCount = m_chunks.size(); CFX_WideString wsText; wchar_t* lpDstBuf = wsText.GetBuffer(nLength); int32_t nChunkIndex = chunkIndex; ChunkHeader* chunkHeader = m_chunks[nChunkIndex].get(); int32_t nCopyLength = chunkHeader->nUsed - charIndex; wchar_t* lpSrcBuf = chunkHeader->wChars.get() + charIndex; while (nLeave > 0) { if (nLeave <= nCopyLength) { nCopyLength = nLeave; } FXSYS_memcpy(lpDstBuf, lpSrcBuf, nCopyLength * sizeof(wchar_t)); nChunkIndex++; if (nChunkIndex >= nCount) { break; } chunkHeader = m_chunks[nChunkIndex].get(); lpSrcBuf = chunkHeader->wChars.get(); nLeave -= nCopyLength; lpDstBuf += nCopyLength; nCopyLength = chunkHeader->nUsed; } wsText.ReleaseBuffer(); return wsText; } void CFDE_TxtEdtBuf::Insert(int32_t nPos, const wchar_t* lpText, int32_t nLength) { ASSERT(nPos >= 0 && nPos <= m_nTotal); ASSERT(nLength > 0); int32_t chunkIndex = 0; int32_t charIndex = 0; std::tie(chunkIndex, charIndex) = Index2CP(nPos); int32_t nLengthTemp = nLength; if (charIndex != 0) { auto newChunk = NewChunk(); ChunkHeader* chunk = m_chunks[chunkIndex].get(); int32_t nCopy = chunk->nUsed - charIndex; FXSYS_memcpy(newChunk->wChars.get(), chunk->wChars.get() + charIndex, nCopy * sizeof(wchar_t)); chunk->nUsed -= nCopy; chunkIndex++; newChunk->nUsed = nCopy; m_chunks.insert(m_chunks.begin() + chunkIndex, std::move(newChunk)); charIndex = 0; } if (chunkIndex != 0) { ChunkHeader* chunk = m_chunks[chunkIndex - 1].get(); if (chunk->nUsed != GetChunkSize()) { chunkIndex--; int32_t nFree = GetChunkSize() - chunk->nUsed; int32_t nCopy = std::min(nLengthTemp, nFree); FXSYS_memcpy(chunk->wChars.get() + chunk->nUsed, lpText, nCopy * sizeof(wchar_t)); lpText += nCopy; nLengthTemp -= nCopy; chunk->nUsed += nCopy; chunkIndex++; } } while (nLengthTemp > 0) { auto chunk = NewChunk(); int32_t nCopy = std::min(nLengthTemp, GetChunkSize()); FXSYS_memcpy(chunk->wChars.get(), lpText, nCopy * sizeof(wchar_t)); lpText += nCopy; nLengthTemp -= nCopy; chunk->nUsed = nCopy; m_chunks.insert(m_chunks.begin() + chunkIndex, std::move(chunk)); chunkIndex++; } m_nTotal += nLength; } void CFDE_TxtEdtBuf::Delete(int32_t nIndex, int32_t nLength) { ASSERT(nLength > 0 && nIndex >= 0 && nIndex + nLength <= m_nTotal); int32_t endChunkIndex = 0; int32_t endCharIndex = 0; std::tie(endChunkIndex, endCharIndex) = Index2CP(nIndex + nLength - 1); m_nTotal -= nLength; ChunkHeader* chunk = m_chunks[endChunkIndex].get(); int32_t nFirstPart = endCharIndex + 1; int32_t nMovePart = chunk->nUsed - nFirstPart; if (nMovePart != 0) { int32_t nDelete = std::min(nFirstPart, nLength); FXSYS_memmove(chunk->wChars.get() + nFirstPart - nDelete, chunk->wChars.get() + nFirstPart, nMovePart * sizeof(wchar_t)); chunk->nUsed -= nDelete; nLength -= nDelete; endChunkIndex--; } while (nLength > 0) { ChunkHeader* curChunk = m_chunks[endChunkIndex].get(); int32_t nDeleted = std::min(curChunk->nUsed, nLength); curChunk->nUsed -= nDeleted; if (curChunk->nUsed == 0) m_chunks.erase(m_chunks.begin() + endChunkIndex); nLength -= nDeleted; endChunkIndex--; } } void CFDE_TxtEdtBuf::Clear(bool bRelease) { if (bRelease) { m_chunks.clear(); } else { size_t i = 0; while (i < m_chunks.size()) m_chunks[i++]->nUsed = 0; } m_nTotal = 0; } void CFDE_TxtEdtBuf::SetChunkSizeForTesting(size_t size) { ASSERT(size > 0); m_chunkSize = size; m_chunks.clear(); m_chunks.push_back(NewChunk()); } std::tuple CFDE_TxtEdtBuf::Index2CP(int32_t nIndex) const { ASSERT(nIndex <= GetTextLength()); if (nIndex == m_nTotal) { return std::tuple(m_chunks.size() - 1, m_chunks.back()->nUsed); } int32_t chunkIndex = 0; int32_t nTotal = 0; for (auto& chunk : m_chunks) { nTotal += chunk->nUsed; if (nTotal > nIndex) break; chunkIndex++; } int32_t charIndex = m_chunks[chunkIndex]->nUsed - (nTotal - nIndex); return std::tuple(chunkIndex, charIndex); } std::unique_ptr CFDE_TxtEdtBuf::NewChunk() { auto chunk = pdfium::MakeUnique(); chunk->wChars.reset(FX_Alloc(wchar_t, GetChunkSize())); chunk->nUsed = 0; return chunk; } CFDE_TxtEdtBuf::ChunkHeader::ChunkHeader() {} CFDE_TxtEdtBuf::ChunkHeader::~ChunkHeader() {} CFDE_TxtEdtBuf::Iterator::Iterator(CFDE_TxtEdtBuf* pBuf, wchar_t wcAlias) : m_pBuf(pBuf), m_nCurChunk(0), m_nCurIndex(0), m_nIndex(0), m_Alias(wcAlias) { ASSERT(m_pBuf); } CFDE_TxtEdtBuf::Iterator::~Iterator() {} bool CFDE_TxtEdtBuf::Iterator::Next(bool bPrev) { if (bPrev) { if (m_nIndex == 0) return false; ASSERT(m_nCurChunk < pdfium::CollectionSize(m_pBuf->m_chunks)); ChunkHeader* chunk = nullptr; if (m_nCurIndex > 0) { m_nCurIndex--; } else { while (m_nCurChunk > 0) { --m_nCurChunk; chunk = m_pBuf->m_chunks[m_nCurChunk].get(); if (chunk->nUsed > 0) { m_nCurIndex = chunk->nUsed - 1; break; } } } ASSERT(m_nCurChunk >= 0); m_nIndex--; return true; } else { if (m_nIndex >= (m_pBuf->m_nTotal - 1)) return false; ASSERT(m_nCurChunk < pdfium::CollectionSize(m_pBuf->m_chunks)); if (m_pBuf->m_chunks[m_nCurChunk]->nUsed != (m_nCurIndex + 1)) { m_nCurIndex++; } else { int32_t nEnd = m_pBuf->m_chunks.size() - 1; while (m_nCurChunk < nEnd) { m_nCurChunk++; ChunkHeader* chunkTemp = m_pBuf->m_chunks[m_nCurChunk].get(); if (chunkTemp->nUsed > 0) { m_nCurIndex = 0; break; } } } m_nIndex++; return true; } } void CFDE_TxtEdtBuf::Iterator::SetAt(int32_t nIndex) { ASSERT(nIndex >= 0 && nIndex < m_pBuf->m_nTotal); std::tie(m_nCurChunk, m_nCurIndex) = m_pBuf->Index2CP(nIndex); m_nIndex = nIndex; } int32_t CFDE_TxtEdtBuf::Iterator::GetAt() const { return m_nIndex; } wchar_t CFDE_TxtEdtBuf::Iterator::GetChar() { ASSERT(m_nIndex >= 0 && m_nIndex < m_pBuf->m_nTotal); if (m_Alias == 0 || m_nIndex == (m_pBuf->m_nTotal - 1)) { wchar_t* buf = m_pBuf->m_chunks[m_nCurChunk]->wChars.get(); return buf[m_nCurIndex]; } return m_Alias; } bool CFDE_TxtEdtBuf::Iterator::IsEOF(bool bTail) const { return bTail ? m_nIndex == (m_pBuf->GetTextLength() - 2) : m_nIndex == 0; } IFX_CharIter* CFDE_TxtEdtBuf::Iterator::Clone() { CFDE_TxtEdtBuf::Iterator* pIter = new CFDE_TxtEdtBuf::Iterator(m_pBuf); pIter->m_nCurChunk = m_nCurChunk; pIter->m_nCurIndex = m_nCurIndex; pIter->m_nIndex = m_nIndex; pIter->m_Alias = m_Alias; return pIter; }