gecko-dev/xpcom/string/nsReadableUtils.cpp
Nathan Froyd 9a2523bcc3 Bug 1247359 - micro-optimize the common case of String{Begins,End}With; r=erahm
StringBeginsWith (resp. StringEndsWith) takes a defaulted
nsStringComparator object for doing comparisons.  The flexibility this
affords is great, but the cost is not: nsStringComparator has virtual
methods, so initializing that defaulted object (at every callsite)
requires a temporary object whose vtable must be initialized.

Since the overwhemingly common case is to use the default comparator
anyway, we should not use defaulted arguments and instead provide the
default comparator/user-provided comparator cases as separate overloads.
This change eliminates the virtual call for the majority of callsites
and reduces codesize as well.
2016-02-10 14:31:06 -05:00

1246 lines
32 KiB
C++

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "nsReadableUtils.h"
#include "nsMemory.h"
#include "nsString.h"
#include "nsTArray.h"
#include "nsUTF8Utils.h"
void
LossyCopyUTF16toASCII(const nsAString& aSource, nsACString& aDest)
{
aDest.Truncate();
LossyAppendUTF16toASCII(aSource, aDest);
}
void
CopyASCIItoUTF16(const nsACString& aSource, nsAString& aDest)
{
aDest.Truncate();
AppendASCIItoUTF16(aSource, aDest);
}
void
LossyCopyUTF16toASCII(const char16ptr_t aSource, nsACString& aDest)
{
aDest.Truncate();
if (aSource) {
LossyAppendUTF16toASCII(nsDependentString(aSource), aDest);
}
}
void
CopyASCIItoUTF16(const char* aSource, nsAString& aDest)
{
aDest.Truncate();
if (aSource) {
AppendASCIItoUTF16(nsDependentCString(aSource), aDest);
}
}
void
CopyUTF16toUTF8(const nsAString& aSource, nsACString& aDest)
{
if (!CopyUTF16toUTF8(aSource, aDest, mozilla::fallible)) {
// Note that this may wildly underestimate the allocation that failed, as
// we report the length of aSource as UTF-16 instead of UTF-8.
aDest.AllocFailed(aDest.Length() + aSource.Length());
}
}
bool
CopyUTF16toUTF8(const nsAString& aSource, nsACString& aDest,
const mozilla::fallible_t& aFallible)
{
aDest.Truncate();
if (!AppendUTF16toUTF8(aSource, aDest, aFallible)) {
return false;
}
return true;
}
void
CopyUTF8toUTF16(const nsACString& aSource, nsAString& aDest)
{
aDest.Truncate();
AppendUTF8toUTF16(aSource, aDest);
}
void
CopyUTF16toUTF8(const char16ptr_t aSource, nsACString& aDest)
{
aDest.Truncate();
AppendUTF16toUTF8(aSource, aDest);
}
void
CopyUTF8toUTF16(const char* aSource, nsAString& aDest)
{
aDest.Truncate();
AppendUTF8toUTF16(aSource, aDest);
}
void
LossyAppendUTF16toASCII(const nsAString& aSource, nsACString& aDest)
{
uint32_t old_dest_length = aDest.Length();
aDest.SetLength(old_dest_length + aSource.Length());
nsAString::const_iterator fromBegin, fromEnd;
nsACString::iterator dest;
aDest.BeginWriting(dest);
dest.advance(old_dest_length);
// right now, this won't work on multi-fragment destinations
LossyConvertEncoding16to8 converter(dest.get());
copy_string(aSource.BeginReading(fromBegin), aSource.EndReading(fromEnd),
converter);
}
void
AppendASCIItoUTF16(const nsACString& aSource, nsAString& aDest)
{
if (!AppendASCIItoUTF16(aSource, aDest, mozilla::fallible)) {
aDest.AllocFailed(aDest.Length() + aSource.Length());
}
}
bool
AppendASCIItoUTF16(const nsACString& aSource, nsAString& aDest,
const mozilla::fallible_t& aFallible)
{
uint32_t old_dest_length = aDest.Length();
if (!aDest.SetLength(old_dest_length + aSource.Length(),
aFallible)) {
return false;
}
nsACString::const_iterator fromBegin, fromEnd;
nsAString::iterator dest;
aDest.BeginWriting(dest);
dest.advance(old_dest_length);
// right now, this won't work on multi-fragment destinations
LossyConvertEncoding8to16 converter(dest.get());
copy_string(aSource.BeginReading(fromBegin), aSource.EndReading(fromEnd),
converter);
return true;
}
void
LossyAppendUTF16toASCII(const char16ptr_t aSource, nsACString& aDest)
{
if (aSource) {
LossyAppendUTF16toASCII(nsDependentString(aSource), aDest);
}
}
bool
AppendASCIItoUTF16(const char* aSource, nsAString& aDest, const mozilla::fallible_t& aFallible)
{
if (aSource) {
return AppendASCIItoUTF16(nsDependentCString(aSource), aDest, aFallible);
}
return true;
}
void
AppendASCIItoUTF16(const char* aSource, nsAString& aDest)
{
if (aSource) {
AppendASCIItoUTF16(nsDependentCString(aSource), aDest);
}
}
void
AppendUTF16toUTF8(const nsAString& aSource, nsACString& aDest)
{
if (!AppendUTF16toUTF8(aSource, aDest, mozilla::fallible)) {
// Note that this may wildly underestimate the allocation that failed, as
// we report the length of aSource as UTF-16 instead of UTF-8.
aDest.AllocFailed(aDest.Length() + aSource.Length());
}
}
bool
AppendUTF16toUTF8(const nsAString& aSource, nsACString& aDest,
const mozilla::fallible_t& aFallible)
{
nsAString::const_iterator source_start, source_end;
CalculateUTF8Size calculator;
copy_string(aSource.BeginReading(source_start),
aSource.EndReading(source_end), calculator);
uint32_t count = calculator.Size();
if (count) {
uint32_t old_dest_length = aDest.Length();
// Grow the buffer if we need to.
if (!aDest.SetLength(old_dest_length + count, aFallible)) {
return false;
}
// All ready? Time to convert
ConvertUTF16toUTF8 converter(aDest.BeginWriting() + old_dest_length);
copy_string(aSource.BeginReading(source_start),
aSource.EndReading(source_end), converter);
NS_ASSERTION(converter.Size() == count,
"Unexpected disparity between CalculateUTF8Size and "
"ConvertUTF16toUTF8");
}
return true;
}
void
AppendUTF8toUTF16(const nsACString& aSource, nsAString& aDest)
{
if (!AppendUTF8toUTF16(aSource, aDest, mozilla::fallible)) {
aDest.AllocFailed(aDest.Length() + aSource.Length());
}
}
bool
AppendUTF8toUTF16(const nsACString& aSource, nsAString& aDest,
const mozilla::fallible_t& aFallible)
{
nsACString::const_iterator source_start, source_end;
CalculateUTF8Length calculator;
copy_string(aSource.BeginReading(source_start),
aSource.EndReading(source_end), calculator);
uint32_t count = calculator.Length();
// Avoid making the string mutable if we're appending an empty string
if (count) {
uint32_t old_dest_length = aDest.Length();
// Grow the buffer if we need to.
if (!aDest.SetLength(old_dest_length + count, aFallible)) {
return false;
}
// All ready? Time to convert
ConvertUTF8toUTF16 converter(aDest.BeginWriting() + old_dest_length);
copy_string(aSource.BeginReading(source_start),
aSource.EndReading(source_end), converter);
NS_ASSERTION(converter.ErrorEncountered() ||
converter.Length() == count,
"CalculateUTF8Length produced the wrong length");
if (converter.ErrorEncountered()) {
NS_ERROR("Input wasn't UTF8 or incorrect length was calculated");
aDest.SetLength(old_dest_length);
}
}
return true;
}
void
AppendUTF16toUTF8(const char16ptr_t aSource, nsACString& aDest)
{
if (aSource) {
AppendUTF16toUTF8(nsDependentString(aSource), aDest);
}
}
void
AppendUTF8toUTF16(const char* aSource, nsAString& aDest)
{
if (aSource) {
AppendUTF8toUTF16(nsDependentCString(aSource), aDest);
}
}
/**
* A helper function that allocates a buffer of the desired character type big enough to hold a copy of the supplied string (plus a zero terminator).
*
* @param aSource an string you will eventually be making a copy of
* @return a new buffer (of the type specified by the second parameter) which you must free with |free|.
*
*/
template <class FromStringT, class ToCharT>
inline
ToCharT*
AllocateStringCopy(const FromStringT& aSource, ToCharT*)
{
return static_cast<ToCharT*>(moz_xmalloc(
(aSource.Length() + 1) * sizeof(ToCharT)));
}
char*
ToNewCString(const nsAString& aSource)
{
char* result = AllocateStringCopy(aSource, (char*)0);
if (!result) {
return nullptr;
}
nsAString::const_iterator fromBegin, fromEnd;
LossyConvertEncoding16to8 converter(result);
copy_string(aSource.BeginReading(fromBegin), aSource.EndReading(fromEnd),
converter).write_terminator();
return result;
}
char*
ToNewUTF8String(const nsAString& aSource, uint32_t* aUTF8Count)
{
nsAString::const_iterator start, end;
CalculateUTF8Size calculator;
copy_string(aSource.BeginReading(start), aSource.EndReading(end),
calculator);
if (aUTF8Count) {
*aUTF8Count = calculator.Size();
}
char* result = static_cast<char*>
(moz_xmalloc(calculator.Size() + 1));
if (!result) {
return nullptr;
}
ConvertUTF16toUTF8 converter(result);
copy_string(aSource.BeginReading(start), aSource.EndReading(end),
converter).write_terminator();
NS_ASSERTION(calculator.Size() == converter.Size(), "length mismatch");
return result;
}
char*
ToNewCString(const nsACString& aSource)
{
// no conversion needed, just allocate a buffer of the correct length and copy into it
char* result = AllocateStringCopy(aSource, (char*)0);
if (!result) {
return nullptr;
}
nsACString::const_iterator fromBegin, fromEnd;
char* toBegin = result;
*copy_string(aSource.BeginReading(fromBegin), aSource.EndReading(fromEnd),
toBegin) = char(0);
return result;
}
char16_t*
ToNewUnicode(const nsAString& aSource)
{
// no conversion needed, just allocate a buffer of the correct length and copy into it
char16_t* result = AllocateStringCopy(aSource, (char16_t*)0);
if (!result) {
return nullptr;
}
nsAString::const_iterator fromBegin, fromEnd;
char16_t* toBegin = result;
*copy_string(aSource.BeginReading(fromBegin), aSource.EndReading(fromEnd),
toBegin) = char16_t(0);
return result;
}
char16_t*
ToNewUnicode(const nsACString& aSource)
{
char16_t* result = AllocateStringCopy(aSource, (char16_t*)0);
if (!result) {
return nullptr;
}
nsACString::const_iterator fromBegin, fromEnd;
LossyConvertEncoding8to16 converter(result);
copy_string(aSource.BeginReading(fromBegin), aSource.EndReading(fromEnd),
converter).write_terminator();
return result;
}
uint32_t
CalcUTF8ToUnicodeLength(const nsACString& aSource)
{
nsACString::const_iterator start, end;
CalculateUTF8Length calculator;
copy_string(aSource.BeginReading(start), aSource.EndReading(end),
calculator);
return calculator.Length();
}
char16_t*
UTF8ToUnicodeBuffer(const nsACString& aSource, char16_t* aBuffer,
uint32_t* aUTF16Count)
{
nsACString::const_iterator start, end;
ConvertUTF8toUTF16 converter(aBuffer);
copy_string(aSource.BeginReading(start),
aSource.EndReading(end),
converter).write_terminator();
if (aUTF16Count) {
*aUTF16Count = converter.Length();
}
return aBuffer;
}
char16_t*
UTF8ToNewUnicode(const nsACString& aSource, uint32_t* aUTF16Count)
{
const uint32_t length = CalcUTF8ToUnicodeLength(aSource);
const size_t buffer_size = (length + 1) * sizeof(char16_t);
char16_t* buffer = static_cast<char16_t*>(moz_xmalloc(buffer_size));
if (!buffer) {
return nullptr;
}
uint32_t copied;
UTF8ToUnicodeBuffer(aSource, buffer, &copied);
NS_ASSERTION(length == copied, "length mismatch");
if (aUTF16Count) {
*aUTF16Count = copied;
}
return buffer;
}
char16_t*
CopyUnicodeTo(const nsAString& aSource, uint32_t aSrcOffset, char16_t* aDest,
uint32_t aLength)
{
nsAString::const_iterator fromBegin, fromEnd;
char16_t* toBegin = aDest;
copy_string(aSource.BeginReading(fromBegin).advance(int32_t(aSrcOffset)),
aSource.BeginReading(fromEnd).advance(int32_t(aSrcOffset + aLength)),
toBegin);
return aDest;
}
void
CopyUnicodeTo(const nsAString::const_iterator& aSrcStart,
const nsAString::const_iterator& aSrcEnd,
nsAString& aDest)
{
nsAString::iterator writer;
aDest.SetLength(Distance(aSrcStart, aSrcEnd));
aDest.BeginWriting(writer);
nsAString::const_iterator fromBegin(aSrcStart);
copy_string(fromBegin, aSrcEnd, writer);
}
void
AppendUnicodeTo(const nsAString::const_iterator& aSrcStart,
const nsAString::const_iterator& aSrcEnd,
nsAString& aDest)
{
nsAString::iterator writer;
uint32_t oldLength = aDest.Length();
aDest.SetLength(oldLength + Distance(aSrcStart, aSrcEnd));
aDest.BeginWriting(writer).advance(oldLength);
nsAString::const_iterator fromBegin(aSrcStart);
copy_string(fromBegin, aSrcEnd, writer);
}
bool
IsASCII(const nsAString& aString)
{
static const char16_t NOT_ASCII = char16_t(~0x007F);
// Don't want to use |copy_string| for this task, since we can stop at the first non-ASCII character
nsAString::const_iterator iter, done_reading;
aString.BeginReading(iter);
aString.EndReading(done_reading);
const char16_t* c = iter.get();
const char16_t* end = done_reading.get();
while (c < end) {
if (*c++ & NOT_ASCII) {
return false;
}
}
return true;
}
bool
IsASCII(const nsACString& aString)
{
static const char NOT_ASCII = char(~0x7F);
// Don't want to use |copy_string| for this task, since we can stop at the first non-ASCII character
nsACString::const_iterator iter, done_reading;
aString.BeginReading(iter);
aString.EndReading(done_reading);
const char* c = iter.get();
const char* end = done_reading.get();
while (c < end) {
if (*c++ & NOT_ASCII) {
return false;
}
}
return true;
}
bool
IsUTF8(const nsACString& aString, bool aRejectNonChar)
{
nsReadingIterator<char> done_reading;
aString.EndReading(done_reading);
int32_t state = 0;
bool overlong = false;
bool surrogate = false;
bool nonchar = false;
uint16_t olupper = 0; // overlong byte upper bound.
uint16_t slower = 0; // surrogate byte lower bound.
nsReadingIterator<char> iter;
aString.BeginReading(iter);
const char* ptr = iter.get();
const char* end = done_reading.get();
while (ptr < end) {
uint8_t c;
if (0 == state) {
c = *ptr++;
if (UTF8traits::isASCII(c)) {
continue;
}
if (c <= 0xC1) { // [80-BF] where not expected, [C0-C1] for overlong.
return false;
} else if (UTF8traits::is2byte(c)) {
state = 1;
} else if (UTF8traits::is3byte(c)) {
state = 2;
if (c == 0xE0) { // to exclude E0[80-9F][80-BF]
overlong = true;
olupper = 0x9F;
} else if (c == 0xED) { // ED[A0-BF][80-BF] : surrogate codepoint
surrogate = true;
slower = 0xA0;
} else if (c == 0xEF) { // EF BF [BE-BF] : non-character
nonchar = true;
}
} else if (c <= 0xF4) { // XXX replace /w UTF8traits::is4byte when it's updated to exclude [F5-F7].(bug 199090)
state = 3;
nonchar = true;
if (c == 0xF0) { // to exclude F0[80-8F][80-BF]{2}
overlong = true;
olupper = 0x8F;
} else if (c == 0xF4) { // to exclude F4[90-BF][80-BF]
// actually not surrogates but codepoints beyond 0x10FFFF
surrogate = true;
slower = 0x90;
}
} else {
return false; // Not UTF-8 string
}
}
if (nonchar && !aRejectNonChar) {
nonchar = false;
}
while (ptr < end && state) {
c = *ptr++;
--state;
// non-character : EF BF [BE-BF] or F[0-7] [89AB]F BF [BE-BF]
if (nonchar &&
((!state && c < 0xBE) ||
(state == 1 && c != 0xBF) ||
(state == 2 && 0x0F != (0x0F & c)))) {
nonchar = false;
}
if (!UTF8traits::isInSeq(c) || (overlong && c <= olupper) ||
(surrogate && slower <= c) || (nonchar && !state)) {
return false; // Not UTF-8 string
}
overlong = surrogate = false;
}
}
return !state; // state != 0 at the end indicates an invalid UTF-8 seq.
}
/**
* A character sink for in-place case conversion.
*/
class ConvertToUpperCase
{
public:
typedef char value_type;
uint32_t
write(const char* aSource, uint32_t aSourceLength)
{
char* cp = const_cast<char*>(aSource);
const char* end = aSource + aSourceLength;
while (cp != end) {
char ch = *cp;
if (ch >= 'a' && ch <= 'z') {
*cp = ch - ('a' - 'A');
}
++cp;
}
return aSourceLength;
}
};
void
ToUpperCase(nsCSubstring& aCString)
{
ConvertToUpperCase converter;
char* start;
converter.write(aCString.BeginWriting(start), aCString.Length());
}
/**
* A character sink for copying with case conversion.
*/
class CopyToUpperCase
{
public:
typedef char value_type;
explicit CopyToUpperCase(nsACString::iterator& aDestIter)
: mIter(aDestIter)
{
}
uint32_t
write(const char* aSource, uint32_t aSourceLength)
{
uint32_t len = XPCOM_MIN(uint32_t(mIter.size_forward()), aSourceLength);
char* cp = mIter.get();
const char* end = aSource + len;
while (aSource != end) {
char ch = *aSource;
if ((ch >= 'a') && (ch <= 'z')) {
*cp = ch - ('a' - 'A');
} else {
*cp = ch;
}
++aSource;
++cp;
}
mIter.advance(len);
return len;
}
protected:
nsACString::iterator& mIter;
};
void
ToUpperCase(const nsACString& aSource, nsACString& aDest)
{
nsACString::const_iterator fromBegin, fromEnd;
nsACString::iterator toBegin;
aDest.SetLength(aSource.Length());
CopyToUpperCase converter(aDest.BeginWriting(toBegin));
copy_string(aSource.BeginReading(fromBegin), aSource.EndReading(fromEnd),
converter);
}
/**
* A character sink for case conversion.
*/
class ConvertToLowerCase
{
public:
typedef char value_type;
uint32_t
write(const char* aSource, uint32_t aSourceLength)
{
char* cp = const_cast<char*>(aSource);
const char* end = aSource + aSourceLength;
while (cp != end) {
char ch = *cp;
if ((ch >= 'A') && (ch <= 'Z')) {
*cp = ch + ('a' - 'A');
}
++cp;
}
return aSourceLength;
}
};
void
ToLowerCase(nsCSubstring& aCString)
{
ConvertToLowerCase converter;
char* start;
converter.write(aCString.BeginWriting(start), aCString.Length());
}
/**
* A character sink for copying with case conversion.
*/
class CopyToLowerCase
{
public:
typedef char value_type;
explicit CopyToLowerCase(nsACString::iterator& aDestIter)
: mIter(aDestIter)
{
}
uint32_t
write(const char* aSource, uint32_t aSourceLength)
{
uint32_t len = XPCOM_MIN(uint32_t(mIter.size_forward()), aSourceLength);
char* cp = mIter.get();
const char* end = aSource + len;
while (aSource != end) {
char ch = *aSource;
if ((ch >= 'A') && (ch <= 'Z')) {
*cp = ch + ('a' - 'A');
} else {
*cp = ch;
}
++aSource;
++cp;
}
mIter.advance(len);
return len;
}
protected:
nsACString::iterator& mIter;
};
void
ToLowerCase(const nsACString& aSource, nsACString& aDest)
{
nsACString::const_iterator fromBegin, fromEnd;
nsACString::iterator toBegin;
aDest.SetLength(aSource.Length());
CopyToLowerCase converter(aDest.BeginWriting(toBegin));
copy_string(aSource.BeginReading(fromBegin), aSource.EndReading(fromEnd),
converter);
}
bool
ParseString(const nsACString& aSource, char aDelimiter,
nsTArray<nsCString>& aArray)
{
nsACString::const_iterator start, end;
aSource.BeginReading(start);
aSource.EndReading(end);
uint32_t oldLength = aArray.Length();
for (;;) {
nsACString::const_iterator delimiter = start;
FindCharInReadable(aDelimiter, delimiter, end);
if (delimiter != start) {
if (!aArray.AppendElement(Substring(start, delimiter))) {
aArray.RemoveElementsAt(oldLength, aArray.Length() - oldLength);
return false;
}
}
if (delimiter == end) {
break;
}
start = ++delimiter;
if (start == end) {
break;
}
}
return true;
}
template <class StringT, class IteratorT, class Comparator>
bool
FindInReadable_Impl(const StringT& aPattern, IteratorT& aSearchStart,
IteratorT& aSearchEnd, const Comparator& aCompare)
{
bool found_it = false;
// only bother searching at all if we're given a non-empty range to search
if (aSearchStart != aSearchEnd) {
IteratorT aPatternStart, aPatternEnd;
aPattern.BeginReading(aPatternStart);
aPattern.EndReading(aPatternEnd);
// outer loop keeps searching till we find it or run out of string to search
while (!found_it) {
// fast inner loop (that's what it's called, not what it is) looks for a potential match
while (aSearchStart != aSearchEnd &&
aCompare(aPatternStart.get(), aSearchStart.get(), 1, 1)) {
++aSearchStart;
}
// if we broke out of the `fast' loop because we're out of string ... we're done: no match
if (aSearchStart == aSearchEnd) {
break;
}
// otherwise, we're at a potential match, let's see if we really hit one
IteratorT testPattern(aPatternStart);
IteratorT testSearch(aSearchStart);
// slow inner loop verifies the potential match (found by the `fast' loop) at the current position
for (;;) {
// we already compared the first character in the outer loop,
// so we'll advance before the next comparison
++testPattern;
++testSearch;
// if we verified all the way to the end of the pattern, then we found it!
if (testPattern == aPatternEnd) {
found_it = true;
aSearchEnd = testSearch; // return the exact found range through the parameters
break;
}
// if we got to end of the string we're searching before we hit the end of the
// pattern, we'll never find what we're looking for
if (testSearch == aSearchEnd) {
aSearchStart = aSearchEnd;
break;
}
// else if we mismatched ... it's time to advance to the next search position
// and get back into the `fast' loop
if (aCompare(testPattern.get(), testSearch.get(), 1, 1)) {
++aSearchStart;
break;
}
}
}
}
return found_it;
}
/**
* This searches the entire string from right to left, and returns the first match found, if any.
*/
template <class StringT, class IteratorT, class Comparator>
bool
RFindInReadable_Impl(const StringT& aPattern, IteratorT& aSearchStart,
IteratorT& aSearchEnd, const Comparator& aCompare)
{
IteratorT patternStart, patternEnd, searchEnd = aSearchEnd;
aPattern.BeginReading(patternStart);
aPattern.EndReading(patternEnd);
// Point to the last character in the pattern
--patternEnd;
// outer loop keeps searching till we run out of string to search
while (aSearchStart != searchEnd) {
// Point to the end position of the next possible match
--searchEnd;
// Check last character, if a match, explore further from here
if (aCompare(patternEnd.get(), searchEnd.get(), 1, 1) == 0) {
// We're at a potential match, let's see if we really hit one
IteratorT testPattern(patternEnd);
IteratorT testSearch(searchEnd);
// inner loop verifies the potential match at the current position
do {
// if we verified all the way to the end of the pattern, then we found it!
if (testPattern == patternStart) {
aSearchStart = testSearch; // point to start of match
aSearchEnd = ++searchEnd; // point to end of match
return true;
}
// if we got to end of the string we're searching before we hit the end of the
// pattern, we'll never find what we're looking for
if (testSearch == aSearchStart) {
aSearchStart = aSearchEnd;
return false;
}
// test previous character for a match
--testPattern;
--testSearch;
} while (aCompare(testPattern.get(), testSearch.get(), 1, 1) == 0);
}
}
aSearchStart = aSearchEnd;
return false;
}
bool
FindInReadable(const nsAString& aPattern,
nsAString::const_iterator& aSearchStart,
nsAString::const_iterator& aSearchEnd,
const nsStringComparator& aComparator)
{
return FindInReadable_Impl(aPattern, aSearchStart, aSearchEnd, aComparator);
}
bool
FindInReadable(const nsACString& aPattern,
nsACString::const_iterator& aSearchStart,
nsACString::const_iterator& aSearchEnd,
const nsCStringComparator& aComparator)
{
return FindInReadable_Impl(aPattern, aSearchStart, aSearchEnd, aComparator);
}
bool
CaseInsensitiveFindInReadable(const nsACString& aPattern,
nsACString::const_iterator& aSearchStart,
nsACString::const_iterator& aSearchEnd)
{
return FindInReadable_Impl(aPattern, aSearchStart, aSearchEnd,
nsCaseInsensitiveCStringComparator());
}
bool
RFindInReadable(const nsAString& aPattern,
nsAString::const_iterator& aSearchStart,
nsAString::const_iterator& aSearchEnd,
const nsStringComparator& aComparator)
{
return RFindInReadable_Impl(aPattern, aSearchStart, aSearchEnd, aComparator);
}
bool
RFindInReadable(const nsACString& aPattern,
nsACString::const_iterator& aSearchStart,
nsACString::const_iterator& aSearchEnd,
const nsCStringComparator& aComparator)
{
return RFindInReadable_Impl(aPattern, aSearchStart, aSearchEnd, aComparator);
}
bool
FindCharInReadable(char16_t aChar, nsAString::const_iterator& aSearchStart,
const nsAString::const_iterator& aSearchEnd)
{
int32_t fragmentLength = aSearchEnd.get() - aSearchStart.get();
const char16_t* charFoundAt =
nsCharTraits<char16_t>::find(aSearchStart.get(), fragmentLength, aChar);
if (charFoundAt) {
aSearchStart.advance(charFoundAt - aSearchStart.get());
return true;
}
aSearchStart.advance(fragmentLength);
return false;
}
bool
FindCharInReadable(char aChar, nsACString::const_iterator& aSearchStart,
const nsACString::const_iterator& aSearchEnd)
{
int32_t fragmentLength = aSearchEnd.get() - aSearchStart.get();
const char* charFoundAt =
nsCharTraits<char>::find(aSearchStart.get(), fragmentLength, aChar);
if (charFoundAt) {
aSearchStart.advance(charFoundAt - aSearchStart.get());
return true;
}
aSearchStart.advance(fragmentLength);
return false;
}
uint32_t
CountCharInReadable(const nsAString& aStr, char16_t aChar)
{
uint32_t count = 0;
nsAString::const_iterator begin, end;
aStr.BeginReading(begin);
aStr.EndReading(end);
while (begin != end) {
if (*begin == aChar) {
++count;
}
++begin;
}
return count;
}
uint32_t
CountCharInReadable(const nsACString& aStr, char aChar)
{
uint32_t count = 0;
nsACString::const_iterator begin, end;
aStr.BeginReading(begin);
aStr.EndReading(end);
while (begin != end) {
if (*begin == aChar) {
++count;
}
++begin;
}
return count;
}
bool
StringBeginsWith(const nsAString& aSource, const nsAString& aSubstring)
{
nsAString::size_type src_len = aSource.Length(),
sub_len = aSubstring.Length();
if (sub_len > src_len) {
return false;
}
return Substring(aSource, 0, sub_len).Equals(aSubstring);
}
bool
StringBeginsWith(const nsAString& aSource, const nsAString& aSubstring,
const nsStringComparator& aComparator)
{
nsAString::size_type src_len = aSource.Length(),
sub_len = aSubstring.Length();
if (sub_len > src_len) {
return false;
}
return Substring(aSource, 0, sub_len).Equals(aSubstring, aComparator);
}
bool
StringBeginsWith(const nsACString& aSource, const nsACString& aSubstring)
{
nsACString::size_type src_len = aSource.Length(),
sub_len = aSubstring.Length();
if (sub_len > src_len) {
return false;
}
return Substring(aSource, 0, sub_len).Equals(aSubstring);
}
bool
StringBeginsWith(const nsACString& aSource, const nsACString& aSubstring,
const nsCStringComparator& aComparator)
{
nsACString::size_type src_len = aSource.Length(),
sub_len = aSubstring.Length();
if (sub_len > src_len) {
return false;
}
return Substring(aSource, 0, sub_len).Equals(aSubstring, aComparator);
}
bool
StringEndsWith(const nsAString& aSource, const nsAString& aSubstring)
{
nsAString::size_type src_len = aSource.Length(),
sub_len = aSubstring.Length();
if (sub_len > src_len) {
return false;
}
return Substring(aSource, src_len - sub_len, sub_len).Equals(aSubstring);
}
bool
StringEndsWith(const nsAString& aSource, const nsAString& aSubstring,
const nsStringComparator& aComparator)
{
nsAString::size_type src_len = aSource.Length(),
sub_len = aSubstring.Length();
if (sub_len > src_len) {
return false;
}
return Substring(aSource, src_len - sub_len, sub_len).Equals(aSubstring,
aComparator);
}
bool
StringEndsWith(const nsACString& aSource, const nsACString& aSubstring)
{
nsACString::size_type src_len = aSource.Length(),
sub_len = aSubstring.Length();
if (sub_len > src_len) {
return false;
}
return Substring(aSource, src_len - sub_len, sub_len).Equals(aSubstring);
}
bool
StringEndsWith(const nsACString& aSource, const nsACString& aSubstring,
const nsCStringComparator& aComparator)
{
nsACString::size_type src_len = aSource.Length(),
sub_len = aSubstring.Length();
if (sub_len > src_len) {
return false;
}
return Substring(aSource, src_len - sub_len, sub_len).Equals(aSubstring,
aComparator);
}
static const char16_t empty_buffer[1] = { '\0' };
const nsAFlatString&
EmptyString()
{
static const nsDependentString sEmpty(empty_buffer);
return sEmpty;
}
const nsAFlatCString&
EmptyCString()
{
static const nsDependentCString sEmpty((const char*)empty_buffer);
return sEmpty;
}
const nsAFlatString&
NullString()
{
static const nsXPIDLString sNull;
return sNull;
}
const nsAFlatCString&
NullCString()
{
static const nsXPIDLCString sNull;
return sNull;
}
int32_t
CompareUTF8toUTF16(const nsASingleFragmentCString& aUTF8String,
const nsASingleFragmentString& aUTF16String)
{
static const uint32_t NOT_ASCII = uint32_t(~0x7F);
const char* u8;
const char* u8end;
aUTF8String.BeginReading(u8);
aUTF8String.EndReading(u8end);
const char16_t* u16;
const char16_t* u16end;
aUTF16String.BeginReading(u16);
aUTF16String.EndReading(u16end);
while (u8 != u8end && u16 != u16end) {
// Cast away the signedness of *u8 to prevent signextension when
// converting to uint32_t
uint32_t c8_32 = (uint8_t)*u8;
if (c8_32 & NOT_ASCII) {
bool err;
c8_32 = UTF8CharEnumerator::NextChar(&u8, u8end, &err);
if (err) {
return INT32_MIN;
}
uint32_t c16_32 = UTF16CharEnumerator::NextChar(&u16, u16end);
// The above UTF16CharEnumerator::NextChar() calls can
// fail, but if it does for anything other than no data to
// look at (which can't happen here), it returns the
// Unicode replacement character 0xFFFD for the invalid
// data they were fed. Ignore that error and treat invalid
// UTF16 as 0xFFFD.
//
// This matches what our UTF16 to UTF8 conversion code
// does, and thus a UTF8 string that came from an invalid
// UTF16 string will compare equal to the invalid UTF16
// string it came from. Same is true for any other UTF16
// string differs only in the invalid part of the string.
if (c8_32 != c16_32) {
return c8_32 < c16_32 ? -1 : 1;
}
} else {
if (c8_32 != *u16) {
return c8_32 > *u16 ? 1 : -1;
}
++u8;
++u16;
}
}
if (u8 != u8end) {
// We get to the end of the UTF16 string, but no to the end of
// the UTF8 string. The UTF8 string is longer than the UTF16
// string
return 1;
}
if (u16 != u16end) {
// We get to the end of the UTF8 string, but no to the end of
// the UTF16 string. The UTF16 string is longer than the UTF8
// string
return -1;
}
// The two strings match.
return 0;
}
void
AppendUCS4ToUTF16(const uint32_t aSource, nsAString& aDest)
{
NS_ASSERTION(IS_VALID_CHAR(aSource), "Invalid UCS4 char");
if (IS_IN_BMP(aSource)) {
aDest.Append(char16_t(aSource));
} else {
aDest.Append(H_SURROGATE(aSource));
aDest.Append(L_SURROGATE(aSource));
}
}