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 
CFDE_TxtEdtBuf()21 CFDE_TxtEdtBuf::CFDE_TxtEdtBuf() : m_chunkSize(kDefaultChunkSize), m_nTotal(0) {
22   m_chunks.push_back(NewChunk());
23 }
24 
~CFDE_TxtEdtBuf()25 CFDE_TxtEdtBuf::~CFDE_TxtEdtBuf() {}
26 
GetChunkSize() const27 int32_t CFDE_TxtEdtBuf::GetChunkSize() const {
28   return m_chunkSize;
29 }
30 
GetTextLength() const31 int32_t CFDE_TxtEdtBuf::GetTextLength() const {
32   return m_nTotal;
33 }
34 
SetText(const CFX_WideString & wsText)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 
GetText() const65 CFX_WideString CFDE_TxtEdtBuf::GetText() const {
66   return GetRange(0, m_nTotal);
67 }
68 
GetCharByIndex(int32_t nIndex) const69 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 
GetRange(int32_t nBegin,int32_t nLength) const86 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 
Insert(int32_t nPos,const FX_WCHAR * lpText,int32_t nLength)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 
Delete(int32_t nIndex,int32_t nLength)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 
Clear(bool bRelease)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 
SetChunkSizeForTesting(size_t size)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 
Index2CP(int32_t nIndex) const235 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 
NewChunk()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 
ChunkHeader()263 CFDE_TxtEdtBuf::ChunkHeader::ChunkHeader() {}
264 
~ChunkHeader()265 CFDE_TxtEdtBuf::ChunkHeader::~ChunkHeader() {}
266 
Iterator(CFDE_TxtEdtBuf * pBuf,FX_WCHAR wcAlias)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 
~Iterator()276 CFDE_TxtEdtBuf::Iterator::~Iterator() {}
277 
Next(bool bPrev)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 
SetAt(int32_t nIndex)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 
GetAt() const331 int32_t CFDE_TxtEdtBuf::Iterator::GetAt() const {
332   return m_nIndex;
333 }
334 
GetChar()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 
IsEOF(bool bTail) const344 bool CFDE_TxtEdtBuf::Iterator::IsEOF(bool bTail) const {
345   return bTail ? m_nIndex == (m_pBuf->GetTextLength() - 2) : m_nIndex == 0;
346 }
347 
Clone()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