Bug 1927094 - optimize lineScrollAmount so it doesn't iterate over all tabs, r=mconley
[gecko.git] / xpcom / io / nsLinebreakConverter.cpp
blob019fc0e4ae2ae4e9af9de212a965912814595a4a
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "nsLinebreakConverter.h"
9 #include "nsCRT.h"
11 /*----------------------------------------------------------------------------
12 GetLinebreakString
14 Could make this inline
15 ----------------------------------------------------------------------------*/
16 static const char* GetLinebreakString(
17 nsLinebreakConverter::ELinebreakType aBreakType) {
18 static const char* const sLinebreaks[] = {"", // any
19 NS_LINEBREAK, // platform
20 LFSTR, // content
21 CRLF, // net
22 CRSTR, // Mac
23 LFSTR, // Unix
24 CRLF, // Windows
25 " ", // space
26 nullptr};
28 return sLinebreaks[aBreakType];
31 /*----------------------------------------------------------------------------
32 AppendLinebreak
34 Wee inline method to append a line break. Modifies ioDest.
35 ----------------------------------------------------------------------------*/
36 template <class T>
37 void AppendLinebreak(T*& aIoDest, const char* aLineBreakStr) {
38 *aIoDest++ = *aLineBreakStr;
40 if (aLineBreakStr[1]) {
41 *aIoDest++ = aLineBreakStr[1];
45 /*----------------------------------------------------------------------------
46 CountChars
48 Counts occurrences of breakStr in aSrc
49 ----------------------------------------------------------------------------*/
50 template <class T>
51 int32_t CountLinebreaks(const T* aSrc, int32_t aInLen, const char* aBreakStr) {
52 const T* src = aSrc;
53 const T* srcEnd = aSrc + aInLen;
54 int32_t theCount = 0;
56 while (src < srcEnd) {
57 if (*src == *aBreakStr) {
58 src++;
60 if (aBreakStr[1]) {
61 if (src < srcEnd && *src == aBreakStr[1]) {
62 src++;
63 theCount++;
65 } else {
66 theCount++;
68 } else {
69 src++;
73 return theCount;
76 /*----------------------------------------------------------------------------
77 ConvertBreaks
79 ioLen *includes* a terminating null, if any
80 ----------------------------------------------------------------------------*/
81 template <class T>
82 static T* ConvertBreaks(const T* aInSrc, int32_t& aIoLen, const char* aSrcBreak,
83 const char* aDestBreak) {
84 NS_ASSERTION(aInSrc && aSrcBreak && aDestBreak, "Got a null string");
86 T* resultString = nullptr;
88 // handle the no conversion case
89 if (nsCRT::strcmp(aSrcBreak, aDestBreak) == 0) {
90 resultString = (T*)malloc(sizeof(T) * aIoLen);
91 if (!resultString) {
92 return nullptr;
94 memcpy(resultString, aInSrc,
95 sizeof(T) * aIoLen); // includes the null, if any
96 return resultString;
99 int32_t srcBreakLen = strlen(aSrcBreak);
100 int32_t destBreakLen = strlen(aDestBreak);
102 // handle the easy case, where the string length does not change, and the
103 // breaks are only 1 char long, i.e. CR <-> LF
104 if (srcBreakLen == destBreakLen && srcBreakLen == 1) {
105 resultString = (T*)malloc(sizeof(T) * aIoLen);
106 if (!resultString) {
107 return nullptr;
110 const T* src = aInSrc;
111 const T* srcEnd = aInSrc + aIoLen; // includes null, if any
112 T* dst = resultString;
114 char srcBreakChar = *aSrcBreak; // we know it's one char long already
115 char dstBreakChar = *aDestBreak;
117 while (src < srcEnd) {
118 if (*src == srcBreakChar) {
119 *dst++ = dstBreakChar;
120 src++;
121 } else {
122 *dst++ = *src++;
126 // aIoLen does not change
127 } else {
128 // src and dest termination is different length. Do it a slower way.
130 // count linebreaks in src. Assumes that chars in 2-char linebreaks are
131 // unique.
132 int32_t numLinebreaks = CountLinebreaks(aInSrc, aIoLen, aSrcBreak);
134 int32_t newBufLen =
135 aIoLen - (numLinebreaks * srcBreakLen) + (numLinebreaks * destBreakLen);
136 resultString = (T*)malloc(sizeof(T) * newBufLen);
137 if (!resultString) {
138 return nullptr;
141 const T* src = aInSrc;
142 const T* srcEnd = aInSrc + aIoLen; // includes null, if any
143 T* dst = resultString;
145 while (src < srcEnd) {
146 if (*src == *aSrcBreak) {
147 *dst++ = *aDestBreak;
148 if (aDestBreak[1]) {
149 *dst++ = aDestBreak[1];
152 src++;
153 if (src < srcEnd && aSrcBreak[1] && *src == aSrcBreak[1]) {
154 src++;
156 } else {
157 *dst++ = *src++;
161 aIoLen = newBufLen;
164 return resultString;
167 /*----------------------------------------------------------------------------
168 ConvertBreaksInSitu
170 Convert breaks in situ. Can only do this if the linebreak length
171 does not change.
172 ----------------------------------------------------------------------------*/
173 template <class T>
174 static void ConvertBreaksInSitu(T* aInSrc, int32_t aInLen, char aSrcBreak,
175 char aDestBreak) {
176 T* src = aInSrc;
177 T* srcEnd = aInSrc + aInLen;
179 while (src < srcEnd) {
180 if (*src == aSrcBreak) {
181 *src = aDestBreak;
184 src++;
188 /*----------------------------------------------------------------------------
189 ConvertUnknownBreaks
191 Convert unknown line breaks to the specified break.
193 This will convert CRLF pairs to one break, and single CR or LF to a break.
194 ----------------------------------------------------------------------------*/
195 template <class T>
196 static T* ConvertUnknownBreaks(const T* aInSrc, int32_t& aIoLen,
197 const char* aDestBreak) {
198 const T* src = aInSrc;
199 const T* srcEnd = aInSrc + aIoLen; // includes null, if any
201 int32_t destBreakLen = strlen(aDestBreak);
202 int32_t finalLen = 0;
204 while (src < srcEnd) {
205 if (*src == nsCRT::CR) {
206 if (src + 1 < srcEnd && src[1] == nsCRT::LF) {
207 // CRLF
208 finalLen += destBreakLen;
209 src++;
210 } else {
211 // Lone CR
212 finalLen += destBreakLen;
214 } else if (*src == nsCRT::LF) {
215 // Lone LF
216 finalLen += destBreakLen;
217 } else {
218 finalLen++;
220 src++;
223 T* resultString = (T*)malloc(sizeof(T) * finalLen);
224 if (!resultString) {
225 return nullptr;
228 src = aInSrc;
229 srcEnd = aInSrc + aIoLen; // includes null, if any
231 T* dst = resultString;
233 while (src < srcEnd) {
234 if (*src == nsCRT::CR) {
235 if (src + 1 < srcEnd && src[1] == nsCRT::LF) {
236 // CRLF
237 AppendLinebreak(dst, aDestBreak);
238 src++;
239 } else {
240 // Lone CR
241 AppendLinebreak(dst, aDestBreak);
243 } else if (*src == nsCRT::LF) {
244 // Lone LF
245 AppendLinebreak(dst, aDestBreak);
246 } else {
247 *dst++ = *src;
249 src++;
252 aIoLen = finalLen;
253 return resultString;
256 /*----------------------------------------------------------------------------
257 ConvertLineBreaks
259 ----------------------------------------------------------------------------*/
260 char* nsLinebreakConverter::ConvertLineBreaks(const char* aSrc,
261 ELinebreakType aSrcBreaks,
262 ELinebreakType aDestBreaks,
263 int32_t aSrcLen,
264 int32_t* aOutLen) {
265 NS_ASSERTION(aDestBreaks != eLinebreakAny && aSrcBreaks != eLinebreakSpace,
266 "Invalid parameter");
267 if (!aSrc) {
268 return nullptr;
271 int32_t sourceLen = (aSrcLen == kIgnoreLen) ? strlen(aSrc) + 1 : aSrcLen;
273 char* resultString;
274 if (aSrcBreaks == eLinebreakAny) {
275 resultString =
276 ConvertUnknownBreaks(aSrc, sourceLen, GetLinebreakString(aDestBreaks));
277 } else
278 resultString =
279 ConvertBreaks(aSrc, sourceLen, GetLinebreakString(aSrcBreaks),
280 GetLinebreakString(aDestBreaks));
282 if (aOutLen) {
283 *aOutLen = sourceLen;
285 return resultString;
288 /*----------------------------------------------------------------------------
289 ConvertLineBreaksInSitu
291 ----------------------------------------------------------------------------*/
292 nsresult nsLinebreakConverter::ConvertLineBreaksInSitu(
293 char** aIoBuffer, ELinebreakType aSrcBreaks, ELinebreakType aDestBreaks,
294 int32_t aSrcLen, int32_t* aOutLen) {
295 NS_ASSERTION(aIoBuffer && *aIoBuffer, "Null pointer passed");
296 if (!aIoBuffer || !*aIoBuffer) {
297 return NS_ERROR_NULL_POINTER;
300 NS_ASSERTION(aDestBreaks != eLinebreakAny && aSrcBreaks != eLinebreakSpace,
301 "Invalid parameter");
303 int32_t sourceLen =
304 (aSrcLen == kIgnoreLen) ? strlen(*aIoBuffer) + 1 : aSrcLen;
306 // can we convert in-place?
307 const char* srcBreaks = GetLinebreakString(aSrcBreaks);
308 const char* dstBreaks = GetLinebreakString(aDestBreaks);
310 if (aSrcBreaks != eLinebreakAny && strlen(srcBreaks) == 1 &&
311 strlen(dstBreaks) == 1) {
312 ConvertBreaksInSitu(*aIoBuffer, sourceLen, *srcBreaks, *dstBreaks);
313 if (aOutLen) {
314 *aOutLen = sourceLen;
316 } else {
317 char* destBuffer;
319 if (aSrcBreaks == eLinebreakAny) {
320 destBuffer = ConvertUnknownBreaks(*aIoBuffer, sourceLen, dstBreaks);
321 } else {
322 destBuffer = ConvertBreaks(*aIoBuffer, sourceLen, srcBreaks, dstBreaks);
325 if (!destBuffer) {
326 return NS_ERROR_OUT_OF_MEMORY;
328 *aIoBuffer = destBuffer;
329 if (aOutLen) {
330 *aOutLen = sourceLen;
334 return NS_OK;
337 /*----------------------------------------------------------------------------
338 ConvertUnicharLineBreaks
340 ----------------------------------------------------------------------------*/
341 char16_t* nsLinebreakConverter::ConvertUnicharLineBreaks(
342 const char16_t* aSrc, ELinebreakType aSrcBreaks, ELinebreakType aDestBreaks,
343 int32_t aSrcLen, int32_t* aOutLen) {
344 NS_ASSERTION(aDestBreaks != eLinebreakAny && aSrcBreaks != eLinebreakSpace,
345 "Invalid parameter");
346 if (!aSrc) {
347 return nullptr;
350 int32_t bufLen = (aSrcLen == kIgnoreLen) ? NS_strlen(aSrc) + 1 : aSrcLen;
352 char16_t* resultString;
353 if (aSrcBreaks == eLinebreakAny) {
354 resultString =
355 ConvertUnknownBreaks(aSrc, bufLen, GetLinebreakString(aDestBreaks));
356 } else
357 resultString = ConvertBreaks(aSrc, bufLen, GetLinebreakString(aSrcBreaks),
358 GetLinebreakString(aDestBreaks));
360 if (aOutLen) {
361 *aOutLen = bufLen;
363 return resultString;
366 /*----------------------------------------------------------------------------
367 ConvertStringLineBreaks
369 ----------------------------------------------------------------------------*/
370 nsresult nsLinebreakConverter::ConvertUnicharLineBreaksInSitu(
371 char16_t** aIoBuffer, ELinebreakType aSrcBreaks, ELinebreakType aDestBreaks,
372 int32_t aSrcLen, int32_t* aOutLen) {
373 NS_ASSERTION(aIoBuffer && *aIoBuffer, "Null pointer passed");
374 if (!aIoBuffer || !*aIoBuffer) {
375 return NS_ERROR_NULL_POINTER;
377 NS_ASSERTION(aDestBreaks != eLinebreakAny && aSrcBreaks != eLinebreakSpace,
378 "Invalid parameter");
380 int32_t sourceLen =
381 (aSrcLen == kIgnoreLen) ? NS_strlen(*aIoBuffer) + 1 : aSrcLen;
383 // can we convert in-place?
384 const char* srcBreaks = GetLinebreakString(aSrcBreaks);
385 const char* dstBreaks = GetLinebreakString(aDestBreaks);
387 if ((aSrcBreaks != eLinebreakAny) && (strlen(srcBreaks) == 1) &&
388 (strlen(dstBreaks) == 1)) {
389 ConvertBreaksInSitu(*aIoBuffer, sourceLen, *srcBreaks, *dstBreaks);
390 if (aOutLen) {
391 *aOutLen = sourceLen;
393 } else {
394 char16_t* destBuffer;
396 if (aSrcBreaks == eLinebreakAny) {
397 destBuffer = ConvertUnknownBreaks(*aIoBuffer, sourceLen, dstBreaks);
398 } else {
399 destBuffer = ConvertBreaks(*aIoBuffer, sourceLen, srcBreaks, dstBreaks);
402 if (!destBuffer) {
403 return NS_ERROR_OUT_OF_MEMORY;
405 *aIoBuffer = destBuffer;
406 if (aOutLen) {
407 *aOutLen = sourceLen;
411 return NS_OK;
414 /*----------------------------------------------------------------------------
415 ConvertStringLineBreaks
417 ----------------------------------------------------------------------------*/
418 nsresult nsLinebreakConverter::ConvertStringLineBreaks(
419 nsString& aIoString, ELinebreakType aSrcBreaks,
420 ELinebreakType aDestBreaks) {
421 NS_ASSERTION(aDestBreaks != eLinebreakAny && aSrcBreaks != eLinebreakSpace,
422 "Invalid parameter");
424 // nothing to do
425 if (aIoString.IsEmpty()) {
426 return NS_OK;
429 nsresult rv;
431 // remember the old buffer in case
432 // we blow it away later
433 char16_t* stringBuf = aIoString.BeginWriting(mozilla::fallible);
434 if (!stringBuf) {
435 return NS_ERROR_OUT_OF_MEMORY;
438 int32_t newLen;
440 rv = ConvertUnicharLineBreaksInSitu(&stringBuf, aSrcBreaks, aDestBreaks,
441 aIoString.Length() + 1, &newLen);
442 if (NS_FAILED(rv)) {
443 return rv;
446 const char16_t* currentBuf = aIoString.get();
447 if (currentBuf != stringBuf) {
448 aIoString.Adopt(stringBuf, newLen - 1);
451 return NS_OK;