xpcom/string/nsStringObsolete.cpp
author Mike Hommey <mh+mozilla@glandium.org>
Thu, 30 Oct 2014 13:06:12 +0900
changeset 213063 55d15b285436576e1c5979f753e433c50fd9db3a
parent 191042 39192ab47239c73817b4ec8090f4ef498b2110e2
child 215165 8a4595cc5ec48115e92cfa3493b76f311a7f1e0b
permissions -rw-r--r--
Bug 1077148 part 4 - Add and use new moz.build templates for Gecko programs and libraries. r=gps There are, sadly, many combinations of linkage in use throughout the tree. The main differentiator, though, is between program/libraries related to Gecko or not. Kind of. Some need mozglue, some don't. Some need dependent linkage, some standalone. Anyways, these new templates remove the need to manually define the right dependencies against xpcomglue, nspr, mozalloc and mozglue in most cases. Places that build programs and were resetting MOZ_GLUE_PROGRAM_LDFLAGS or that build libraries and were resetting MOZ_GLUE_LDFLAGS can now just not use those Gecko-specific templates.

/* -*- 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 "nsString.h"


/**
 * nsTString obsolete API support
 */

#if MOZ_STRING_WITH_OBSOLETE_API

#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)
    result=int32_t(PL_strncasecmp(aStr1, aStr2, aCount));
  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 ***** */

static const char* kWhitespace="\b\t\r\n ";

// 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;
}

//-----------------------------------------------------------------------------

// define nsString obsolete methods
#include "string-template-def-unichar.h"
#include "nsTStringObsolete.cpp"
#include "string-template-undef.h"

// define nsCString obsolete methods
#include "string-template-def-char.h"
#include "nsTStringObsolete.cpp"
#include "string-template-undef.h"

//-----------------------------------------------------------------------------

// specialized methods:

int32_t
nsString::Find( const nsAFlatString& aString, int32_t aOffset, int32_t aCount ) const
{
  // this method changes the meaning of aOffset and aCount:
  Find_ComputeSearchRange(mLength, aString.Length(), aOffset, aCount);

  int32_t result = FindSubstring(mData + aOffset, aCount, static_cast<const char16_t*>(aString.get()), aString.Length(), false);
  if (result != kNotFound)
    result += aOffset;
  return result;
}

int32_t
nsString::Find( const char16_t* aString, int32_t aOffset, int32_t aCount ) const
{
  return Find(nsDependentString(aString), aOffset, aCount);
}

int32_t
nsString::RFind( const nsAFlatString& aString, int32_t aOffset, int32_t aCount ) const
{
  // this method changes the meaning of aOffset and aCount:
  RFind_ComputeSearchRange(mLength, aString.Length(), aOffset, aCount);

  int32_t result = RFindSubstring(mData + aOffset, aCount, static_cast<const char16_t*>(aString.get()), aString.Length(), false);
  if (result != kNotFound)
    result += aOffset;
  return result;
}

int32_t
nsString::RFind( const char16_t* aString, int32_t aOffset, int32_t aCount ) const
{
  return RFind(nsDependentString(aString), aOffset, aCount);
}

int32_t
nsString::FindCharInSet( const char16_t* aSet, int32_t aOffset ) const
{
  if (aOffset < 0)
    aOffset = 0;
  else if (aOffset >= int32_t(mLength))
    return kNotFound;

  int32_t result = ::FindCharInSet(mData + aOffset, mLength - aOffset, aSet);
  if (result != kNotFound)
    result += aOffset;
  return result;
}

void
nsString::ReplaceChar( const char16_t* aSet, char16_t aNewChar )
{
  if (!EnsureMutable()) // XXX do this lazily?
    NS_ABORT_OOM(mLength);

  char16_t* data = mData;
  uint32_t lenRemaining = mLength;

  while (lenRemaining)
  {
    int32_t i = ::FindCharInSet(data, lenRemaining, aSet);
    if (i == kNotFound)
      break;

    data[i++] = aNewChar;
    data += i;
    lenRemaining -= i;
  }
}


/**
 * nsTString::Compare,CompareWithConversion,etc.
 */

int32_t
nsCString::Compare( const char* aString, bool aIgnoreCase, int32_t aCount ) const
{
  uint32_t strLen = char_traits::length(aString);

  int32_t maxCount = int32_t(XPCOM_MIN(mLength, strLen));

  int32_t compareCount;
  if (aCount < 0 || aCount > maxCount)
    compareCount = maxCount;
  else
    compareCount = aCount;

  int32_t result =
    nsBufferRoutines<char>::compare(mData, aString, compareCount, aIgnoreCase);

  if (result == 0 &&
      (aCount < 0 || strLen < uint32_t(aCount) || mLength < uint32_t(aCount)))
  {
    // 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.

    if (mLength != strLen)
      result = (mLength < strLen) ? -1 : 1;
  }
  return result;
}

bool
nsString::EqualsIgnoreCase( const char* aString, int32_t aCount ) const
{
  uint32_t strLen = nsCharTraits<char>::length(aString);

  int32_t maxCount = int32_t(XPCOM_MIN(mLength, strLen));

  int32_t compareCount;
  if (aCount < 0 || aCount > maxCount)
    compareCount = maxCount;
  else
    compareCount = aCount;

  int32_t result =
    nsBufferRoutines<char16_t>::compare(mData, aString, compareCount, true);

  if (result == 0 &&
      (aCount < 0 || strLen < uint32_t(aCount) || mLength < uint32_t(aCount)))
  {
    // 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.

    if (mLength != strLen)
      result = 1; // Arbitrarily using any number != 0
  }
  return result == 0;
}


/**
 * nsTString::ToDouble
 */

double
nsCString::ToDouble(nsresult* aErrorCode) const
{
  double res = 0.0;
  if (mLength > 0)
  {
    char *conv_stopped;
    const char *str = mData;
    // Use PR_strtod, not strtod, since we don't want locale involved.
    res = PR_strtod(str, &conv_stopped);
    if (conv_stopped == str+mLength)
      *aErrorCode = NS_OK;
    else // Not all the string was scanned
      *aErrorCode = NS_ERROR_ILLEGAL_VALUE;
  }
  else
  {
    // The string was too short (0 characters)
    *aErrorCode = NS_ERROR_ILLEGAL_VALUE;
  }
  return res;
}

double
nsString::ToDouble(nsresult* aErrorCode) const
{
  return NS_LossyConvertUTF16toASCII(*this).ToDouble(aErrorCode);
}


/**
 * nsTString::AssignWithConversion
 */

void
nsCString::AssignWithConversion( const nsAString& aData )
{
  LossyCopyUTF16toASCII(aData, *this);
}

void
nsString::AssignWithConversion( const nsACString& aData )
{
  CopyASCIItoUTF16(aData, *this);
}

#endif // !MOZ_STRING_WITH_OBSOLETE_API