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"
11 /*----------------------------------------------------------------------------
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
28 return sLinebreaks
[aBreakType
];
31 /*----------------------------------------------------------------------------
34 Wee inline method to append a line break. Modifies ioDest.
35 ----------------------------------------------------------------------------*/
37 void AppendLinebreak(T
*& aIoDest
, const char* aLineBreakStr
) {
38 *aIoDest
++ = *aLineBreakStr
;
40 if (aLineBreakStr
[1]) {
41 *aIoDest
++ = aLineBreakStr
[1];
45 /*----------------------------------------------------------------------------
48 Counts occurrences of breakStr in aSrc
49 ----------------------------------------------------------------------------*/
51 int32_t CountLinebreaks(const T
* aSrc
, int32_t aInLen
, const char* aBreakStr
) {
53 const T
* srcEnd
= aSrc
+ aInLen
;
56 while (src
< srcEnd
) {
57 if (*src
== *aBreakStr
) {
61 if (src
< srcEnd
&& *src
== aBreakStr
[1]) {
76 /*----------------------------------------------------------------------------
79 ioLen *includes* a terminating null, if any
80 ----------------------------------------------------------------------------*/
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
);
94 memcpy(resultString
, aInSrc
,
95 sizeof(T
) * aIoLen
); // includes the null, if any
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
);
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
;
126 // aIoLen does not change
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
132 int32_t numLinebreaks
= CountLinebreaks(aInSrc
, aIoLen
, aSrcBreak
);
135 aIoLen
- (numLinebreaks
* srcBreakLen
) + (numLinebreaks
* destBreakLen
);
136 resultString
= (T
*)malloc(sizeof(T
) * newBufLen
);
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
;
149 *dst
++ = aDestBreak
[1];
153 if (src
< srcEnd
&& aSrcBreak
[1] && *src
== aSrcBreak
[1]) {
167 /*----------------------------------------------------------------------------
170 Convert breaks in situ. Can only do this if the linebreak length
172 ----------------------------------------------------------------------------*/
174 static void ConvertBreaksInSitu(T
* aInSrc
, int32_t aInLen
, char aSrcBreak
,
177 T
* srcEnd
= aInSrc
+ aInLen
;
179 while (src
< srcEnd
) {
180 if (*src
== aSrcBreak
) {
188 /*----------------------------------------------------------------------------
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 ----------------------------------------------------------------------------*/
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
) {
208 finalLen
+= destBreakLen
;
212 finalLen
+= destBreakLen
;
214 } else if (*src
== nsCRT::LF
) {
216 finalLen
+= destBreakLen
;
223 T
* resultString
= (T
*)malloc(sizeof(T
) * finalLen
);
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
) {
237 AppendLinebreak(dst
, aDestBreak
);
241 AppendLinebreak(dst
, aDestBreak
);
243 } else if (*src
== nsCRT::LF
) {
245 AppendLinebreak(dst
, aDestBreak
);
256 /*----------------------------------------------------------------------------
259 ----------------------------------------------------------------------------*/
260 char* nsLinebreakConverter::ConvertLineBreaks(const char* aSrc
,
261 ELinebreakType aSrcBreaks
,
262 ELinebreakType aDestBreaks
,
265 NS_ASSERTION(aDestBreaks
!= eLinebreakAny
&& aSrcBreaks
!= eLinebreakSpace
,
266 "Invalid parameter");
271 int32_t sourceLen
= (aSrcLen
== kIgnoreLen
) ? strlen(aSrc
) + 1 : aSrcLen
;
274 if (aSrcBreaks
== eLinebreakAny
) {
276 ConvertUnknownBreaks(aSrc
, sourceLen
, GetLinebreakString(aDestBreaks
));
279 ConvertBreaks(aSrc
, sourceLen
, GetLinebreakString(aSrcBreaks
),
280 GetLinebreakString(aDestBreaks
));
283 *aOutLen
= sourceLen
;
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");
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
);
314 *aOutLen
= sourceLen
;
319 if (aSrcBreaks
== eLinebreakAny
) {
320 destBuffer
= ConvertUnknownBreaks(*aIoBuffer
, sourceLen
, dstBreaks
);
322 destBuffer
= ConvertBreaks(*aIoBuffer
, sourceLen
, srcBreaks
, dstBreaks
);
326 return NS_ERROR_OUT_OF_MEMORY
;
328 *aIoBuffer
= destBuffer
;
330 *aOutLen
= sourceLen
;
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");
350 int32_t bufLen
= (aSrcLen
== kIgnoreLen
) ? NS_strlen(aSrc
) + 1 : aSrcLen
;
352 char16_t
* resultString
;
353 if (aSrcBreaks
== eLinebreakAny
) {
355 ConvertUnknownBreaks(aSrc
, bufLen
, GetLinebreakString(aDestBreaks
));
357 resultString
= ConvertBreaks(aSrc
, bufLen
, GetLinebreakString(aSrcBreaks
),
358 GetLinebreakString(aDestBreaks
));
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");
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
);
391 *aOutLen
= sourceLen
;
394 char16_t
* destBuffer
;
396 if (aSrcBreaks
== eLinebreakAny
) {
397 destBuffer
= ConvertUnknownBreaks(*aIoBuffer
, sourceLen
, dstBreaks
);
399 destBuffer
= ConvertBreaks(*aIoBuffer
, sourceLen
, srcBreaks
, dstBreaks
);
403 return NS_ERROR_OUT_OF_MEMORY
;
405 *aIoBuffer
= destBuffer
;
407 *aOutLen
= sourceLen
;
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");
425 if (aIoString
.IsEmpty()) {
431 // remember the old buffer in case
432 // we blow it away later
433 char16_t
* stringBuf
= aIoString
.BeginWriting(mozilla::fallible
);
435 return NS_ERROR_OUT_OF_MEMORY
;
440 rv
= ConvertUnicharLineBreaksInSitu(&stringBuf
, aSrcBreaks
, aDestBreaks
,
441 aIoString
.Length() + 1, &newLen
);
446 const char16_t
* currentBuf
= aIoString
.get();
447 if (currentBuf
!= stringBuf
) {
448 aIoString
.Adopt(stringBuf
, newLen
- 1);