1 // Copyright 2014 PDFium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com 6 7 #include "xfa/fde/cfde_txtedtbuf.h" 8 9 #include <algorithm> 10 #include <utility> 11 12 #include "third_party/base/ptr_util.h" 13 #include "third_party/base/stl_util.h" 14 15 namespace { 16 17 const int kDefaultChunkSize = 1024; 18 19 } // namespace 20 21 CFDE_TxtEdtBuf::CFDE_TxtEdtBuf() : m_chunkSize(kDefaultChunkSize), m_nTotal(0) { 22 m_chunks.push_back(NewChunk()); 23 } 24 25 CFDE_TxtEdtBuf::~CFDE_TxtEdtBuf() {} 26 27 int32_t CFDE_TxtEdtBuf::GetChunkSize() const { 28 return m_chunkSize; 29 } 30 31 int32_t CFDE_TxtEdtBuf::GetTextLength() const { 32 return m_nTotal; 33 } 34 35 void CFDE_TxtEdtBuf::SetText(const CFX_WideString& wsText) { 36 ASSERT(!wsText.IsEmpty()); 37 38 Clear(false); 39 int32_t nTextLength = wsText.GetLength(); 40 int32_t nNeedCount = 41 ((nTextLength - 1) / GetChunkSize() + 1) - m_chunks.size(); 42 int32_t i = 0; 43 for (i = 0; i < nNeedCount; i++) 44 m_chunks.push_back(NewChunk()); 45 46 int32_t nTotalCount = m_chunks.size(); 47 const FX_WCHAR* lpSrcBuf = wsText.c_str(); 48 int32_t nLeave = nTextLength; 49 int32_t nCopyedLength = GetChunkSize(); 50 for (i = 0; i < nTotalCount && nLeave > 0; i++) { 51 if (nLeave < nCopyedLength) { 52 nCopyedLength = nLeave; 53 } 54 55 ChunkHeader* chunk = m_chunks[i].get(); 56 FXSYS_memcpy(chunk->wChars.get(), lpSrcBuf, 57 nCopyedLength * sizeof(FX_WCHAR)); 58 nLeave -= nCopyedLength; 59 lpSrcBuf += nCopyedLength; 60 chunk->nUsed = nCopyedLength; 61 } 62 m_nTotal = nTextLength; 63 } 64 65 CFX_WideString CFDE_TxtEdtBuf::GetText() const { 66 return GetRange(0, m_nTotal); 67 } 68 69 FX_WCHAR CFDE_TxtEdtBuf::GetCharByIndex(int32_t nIndex) const { 70 ASSERT(nIndex >= 0 && nIndex < GetTextLength()); 71 72 ChunkHeader* pChunkHeader = nullptr; 73 int32_t nTotal = 0; 74 for (const auto& chunk : m_chunks) { 75 pChunkHeader = chunk.get(); 76 nTotal += pChunkHeader->nUsed; 77 if (nTotal > nIndex) 78 break; 79 } 80 ASSERT(pChunkHeader); 81 82 FX_WCHAR* buf = pChunkHeader->wChars.get(); 83 return buf[pChunkHeader->nUsed - (nTotal - nIndex)]; 84 } 85 86 CFX_WideString CFDE_TxtEdtBuf::GetRange(int32_t nBegin, int32_t nLength) const { 87 if (nLength == 0 || GetTextLength() == 0) 88 return CFX_WideString(); 89 90 ASSERT(nBegin >= 0 && nLength > 0 && nBegin < GetTextLength() && 91 nBegin + nLength <= GetTextLength()); 92 93 int32_t chunkIndex = 0; 94 int32_t charIndex = 0; 95 std::tie(chunkIndex, charIndex) = Index2CP(nBegin); 96 97 int32_t nLeave = nLength; 98 int32_t nCount = m_chunks.size(); 99 100 CFX_WideString wsText; 101 FX_WCHAR* lpDstBuf = wsText.GetBuffer(nLength); 102 int32_t nChunkIndex = chunkIndex; 103 104 ChunkHeader* chunkHeader = m_chunks[nChunkIndex].get(); 105 int32_t nCopyLength = chunkHeader->nUsed - charIndex; 106 FX_WCHAR* lpSrcBuf = chunkHeader->wChars.get() + charIndex; 107 while (nLeave > 0) { 108 if (nLeave <= nCopyLength) { 109 nCopyLength = nLeave; 110 } 111 FXSYS_memcpy(lpDstBuf, lpSrcBuf, nCopyLength * sizeof(FX_WCHAR)); 112 nChunkIndex++; 113 if (nChunkIndex >= nCount) { 114 break; 115 } 116 chunkHeader = m_chunks[nChunkIndex].get(); 117 lpSrcBuf = chunkHeader->wChars.get(); 118 nLeave -= nCopyLength; 119 lpDstBuf += nCopyLength; 120 nCopyLength = chunkHeader->nUsed; 121 } 122 wsText.ReleaseBuffer(); 123 124 return wsText; 125 } 126 127 void CFDE_TxtEdtBuf::Insert(int32_t nPos, 128 const FX_WCHAR* lpText, 129 int32_t nLength) { 130 ASSERT(nPos >= 0 && nPos <= m_nTotal); 131 ASSERT(nLength > 0); 132 133 int32_t chunkIndex = 0; 134 int32_t charIndex = 0; 135 std::tie(chunkIndex, charIndex) = Index2CP(nPos); 136 137 int32_t nLengthTemp = nLength; 138 if (charIndex != 0) { 139 auto newChunk = NewChunk(); 140 141 ChunkHeader* chunk = m_chunks[chunkIndex].get(); 142 int32_t nCopy = chunk->nUsed - charIndex; 143 144 FXSYS_memcpy(newChunk->wChars.get(), chunk->wChars.get() + charIndex, 145 nCopy * sizeof(FX_WCHAR)); 146 chunk->nUsed -= nCopy; 147 chunkIndex++; 148 149 newChunk->nUsed = nCopy; 150 m_chunks.insert(m_chunks.begin() + chunkIndex, std::move(newChunk)); 151 charIndex = 0; 152 } 153 154 if (chunkIndex != 0) { 155 ChunkHeader* chunk = m_chunks[chunkIndex - 1].get(); 156 if (chunk->nUsed != GetChunkSize()) { 157 chunkIndex--; 158 int32_t nFree = GetChunkSize() - chunk->nUsed; 159 int32_t nCopy = std::min(nLengthTemp, nFree); 160 FXSYS_memcpy(chunk->wChars.get() + chunk->nUsed, lpText, 161 nCopy * sizeof(FX_WCHAR)); 162 lpText += nCopy; 163 nLengthTemp -= nCopy; 164 chunk->nUsed += nCopy; 165 chunkIndex++; 166 } 167 } 168 169 while (nLengthTemp > 0) { 170 auto chunk = NewChunk(); 171 172 int32_t nCopy = std::min(nLengthTemp, GetChunkSize()); 173 FXSYS_memcpy(chunk->wChars.get(), lpText, nCopy * sizeof(FX_WCHAR)); 174 lpText += nCopy; 175 nLengthTemp -= nCopy; 176 chunk->nUsed = nCopy; 177 m_chunks.insert(m_chunks.begin() + chunkIndex, std::move(chunk)); 178 chunkIndex++; 179 } 180 m_nTotal += nLength; 181 } 182 183 void CFDE_TxtEdtBuf::Delete(int32_t nIndex, int32_t nLength) { 184 ASSERT(nLength > 0 && nIndex >= 0 && nIndex + nLength <= m_nTotal); 185 186 int32_t endChunkIndex = 0; 187 int32_t endCharIndex = 0; 188 std::tie(endChunkIndex, endCharIndex) = Index2CP(nIndex + nLength - 1); 189 m_nTotal -= nLength; 190 191 ChunkHeader* chunk = m_chunks[endChunkIndex].get(); 192 int32_t nFirstPart = endCharIndex + 1; 193 int32_t nMovePart = chunk->nUsed - nFirstPart; 194 if (nMovePart != 0) { 195 int32_t nDelete = std::min(nFirstPart, nLength); 196 FXSYS_memmove(chunk->wChars.get() + nFirstPart - nDelete, 197 chunk->wChars.get() + nFirstPart, 198 nMovePart * sizeof(FX_WCHAR)); 199 chunk->nUsed -= nDelete; 200 nLength -= nDelete; 201 endChunkIndex--; 202 } 203 204 while (nLength > 0) { 205 ChunkHeader* curChunk = m_chunks[endChunkIndex].get(); 206 int32_t nDeleted = std::min(curChunk->nUsed, nLength); 207 curChunk->nUsed -= nDeleted; 208 if (curChunk->nUsed == 0) 209 m_chunks.erase(m_chunks.begin() + endChunkIndex); 210 211 nLength -= nDeleted; 212 endChunkIndex--; 213 } 214 } 215 216 void CFDE_TxtEdtBuf::Clear(bool bRelease) { 217 if (bRelease) { 218 m_chunks.clear(); 219 } else { 220 size_t i = 0; 221 while (i < m_chunks.size()) 222 m_chunks[i++]->nUsed = 0; 223 } 224 m_nTotal = 0; 225 } 226 227 void CFDE_TxtEdtBuf::SetChunkSizeForTesting(size_t size) { 228 ASSERT(size > 0); 229 230 m_chunkSize = size; 231 m_chunks.clear(); 232 m_chunks.push_back(NewChunk()); 233 } 234 235 std::tuple<int32_t, int32_t> CFDE_TxtEdtBuf::Index2CP(int32_t nIndex) const { 236 ASSERT(nIndex <= GetTextLength()); 237 238 if (nIndex == m_nTotal) { 239 return std::tuple<int32_t, int32_t>(m_chunks.size() - 1, 240 m_chunks.back()->nUsed); 241 } 242 243 int32_t chunkIndex = 0; 244 int32_t nTotal = 0; 245 for (auto& chunk : m_chunks) { 246 nTotal += chunk->nUsed; 247 if (nTotal > nIndex) 248 break; 249 chunkIndex++; 250 } 251 252 int32_t charIndex = m_chunks[chunkIndex]->nUsed - (nTotal - nIndex); 253 return std::tuple<int32_t, int32_t>(chunkIndex, charIndex); 254 } 255 256 std::unique_ptr<CFDE_TxtEdtBuf::ChunkHeader> CFDE_TxtEdtBuf::NewChunk() { 257 auto chunk = pdfium::MakeUnique<ChunkHeader>(); 258 chunk->wChars.reset(FX_Alloc(FX_WCHAR, GetChunkSize())); 259 chunk->nUsed = 0; 260 return chunk; 261 } 262 263 CFDE_TxtEdtBuf::ChunkHeader::ChunkHeader() {} 264 265 CFDE_TxtEdtBuf::ChunkHeader::~ChunkHeader() {} 266 267 CFDE_TxtEdtBuf::Iterator::Iterator(CFDE_TxtEdtBuf* pBuf, FX_WCHAR wcAlias) 268 : m_pBuf(pBuf), 269 m_nCurChunk(0), 270 m_nCurIndex(0), 271 m_nIndex(0), 272 m_Alias(wcAlias) { 273 ASSERT(m_pBuf); 274 } 275 276 CFDE_TxtEdtBuf::Iterator::~Iterator() {} 277 278 bool CFDE_TxtEdtBuf::Iterator::Next(bool bPrev) { 279 if (bPrev) { 280 if (m_nIndex == 0) 281 return false; 282 283 ASSERT(m_nCurChunk < pdfium::CollectionSize<int32_t>(m_pBuf->m_chunks)); 284 285 ChunkHeader* chunk = nullptr; 286 if (m_nCurIndex > 0) { 287 m_nCurIndex--; 288 } else { 289 while (m_nCurChunk > 0) { 290 --m_nCurChunk; 291 chunk = m_pBuf->m_chunks[m_nCurChunk].get(); 292 if (chunk->nUsed > 0) { 293 m_nCurIndex = chunk->nUsed - 1; 294 break; 295 } 296 } 297 } 298 ASSERT(m_nCurChunk >= 0); 299 m_nIndex--; 300 return true; 301 } else { 302 if (m_nIndex >= (m_pBuf->m_nTotal - 1)) 303 return false; 304 305 ASSERT(m_nCurChunk < pdfium::CollectionSize<int32_t>(m_pBuf->m_chunks)); 306 if (m_pBuf->m_chunks[m_nCurChunk]->nUsed != (m_nCurIndex + 1)) { 307 m_nCurIndex++; 308 } else { 309 int32_t nEnd = m_pBuf->m_chunks.size() - 1; 310 while (m_nCurChunk < nEnd) { 311 m_nCurChunk++; 312 ChunkHeader* chunkTemp = m_pBuf->m_chunks[m_nCurChunk].get(); 313 if (chunkTemp->nUsed > 0) { 314 m_nCurIndex = 0; 315 break; 316 } 317 } 318 } 319 m_nIndex++; 320 return true; 321 } 322 } 323 324 void CFDE_TxtEdtBuf::Iterator::SetAt(int32_t nIndex) { 325 ASSERT(nIndex >= 0 && nIndex < m_pBuf->m_nTotal); 326 327 std::tie(m_nCurChunk, m_nCurIndex) = m_pBuf->Index2CP(nIndex); 328 m_nIndex = nIndex; 329 } 330 331 int32_t CFDE_TxtEdtBuf::Iterator::GetAt() const { 332 return m_nIndex; 333 } 334 335 FX_WCHAR CFDE_TxtEdtBuf::Iterator::GetChar() { 336 ASSERT(m_nIndex >= 0 && m_nIndex < m_pBuf->m_nTotal); 337 if (m_Alias == 0 || m_nIndex == (m_pBuf->m_nTotal - 1)) { 338 FX_WCHAR* buf = m_pBuf->m_chunks[m_nCurChunk]->wChars.get(); 339 return buf[m_nCurIndex]; 340 } 341 return m_Alias; 342 } 343 344 bool CFDE_TxtEdtBuf::Iterator::IsEOF(bool bTail) const { 345 return bTail ? m_nIndex == (m_pBuf->GetTextLength() - 2) : m_nIndex == 0; 346 } 347 348 IFX_CharIter* CFDE_TxtEdtBuf::Iterator::Clone() { 349 CFDE_TxtEdtBuf::Iterator* pIter = new CFDE_TxtEdtBuf::Iterator(m_pBuf); 350 pIter->m_nCurChunk = m_nCurChunk; 351 pIter->m_nCurIndex = m_nCurIndex; 352 pIter->m_nIndex = m_nIndex; 353 pIter->m_Alias = m_Alias; 354 return pIter; 355 } 356