OSDN Git Service

refator & add LineInfoTests.cpp
[winmerge-jp/winmerge-jp.git] / Externals / crystaledit / editlib / LineInfo.cpp
1 /** 
2  * @file  LineInfo.cpp
3  *
4  * @brief Implementation of LineInfo class.
5  */
6
7 #include "pch.h"
8 #include "LineInfo.h"
9 #include <cassert>
10 #include <utility>
11
12 /**
13  @brief Constructor.
14  */
15 LineInfo::LineInfo()
16 : m_pcLine(nullptr)
17 , m_nLength(0)
18 , m_nMax(0)
19 , m_nEolChars(0)
20 , m_dwFlags(0)
21 , m_dwRevisionNumber(0)
22 {
23 }
24
25 LineInfo::LineInfo(const tchar_t* pszLine, size_t nLength)
26 : m_pcLine(nullptr)
27 , m_nLength(0)
28 , m_nMax(0)
29 , m_nEolChars(0)
30 , m_dwFlags(0)
31 , m_dwRevisionNumber(0)
32 {
33   Create(pszLine, nLength);
34 }
35
36 LineInfo::LineInfo(const LineInfo& li)
37 : m_pcLine(new tchar_t[li.m_nMax])
38 , m_nLength(li.m_nLength)
39 , m_nMax(li.m_nMax)
40 , m_nEolChars(li.m_nEolChars)
41 , m_dwFlags(li.m_dwFlags)
42 , m_dwRevisionNumber(li.m_dwRevisionNumber)
43 {
44   memcpy (m_pcLine, li.m_pcLine, sizeof (tchar_t) * m_nMax);
45 }
46
47 LineInfo::LineInfo(LineInfo&& li) noexcept
48 : m_pcLine(nullptr)
49 , m_nLength(0)
50 {
51   *this = std::move(li);
52 }
53
54 LineInfo::~LineInfo()
55 {
56   delete[] m_pcLine;
57 }
58
59 LineInfo& LineInfo::operator=(const LineInfo& li)
60 {
61   m_pcLine = new tchar_t[li.m_nMax];
62   m_nLength = li.m_nLength;
63   m_nMax = li.m_nMax;
64   m_nEolChars = li.m_nEolChars;
65   m_dwFlags = li.m_dwFlags;
66   m_dwRevisionNumber = li.m_dwRevisionNumber;
67   memcpy (m_pcLine, li.m_pcLine, sizeof (tchar_t) * m_nMax);
68   return *this;
69 }
70
71 LineInfo& LineInfo::operator=(LineInfo&& li) noexcept
72 {
73   m_pcLine = li.m_pcLine;
74   m_nLength = li.m_nLength;
75   m_nMax = li.m_nMax;
76   m_nEolChars = li.m_nEolChars;
77   m_dwFlags = li.m_dwFlags;
78   m_dwRevisionNumber = li.m_dwRevisionNumber;
79   li.m_pcLine = nullptr;
80   li.m_nLength = 0;
81   return *this;
82 }
83
84 /**
85  * @brief Clear item.
86  * Frees buffer, sets members to initial values.
87  */
88 void LineInfo::Clear()
89 {
90   if (m_pcLine != nullptr)
91     {
92       delete[] m_pcLine;
93       m_pcLine = nullptr;
94       m_nLength = 0;
95       m_nMax = 0;
96       m_nEolChars = 0;
97       m_dwFlags = 0;
98       m_dwRevisionNumber = 0;
99     }
100 }
101
102 /**
103  * @brief Free reserved memory.
104  * Frees reserved memory, but does not clear flags.
105  */
106 void LineInfo::FreeBuffer()
107 {
108   if (m_pcLine != nullptr)
109     {
110       delete[] m_pcLine;
111       m_pcLine = nullptr;
112       m_nLength = 0;
113       m_nMax = 0;
114       m_nEolChars = 0;
115     }
116 }
117
118 /**
119  * @brief Create a line.
120  * @param [in] pszLine Line data.
121  * @param [in] nLength Line length.
122  */
123 void LineInfo::Create(const tchar_t* pszLine, size_t nLength)
124 {
125   if (nLength == 0)
126     {
127       CreateEmpty();
128       return;
129     }
130
131   assert (nLength <= INT_MAX);          // assert "positive int"
132   m_nLength = nLength;
133   m_nMax = ALIGN_BUF_SIZE (m_nLength + 1);
134   assert (m_nMax < INT_MAX);
135   assert (m_nMax >= m_nLength + 1);
136   if (m_pcLine != nullptr)
137     delete[] m_pcLine;
138   m_pcLine = new tchar_t[m_nMax];
139   memset(m_pcLine, 0, m_nMax * sizeof(tchar_t));
140   const size_t dwLen = sizeof (tchar_t) * m_nLength;
141   memcpy (m_pcLine, pszLine, dwLen);
142   m_pcLine[m_nLength] = '\0';
143
144   int nEols = 0;
145   if (nLength > 1 && IsDosEol(&pszLine[nLength - 2]))
146     nEols = 2;
147   else if (IsEol(pszLine[nLength - 1]))
148     nEols = 1;
149   assert (static_cast<size_t>(nEols) <= m_nLength);
150   m_nLength -= nEols;
151   m_nEolChars = nEols;
152 }
153
154 /**
155  * @brief Create an empty line.
156  */
157 void LineInfo::CreateEmpty()
158 {
159   m_nLength = 0;
160   m_nEolChars = 0;
161   m_nMax = ALIGN_BUF_SIZE (m_nLength + 1);
162   delete [] m_pcLine;
163   m_pcLine = new tchar_t[m_nMax];
164   memset (m_pcLine, 0, m_nMax * sizeof(tchar_t));
165 }
166
167 /**
168  * @brief Append a text to the line.
169  * @param [in] pszChars String to append to the line.
170  * @param [in] nLength Length of the string to append.
171  */
172 void LineInfo::Append(const tchar_t* pszChars, size_t nLength, bool bDetectEol)
173 {
174   assert (nLength <= INT_MAX);          // assert "positive int"
175   size_t nBufNeeded = m_nLength + m_nEolChars + nLength + 1;
176   if (nBufNeeded > m_nMax)
177     {
178       m_nMax = ALIGN_BUF_SIZE (nBufNeeded);
179       assert (m_nMax < INT_MAX);
180       assert (m_nMax >= m_nLength + nLength);
181       tchar_t *pcNewBuf = new tchar_t[m_nMax];
182       if (FullLength() > 0)
183         memcpy (pcNewBuf, m_pcLine, sizeof (tchar_t) * (FullLength() + 1));
184       delete[] m_pcLine;
185       m_pcLine = pcNewBuf;
186     }
187
188   memcpy (m_pcLine + m_nLength + m_nEolChars, pszChars, sizeof (tchar_t) * nLength);
189   m_nLength += nLength + m_nEolChars;
190   m_pcLine[m_nLength] = '\0';
191
192   if (!bDetectEol)
193     return;
194
195   // Did line gain eol ? (We asserted above that it had none at start)
196    if (nLength > 1 && IsDosEol(&m_pcLine[m_nLength - 2]))
197      {
198        m_nEolChars = 2;
199      }
200    else if (LineInfo::IsEol(m_pcLine[m_nLength - 1]))
201       {
202        m_nEolChars = 1;
203       }
204    assert (static_cast<size_t>(m_nEolChars) <= m_nLength);
205    m_nLength -= m_nEolChars;
206    assert (m_nLength + m_nEolChars <= m_nMax);
207 }
208
209 /**
210  * @brief Get line's EOL bytes.
211  * @return EOL bytes, or `nullptr` if no EOL bytes.
212  */
213 const tchar_t* LineInfo::GetEol() const
214 {
215   if (HasEol())
216     return &m_pcLine[Length()];
217   else
218     return nullptr;
219 }
220
221 /**
222  * @brief Change line's EOL.
223  * @param [in] lpEOL New EOL bytes.
224  * @return true if succeeded, false if failed (nothing to change).
225  */
226 bool LineInfo::ChangeEol(const tchar_t* lpEOL)
227 {
228   const int nNewEolChars = (int) tc::tcslen(lpEOL);
229
230   // Check if we really are changing EOL.
231   if (nNewEolChars == m_nEolChars)
232     if (tc::tcscmp(m_pcLine + Length(), lpEOL) == 0)
233       return false;
234
235   size_t nBufNeeded = m_nLength + nNewEolChars+1;
236   assert (nBufNeeded < INT_MAX);
237   if (nBufNeeded > m_nMax)
238     {
239       m_nMax = ALIGN_BUF_SIZE (nBufNeeded);
240       assert (m_nMax >= nBufNeeded);
241       tchar_t *pcNewBuf = new tchar_t[m_nMax];
242       if (FullLength() > 0)
243         memcpy (pcNewBuf, m_pcLine, sizeof (tchar_t) * (FullLength() + 1));
244       delete[] m_pcLine;
245       m_pcLine = pcNewBuf;
246     }
247   
248   // copy also the 0 to zero-terminate the line
249   memcpy (m_pcLine + m_nLength, lpEOL, sizeof (tchar_t) * (nNewEolChars + 1));
250   m_nEolChars = nNewEolChars;
251   return true;
252 }
253
254 /**
255  * @brief Delete part of the line.
256  * @param [in] nStartChar Start position for removal.
257  * @param [in] nEndChar End position for removal.
258  */
259 void LineInfo::Delete(size_t nStartChar, size_t nEndChar)
260 {
261   if (nEndChar < Length() || m_nEolChars)
262     {
263       // preserve characters after deleted range by shifting up
264       memcpy (m_pcLine + nStartChar, m_pcLine + nEndChar,
265               sizeof (tchar_t) * (FullLength() - nEndChar));
266     }
267   size_t nDelete = (nEndChar - nStartChar);
268   if (nDelete <= m_nLength)
269     {
270       m_nLength -= nDelete;
271     }
272   else
273     {
274       assert( (m_nLength + m_nEolChars) <= nDelete );
275       nDelete -= m_nLength;
276       m_nLength = 0;
277       m_nEolChars -= static_cast<int>(nDelete);
278     }
279   assert (m_nLength <= INT_MAX);                // assert "positive int"
280   if (m_pcLine != nullptr)
281     m_pcLine[FullLength()] = '\0';
282 }
283
284 /**
285  * @brief Delete line contents from given index to the end.
286  * @param [in] Index of first character to remove.
287  */
288 void LineInfo::DeleteEnd(size_t nStartChar)
289 {
290   m_nLength = nStartChar;
291   assert (m_nLength <= INT_MAX);                // assert "positive int"
292   if (m_pcLine != nullptr)
293     m_pcLine[nStartChar] = 0;
294   m_nEolChars = 0;
295 }
296
297 /**
298  * @brief Remove EOL from line.
299  */
300 void LineInfo::RemoveEol()
301 {
302   if (HasEol())
303   {
304     m_pcLine[m_nLength] = '\0';
305     m_nEolChars = 0;
306   }
307 }