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/. */
6 
7 #include "nsReadableUtils.h"
8 
9 #include <algorithm>
10 
11 #include "mozilla/CheckedInt.h"
12 #include "mozilla/Utf8.h"
13 
14 #include "nscore.h"
15 #include "nsMemory.h"
16 #include "nsString.h"
17 #include "nsTArray.h"
18 #include "nsUTF8Utils.h"
19 
20 using mozilla::Span;
21 
22 /**
23  * A helper function that allocates a buffer of the desired character type big
24  * enough to hold a copy of the supplied string (plus a zero terminator).
25  *
26  * @param aSource an string you will eventually be making a copy of
27  * @return a new buffer which you must free with |free|.
28  *
29  */
30 template <class FromStringT, class CharT>
AllocateStringCopy(const FromStringT & aSource,CharT *)31 inline CharT* AllocateStringCopy(const FromStringT& aSource, CharT*) {
32   return static_cast<CharT*>(
33       malloc((size_t(aSource.Length()) + 1) * sizeof(CharT)));
34 }
35 
ToNewCString(const nsAString & aSource)36 char* ToNewCString(const nsAString& aSource) {
37   char* str = ToNewCString(aSource, mozilla::fallible);
38   if (!str) {
39     MOZ_CRASH("Unable to allocate memory");
40   }
41   return str;
42 }
43 
ToNewCString(const nsAString & aSource,const mozilla::fallible_t & aFallible)44 char* ToNewCString(const nsAString& aSource,
45                    const mozilla::fallible_t& aFallible) {
46   char* dest = AllocateStringCopy(aSource, (char*)nullptr);
47   if (!dest) {
48     return nullptr;
49   }
50 
51   auto len = aSource.Length();
52   LossyConvertUtf16toLatin1(aSource, Span(dest, len));
53   dest[len] = 0;
54   return dest;
55 }
56 
ToNewUTF8String(const nsAString & aSource,uint32_t * aUTF8Count,const mozilla::fallible_t & aFallible)57 char* ToNewUTF8String(const nsAString& aSource, uint32_t* aUTF8Count,
58                       const mozilla::fallible_t& aFallible) {
59   auto len = aSource.Length();
60   // The uses of this function seem temporary enough that it's not
61   // worthwhile to be fancy about the allocation size. Let's just use
62   // the worst case.
63   // Times 3 plus 1, because ConvertUTF16toUTF8 requires times 3 and
64   // then we have the terminator.
65   // Using CheckedInt<uint32_t>, because aUTF8Count is uint32_t* for
66   // historical reasons.
67   mozilla::CheckedInt<uint32_t> destLen(len);
68   destLen *= 3;
69   destLen += 1;
70   if (!destLen.isValid()) {
71     return nullptr;
72   }
73   size_t destLenVal = destLen.value();
74   char* dest = static_cast<char*>(malloc(destLenVal));
75   if (!dest) {
76     return nullptr;
77   }
78 
79   size_t written = ConvertUtf16toUtf8(aSource, Span(dest, destLenVal));
80   dest[written] = 0;
81 
82   if (aUTF8Count) {
83     *aUTF8Count = written;
84   }
85 
86   return dest;
87 }
88 
ToNewUTF8String(const nsAString & aSource,uint32_t * aUTF8Count)89 char* ToNewUTF8String(const nsAString& aSource, uint32_t* aUTF8Count) {
90   char* str = ToNewUTF8String(aSource, aUTF8Count, mozilla::fallible);
91   if (!str) {
92     MOZ_CRASH("Unable to allocate memory");
93   }
94   return str;
95 }
96 
ToNewCString(const nsACString & aSource)97 char* ToNewCString(const nsACString& aSource) {
98   char* str = ToNewCString(aSource, mozilla::fallible);
99   if (!str) {
100     MOZ_CRASH("Unable to allocate memory");
101   }
102   return str;
103 }
104 
ToNewCString(const nsACString & aSource,const mozilla::fallible_t & aFallible)105 char* ToNewCString(const nsACString& aSource,
106                    const mozilla::fallible_t& aFallible) {
107   // no conversion needed, just allocate a buffer of the correct length and copy
108   // into it
109 
110   char* dest = AllocateStringCopy(aSource, (char*)nullptr);
111   if (!dest) {
112     return nullptr;
113   }
114 
115   auto len = aSource.Length();
116   memcpy(dest, aSource.BeginReading(), len * sizeof(char));
117   dest[len] = 0;
118   return dest;
119 }
120 
ToNewUnicode(const nsAString & aSource)121 char16_t* ToNewUnicode(const nsAString& aSource) {
122   char16_t* str = ToNewUnicode(aSource, mozilla::fallible);
123   if (!str) {
124     MOZ_CRASH("Unable to allocate memory");
125   }
126   return str;
127 }
128 
ToNewUnicode(const nsAString & aSource,const mozilla::fallible_t & aFallible)129 char16_t* ToNewUnicode(const nsAString& aSource,
130                        const mozilla::fallible_t& aFallible) {
131   // no conversion needed, just allocate a buffer of the correct length and copy
132   // into it
133 
134   char16_t* dest = AllocateStringCopy(aSource, (char16_t*)nullptr);
135   if (!dest) {
136     return nullptr;
137   }
138 
139   auto len = aSource.Length();
140   memcpy(dest, aSource.BeginReading(), len * sizeof(char16_t));
141   dest[len] = 0;
142   return dest;
143 }
144 
ToNewUnicode(const nsACString & aSource)145 char16_t* ToNewUnicode(const nsACString& aSource) {
146   char16_t* str = ToNewUnicode(aSource, mozilla::fallible);
147   if (!str) {
148     MOZ_CRASH("Unable to allocate memory");
149   }
150   return str;
151 }
152 
ToNewUnicode(const nsACString & aSource,const mozilla::fallible_t & aFallible)153 char16_t* ToNewUnicode(const nsACString& aSource,
154                        const mozilla::fallible_t& aFallible) {
155   char16_t* dest = AllocateStringCopy(aSource, (char16_t*)nullptr);
156   if (!dest) {
157     return nullptr;
158   }
159 
160   auto len = aSource.Length();
161   ConvertLatin1toUtf16(aSource, Span(dest, len));
162   dest[len] = 0;
163   return dest;
164 }
165 
UTF8ToNewUnicode(const nsACString & aSource,uint32_t * aUTF16Count,const mozilla::fallible_t & aFallible)166 char16_t* UTF8ToNewUnicode(const nsACString& aSource, uint32_t* aUTF16Count,
167                            const mozilla::fallible_t& aFallible) {
168   // Compute length plus one as required by ConvertUTF8toUTF16
169   uint32_t lengthPlusOne = aSource.Length() + 1;  // Can't overflow
170 
171   mozilla::CheckedInt<size_t> allocLength(lengthPlusOne);
172   // Add space for zero-termination
173   allocLength += 1;
174   // We need UTF-16 units
175   allocLength *= sizeof(char16_t);
176 
177   if (!allocLength.isValid()) {
178     return nullptr;
179   }
180 
181   char16_t* dest = (char16_t*)malloc(allocLength.value());
182   if (!dest) {
183     return nullptr;
184   }
185 
186   size_t written = ConvertUtf8toUtf16(aSource, Span(dest, lengthPlusOne));
187   dest[written] = 0;
188 
189   if (aUTF16Count) {
190     *aUTF16Count = written;
191   }
192 
193   return dest;
194 }
195 
UTF8ToNewUnicode(const nsACString & aSource,uint32_t * aUTF16Count)196 char16_t* UTF8ToNewUnicode(const nsACString& aSource, uint32_t* aUTF16Count) {
197   char16_t* str = UTF8ToNewUnicode(aSource, aUTF16Count, mozilla::fallible);
198   if (!str) {
199     MOZ_CRASH("Unable to allocate memory");
200   }
201   return str;
202 }
203 
CopyUnicodeTo(const nsAString & aSource,uint32_t aSrcOffset,char16_t * aDest,uint32_t aLength)204 char16_t* CopyUnicodeTo(const nsAString& aSource, uint32_t aSrcOffset,
205                         char16_t* aDest, uint32_t aLength) {
206   MOZ_ASSERT(aSrcOffset + aLength <= aSource.Length());
207   memcpy(aDest, aSource.BeginReading() + aSrcOffset,
208          size_t(aLength) * sizeof(char16_t));
209   return aDest;
210 }
211 
ToUpperCase(nsACString & aCString)212 void ToUpperCase(nsACString& aCString) {
213   char* cp = aCString.BeginWriting();
214   char* end = cp + aCString.Length();
215   while (cp != end) {
216     char ch = *cp;
217     if (ch >= 'a' && ch <= 'z') {
218       *cp = ch - ('a' - 'A');
219     }
220     ++cp;
221   }
222 }
223 
ToUpperCase(const nsACString & aSource,nsACString & aDest)224 void ToUpperCase(const nsACString& aSource, nsACString& aDest) {
225   aDest.SetLength(aSource.Length());
226   const char* src = aSource.BeginReading();
227   const char* end = src + aSource.Length();
228   char* dst = aDest.BeginWriting();
229   while (src != end) {
230     char ch = *src;
231     if (ch >= 'a' && ch <= 'z') {
232       *dst = ch - ('a' - 'A');
233     } else {
234       *dst = ch;
235     }
236     ++src;
237     ++dst;
238   }
239 }
240 
ToLowerCase(nsACString & aCString)241 void ToLowerCase(nsACString& aCString) {
242   char* cp = aCString.BeginWriting();
243   char* end = cp + aCString.Length();
244   while (cp != end) {
245     char ch = *cp;
246     if (ch >= 'A' && ch <= 'Z') {
247       *cp = ch + ('a' - 'A');
248     }
249     ++cp;
250   }
251 }
252 
ToLowerCase(const nsACString & aSource,nsACString & aDest)253 void ToLowerCase(const nsACString& aSource, nsACString& aDest) {
254   aDest.SetLength(aSource.Length());
255   const char* src = aSource.BeginReading();
256   const char* end = src + aSource.Length();
257   char* dst = aDest.BeginWriting();
258   while (src != end) {
259     char ch = *src;
260     if (ch >= 'A' && ch <= 'Z') {
261       *dst = ch + ('a' - 'A');
262     } else {
263       *dst = ch;
264     }
265     ++src;
266     ++dst;
267   }
268 }
269 
ParseString(const nsACString & aSource,char aDelimiter,nsTArray<nsCString> & aArray)270 void ParseString(const nsACString& aSource, char aDelimiter,
271                  nsTArray<nsCString>& aArray) {
272   nsACString::const_iterator start, end;
273   aSource.BeginReading(start);
274   aSource.EndReading(end);
275 
276   for (;;) {
277     nsACString::const_iterator delimiter = start;
278     FindCharInReadable(aDelimiter, delimiter, end);
279 
280     if (delimiter != start) {
281       aArray.AppendElement(Substring(start, delimiter));
282     }
283 
284     if (delimiter == end) {
285       break;
286     }
287     start = ++delimiter;
288     if (start == end) {
289       break;
290     }
291   }
292 }
293 
294 template <class StringT, class IteratorT>
FindInReadable_Impl(const StringT & aPattern,IteratorT & aSearchStart,IteratorT & aSearchEnd,nsTStringComparator<typename StringT::char_type> aCompare)295 bool FindInReadable_Impl(
296     const StringT& aPattern, IteratorT& aSearchStart, IteratorT& aSearchEnd,
297     nsTStringComparator<typename StringT::char_type> aCompare) {
298   bool found_it = false;
299 
300   // only bother searching at all if we're given a non-empty range to search
301   if (aSearchStart != aSearchEnd) {
302     IteratorT aPatternStart, aPatternEnd;
303     aPattern.BeginReading(aPatternStart);
304     aPattern.EndReading(aPatternEnd);
305 
306     // outer loop keeps searching till we find it or run out of string to search
307     while (!found_it) {
308       // fast inner loop (that's what it's called, not what it is) looks for a
309       // potential match
310       while (aSearchStart != aSearchEnd &&
311              aCompare(aPatternStart.get(), aSearchStart.get(), 1, 1)) {
312         ++aSearchStart;
313       }
314 
315       // if we broke out of the `fast' loop because we're out of string ...
316       // we're done: no match
317       if (aSearchStart == aSearchEnd) {
318         break;
319       }
320 
321       // otherwise, we're at a potential match, let's see if we really hit one
322       IteratorT testPattern(aPatternStart);
323       IteratorT testSearch(aSearchStart);
324 
325       // slow inner loop verifies the potential match (found by the `fast' loop)
326       // at the current position
327       for (;;) {
328         // we already compared the first character in the outer loop,
329         //  so we'll advance before the next comparison
330         ++testPattern;
331         ++testSearch;
332 
333         // if we verified all the way to the end of the pattern, then we found
334         // it!
335         if (testPattern == aPatternEnd) {
336           found_it = true;
337           aSearchEnd = testSearch;  // return the exact found range through the
338                                     // parameters
339           break;
340         }
341 
342         // if we got to end of the string we're searching before we hit the end
343         // of the
344         //  pattern, we'll never find what we're looking for
345         if (testSearch == aSearchEnd) {
346           aSearchStart = aSearchEnd;
347           break;
348         }
349 
350         // else if we mismatched ... it's time to advance to the next search
351         // position
352         //  and get back into the `fast' loop
353         if (aCompare(testPattern.get(), testSearch.get(), 1, 1)) {
354           ++aSearchStart;
355           break;
356         }
357       }
358     }
359   }
360 
361   return found_it;
362 }
363 
364 /**
365  * This searches the entire string from right to left, and returns the first
366  * match found, if any.
367  */
368 template <class StringT, class IteratorT>
RFindInReadable_Impl(const StringT & aPattern,IteratorT & aSearchStart,IteratorT & aSearchEnd,nsTStringComparator<typename StringT::char_type> aCompare)369 bool RFindInReadable_Impl(
370     const StringT& aPattern, IteratorT& aSearchStart, IteratorT& aSearchEnd,
371     nsTStringComparator<typename StringT::char_type> aCompare) {
372   IteratorT patternStart, patternEnd, searchEnd = aSearchEnd;
373   aPattern.BeginReading(patternStart);
374   aPattern.EndReading(patternEnd);
375 
376   // Point to the last character in the pattern
377   --patternEnd;
378   // outer loop keeps searching till we run out of string to search
379   while (aSearchStart != searchEnd) {
380     // Point to the end position of the next possible match
381     --searchEnd;
382 
383     // Check last character, if a match, explore further from here
384     if (aCompare(patternEnd.get(), searchEnd.get(), 1, 1) == 0) {
385       // We're at a potential match, let's see if we really hit one
386       IteratorT testPattern(patternEnd);
387       IteratorT testSearch(searchEnd);
388 
389       // inner loop verifies the potential match at the current position
390       do {
391         // if we verified all the way to the end of the pattern, then we found
392         // it!
393         if (testPattern == patternStart) {
394           aSearchStart = testSearch;  // point to start of match
395           aSearchEnd = ++searchEnd;   // point to end of match
396           return true;
397         }
398 
399         // if we got to end of the string we're searching before we hit the end
400         // of the
401         //  pattern, we'll never find what we're looking for
402         if (testSearch == aSearchStart) {
403           aSearchStart = aSearchEnd;
404           return false;
405         }
406 
407         // test previous character for a match
408         --testPattern;
409         --testSearch;
410       } while (aCompare(testPattern.get(), testSearch.get(), 1, 1) == 0);
411     }
412   }
413 
414   aSearchStart = aSearchEnd;
415   return false;
416 }
417 
FindInReadable(const nsAString & aPattern,nsAString::const_iterator & aSearchStart,nsAString::const_iterator & aSearchEnd,nsStringComparator aComparator)418 bool FindInReadable(const nsAString& aPattern,
419                     nsAString::const_iterator& aSearchStart,
420                     nsAString::const_iterator& aSearchEnd,
421                     nsStringComparator aComparator) {
422   return FindInReadable_Impl(aPattern, aSearchStart, aSearchEnd, aComparator);
423 }
424 
FindInReadable(const nsACString & aPattern,nsACString::const_iterator & aSearchStart,nsACString::const_iterator & aSearchEnd,nsCStringComparator aComparator)425 bool FindInReadable(const nsACString& aPattern,
426                     nsACString::const_iterator& aSearchStart,
427                     nsACString::const_iterator& aSearchEnd,
428                     nsCStringComparator aComparator) {
429   return FindInReadable_Impl(aPattern, aSearchStart, aSearchEnd, aComparator);
430 }
431 
CaseInsensitiveFindInReadable(const nsACString & aPattern,nsACString::const_iterator & aSearchStart,nsACString::const_iterator & aSearchEnd)432 bool CaseInsensitiveFindInReadable(const nsACString& aPattern,
433                                    nsACString::const_iterator& aSearchStart,
434                                    nsACString::const_iterator& aSearchEnd) {
435   return FindInReadable_Impl(aPattern, aSearchStart, aSearchEnd,
436                              nsCaseInsensitiveCStringComparator);
437 }
438 
RFindInReadable(const nsAString & aPattern,nsAString::const_iterator & aSearchStart,nsAString::const_iterator & aSearchEnd,const nsStringComparator aComparator)439 bool RFindInReadable(const nsAString& aPattern,
440                      nsAString::const_iterator& aSearchStart,
441                      nsAString::const_iterator& aSearchEnd,
442                      const nsStringComparator aComparator) {
443   return RFindInReadable_Impl(aPattern, aSearchStart, aSearchEnd, aComparator);
444 }
445 
RFindInReadable(const nsACString & aPattern,nsACString::const_iterator & aSearchStart,nsACString::const_iterator & aSearchEnd,const nsCStringComparator aComparator)446 bool RFindInReadable(const nsACString& aPattern,
447                      nsACString::const_iterator& aSearchStart,
448                      nsACString::const_iterator& aSearchEnd,
449                      const nsCStringComparator aComparator) {
450   return RFindInReadable_Impl(aPattern, aSearchStart, aSearchEnd, aComparator);
451 }
452 
FindCharInReadable(char16_t aChar,nsAString::const_iterator & aSearchStart,const nsAString::const_iterator & aSearchEnd)453 bool FindCharInReadable(char16_t aChar, nsAString::const_iterator& aSearchStart,
454                         const nsAString::const_iterator& aSearchEnd) {
455   ptrdiff_t fragmentLength = aSearchEnd.get() - aSearchStart.get();
456 
457   const char16_t* charFoundAt =
458       nsCharTraits<char16_t>::find(aSearchStart.get(), fragmentLength, aChar);
459   if (charFoundAt) {
460     aSearchStart.advance(charFoundAt - aSearchStart.get());
461     return true;
462   }
463 
464   aSearchStart.advance(fragmentLength);
465   return false;
466 }
467 
FindCharInReadable(char aChar,nsACString::const_iterator & aSearchStart,const nsACString::const_iterator & aSearchEnd)468 bool FindCharInReadable(char aChar, nsACString::const_iterator& aSearchStart,
469                         const nsACString::const_iterator& aSearchEnd) {
470   ptrdiff_t fragmentLength = aSearchEnd.get() - aSearchStart.get();
471 
472   const char* charFoundAt =
473       nsCharTraits<char>::find(aSearchStart.get(), fragmentLength, aChar);
474   if (charFoundAt) {
475     aSearchStart.advance(charFoundAt - aSearchStart.get());
476     return true;
477   }
478 
479   aSearchStart.advance(fragmentLength);
480   return false;
481 }
482 
StringBeginsWith(const nsAString & aSource,const nsAString & aSubstring)483 bool StringBeginsWith(const nsAString& aSource, const nsAString& aSubstring) {
484   nsAString::size_type src_len = aSource.Length(),
485                        sub_len = aSubstring.Length();
486   if (sub_len > src_len) {
487     return false;
488   }
489   return Substring(aSource, 0, sub_len).Equals(aSubstring);
490 }
491 
StringBeginsWith(const nsAString & aSource,const nsAString & aSubstring,nsStringComparator aComparator)492 bool StringBeginsWith(const nsAString& aSource, const nsAString& aSubstring,
493                       nsStringComparator aComparator) {
494   nsAString::size_type src_len = aSource.Length(),
495                        sub_len = aSubstring.Length();
496   if (sub_len > src_len) {
497     return false;
498   }
499   return Substring(aSource, 0, sub_len).Equals(aSubstring, aComparator);
500 }
501 
StringBeginsWith(const nsACString & aSource,const nsACString & aSubstring)502 bool StringBeginsWith(const nsACString& aSource, const nsACString& aSubstring) {
503   nsACString::size_type src_len = aSource.Length(),
504                         sub_len = aSubstring.Length();
505   if (sub_len > src_len) {
506     return false;
507   }
508   return Substring(aSource, 0, sub_len).Equals(aSubstring);
509 }
510 
StringBeginsWith(const nsACString & aSource,const nsACString & aSubstring,nsCStringComparator aComparator)511 bool StringBeginsWith(const nsACString& aSource, const nsACString& aSubstring,
512                       nsCStringComparator aComparator) {
513   nsACString::size_type src_len = aSource.Length(),
514                         sub_len = aSubstring.Length();
515   if (sub_len > src_len) {
516     return false;
517   }
518   return Substring(aSource, 0, sub_len).Equals(aSubstring, aComparator);
519 }
520 
StringEndsWith(const nsAString & aSource,const nsAString & aSubstring)521 bool StringEndsWith(const nsAString& aSource, const nsAString& aSubstring) {
522   nsAString::size_type src_len = aSource.Length(),
523                        sub_len = aSubstring.Length();
524   if (sub_len > src_len) {
525     return false;
526   }
527   return Substring(aSource, src_len - sub_len, sub_len).Equals(aSubstring);
528 }
529 
StringEndsWith(const nsAString & aSource,const nsAString & aSubstring,nsStringComparator aComparator)530 bool StringEndsWith(const nsAString& aSource, const nsAString& aSubstring,
531                     nsStringComparator aComparator) {
532   nsAString::size_type src_len = aSource.Length(),
533                        sub_len = aSubstring.Length();
534   if (sub_len > src_len) {
535     return false;
536   }
537   return Substring(aSource, src_len - sub_len, sub_len)
538       .Equals(aSubstring, aComparator);
539 }
540 
StringEndsWith(const nsACString & aSource,const nsACString & aSubstring)541 bool StringEndsWith(const nsACString& aSource, const nsACString& aSubstring) {
542   nsACString::size_type src_len = aSource.Length(),
543                         sub_len = aSubstring.Length();
544   if (sub_len > src_len) {
545     return false;
546   }
547   return Substring(aSource, src_len - sub_len, sub_len).Equals(aSubstring);
548 }
549 
StringEndsWith(const nsACString & aSource,const nsACString & aSubstring,nsCStringComparator aComparator)550 bool StringEndsWith(const nsACString& aSource, const nsACString& aSubstring,
551                     nsCStringComparator aComparator) {
552   nsACString::size_type src_len = aSource.Length(),
553                         sub_len = aSubstring.Length();
554   if (sub_len > src_len) {
555     return false;
556   }
557   return Substring(aSource, src_len - sub_len, sub_len)
558       .Equals(aSubstring, aComparator);
559 }
560 
561 static const char16_t empty_buffer[1] = {'\0'};
562 
EmptyString()563 const nsString& EmptyString() {
564   static const nsDependentString sEmpty(empty_buffer);
565 
566   return sEmpty;
567 }
568 
EmptyCString()569 const nsCString& EmptyCString() {
570   static const nsDependentCString sEmpty((const char*)empty_buffer);
571 
572   return sEmpty;
573 }
574 
VoidString()575 const nsString& VoidString() {
576   static const nsString sNull(mozilla::detail::StringDataFlags::VOIDED);
577 
578   return sNull;
579 }
580 
VoidCString()581 const nsCString& VoidCString() {
582   static const nsCString sNull(mozilla::detail::StringDataFlags::VOIDED);
583 
584   return sNull;
585 }
586 
CompareUTF8toUTF16(const nsACString & aUTF8String,const nsAString & aUTF16String,bool * aErr)587 int32_t CompareUTF8toUTF16(const nsACString& aUTF8String,
588                            const nsAString& aUTF16String, bool* aErr) {
589   const char* u8;
590   const char* u8end;
591   aUTF8String.BeginReading(u8);
592   aUTF8String.EndReading(u8end);
593 
594   const char16_t* u16;
595   const char16_t* u16end;
596   aUTF16String.BeginReading(u16);
597   aUTF16String.EndReading(u16end);
598 
599   for (;;) {
600     if (u8 == u8end) {
601       if (u16 == u16end) {
602         return 0;
603       }
604       return -1;
605     }
606     if (u16 == u16end) {
607       return 1;
608     }
609     // No need for ASCII optimization, since both NextChar()
610     // calls get inlined.
611     uint32_t scalar8 = UTF8CharEnumerator::NextChar(&u8, u8end, aErr);
612     uint32_t scalar16 = UTF16CharEnumerator::NextChar(&u16, u16end, aErr);
613     if (scalar16 == scalar8) {
614       continue;
615     }
616     if (scalar8 < scalar16) {
617       return -1;
618     }
619     return 1;
620   }
621 }
622 
AppendUCS4ToUTF16(const uint32_t aSource,nsAString & aDest)623 void AppendUCS4ToUTF16(const uint32_t aSource, nsAString& aDest) {
624   NS_ASSERTION(IS_VALID_CHAR(aSource), "Invalid UCS4 char");
625   if (IS_IN_BMP(aSource)) {
626     aDest.Append(char16_t(aSource));
627   } else {
628     aDest.Append(H_SURROGATE(aSource));
629     aDest.Append(L_SURROGATE(aSource));
630   }
631 }
632