зеркало из https://github.com/mozilla/gecko-dev.git
996 строки
31 KiB
C++
996 строки
31 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 "mozilla/Casting.h"
|
|
#include "nsString.h"
|
|
|
|
/**
|
|
* nsTString obsolete API support
|
|
*/
|
|
|
|
#include "nsDependentString.h"
|
|
#include "nsDependentSubstring.h"
|
|
#include "nsReadableUtils.h"
|
|
#include "nsCRT.h"
|
|
#include "nsUTF8Utils.h"
|
|
#include "prdtoa.h"
|
|
|
|
/* ***** BEGIN RICKG BLOCK *****
|
|
*
|
|
* NOTE: This section of code was extracted from rickg's bufferRoutines.h file.
|
|
* For the most part it remains unmodified. We want to eliminate (or at
|
|
* least clean up) this code at some point. If you find the formatting
|
|
* in this section somewhat inconsistent, don't blame me! ;-)
|
|
*/
|
|
|
|
// avoid STDC's tolower since it may do weird things with non-ASCII bytes
|
|
inline char ascii_tolower(char aChar) {
|
|
if (aChar >= 'A' && aChar <= 'Z') return aChar + ('a' - 'A');
|
|
return aChar;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
// This set of methods is used to search a buffer looking for a char.
|
|
//
|
|
|
|
/**
|
|
* This methods cans the given buffer for the given char
|
|
*
|
|
* @update gess 02/17/00
|
|
* @param aDest is the buffer to be searched
|
|
* @param aDestLength is the size (in char-units, not bytes) of the buffer
|
|
* @param anOffset is the start pos to begin searching
|
|
* @param aChar is the target character we're looking for
|
|
* @param aCount tells us how many characters to iterate through (which may
|
|
* be different than aLength); -1 means use full length.
|
|
* @return index of pos if found, else -1 (kNotFound)
|
|
*/
|
|
static int32_t FindChar1(const char* aDest, uint32_t aDestLength,
|
|
int32_t anOffset, const char16_t aChar,
|
|
int32_t aCount) {
|
|
if (anOffset < 0) anOffset = 0;
|
|
|
|
if (aCount < 0) aCount = (int32_t)aDestLength;
|
|
|
|
if ((aChar < 256) && (0 < aDestLength) &&
|
|
((uint32_t)anOffset < aDestLength)) {
|
|
// We'll only search if the given aChar is within the normal ascii a range,
|
|
//(Since this string is definitely within the ascii range).
|
|
|
|
if (0 < aCount) {
|
|
const char* left = aDest + anOffset;
|
|
const char* last = left + aCount;
|
|
const char* max = aDest + aDestLength;
|
|
const char* end = (last < max) ? last : max;
|
|
|
|
int32_t theMax = end - left;
|
|
if (0 < theMax) {
|
|
unsigned char theChar = (unsigned char)aChar;
|
|
const char* result = (const char*)memchr(left, (int)theChar, theMax);
|
|
|
|
if (result) return result - aDest;
|
|
}
|
|
}
|
|
}
|
|
|
|
return kNotFound;
|
|
}
|
|
|
|
/**
|
|
* This methods cans the given buffer for the given char
|
|
*
|
|
* @update gess 3/25/98
|
|
* @param aDest is the buffer to be searched
|
|
* @param aDestLength is the size (in char-units, not bytes) of the buffer
|
|
* @param anOffset is the start pos to begin searching
|
|
* @param aChar is the target character we're looking for
|
|
* @param aCount tells us how many characters to iterate through (which may
|
|
* be different than aLength); -1 means use full length.
|
|
* @return index of pos if found, else -1 (kNotFound)
|
|
*/
|
|
static int32_t FindChar2(const char16_t* aDest, uint32_t aDestLength,
|
|
int32_t anOffset, const char16_t aChar,
|
|
int32_t aCount) {
|
|
if (anOffset < 0) anOffset = 0;
|
|
|
|
if (aCount < 0) aCount = (int32_t)aDestLength;
|
|
|
|
if ((0 < aDestLength) && ((uint32_t)anOffset < aDestLength)) {
|
|
if (0 < aCount) {
|
|
const char16_t* root = aDest;
|
|
const char16_t* left = root + anOffset;
|
|
const char16_t* last = left + aCount;
|
|
const char16_t* max = root + aDestLength;
|
|
const char16_t* end = (last < max) ? last : max;
|
|
|
|
while (left < end) {
|
|
if (*left == aChar) return (left - root);
|
|
|
|
++left;
|
|
}
|
|
}
|
|
}
|
|
|
|
return kNotFound;
|
|
}
|
|
|
|
/**
|
|
* This methods cans the given buffer (in reverse) for the given char
|
|
*
|
|
* @update gess 02/17/00
|
|
* @param aDest is the buffer to be searched
|
|
* @param aDestLength is the size (in char-units, not bytes) of the buffer
|
|
* @param anOffset is the start pos to begin searching
|
|
* @param aChar is the target character we're looking for
|
|
* @param aCount tells us how many characters to iterate through (which may
|
|
* be different than aLength); -1 means use full length.
|
|
* @return index of pos if found, else -1 (kNotFound)
|
|
*/
|
|
|
|
static int32_t RFindChar1(const char* aDest, uint32_t aDestLength,
|
|
int32_t anOffset, const char16_t aChar,
|
|
int32_t aCount) {
|
|
if (anOffset < 0) anOffset = (int32_t)aDestLength - 1;
|
|
|
|
if (aCount < 0) aCount = int32_t(aDestLength);
|
|
|
|
if ((aChar < 256) && (0 < aDestLength) &&
|
|
((uint32_t)anOffset < aDestLength)) {
|
|
// We'll only search if the given aChar is within the normal ascii a range,
|
|
//(Since this string is definitely within the ascii range).
|
|
|
|
if (0 < aCount) {
|
|
const char* rightmost = aDest + anOffset;
|
|
const char* min = rightmost - aCount + 1;
|
|
const char* leftmost = (min < aDest) ? aDest : min;
|
|
|
|
char theChar = (char)aChar;
|
|
while (leftmost <= rightmost) {
|
|
if ((*rightmost) == theChar) return rightmost - aDest;
|
|
|
|
--rightmost;
|
|
}
|
|
}
|
|
}
|
|
|
|
return kNotFound;
|
|
}
|
|
|
|
/**
|
|
* This methods cans the given buffer for the given char
|
|
*
|
|
* @update gess 3/25/98
|
|
* @param aDest is the buffer to be searched
|
|
* @param aDestLength is the size (in char-units, not bytes) of the buffer
|
|
* @param anOffset is the start pos to begin searching
|
|
* @param aChar is the target character we're looking for
|
|
* @param aCount tells us how many characters to iterate through (which may
|
|
* be different than aLength); -1 means use full length.
|
|
* @return index of pos if found, else -1 (kNotFound)
|
|
*/
|
|
static int32_t RFindChar2(const char16_t* aDest, uint32_t aDestLength,
|
|
int32_t anOffset, const char16_t aChar,
|
|
int32_t aCount) {
|
|
if (anOffset < 0) anOffset = (int32_t)aDestLength - 1;
|
|
|
|
if (aCount < 0) aCount = int32_t(aDestLength);
|
|
|
|
if ((0 < aDestLength) && ((uint32_t)anOffset < aDestLength)) {
|
|
if (0 < aCount) {
|
|
const char16_t* root = aDest;
|
|
const char16_t* rightmost = root + anOffset;
|
|
const char16_t* min = rightmost - aCount + 1;
|
|
const char16_t* leftmost = (min < root) ? root : min;
|
|
|
|
while (leftmost <= rightmost) {
|
|
if ((*rightmost) == aChar) return rightmost - root;
|
|
|
|
--rightmost;
|
|
}
|
|
}
|
|
}
|
|
|
|
return kNotFound;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
// This set of methods is used to compare one buffer onto another. The
|
|
// functions are differentiated by the size of source and dest character
|
|
// sizes. WARNING: Your destination buffer MUST be big enough to hold all the
|
|
// source bytes. We don't validate these ranges here (this should be done in
|
|
// higher level routines).
|
|
//
|
|
|
|
/**
|
|
* This method compares the data in one buffer with another
|
|
* @update gess 01/04/99
|
|
* @param aStr1 is the first buffer to be compared
|
|
* @param aStr2 is the 2nd buffer to be compared
|
|
* @param aCount is the number of chars to compare
|
|
* @param aIgnoreCase tells us whether to use a case-sensitive comparison
|
|
* @return -1,0,1 depending on <,==,>
|
|
*/
|
|
static
|
|
#ifdef __SUNPRO_CC
|
|
inline
|
|
#endif /* __SUNPRO_CC */
|
|
int32_t
|
|
Compare1To1(const char* aStr1, const char* aStr2, uint32_t aCount,
|
|
bool aIgnoreCase) {
|
|
int32_t result = 0;
|
|
if (aIgnoreCase)
|
|
#if defined(LIBFUZZER) && defined(LINUX)
|
|
result = int32_t(strncasecmp(aStr1, aStr2, aCount));
|
|
#else
|
|
result = int32_t(PL_strncasecmp(aStr1, aStr2, aCount));
|
|
#endif
|
|
else
|
|
result = nsCharTraits<char>::compare(aStr1, aStr2, aCount);
|
|
|
|
// alien comparisons may return out-of-bound answers
|
|
// instead of the -1, 0, 1 expected by most clients
|
|
if (result < -1)
|
|
result = -1;
|
|
else if (result > 1)
|
|
result = 1;
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* This method compares the data in one buffer with another
|
|
* @update gess 01/04/99
|
|
* @param aStr1 is the first buffer to be compared
|
|
* @param aStr2 is the 2nd buffer to be compared
|
|
* @param aCount is the number of chars to compare
|
|
* @param aIgnoreCase tells us whether to use a case-sensitive comparison
|
|
* @return -1,0,1 depending on <,==,>
|
|
*/
|
|
static
|
|
#ifdef __SUNPRO_CC
|
|
inline
|
|
#endif /* __SUNPRO_CC */
|
|
int32_t
|
|
Compare2To2(const char16_t* aStr1, const char16_t* aStr2, uint32_t aCount) {
|
|
int32_t result;
|
|
|
|
if (aStr1 && aStr2)
|
|
result = nsCharTraits<char16_t>::compare(aStr1, aStr2, aCount);
|
|
|
|
// The following cases are rare and survivable caller errors.
|
|
// Two null pointers are equal, but any string, even 0 length
|
|
// is greater than a null pointer. It might not really matter,
|
|
// but we pick something reasonable anyway.
|
|
else if (!aStr1 && !aStr2)
|
|
result = 0;
|
|
else if (aStr1)
|
|
result = 1;
|
|
else
|
|
result = -1;
|
|
|
|
// alien comparisons may give answers outside the -1, 0, 1 expected by callers
|
|
if (result < -1)
|
|
result = -1;
|
|
else if (result > 1)
|
|
result = 1;
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* This method compares the data in one buffer with another
|
|
* @update gess 01/04/99
|
|
* @param aStr1 is the first buffer to be compared
|
|
* @param aStr2 is the 2nd buffer to be compared
|
|
* @param aCount is the number of chars to compare
|
|
* @param aIgnoreCase tells us whether to use a case-sensitive comparison
|
|
* @return -1,0,1 depending on <,==,>
|
|
*/
|
|
static
|
|
#ifdef __SUNPRO_CC
|
|
inline
|
|
#endif /* __SUNPRO_CC */
|
|
int32_t
|
|
Compare2To1(const char16_t* aStr1, const char* aStr2, uint32_t aCount,
|
|
bool aIgnoreCase) {
|
|
const char16_t* s1 = aStr1;
|
|
const char* s2 = aStr2;
|
|
|
|
if (aStr1 && aStr2) {
|
|
if (aCount != 0) {
|
|
do {
|
|
char16_t c1 = *s1++;
|
|
char16_t c2 = char16_t((unsigned char)*s2++);
|
|
|
|
if (c1 != c2) {
|
|
#ifdef DEBUG
|
|
// we won't warn on c1>=128 (the 2-byte value) because often
|
|
// it is just fine to compare an constant, ascii value (i.e. "body")
|
|
// against some non-ascii value (i.e. a unicode string that
|
|
// was downloaded from a web page)
|
|
if (aIgnoreCase && c2 >= 128)
|
|
NS_WARNING(
|
|
"got a non-ASCII string, but we can't do an accurate case "
|
|
"conversion!");
|
|
#endif
|
|
|
|
// can't do case conversion on characters out of our range
|
|
if (aIgnoreCase && c1 < 128 && c2 < 128) {
|
|
c1 = ascii_tolower(char(c1));
|
|
c2 = ascii_tolower(char(c2));
|
|
|
|
if (c1 == c2) continue;
|
|
}
|
|
|
|
if (c1 < c2) return -1;
|
|
return 1;
|
|
}
|
|
} while (--aCount);
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* This method compares the data in one buffer with another
|
|
* @update gess 01/04/99
|
|
* @param aStr1 is the first buffer to be compared
|
|
* @param aStr2 is the 2nd buffer to be compared
|
|
* @param aCount is the number of chars to compare
|
|
* @param aIgnoreCase tells us whether to use a case-sensitive comparison
|
|
* @return -1,0,1 depending on <,==,>
|
|
*/
|
|
inline int32_t Compare1To2(const char* aStr1, const char16_t* aStr2,
|
|
uint32_t aCount, bool aIgnoreCase) {
|
|
return Compare2To1(aStr2, aStr1, aCount, aIgnoreCase) * -1;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
// This set of methods is used compress char sequences in a buffer...
|
|
//
|
|
|
|
/**
|
|
* This method compresses duplicate runs of a given char from the given buffer
|
|
*
|
|
* @update rickg 03.23.2000
|
|
* @param aString is the buffer to be manipulated
|
|
* @param aLength is the length of the buffer
|
|
* @param aSet tells us which chars to compress from given buffer
|
|
* @param aEliminateLeading tells us whether to strip chars from the start of
|
|
* the buffer
|
|
* @param aEliminateTrailing tells us whether to strip chars from the start
|
|
* of the buffer
|
|
* @return the new length of the given buffer
|
|
*/
|
|
static int32_t CompressChars1(char* aString, uint32_t aLength,
|
|
const char* aSet) {
|
|
char* from = aString;
|
|
char* end = aString + aLength;
|
|
char* to = from;
|
|
|
|
// this code converts /n, /t, /r into normal space ' ';
|
|
// it also compresses runs of whitespace down to a single char...
|
|
if (aSet && aString && (0 < aLength)) {
|
|
uint32_t aSetLen = strlen(aSet);
|
|
|
|
while (from < end) {
|
|
char theChar = *from++;
|
|
|
|
*to++ = theChar; // always copy this char...
|
|
|
|
if ((kNotFound != FindChar1(aSet, aSetLen, 0, theChar, aSetLen))) {
|
|
while (from < end) {
|
|
theChar = *from++;
|
|
if (kNotFound == FindChar1(aSet, aSetLen, 0, theChar, aSetLen)) {
|
|
*to++ = theChar;
|
|
break;
|
|
}
|
|
} // while
|
|
} // if
|
|
} // if
|
|
*to = 0;
|
|
}
|
|
return to - aString;
|
|
}
|
|
|
|
/**
|
|
* This method compresses duplicate runs of a given char from the given buffer
|
|
*
|
|
* @update rickg 03.23.2000
|
|
* @param aString is the buffer to be manipulated
|
|
* @param aLength is the length of the buffer
|
|
* @param aSet tells us which chars to compress from given buffer
|
|
* @param aEliminateLeading tells us whether to strip chars from the start of
|
|
* the buffer
|
|
* @param aEliminateTrailing tells us whether to strip chars from the start
|
|
* of the buffer
|
|
* @return the new length of the given buffer
|
|
*/
|
|
static int32_t CompressChars2(char16_t* aString, uint32_t aLength,
|
|
const char* aSet) {
|
|
char16_t* from = aString;
|
|
char16_t* end = from + aLength;
|
|
char16_t* to = from;
|
|
|
|
// this code converts /n, /t, /r into normal space ' ';
|
|
// it also compresses runs of whitespace down to a single char...
|
|
if (aSet && aString && (0 < aLength)) {
|
|
uint32_t aSetLen = strlen(aSet);
|
|
|
|
while (from < end) {
|
|
char16_t theChar = *from++;
|
|
|
|
*to++ = theChar; // always copy this char...
|
|
|
|
if ((theChar < 256) &&
|
|
(kNotFound != FindChar1(aSet, aSetLen, 0, theChar, aSetLen))) {
|
|
while (from < end) {
|
|
theChar = *from++;
|
|
if (kNotFound == FindChar1(aSet, aSetLen, 0, theChar, aSetLen)) {
|
|
*to++ = theChar;
|
|
break;
|
|
}
|
|
} // while
|
|
} // if
|
|
} // if
|
|
*to = 0;
|
|
}
|
|
return to - (char16_t*)aString;
|
|
}
|
|
|
|
/**
|
|
* This method strips chars in a given set from the given buffer
|
|
*
|
|
* @update gess 01/04/99
|
|
* @param aString is the buffer to be manipulated
|
|
* @param aLength is the length of the buffer
|
|
* @param aSet tells us which chars to compress from given buffer
|
|
* @param aEliminateLeading tells us whether to strip chars from the start of
|
|
* the buffer
|
|
* @param aEliminateTrailing tells us whether to strip chars from the start
|
|
* of the buffer
|
|
* @return the new length of the given buffer
|
|
*/
|
|
static int32_t StripChars1(char* aString, uint32_t aLength, const char* aSet) {
|
|
// XXX(darin): this code should defer writing until necessary.
|
|
|
|
char* to = aString;
|
|
char* from = aString - 1;
|
|
char* end = aString + aLength;
|
|
|
|
if (aSet && aString && (0 < aLength)) {
|
|
uint32_t aSetLen = strlen(aSet);
|
|
while (++from < end) {
|
|
char theChar = *from;
|
|
if (kNotFound == FindChar1(aSet, aSetLen, 0, theChar, aSetLen)) {
|
|
*to++ = theChar;
|
|
}
|
|
}
|
|
*to = 0;
|
|
}
|
|
return to - (char*)aString;
|
|
}
|
|
|
|
/**
|
|
* This method strips chars in a given set from the given buffer
|
|
*
|
|
* @update gess 01/04/99
|
|
* @param aString is the buffer to be manipulated
|
|
* @param aLength is the length of the buffer
|
|
* @param aSet tells us which chars to compress from given buffer
|
|
* @param aEliminateLeading tells us whether to strip chars from the start of
|
|
* the buffer
|
|
* @param aEliminateTrailing tells us whether to strip chars from the start
|
|
* of the buffer
|
|
* @return the new length of the given buffer
|
|
*/
|
|
static int32_t StripChars2(char16_t* aString, uint32_t aLength,
|
|
const char* aSet) {
|
|
// XXX(darin): this code should defer writing until necessary.
|
|
|
|
char16_t* to = aString;
|
|
char16_t* from = aString - 1;
|
|
char16_t* end = to + aLength;
|
|
|
|
if (aSet && aString && (0 < aLength)) {
|
|
uint32_t aSetLen = strlen(aSet);
|
|
while (++from < end) {
|
|
char16_t theChar = *from;
|
|
// Note the test for ascii range below. If you have a real unicode char,
|
|
// and you're searching for chars in the (given) ascii string, there's no
|
|
// point in doing the real search since it's out of the ascii range.
|
|
if ((255 < theChar) ||
|
|
(kNotFound == FindChar1(aSet, aSetLen, 0, theChar, aSetLen))) {
|
|
*to++ = theChar;
|
|
}
|
|
}
|
|
*to = 0;
|
|
}
|
|
return to - (char16_t*)aString;
|
|
}
|
|
|
|
/* ***** END RICKG BLOCK ***** */
|
|
|
|
// This function is used to implement FindCharInSet and friends
|
|
template <class CharT>
|
|
#ifndef __SUNPRO_CC
|
|
static
|
|
#endif /* !__SUNPRO_CC */
|
|
CharT
|
|
GetFindInSetFilter(const CharT* set) {
|
|
CharT filter = ~CharT(0); // All bits set
|
|
while (*set) {
|
|
filter &= ~(*set);
|
|
++set;
|
|
}
|
|
return filter;
|
|
}
|
|
|
|
// This template class is used by our code to access rickg's buffer routines.
|
|
template <class CharT>
|
|
struct nsBufferRoutines {};
|
|
|
|
template <>
|
|
struct nsBufferRoutines<char> {
|
|
static int32_t compare(const char* a, const char* b, uint32_t max, bool ic) {
|
|
return Compare1To1(a, b, max, ic);
|
|
}
|
|
|
|
static int32_t compare(const char* a, const char16_t* b, uint32_t max,
|
|
bool ic) {
|
|
return Compare1To2(a, b, max, ic);
|
|
}
|
|
|
|
static int32_t find_char(const char* s, uint32_t max, int32_t offset,
|
|
const char16_t c, int32_t count) {
|
|
return FindChar1(s, max, offset, c, count);
|
|
}
|
|
|
|
static int32_t rfind_char(const char* s, uint32_t max, int32_t offset,
|
|
const char16_t c, int32_t count) {
|
|
return RFindChar1(s, max, offset, c, count);
|
|
}
|
|
|
|
static char get_find_in_set_filter(const char* set) {
|
|
return GetFindInSetFilter(set);
|
|
}
|
|
|
|
static int32_t strip_chars(char* s, uint32_t len, const char* set) {
|
|
return StripChars1(s, len, set);
|
|
}
|
|
|
|
static int32_t compress_chars(char* s, uint32_t len, const char* set) {
|
|
return CompressChars1(s, len, set);
|
|
}
|
|
};
|
|
|
|
template <>
|
|
struct nsBufferRoutines<char16_t> {
|
|
static int32_t compare(const char16_t* a, const char16_t* b, uint32_t max,
|
|
bool ic) {
|
|
NS_ASSERTION(!ic, "no case-insensitive compare here");
|
|
return Compare2To2(a, b, max);
|
|
}
|
|
|
|
static int32_t compare(const char16_t* a, const char* b, uint32_t max,
|
|
bool ic) {
|
|
return Compare2To1(a, b, max, ic);
|
|
}
|
|
|
|
static int32_t find_char(const char16_t* s, uint32_t max, int32_t offset,
|
|
const char16_t c, int32_t count) {
|
|
return FindChar2(s, max, offset, c, count);
|
|
}
|
|
|
|
static int32_t rfind_char(const char16_t* s, uint32_t max, int32_t offset,
|
|
const char16_t c, int32_t count) {
|
|
return RFindChar2(s, max, offset, c, count);
|
|
}
|
|
|
|
static char16_t get_find_in_set_filter(const char16_t* set) {
|
|
return GetFindInSetFilter(set);
|
|
}
|
|
|
|
static char16_t get_find_in_set_filter(const char* set) {
|
|
return (~char16_t(0) ^ ~char(0)) | GetFindInSetFilter(set);
|
|
}
|
|
|
|
static int32_t strip_chars(char16_t* s, uint32_t max, const char* set) {
|
|
return StripChars2(s, max, set);
|
|
}
|
|
|
|
static int32_t compress_chars(char16_t* s, uint32_t len, const char* set) {
|
|
return CompressChars2(s, len, set);
|
|
}
|
|
};
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
template <class L, class R>
|
|
#ifndef __SUNPRO_CC
|
|
static
|
|
#endif /* !__SUNPRO_CC */
|
|
int32_t
|
|
FindSubstring(const L* big, uint32_t bigLen, const R* little,
|
|
uint32_t littleLen, bool ignoreCase) {
|
|
if (littleLen > bigLen) return kNotFound;
|
|
|
|
int32_t i, max = int32_t(bigLen - littleLen);
|
|
for (i = 0; i <= max; ++i, ++big) {
|
|
if (nsBufferRoutines<L>::compare(big, little, littleLen, ignoreCase) == 0)
|
|
return i;
|
|
}
|
|
|
|
return kNotFound;
|
|
}
|
|
|
|
template <class L, class R>
|
|
#ifndef __SUNPRO_CC
|
|
static
|
|
#endif /* !__SUNPRO_CC */
|
|
int32_t
|
|
RFindSubstring(const L* big, uint32_t bigLen, const R* little,
|
|
uint32_t littleLen, bool ignoreCase) {
|
|
if (littleLen > bigLen) return kNotFound;
|
|
|
|
int32_t i, max = int32_t(bigLen - littleLen);
|
|
|
|
const L* iter = big + max;
|
|
for (i = max; iter >= big; --i, --iter) {
|
|
if (nsBufferRoutines<L>::compare(iter, little, littleLen, ignoreCase) == 0)
|
|
return i;
|
|
}
|
|
|
|
return kNotFound;
|
|
}
|
|
|
|
template <class CharT, class SetCharT>
|
|
#ifndef __SUNPRO_CC
|
|
static
|
|
#endif /* !__SUNPRO_CC */
|
|
int32_t
|
|
FindCharInSet(const CharT* data, uint32_t dataLen, const SetCharT* set) {
|
|
CharT filter = nsBufferRoutines<CharT>::get_find_in_set_filter(set);
|
|
|
|
const CharT* end = data + dataLen;
|
|
for (const CharT* iter = data; iter < end; ++iter) {
|
|
CharT currentChar = *iter;
|
|
if (currentChar & filter)
|
|
continue; // char is not in filter set; go on with next char.
|
|
|
|
// test all chars
|
|
const SetCharT* charInSet = set;
|
|
CharT setChar = CharT(*charInSet);
|
|
while (setChar) {
|
|
if (setChar == currentChar)
|
|
return iter - data; // found it! return index of the found char.
|
|
|
|
setChar = CharT(*(++charInSet));
|
|
}
|
|
}
|
|
return kNotFound;
|
|
}
|
|
|
|
template <class CharT, class SetCharT>
|
|
#ifndef __SUNPRO_CC
|
|
static
|
|
#endif /* !__SUNPRO_CC */
|
|
int32_t
|
|
RFindCharInSet(const CharT* data, uint32_t dataLen, const SetCharT* set) {
|
|
CharT filter = nsBufferRoutines<CharT>::get_find_in_set_filter(set);
|
|
|
|
for (const CharT* iter = data + dataLen - 1; iter >= data; --iter) {
|
|
CharT currentChar = *iter;
|
|
if (currentChar & filter)
|
|
continue; // char is not in filter set; go on with next char.
|
|
|
|
// test all chars
|
|
const CharT* charInSet = set;
|
|
CharT setChar = *charInSet;
|
|
while (setChar) {
|
|
if (setChar == currentChar)
|
|
return iter - data; // found it! return index of the found char.
|
|
|
|
setChar = *(++charInSet);
|
|
}
|
|
}
|
|
return kNotFound;
|
|
}
|
|
|
|
/**
|
|
* this method changes the meaning of |offset| and |count|:
|
|
*
|
|
* upon return,
|
|
* |offset| specifies start of search range
|
|
* |count| specifies length of search range
|
|
*/
|
|
static void Find_ComputeSearchRange(uint32_t bigLen, uint32_t littleLen,
|
|
int32_t& offset, int32_t& count) {
|
|
// |count| specifies how many iterations to make from |offset|
|
|
|
|
if (offset < 0) {
|
|
offset = 0;
|
|
} else if (uint32_t(offset) > bigLen) {
|
|
count = 0;
|
|
return;
|
|
}
|
|
|
|
int32_t maxCount = bigLen - offset;
|
|
if (count < 0 || count > maxCount) {
|
|
count = maxCount;
|
|
} else {
|
|
count += littleLen;
|
|
if (count > maxCount) count = maxCount;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* this method changes the meaning of |offset| and |count|:
|
|
*
|
|
* upon entry,
|
|
* |offset| specifies the end point from which to search backwards
|
|
* |count| specifies the number of iterations from |offset|
|
|
*
|
|
* upon return,
|
|
* |offset| specifies start of search range
|
|
* |count| specifies length of search range
|
|
*
|
|
*
|
|
* EXAMPLE
|
|
*
|
|
* + -- littleLen=4 -- +
|
|
* : :
|
|
* |____|____|____|____|____|____|____|____|____|____|____|____|
|
|
* : :
|
|
* offset=5 bigLen=12
|
|
*
|
|
* if count = 4, then we expect this function to return offset = 2 and
|
|
* count = 7.
|
|
*
|
|
*/
|
|
static void RFind_ComputeSearchRange(uint32_t bigLen, uint32_t littleLen,
|
|
int32_t& offset, int32_t& count) {
|
|
if (littleLen > bigLen) {
|
|
offset = 0;
|
|
count = 0;
|
|
return;
|
|
}
|
|
|
|
if (offset < 0) offset = bigLen - littleLen;
|
|
if (count < 0) count = offset + 1;
|
|
|
|
int32_t start = offset - count + 1;
|
|
if (start < 0) start = 0;
|
|
|
|
count = offset + littleLen - start;
|
|
offset = start;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
#include "nsTStringObsolete.cpp"
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// specialized methods:
|
|
|
|
template <typename T>
|
|
template <typename Q, typename EnableIfChar16>
|
|
int32_t nsTString<T>::Find(const self_type& aString, int32_t aOffset,
|
|
int32_t aCount) const {
|
|
// this method changes the meaning of aOffset and aCount:
|
|
Find_ComputeSearchRange(this->mLength, aString.Length(), aOffset, aCount);
|
|
|
|
// Capture the raw buffer locally to help msvc deduce the type.
|
|
const char_type* str = aString.get();
|
|
int32_t result = FindSubstring(this->mData + aOffset, aCount, str,
|
|
aString.Length(), false);
|
|
if (result != kNotFound) result += aOffset;
|
|
return result;
|
|
}
|
|
|
|
template int32_t nsTString<char16_t>::Find(const self_type&, int32_t,
|
|
int32_t) const;
|
|
|
|
template <typename T>
|
|
template <typename Q, typename EnableIfChar16>
|
|
int32_t nsTString<T>::Find(const char_type* aString, int32_t aOffset,
|
|
int32_t aCount) const {
|
|
return Find(nsTDependentString<T>(aString), aOffset, aCount);
|
|
}
|
|
|
|
template int32_t nsTString<char16_t>::Find(const char_type*, int32_t,
|
|
int32_t) const;
|
|
|
|
template <typename T>
|
|
template <typename Q, typename EnableIfChar16>
|
|
int32_t nsTString<T>::RFind(const self_type& aString, int32_t aOffset,
|
|
int32_t aCount) const {
|
|
// this method changes the meaning of aOffset and aCount:
|
|
RFind_ComputeSearchRange(this->mLength, aString.Length(), aOffset, aCount);
|
|
|
|
// Capture the raw buffer locally to help msvc deduce the type.
|
|
const char_type* str = aString.get();
|
|
int32_t result = RFindSubstring(this->mData + aOffset, aCount, str,
|
|
aString.Length(), false);
|
|
if (result != kNotFound) result += aOffset;
|
|
return result;
|
|
}
|
|
|
|
template int32_t nsTString<char16_t>::RFind(const self_type&, int32_t,
|
|
int32_t) const;
|
|
|
|
template <typename T>
|
|
template <typename Q, typename EnableIfChar16>
|
|
int32_t nsTString<T>::RFind(const char_type* aString, int32_t aOffset,
|
|
int32_t aCount) const {
|
|
return RFind(nsTDependentString<T>(aString), aOffset, aCount);
|
|
}
|
|
|
|
template int32_t nsTString<char16_t>::RFind(const char_type*, int32_t,
|
|
int32_t) const;
|
|
|
|
template <typename T>
|
|
template <typename Q, typename EnableIfChar16>
|
|
int32_t nsTString<T>::FindCharInSet(const char* aSet, int32_t aOffset) const {
|
|
if (aOffset < 0)
|
|
aOffset = 0;
|
|
else if (aOffset >= int32_t(this->mLength))
|
|
return kNotFound;
|
|
|
|
int32_t result =
|
|
::FindCharInSet(this->mData + aOffset, this->mLength - aOffset, aSet);
|
|
if (result != kNotFound) result += aOffset;
|
|
return result;
|
|
}
|
|
|
|
template int32_t nsTString<char16_t>::FindCharInSet(const char*, int32_t) const;
|
|
|
|
template <typename T>
|
|
template <typename Q, typename EnableIfChar16>
|
|
void nsTString<T>::ReplaceChar(const char* aSet, char16_t aNewChar) {
|
|
if (!this->EnsureMutable()) // XXX do this lazily?
|
|
this->AllocFailed(this->mLength);
|
|
|
|
char16_t* data = this->mData;
|
|
uint32_t lenRemaining = this->mLength;
|
|
|
|
while (lenRemaining) {
|
|
int32_t i = ::FindCharInSet(data, lenRemaining, aSet);
|
|
if (i == kNotFound) break;
|
|
|
|
data[i++] = aNewChar;
|
|
data += i;
|
|
lenRemaining -= i;
|
|
}
|
|
}
|
|
|
|
namespace mozilla {
|
|
namespace detail {
|
|
|
|
template <typename T>
|
|
template <typename Q, typename EnableIfChar>
|
|
int32_t nsTStringRepr<T>::Compare(const char_type* aString, bool aIgnoreCase,
|
|
size_type aCount) const {
|
|
size_t strLen = char_traits::length(aString);
|
|
|
|
size_t minLen = XPCOM_MIN(this->Length(), strLen);
|
|
|
|
// NOTE: As `minLen <= this->Length()` this value must fit in a `uint32_t`.
|
|
uint32_t compareCount =
|
|
ReleaseAssertedCast<uint32_t>(XPCOM_MIN(minLen, aCount));
|
|
|
|
int32_t result = nsBufferRoutines<T>::compare(this->mData, aString,
|
|
compareCount, aIgnoreCase);
|
|
|
|
if (result == 0 && minLen < aCount && this->Length() != strLen) {
|
|
// Since the caller didn't give us a length to test, or strings shorter
|
|
// than aCount, and compareCount characters matched, we have to assume
|
|
// that the longer string is greater.
|
|
|
|
return (this->Length() < strLen) ? -1 : 1;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
template int32_t nsTStringRepr<char>::Compare(const char_type*, bool,
|
|
size_type) const;
|
|
|
|
template <typename T>
|
|
template <typename Q, typename EnableIfChar16>
|
|
bool nsTStringRepr<T>::EqualsIgnoreCase(const incompatible_char_type* aString,
|
|
size_type aCount) const {
|
|
size_t strLen = nsCharTraits<char>::length(aString);
|
|
|
|
size_t minLen = XPCOM_MIN(this->Length(), strLen);
|
|
|
|
// NOTE: As `minLen <= this->Length()` this value must fit in a `uint32_t`.
|
|
uint32_t compareCount =
|
|
ReleaseAssertedCast<uint32_t>(XPCOM_MIN(minLen, aCount));
|
|
|
|
int32_t result =
|
|
nsBufferRoutines<T>::compare(this->mData, aString, compareCount, true);
|
|
|
|
if (result == 0 && minLen < aCount && this->Length() != strLen) {
|
|
// Since the caller didn't give us a length to test, or strings shorter
|
|
// than aCount, and compareCount characters matched, we have to assume
|
|
// that the longer string is greater.
|
|
return false;
|
|
}
|
|
return result == 0;
|
|
}
|
|
|
|
template bool nsTStringRepr<char16_t>::EqualsIgnoreCase(
|
|
const incompatible_char_type*, size_type) const;
|
|
|
|
} // namespace detail
|
|
} // namespace mozilla
|
|
|
|
/**
|
|
* nsTString::ToDouble
|
|
*/
|
|
|
|
template <>
|
|
double nsTString<char>::ToDouble(TrailingCharsPolicy aTrailingCharsPolicy,
|
|
nsresult* aErrorCode) const {
|
|
double res = 0.0;
|
|
if (this->mLength > 0) {
|
|
char* conv_stopped;
|
|
const char* str = this->mData;
|
|
// Use PR_strtod, not strtod, since we don't want locale involved.
|
|
res = PR_strtod(str, &conv_stopped);
|
|
if (aTrailingCharsPolicy == TrailingCharsPolicy::Allow &&
|
|
conv_stopped != str) {
|
|
*aErrorCode = NS_OK;
|
|
} else if (aTrailingCharsPolicy == TrailingCharsPolicy::Disallow &&
|
|
conv_stopped == str + this->mLength) {
|
|
*aErrorCode = NS_OK;
|
|
} else {
|
|
*aErrorCode = NS_ERROR_ILLEGAL_VALUE;
|
|
}
|
|
} else {
|
|
// The string was too short (0 characters)
|
|
*aErrorCode = NS_ERROR_ILLEGAL_VALUE;
|
|
}
|
|
return res;
|
|
}
|
|
|
|
template <>
|
|
double nsTString<char>::ToDouble(nsresult* aErrorCode) const {
|
|
return ToDouble(TrailingCharsPolicy::Disallow, aErrorCode);
|
|
}
|
|
|
|
template <>
|
|
double nsTString<char16_t>::ToDouble(nsresult* aErrorCode) const {
|
|
return NS_LossyConvertUTF16toASCII(*this).ToDouble(aErrorCode);
|
|
}
|
|
|
|
template <typename T>
|
|
float nsTString<T>::ToFloat(nsresult* aErrorCode) const {
|
|
return (float)ToDouble(aErrorCode);
|
|
}
|
|
|
|
template <>
|
|
double nsTString<char>::ToDoubleAllowTrailingChars(nsresult* aErrorCode) const {
|
|
return ToDouble(TrailingCharsPolicy::Allow, aErrorCode);
|
|
}
|
|
|
|
template <>
|
|
double nsTString<char16_t>::ToDoubleAllowTrailingChars(
|
|
nsresult* aErrorCode) const {
|
|
return NS_LossyConvertUTF16toASCII(*this).ToDoubleAllowTrailingChars(
|
|
aErrorCode);
|
|
}
|
|
|
|
template <typename T>
|
|
float nsTString<T>::ToFloatAllowTrailingChars(nsresult* aErrorCode) const {
|
|
return (float)ToDoubleAllowTrailingChars(aErrorCode);
|
|
}
|
|
|
|
template class nsTString<char>;
|
|
template class nsTString<char16_t>;
|