| // 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 <algorithm> |
| |
| #include "xfa/fgas/crt/fgas_memory.h" |
| |
| namespace { |
| |
| const int kDefaultChunkSize = 1024; |
| const int kDefaultChunkCount = 2; |
| |
| } // namespace |
| |
| CFDE_TxtEdtBuf::CFDE_TxtEdtBuf() |
| : m_nChunkSize(kDefaultChunkSize), m_nTotal(0), m_bChanged(false) { |
| ResetChunkBuffer(kDefaultChunkCount, m_nChunkSize); |
| } |
| |
| CFDE_TxtEdtBuf::~CFDE_TxtEdtBuf() { |
| Clear(true); |
| m_Chunks.RemoveAll(); |
| } |
| |
| int32_t CFDE_TxtEdtBuf::GetChunkSize() const { |
| return m_nChunkSize; |
| } |
| |
| 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) / m_nChunkSize + 1) - m_Chunks.GetSize(); |
| int32_t i = 0; |
| for (i = 0; i < nNeedCount; i++) { |
| FDE_CHUNKHEADER* lpChunk = |
| static_cast<FDE_CHUNKHEADER*>(m_pAllocator->Alloc( |
| sizeof(FDE_CHUNKHEADER) + (m_nChunkSize - 1) * sizeof(FX_WCHAR))); |
| lpChunk->nUsed = 0; |
| m_Chunks.Add(lpChunk); |
| } |
| int32_t nTotalCount = m_Chunks.GetSize(); |
| const FX_WCHAR* lpSrcBuf = wsText.c_str(); |
| int32_t nLeave = nTextLength; |
| int32_t nCopyedLength = m_nChunkSize; |
| for (i = 0; i < nTotalCount && nLeave > 0; i++) { |
| if (nLeave < nCopyedLength) { |
| nCopyedLength = nLeave; |
| } |
| FDE_CHUNKHEADER* lpChunk = m_Chunks[i]; |
| FXSYS_memcpy(lpChunk->wChars, lpSrcBuf, nCopyedLength * sizeof(FX_WCHAR)); |
| nLeave -= nCopyedLength; |
| lpSrcBuf += nCopyedLength; |
| lpChunk->nUsed = nCopyedLength; |
| } |
| m_nTotal = nTextLength; |
| m_bChanged = true; |
| } |
| |
| CFX_WideString CFDE_TxtEdtBuf::GetText() const { |
| return GetRange(0, m_nTotal); |
| } |
| |
| FX_WCHAR CFDE_TxtEdtBuf::GetCharByIndex(int32_t nIndex) const { |
| ASSERT(nIndex >= 0 && nIndex < GetTextLength()); |
| FDE_CHUNKHEADER* pChunkHeader = nullptr; |
| int32_t nTotal = 0; |
| int32_t nCount = m_Chunks.GetSize(); |
| int32_t i = 0; |
| for (i = 0; i < nCount; i++) { |
| pChunkHeader = m_Chunks[i]; |
| nTotal += pChunkHeader->nUsed; |
| if (nTotal > nIndex) { |
| break; |
| } |
| } |
| ASSERT(pChunkHeader); |
| return pChunkHeader->wChars[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()); |
| |
| FDE_CHUNKPLACE cp; |
| Index2CP(nBegin, cp); |
| int32_t nLeave = nLength; |
| int32_t nCount = m_Chunks.GetSize(); |
| |
| CFX_WideString wsText; |
| FX_WCHAR* lpDstBuf = wsText.GetBuffer(nLength); |
| int32_t nChunkIndex = cp.nChunkIndex; |
| FDE_CHUNKHEADER* lpChunkHeader = m_Chunks[nChunkIndex]; |
| int32_t nCopyLength = lpChunkHeader->nUsed - cp.nCharIndex; |
| FX_WCHAR* lpSrcBuf = lpChunkHeader->wChars + cp.nCharIndex; |
| while (nLeave > 0) { |
| if (nLeave <= nCopyLength) { |
| nCopyLength = nLeave; |
| } |
| FXSYS_memcpy(lpDstBuf, lpSrcBuf, nCopyLength * sizeof(FX_WCHAR)); |
| nChunkIndex++; |
| if (nChunkIndex >= nCount) { |
| break; |
| } |
| lpChunkHeader = m_Chunks[nChunkIndex]; |
| lpSrcBuf = lpChunkHeader->wChars; |
| nLeave -= nCopyLength; |
| lpDstBuf += nCopyLength; |
| nCopyLength = lpChunkHeader->nUsed; |
| } |
| wsText.ReleaseBuffer(); |
| |
| return wsText; |
| } |
| |
| void CFDE_TxtEdtBuf::Insert(int32_t nPos, |
| const FX_WCHAR* lpText, |
| int32_t nLength) { |
| ASSERT(nPos >= 0 && nPos <= m_nTotal); |
| ASSERT(nLength > 0); |
| |
| FDE_CHUNKPLACE cp; |
| Index2CP(nPos, cp); |
| int32_t nLengthTemp = nLength; |
| if (cp.nCharIndex != 0) { |
| FDE_CHUNKHEADER* lpNewChunk = |
| static_cast<FDE_CHUNKHEADER*>(m_pAllocator->Alloc( |
| sizeof(FDE_CHUNKHEADER) + (m_nChunkSize - 1) * sizeof(FX_WCHAR))); |
| FDE_CHUNKHEADER* lpChunk = m_Chunks[cp.nChunkIndex]; |
| int32_t nCopy = lpChunk->nUsed - cp.nCharIndex; |
| FXSYS_memcpy(lpNewChunk->wChars, lpChunk->wChars + cp.nCharIndex, |
| nCopy * sizeof(FX_WCHAR)); |
| lpChunk->nUsed -= nCopy; |
| cp.nChunkIndex++; |
| m_Chunks.InsertAt(cp.nChunkIndex, lpNewChunk); |
| lpNewChunk->nUsed = nCopy; |
| cp.nCharIndex = 0; |
| } |
| if (cp.nChunkIndex != 0) { |
| FDE_CHUNKHEADER* lpChunk = m_Chunks[cp.nChunkIndex - 1]; |
| if (lpChunk->nUsed != m_nChunkSize) { |
| cp.nChunkIndex--; |
| int32_t nFree = m_nChunkSize - lpChunk->nUsed; |
| int32_t nCopy = std::min(nLengthTemp, nFree); |
| FXSYS_memcpy(lpChunk->wChars + lpChunk->nUsed, lpText, |
| nCopy * sizeof(FX_WCHAR)); |
| lpText += nCopy; |
| nLengthTemp -= nCopy; |
| lpChunk->nUsed += nCopy; |
| cp.nChunkIndex++; |
| } |
| } |
| while (nLengthTemp > 0) { |
| FDE_CHUNKHEADER* lpChunk = |
| static_cast<FDE_CHUNKHEADER*>(m_pAllocator->Alloc( |
| sizeof(FDE_CHUNKHEADER) + (m_nChunkSize - 1) * sizeof(FX_WCHAR))); |
| ASSERT(lpChunk); |
| int32_t nCopy = std::min(nLengthTemp, m_nChunkSize); |
| FXSYS_memcpy(lpChunk->wChars, lpText, nCopy * sizeof(FX_WCHAR)); |
| lpText += nCopy; |
| nLengthTemp -= nCopy; |
| lpChunk->nUsed = nCopy; |
| m_Chunks.InsertAt(cp.nChunkIndex, lpChunk); |
| cp.nChunkIndex++; |
| } |
| m_nTotal += nLength; |
| m_bChanged = true; |
| } |
| |
| void CFDE_TxtEdtBuf::Delete(int32_t nIndex, int32_t nLength) { |
| ASSERT(nLength > 0 && nIndex >= 0 && nIndex + nLength <= m_nTotal); |
| FDE_CHUNKPLACE cpEnd; |
| Index2CP(nIndex + nLength - 1, cpEnd); |
| m_nTotal -= nLength; |
| FDE_CHUNKHEADER* lpChunk = m_Chunks[cpEnd.nChunkIndex]; |
| int32_t nFirstPart = cpEnd.nCharIndex + 1; |
| int32_t nMovePart = lpChunk->nUsed - nFirstPart; |
| if (nMovePart != 0) { |
| int32_t nDelete = std::min(nFirstPart, nLength); |
| FXSYS_memmove(lpChunk->wChars + nFirstPart - nDelete, |
| lpChunk->wChars + nFirstPart, nMovePart * sizeof(FX_WCHAR)); |
| lpChunk->nUsed -= nDelete; |
| nLength -= nDelete; |
| cpEnd.nChunkIndex--; |
| } |
| while (nLength > 0) { |
| lpChunk = m_Chunks[cpEnd.nChunkIndex]; |
| int32_t nDeleted = std::min(lpChunk->nUsed, nLength); |
| lpChunk->nUsed -= nDeleted; |
| if (lpChunk->nUsed == 0) { |
| m_pAllocator->Free(lpChunk); |
| m_Chunks.RemoveAt(cpEnd.nChunkIndex); |
| lpChunk = nullptr; |
| } |
| nLength -= nDeleted; |
| cpEnd.nChunkIndex--; |
| } |
| m_bChanged = true; |
| } |
| |
| void CFDE_TxtEdtBuf::Clear(bool bRelease) { |
| int32_t i = 0; |
| int32_t nCount = m_Chunks.GetSize(); |
| if (bRelease) { |
| while (i < nCount) { |
| m_pAllocator->Free(m_Chunks[i++]); |
| } |
| m_Chunks.RemoveAll(); |
| } else { |
| while (i < nCount) { |
| m_Chunks[i++]->nUsed = 0; |
| } |
| } |
| m_nTotal = 0; |
| m_bChanged = true; |
| } |
| |
| void CFDE_TxtEdtBuf::ResetChunkBuffer(int32_t nDefChunkCount, |
| int32_t nChunkSize) { |
| ASSERT(nChunkSize); |
| ASSERT(nDefChunkCount); |
| m_Chunks.RemoveAll(); |
| m_nChunkSize = nChunkSize; |
| int32_t nChunkLength = |
| sizeof(FDE_CHUNKHEADER) + (m_nChunkSize - 1) * sizeof(FX_WCHAR); |
| m_pAllocator = IFX_MemoryAllocator::Create(FX_ALLOCTYPE_Fixed, nDefChunkCount, |
| nChunkLength); |
| FDE_CHUNKHEADER* lpChunkHeader = |
| static_cast<FDE_CHUNKHEADER*>(m_pAllocator->Alloc(nChunkLength)); |
| ASSERT(lpChunkHeader); |
| lpChunkHeader->nUsed = 0; |
| m_Chunks.Add(lpChunkHeader); |
| m_nTotal = 0; |
| } |
| |
| void CFDE_TxtEdtBuf::SetChunkSizeForTesting(size_t size) { |
| Clear(true); |
| ResetChunkBuffer(kDefaultChunkCount, size); |
| } |
| |
| int32_t CFDE_TxtEdtBuf::CP2Index(const FDE_CHUNKPLACE& cp) const { |
| int32_t nTotal = cp.nCharIndex; |
| int32_t i = 0; |
| for (i = 0; i < cp.nChunkIndex; i++) { |
| nTotal += m_Chunks[i]->nUsed; |
| } |
| return nTotal; |
| } |
| |
| void CFDE_TxtEdtBuf::Index2CP(int32_t nIndex, FDE_CHUNKPLACE& cp) const { |
| ASSERT(nIndex <= GetTextLength()); |
| if (nIndex == m_nTotal) { |
| cp.nChunkIndex = m_Chunks.GetSize() - 1; |
| cp.nCharIndex = m_Chunks[cp.nChunkIndex]->nUsed; |
| return; |
| } |
| int32_t i = 0; |
| int32_t nTotal = 0; |
| int32_t nCount = m_Chunks.GetSize(); |
| for (; i < nCount; i++) { |
| nTotal += m_Chunks[i]->nUsed; |
| if (nTotal > nIndex) { |
| break; |
| } |
| } |
| cp.nChunkIndex = i; |
| cp.nCharIndex = m_Chunks[i]->nUsed - (nTotal - nIndex); |
| } |