Merge XFA to PDFium master at 4dc95e7 on 10/28/2014
[pdfium.git] / xfa / src / fee / src / fee / fde_txtedtbuf.cpp
1 // Copyright 2014 PDFium Authors. All rights reserved.\r
2 // Use of this source code is governed by a BSD-style license that can be\r
3 // found in the LICENSE file.\r
4 \r
5 // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com\r
6 \r
7 #include "../../../foxitlib.h"\r
8 #include "../../include/ifde_txtedtbuf.h"\r
9 #include "../../include/ifde_txtedtengine.h"\r
10 #include "fde_txtedtbuf.h"\r
11 #define FDE_DEFCHUNKCOUNT       2\r
12 #define FDE_TXTEDT_FORMATBLOCK_BGN              0xFFF9\r
13 #define FDE_TXTEDT_FORMATBLOCK_END              0xFFFB\r
14 #define FDE_TXTEDT_ZEROWIDTHSPACE               0x200B\r
15 #ifdef FDE_USEFORMATBLOCK\r
16 CFDE_TxtEdtBufIter::CFDE_TxtEdtBufIter(CFDE_TxtEdtBuf * pBuf, FX_BOOL bForDisplay )\r
17 #else\r
18 CFDE_TxtEdtBufIter::CFDE_TxtEdtBufIter(CFDE_TxtEdtBuf * pBuf, FX_WCHAR wcAlias )\r
19 #endif\r
20     : m_nCurChunk(0)\r
21     , m_nCurIndex(0)\r
22     , m_nIndex(0)\r
23     , m_pBuf(pBuf)\r
24 #ifdef FDE_USEFORMATBLOCK\r
25     , m_bForDisplay(bForDisplay)\r
26     , m_nAliasCount(0)\r
27 #endif\r
28     , m_bInField(FALSE)\r
29     , m_Alias(wcAlias)\r
30 {\r
31     FXSYS_assert(m_pBuf);\r
32 }\r
33 CFDE_TxtEdtBufIter::~CFDE_TxtEdtBufIter()\r
34 {\r
35 }\r
36 void CFDE_TxtEdtBufIter::Release()\r
37 {\r
38     delete this;\r
39 }\r
40 FX_BOOL CFDE_TxtEdtBufIter::Next(FX_BOOL bPrev )\r
41 {\r
42     if (bPrev) {\r
43         if (m_nIndex == 0) {\r
44             return FALSE;\r
45         }\r
46         FXSYS_assert(m_nCurChunk < m_pBuf->m_Chunks.GetSize());\r
47         CFDE_TxtEdtBuf::FDE_LPCHUNKHEADER lpChunk = NULL;\r
48         if (m_nCurIndex > 0) {\r
49             m_nCurIndex --;\r
50         } else {\r
51             while (m_nCurChunk > 0) {\r
52                 --m_nCurChunk;\r
53                 lpChunk = (CFDE_TxtEdtBuf::FDE_LPCHUNKHEADER)m_pBuf->m_Chunks[m_nCurChunk];\r
54                 if (lpChunk->nUsed > 0) {\r
55                     m_nCurIndex = lpChunk->nUsed - 1;\r
56                     break;\r
57                 }\r
58             }\r
59         }\r
60         FXSYS_assert(m_nCurChunk >= 0);\r
61         m_nIndex --;\r
62         return TRUE;\r
63     } else {\r
64         if (m_nIndex >= (m_pBuf->m_nTotal - 1)) {\r
65             return FALSE;\r
66         }\r
67         FXSYS_assert(m_nCurChunk < m_pBuf->m_Chunks.GetSize());\r
68         CFDE_TxtEdtBuf::FDE_LPCHUNKHEADER lpChunk = \\r
69                 (CFDE_TxtEdtBuf::FDE_LPCHUNKHEADER)m_pBuf->m_Chunks[m_nCurChunk];\r
70         if (lpChunk->nUsed != (m_nCurIndex + 1)) {\r
71             m_nCurIndex ++;\r
72         } else {\r
73             FX_INT32 nEnd = m_pBuf->m_Chunks.GetSize() - 1;\r
74             while (m_nCurChunk < nEnd) {\r
75                 m_nCurChunk ++;\r
76                 CFDE_TxtEdtBuf::FDE_LPCHUNKHEADER lpChunkTemp = \\r
77                         (CFDE_TxtEdtBuf::FDE_LPCHUNKHEADER)m_pBuf->m_Chunks[m_nCurChunk];\r
78                 if (lpChunkTemp->nUsed > 0) {\r
79                     m_nCurIndex = 0;\r
80                     break;\r
81                 }\r
82             }\r
83         }\r
84         m_nIndex ++;\r
85         return TRUE;\r
86     }\r
87 }\r
88 void CFDE_TxtEdtBufIter::SetAt(FX_INT32 nIndex)\r
89 {\r
90     FXSYS_assert(nIndex >= 0 && nIndex < m_pBuf->m_nTotal);\r
91     CFDE_TxtEdtBuf::FDE_CHUNKPLACE cp;\r
92     m_pBuf->Index2CP(nIndex, cp);\r
93     m_nIndex    = nIndex;\r
94     m_nCurChunk = cp.nChunkIndex;\r
95     m_nCurIndex = cp.nCharIndex;\r
96 }\r
97 FX_INT32 CFDE_TxtEdtBufIter::GetAt() const\r
98 {\r
99     return m_nIndex;\r
100 }\r
101 FX_WCHAR CFDE_TxtEdtBufIter::GetChar()\r
102 {\r
103     FXSYS_assert(m_nIndex >= 0 && m_nIndex < m_pBuf->m_nTotal);\r
104 #ifdef FDE_USEFORMATBLOCK\r
105     if (m_bForDisplay) {\r
106         if (m_bInField) {\r
107             FXSYS_assert(m_nAliasCount >= 0 && m_nAliasCount <= 2);\r
108             if (m_nAliasCount > 0) {\r
109                 m_nAliasCount --;\r
110                 return FDE_TXTEDT_ZEROWIDTHSPACE;\r
111             }\r
112             FX_WCHAR wc = ((CFDE_TxtEdtBuf::FDE_LPCHUNKHEADER)m_pBuf->m_Chunks[m_nCurChunk])->wChars[m_nCurIndex];\r
113             if (wc == FDE_TXTEDT_FORMATBLOCK_END) {\r
114                 m_nAliasCount   = 0;\r
115                 m_bInField              = FALSE;\r
116             }\r
117             return wc;\r
118         } else {\r
119             FX_WCHAR wc = ((CFDE_TxtEdtBuf::FDE_LPCHUNKHEADER)m_pBuf->m_Chunks[m_nCurChunk])->wChars[m_nCurIndex];\r
120             if (wc == FDE_TXTEDT_FORMATBLOCK_BGN) {\r
121                 m_nAliasCount   = 2;\r
122                 m_bInField              = TRUE;\r
123             }\r
124             return wc;\r
125         }\r
126     }\r
127 #endif\r
128     if (m_Alias == 0 || m_nIndex == (m_pBuf->m_nTotal - 1)) {\r
129         return ((CFDE_TxtEdtBuf::FDE_LPCHUNKHEADER)m_pBuf->m_Chunks[m_nCurChunk])->wChars[m_nCurIndex];\r
130     }\r
131     return m_Alias;\r
132 }\r
133 FX_BOOL CFDE_TxtEdtBufIter::IsEOF(FX_BOOL bTail ) const\r
134 {\r
135     return bTail ? m_nIndex == (m_pBuf->GetTextLength() - 2) : m_nIndex == 0;\r
136 }\r
137 IFX_CharIter * CFDE_TxtEdtBufIter::Clone()\r
138 {\r
139     CFDE_TxtEdtBufIter * pIter = FX_NEW CFDE_TxtEdtBufIter(m_pBuf);\r
140     pIter->m_nCurChunk  = m_nCurChunk;\r
141     pIter->m_nCurIndex  = m_nCurIndex;\r
142     pIter->m_nIndex             = m_nIndex;\r
143     pIter->m_Alias              = m_Alias;\r
144     return pIter;\r
145 }\r
146 CFDE_TxtEdtBuf::CFDE_TxtEdtBuf(FX_INT32 nDefChunkSize )\r
147     : m_nChunkSize(nDefChunkSize)\r
148     , m_nTotal(0)\r
149     , m_bChanged(FALSE)\r
150     , m_pAllocator(NULL)\r
151 {\r
152     FXSYS_assert(m_nChunkSize);\r
153     ResetChunkBuffer(FDE_DEFCHUNKCOUNT, m_nChunkSize);\r
154 }\r
155 void CFDE_TxtEdtBuf::Release()\r
156 {\r
157     delete this;\r
158 }\r
159 CFDE_TxtEdtBuf::~CFDE_TxtEdtBuf()\r
160 {\r
161     Clear(TRUE);\r
162     m_pAllocator->Release();\r
163     m_Chunks.RemoveAll();\r
164 }\r
165 FX_BOOL CFDE_TxtEdtBuf::SetChunkSize(FX_INT32 nChunkSize)\r
166 {\r
167     FXSYS_assert(nChunkSize);\r
168     ResetChunkBuffer(FDE_DEFCHUNKCOUNT, nChunkSize);\r
169     return TRUE;\r
170 }\r
171 FX_INT32 CFDE_TxtEdtBuf::GetChunkSize() const\r
172 {\r
173     return m_nChunkSize;\r
174 }\r
175 FX_INT32 CFDE_TxtEdtBuf::GetTextLength() const\r
176 {\r
177     return m_nTotal;\r
178 }\r
179 void CFDE_TxtEdtBuf::SetText(const CFX_WideString &wsText)\r
180 {\r
181     FXSYS_assert(!wsText.IsEmpty());\r
182     Clear(FALSE);\r
183     FX_INT32 nTextLength        = wsText.GetLength();\r
184     FX_INT32 nNeedCount         = ((nTextLength - 1) / m_nChunkSize + 1) - m_Chunks.GetSize();\r
185     FX_INT32 i = 0;\r
186     for (i = 0; i < nNeedCount; i ++) {\r
187         FDE_LPCHUNKHEADER lpChunk = (FDE_LPCHUNKHEADER)m_pAllocator->Alloc(sizeof(FDE_CHUNKHEADER) + \\r
188                                     (m_nChunkSize - 1) * sizeof(FX_WCHAR));\r
189         lpChunk->nUsed = 0;\r
190         m_Chunks.Add(lpChunk);\r
191     }\r
192     FX_INT32    nTotalCount             = m_Chunks.GetSize();\r
193     FX_LPCWSTR  lpSrcBuf                = FX_LPCWSTR(wsText);\r
194     FX_INT32    nLeave                  = nTextLength;\r
195     FX_INT32    nCopyedLength   = m_nChunkSize;\r
196     for (i = 0; i < nTotalCount && nLeave > 0; i ++) {\r
197         if (nLeave < nCopyedLength) {\r
198             nCopyedLength = nLeave;\r
199         }\r
200         FDE_LPCHUNKHEADER lpChunk = (FDE_LPCHUNKHEADER)m_Chunks[i];\r
201         FXSYS_memcpy(lpChunk->wChars, lpSrcBuf, nCopyedLength * sizeof(FX_WCHAR));\r
202         nLeave                  -= nCopyedLength;\r
203         lpSrcBuf                += nCopyedLength;\r
204         lpChunk->nUsed  = nCopyedLength;\r
205     }\r
206     m_nTotal    = nTextLength;\r
207     m_bChanged  = TRUE;\r
208 }\r
209 void CFDE_TxtEdtBuf::GetText(CFX_WideString &wsText) const\r
210 {\r
211     GetRange(wsText, 0, m_nTotal);\r
212 }\r
213 FX_WCHAR CFDE_TxtEdtBuf::GetCharByIndex(FX_INT32 nIndex) const\r
214 {\r
215     FXSYS_assert(nIndex >= 0 && nIndex < GetTextLength());\r
216     FDE_LPCHUNKHEADER   pChunkHeader    = NULL;\r
217     FX_INT32                    nTotal                  = 0;\r
218     FX_INT32                    nCount                  = m_Chunks.GetSize();\r
219     FX_INT32 i = 0;\r
220     for (i = 0; i < nCount; i ++) {\r
221         pChunkHeader = (FDE_LPCHUNKHEADER)m_Chunks[i];\r
222         nTotal += pChunkHeader->nUsed;\r
223         if (nTotal > nIndex) {\r
224             break;\r
225         }\r
226     }\r
227     FXSYS_assert(pChunkHeader);\r
228     return pChunkHeader->wChars[pChunkHeader->nUsed - (nTotal - nIndex)];\r
229 }\r
230 void CFDE_TxtEdtBuf::GetRange(CFX_WideString &wsText, FX_INT32 nBegin, FX_INT32 nLength) const\r
231 {\r
232     FDE_CHUNKPLACE cp;\r
233     Index2CP(nBegin, cp);\r
234     FX_INT32    nLeave          = nLength;\r
235     FX_INT32    nCount          = m_Chunks.GetSize();\r
236     FX_LPWSTR   lpDstBuf        = wsText.GetBuffer(nLength);\r
237     FX_INT32    nChunkIndex     = cp.nChunkIndex;\r
238     FDE_LPCHUNKHEADER   lpChunkHeader   = (FDE_LPCHUNKHEADER)m_Chunks[nChunkIndex];\r
239     FX_INT32                    nCopyLength             = lpChunkHeader->nUsed - cp.nCharIndex;\r
240     FX_LPWSTR                   lpSrcBuf                = lpChunkHeader->wChars + cp.nCharIndex;\r
241     while (nLeave > 0) {\r
242         if (nLeave <= nCopyLength) {\r
243             nCopyLength = nLeave;\r
244         }\r
245         FXSYS_memcpy(lpDstBuf, lpSrcBuf, nCopyLength * sizeof(FX_WCHAR));\r
246         nChunkIndex ++;\r
247         if (nChunkIndex >= nCount) {\r
248             break;\r
249         }\r
250         lpChunkHeader   =       (FDE_LPCHUNKHEADER)m_Chunks[nChunkIndex];\r
251         lpSrcBuf                =       lpChunkHeader->wChars;\r
252         nLeave                  -=      nCopyLength;\r
253         lpDstBuf                +=      nCopyLength;\r
254         nCopyLength             =       lpChunkHeader->nUsed;\r
255     }\r
256     wsText.ReleaseBuffer();\r
257 }\r
258 void CFDE_TxtEdtBuf::Insert(FX_INT32 nPos, FX_LPCWSTR lpText, FX_INT32 nLength )\r
259 {\r
260     FXSYS_assert(nPos >= 0 && nPos <= m_nTotal);\r
261     FDE_CHUNKPLACE cp;\r
262     Index2CP(nPos, cp);\r
263     FX_INT32 nLengthTemp = nLength;\r
264     if (cp.nCharIndex != 0) {\r
265         FDE_LPCHUNKHEADER       lpNewChunk      = (FDE_LPCHUNKHEADER)m_pAllocator->Alloc(sizeof(FDE_CHUNKHEADER) + \\r
266                                           (m_nChunkSize - 1) * sizeof(FX_WCHAR));\r
267         FDE_LPCHUNKHEADER       lpChunk         = (FDE_LPCHUNKHEADER)m_Chunks[cp.nChunkIndex];\r
268         FX_INT32 nCopy = lpChunk->nUsed - cp.nCharIndex;\r
269         FXSYS_memcpy(lpNewChunk->wChars, lpChunk->wChars + cp.nCharIndex, nCopy * sizeof(FX_WCHAR));\r
270         lpChunk->nUsed -= nCopy;\r
271         cp.nChunkIndex ++;\r
272         m_Chunks.InsertAt(cp.nChunkIndex, lpNewChunk);\r
273         lpNewChunk->nUsed       = nCopy;\r
274         cp.nCharIndex           = 0;\r
275     }\r
276     if (cp.nChunkIndex != 0) {\r
277         FDE_LPCHUNKHEADER lpChunk = (FDE_LPCHUNKHEADER)m_Chunks[cp.nChunkIndex - 1];\r
278         if (lpChunk->nUsed != m_nChunkSize) {\r
279             cp.nChunkIndex --;\r
280             FX_INT32 nFree = m_nChunkSize - lpChunk->nUsed;\r
281             FX_INT32 nCopy = FX_MIN(nLengthTemp, nFree);\r
282             FXSYS_memcpy(lpChunk->wChars + lpChunk->nUsed, lpText, nCopy * sizeof(FX_WCHAR));\r
283             lpText                      += nCopy;\r
284             nLengthTemp         -= nCopy;\r
285             lpChunk->nUsed      += nCopy;\r
286             cp.nChunkIndex++;\r
287         }\r
288     }\r
289     while (nLengthTemp > 0) {\r
290         FDE_LPCHUNKHEADER lpChunk = (FDE_LPCHUNKHEADER)m_pAllocator->Alloc(sizeof(FDE_CHUNKHEADER) + \\r
291                                     (m_nChunkSize - 1) * sizeof(FX_WCHAR));\r
292         FXSYS_assert(lpChunk);\r
293         FX_INT32 nCopy = FX_MIN(nLengthTemp, m_nChunkSize);\r
294         FXSYS_memcpy(lpChunk->wChars, lpText, nCopy * sizeof(FX_WCHAR));\r
295         lpText                  +=      nCopy;\r
296         nLengthTemp             -=      nCopy;\r
297         lpChunk->nUsed  =       nCopy;\r
298         m_Chunks.InsertAt(cp.nChunkIndex, lpChunk);\r
299         cp.nChunkIndex ++;\r
300     }\r
301     m_nTotal    +=      nLength;\r
302     m_bChanged  =       TRUE;\r
303 }\r
304 void CFDE_TxtEdtBuf::Delete(FX_INT32 nIndex, FX_INT32 nLength )\r
305 {\r
306     FXSYS_assert(nLength > 0 && nIndex >= 0 && nIndex + nLength <= m_nTotal);\r
307     FDE_CHUNKPLACE cpEnd;\r
308     Index2CP(nIndex + nLength - 1, cpEnd);\r
309     m_nTotal -= nLength;\r
310     FDE_LPCHUNKHEADER lpChunk = (FDE_LPCHUNKHEADER)m_Chunks[cpEnd.nChunkIndex];\r
311     FX_INT32 nFirstPart = cpEnd.nCharIndex + 1;\r
312     FX_INT32 nMovePart  = lpChunk->nUsed - nFirstPart;\r
313     if (nMovePart != 0) {\r
314         FX_INT32 nDelete = FX_MIN(nFirstPart, nLength);\r
315         FXSYS_memmove(lpChunk->wChars + nFirstPart - nDelete, \\r
316                       lpChunk->wChars + nFirstPart, nMovePart * sizeof(FX_WCHAR));\r
317         lpChunk->nUsed  -= nDelete;\r
318         nLength                 -= nDelete;\r
319         cpEnd.nChunkIndex --;\r
320     }\r
321     while (nLength > 0) {\r
322         lpChunk = (FDE_LPCHUNKHEADER)m_Chunks[cpEnd.nChunkIndex];\r
323         FX_INT32 nDeleted = FX_MIN(lpChunk->nUsed, nLength);\r
324         lpChunk->nUsed -= nDeleted;\r
325         if (lpChunk->nUsed == 0) {\r
326             m_pAllocator->Free(lpChunk);\r
327             m_Chunks.RemoveAt(cpEnd.nChunkIndex);\r
328             lpChunk = NULL;\r
329         }\r
330         nLength -= nDeleted;\r
331         cpEnd.nChunkIndex --;\r
332     }\r
333     m_bChanged = TRUE;\r
334 }\r
335 void CFDE_TxtEdtBuf::Clear(FX_BOOL bRelease )\r
336 {\r
337     FX_INT32 i          = 0;\r
338     FX_INT32 nCount     = m_Chunks.GetSize();\r
339     if (bRelease) {\r
340         while (i < nCount) {\r
341             m_pAllocator->Free(m_Chunks[i++]);\r
342         }\r
343         m_Chunks.RemoveAll();\r
344     } else {\r
345         while (i < nCount) {\r
346             ((FDE_LPCHUNKHEADER)m_Chunks[i++])->nUsed = 0;\r
347         }\r
348     }\r
349     m_nTotal    = 0;\r
350     m_bChanged  = TRUE;\r
351 }\r
352 FX_BOOL CFDE_TxtEdtBuf::Optimize(IFX_Pause * pPause )\r
353 {\r
354     if (m_bChanged == FALSE) {\r
355         return TRUE;\r
356     }\r
357     if (m_nTotal == 0) {\r
358         return TRUE;\r
359     }\r
360     FX_INT32 nCount = m_Chunks.GetSize();\r
361     if (nCount == 0) {\r
362         return TRUE;\r
363     }\r
364     FX_INT32 i = 0;\r
365     for ( ; i < nCount; i ++) {\r
366         FDE_LPCHUNKHEADER lpChunk = (FDE_LPCHUNKHEADER)m_Chunks[i];\r
367         if (lpChunk->nUsed == 0) {\r
368             m_pAllocator->Free(lpChunk);\r
369             m_Chunks.RemoveAt(i);\r
370             --i;\r
371             --nCount;\r
372         }\r
373     }\r
374     if (pPause != NULL && pPause->NeedToPauseNow()) {\r
375         return FALSE;\r
376     }\r
377     FDE_LPCHUNKHEADER lpPreChunk = (FDE_LPCHUNKHEADER)m_Chunks[0];\r
378     FDE_LPCHUNKHEADER lpCurChunk = NULL;\r
379     for (i = 1; i < nCount; i ++) {\r
380         lpCurChunk = (FDE_LPCHUNKHEADER)m_Chunks[i];\r
381         if (lpPreChunk->nUsed + lpCurChunk->nUsed <= m_nChunkSize) {\r
382             FXSYS_memcpy(lpPreChunk->wChars + lpPreChunk->nUsed, lpCurChunk->wChars, \\r
383                          lpCurChunk->nUsed * sizeof(FX_WCHAR));\r
384             lpPreChunk->nUsed += lpCurChunk->nUsed;\r
385             m_pAllocator->Free(lpCurChunk);\r
386             m_Chunks.RemoveAt(i);\r
387             --i;\r
388             --nCount;\r
389         } else {\r
390             lpPreChunk = lpCurChunk;\r
391         }\r
392         if (pPause != NULL && pPause->NeedToPauseNow()) {\r
393             return FALSE;\r
394         }\r
395     }\r
396     m_bChanged = FALSE;\r
397     return TRUE;\r
398 }\r
399 void CFDE_TxtEdtBuf::ResetChunkBuffer(FX_INT32 nDefChunkCount, FX_INT32 nChunkSize)\r
400 {\r
401     FXSYS_assert(nChunkSize);\r
402     FXSYS_assert(nDefChunkCount);\r
403     if (m_pAllocator) {\r
404         m_pAllocator->Release();\r
405         m_pAllocator = NULL;\r
406     }\r
407     m_Chunks.RemoveAll();\r
408     m_nChunkSize = nChunkSize;\r
409     FX_INT32 nChunkLength = sizeof(FDE_CHUNKHEADER) + (m_nChunkSize - 1) * sizeof(FX_WCHAR);\r
410     m_pAllocator = FX_CreateAllocator(FX_ALLOCTYPE_Fixed, nDefChunkCount, nChunkLength);\r
411     FXSYS_assert(m_pAllocator);\r
412     FDE_LPCHUNKHEADER lpChunkHeader = (FDE_LPCHUNKHEADER)m_pAllocator->Alloc(nChunkLength);\r
413     FXSYS_assert(lpChunkHeader);\r
414     lpChunkHeader->nUsed = 0;\r
415     m_Chunks.Add(lpChunkHeader);\r
416     m_nTotal = 0;\r
417 }\r
418 FX_INT32 CFDE_TxtEdtBuf::CP2Index(const FDE_CHUNKPLACE & cp) const\r
419 {\r
420     FX_INT32 nTotal = cp.nCharIndex;\r
421     FX_INT32 i = 0;\r
422     for (i = 0; i < cp.nChunkIndex; i ++) {\r
423         nTotal += ((FDE_LPCHUNKHEADER)m_Chunks[i])->nUsed;\r
424     }\r
425     return nTotal;\r
426 }\r
427 void CFDE_TxtEdtBuf::Index2CP(FX_INT32 nIndex, FDE_CHUNKPLACE & cp) const\r
428 {\r
429     FXSYS_assert(nIndex <= GetTextLength());\r
430     if (nIndex == m_nTotal) {\r
431         cp.nChunkIndex  = m_Chunks.GetSize() - 1;\r
432         cp.nCharIndex   = ((FDE_LPCHUNKHEADER)m_Chunks[cp.nChunkIndex])->nUsed;\r
433         return;\r
434     }\r
435     FX_INT32    i               = 0;\r
436     FX_INT32    nTotal  = 0;\r
437     FX_INT32    nCount  = m_Chunks.GetSize();\r
438     for ( ; i < nCount; i ++) {\r
439         nTotal += ((FDE_LPCHUNKHEADER)m_Chunks[i])->nUsed;\r
440         if (nTotal > nIndex) {\r
441             break;\r
442         }\r
443     }\r
444     cp.nChunkIndex      = i;\r
445     cp.nCharIndex       = ((FDE_LPCHUNKHEADER)m_Chunks[i])->nUsed - (nTotal - nIndex);\r
446 }\r