/* vim:set ts=2 sw=2 et cindent: */
/* 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 "nscore.h"
#include "nsCRTGlue.h"
#include "prprf.h"
#include "nsStringAPI.h"
#include "nsXPCOMStrings.h"
#include "nsDebug.h"

#include <stdio.h>

#ifdef XP_WIN
#define snprintf _snprintf
#endif

// nsAString

PRUint32
nsAString::BeginReading(const char_type **begin, const char_type **end) const
{
  PRUint32 len = NS_StringGetData(*this, begin);
  if (end)
    *end = *begin + len;

  return len;
}

const nsAString::char_type*
nsAString::BeginReading() const
{
  const char_type *data;
  NS_StringGetData(*this, &data);
  return data;
}

const nsAString::char_type*
nsAString::EndReading() const
{
  const char_type *data;
  PRUint32 len = NS_StringGetData(*this, &data);
  return data + len;
}

PRUint32
nsAString::BeginWriting(char_type **begin, char_type **end, PRUint32 newSize)
{
  PRUint32 len = NS_StringGetMutableData(*this, newSize, begin);
  if (end)
    *end = *begin + len;

  return len;
}

nsAString::char_type*
nsAString::BeginWriting(PRUint32 aLen)
{
  char_type *data;
  NS_StringGetMutableData(*this, aLen, &data);
  return data;
}

nsAString::char_type*
nsAString::EndWriting()
{
  char_type *data;
  PRUint32 len = NS_StringGetMutableData(*this, PR_UINT32_MAX, &data);
  return data + len;
}

bool
nsAString::SetLength(PRUint32 aLen)
{
  char_type *data;
  NS_StringGetMutableData(*this, aLen, &data);
  return data != nsnull;
}

void
nsAString::AssignLiteral(const char *aStr)
{
  PRUint32 len = strlen(aStr);
  PRUnichar *buf = BeginWriting(len);
  if (!buf)
    return;

  for (; *aStr; ++aStr, ++buf)
    *buf = *aStr;
}

void
nsAString::AppendLiteral(const char *aASCIIStr)
{
  PRUint32 appendLen = strlen(aASCIIStr);

  PRUint32 thisLen = Length();
  PRUnichar *begin, *end;
  BeginWriting(&begin, &end, appendLen + thisLen);
  if (!begin)
    return;

  for (begin += thisLen; begin < end; ++begin, ++aASCIIStr)
    *begin = *aASCIIStr;
}

void
nsAString::StripChars(const char *aSet)
{
  nsString copy(*this);

  const char_type *source, *sourceEnd;
  copy.BeginReading(&source, &sourceEnd);

  char_type *dest;
  BeginWriting(&dest);
  if (!dest)
    return;

  char_type *curDest = dest;

  for (; source < sourceEnd; ++source) {
    const char *test;
    for (test = aSet; *test; ++test) {
      if (*source == char_type(*test))
        break;
    }

    if (!*test) {
      // not stripped, copy this char
      *curDest = *source;
      ++curDest;
    }
  }

  SetLength(curDest - dest);
}

void
nsAString::Trim(const char *aSet, bool aLeading, bool aTrailing)
{
  NS_ASSERTION(aLeading || aTrailing, "Ineffective Trim");

  const PRUnichar *start, *end;
  PRUint32 cutLen;

  if (aLeading) {
    BeginReading(&start, &end);
    for (cutLen = 0; start < end; ++start, ++cutLen) {
      const char *test;
      for (test = aSet; *test; ++test) {
        if (*test == *start)
          break;
      }
      if (!*test)
        break;
    }
    if (cutLen) {
      NS_StringCutData(*this, 0, cutLen);
    }
  }
  if (aTrailing) {
    PRUint32 len = BeginReading(&start, &end);
    --end;
    for (cutLen = 0; end >= start; --end, ++cutLen) {
      const char *test;
      for (test = aSet; *test; ++test) {
        if (*test == *end)
          break;
      }
      if (!*test)
        break;
    }
    if (cutLen) {
      NS_StringCutData(*this, len - cutLen, cutLen);
    }
  }
}

PRInt32
nsAString::DefaultComparator(const char_type *a, const char_type *b,
                             PRUint32 len)
{
  for (const char_type *end = a + len; a < end; ++a, ++b) {
    if (*a == *b)
      continue;

    return *a < *b ? -1 : 1;
  }

  return 0;
}

PRInt32
nsAString::Compare(const char_type *other, ComparatorFunc c) const
{
  const char_type *cself;
  PRUint32 selflen = NS_StringGetData(*this, &cself);
  PRUint32 otherlen = NS_strlen(other);
  PRUint32 comparelen = selflen <= otherlen ? selflen : otherlen;

  PRInt32 result = c(cself, other, comparelen);
  if (result == 0) {
    if (selflen < otherlen)
      return -1;
    else if (selflen > otherlen)
      return 1;
  }
  return result;
}

PRInt32
nsAString::Compare(const self_type &other, ComparatorFunc c) const
{
  const char_type *cself, *cother;
  PRUint32 selflen = NS_StringGetData(*this, &cself);
  PRUint32 otherlen = NS_StringGetData(other, &cother);
  PRUint32 comparelen = selflen <= otherlen ? selflen : otherlen;

  PRInt32 result = c(cself, cother, comparelen);
  if (result == 0) {
    if (selflen < otherlen)
      return -1;
    else if (selflen > otherlen)
      return 1;
  }
  return result;
}

bool
nsAString::Equals(const char_type *other, ComparatorFunc c) const
{
  const char_type *cself;
  PRUint32 selflen = NS_StringGetData(*this, &cself);
  PRUint32 otherlen = NS_strlen(other);

  if (selflen != otherlen)
    return false;

  return c(cself, other, selflen) == 0;
}

bool
nsAString::Equals(const self_type &other, ComparatorFunc c) const
{
  const char_type *cself;
  const char_type *cother;
  PRUint32 selflen = NS_StringGetData(*this, &cself);
  PRUint32 otherlen = NS_StringGetData(other, &cother);

  if (selflen != otherlen)
    return false;

  return c(cself, cother, selflen) == 0;
}

bool
nsAString::EqualsLiteral(const char *aASCIIString) const
{
  const PRUnichar *begin, *end;
  BeginReading(&begin, &end);

  for (; begin < end; ++begin, ++aASCIIString) {
    if (!*aASCIIString || !NS_IsAscii(*begin) ||
        (char) *begin != *aASCIIString) {
      return false;
    }
  }

  return *aASCIIString == nsnull;
}

bool
nsAString::LowerCaseEqualsLiteral(const char *aASCIIString) const
{
  const PRUnichar *begin, *end;
  BeginReading(&begin, &end);

  for (; begin < end; ++begin, ++aASCIIString) {
    if (!*aASCIIString || !NS_IsAscii(*begin) ||
        NS_ToLower((char) *begin) != *aASCIIString) {
      return false;
    }
  }

  return *aASCIIString == nsnull;
}

PRInt32
nsAString::Find(const self_type& aStr, PRUint32 aOffset,
                ComparatorFunc c) const
{
  const char_type *begin, *end;
  PRUint32 selflen = BeginReading(&begin, &end);

  if (aOffset > selflen)
    return -1;

  const char_type *other;
  PRUint32 otherlen = aStr.BeginReading(&other);

  if (otherlen > selflen - aOffset)
    return -1;

  // We want to stop searching otherlen characters before the end of the string
  end -= otherlen;

  for (const char_type *cur = begin + aOffset; cur <= end; ++cur) {
    if (!c(cur, other, otherlen))
      return cur - begin;
  }
  return -1;
}

static bool ns_strnmatch(const PRUnichar *aStr, const char* aSubstring,
                           PRUint32 aLen)
{
  for (; aLen; ++aStr, ++aSubstring, --aLen) {
    if (!NS_IsAscii(*aStr))
      return false;

    if ((char) *aStr != *aSubstring)
      return false;
  }

  return true;
}

static bool ns_strnimatch(const PRUnichar *aStr, const char* aSubstring,
                            PRUint32 aLen)
{
  for (; aLen; ++aStr, ++aSubstring, --aLen) {
    if (!NS_IsAscii(*aStr))
      return false;

    if (NS_ToLower((char) *aStr) != NS_ToLower(*aSubstring))
      return false;
  }

  return true;
}

PRInt32
nsAString::Find(const char *aStr, PRUint32 aOffset, bool aIgnoreCase) const
{
  bool (*match)(const PRUnichar*, const char*, PRUint32) =
    aIgnoreCase ? ns_strnimatch : ns_strnmatch;

  const char_type *begin, *end;
  PRUint32 selflen = BeginReading(&begin, &end);

  if (aOffset > selflen)
    return -1;

  PRUint32 otherlen = strlen(aStr);

  if (otherlen > selflen - aOffset)
    return -1;

  // We want to stop searching otherlen characters before the end of the string
  end -= otherlen;

  for (const char_type *cur = begin + aOffset; cur <= end; ++cur) {
    if (match(cur, aStr, otherlen)) {
      return cur - begin;
    }
  }
  return -1;
}

PRInt32
nsAString::RFind(const self_type& aStr, PRInt32 aOffset, ComparatorFunc c) const
{
  const char_type *begin, *end;
  PRUint32 selflen = BeginReading(&begin, &end);

  const char_type *other;
  PRUint32 otherlen = aStr.BeginReading(&other);

  if (selflen < otherlen)
    return -1;

  if (aOffset < 0 || PRUint32(aOffset) > (selflen - otherlen))
    end -= otherlen;
  else
    end = begin + aOffset;

  for (const char_type *cur = end; cur >= begin; --cur) {
    if (!c(cur, other, otherlen))
      return cur - begin;
  }
  return -1;
}

PRInt32
nsAString::RFind(const char *aStr, PRInt32 aOffset, bool aIgnoreCase) const
{
  bool (*match)(const PRUnichar*, const char*, PRUint32) =
    aIgnoreCase ? ns_strnimatch : ns_strnmatch;

  const char_type *begin, *end;
  PRUint32 selflen = BeginReading(&begin, &end);
  PRUint32 otherlen = strlen(aStr);
  
  if (selflen < otherlen)
    return -1;

  if (aOffset < 0 || PRUint32(aOffset) > (selflen - otherlen))
    end -= otherlen;
  else
    end = begin + aOffset;

  for (const char_type *cur = end; cur >= begin; --cur) {
    if (match(cur, aStr, otherlen)) {
      return cur - begin;
    }
  }
  return -1;
}

PRInt32
nsAString::FindChar(char_type aChar, PRUint32 aOffset) const
{
  const char_type *start, *end;
  PRUint32 len = BeginReading(&start, &end);
  if (aOffset > len)
    return -1;

  const char_type *cur;

  for (cur = start + aOffset; cur < end; ++cur) {
    if (*cur == aChar)
      return cur - start;
  }

  return -1;
}

PRInt32
nsAString::RFindChar(char_type aChar) const
{
  const PRUnichar *start, *end;
  BeginReading(&start, &end);

  do {
    --end;

    if (*end == aChar)
      return end - start;

  } while (end >= start);

  return -1;
}

void
nsAString::AppendInt(int aInt, PRInt32 aRadix)
{
  const char *fmt;
  switch (aRadix) {
  case 8:
    fmt = "%o";
    break;

  case 10:
    fmt = "%d";
    break;

  case 16:
    fmt = "%x";
    break;

  default:
    NS_ERROR("Unrecognized radix");
    fmt = "";
  };

  char buf[20];
  int len = snprintf(buf, sizeof(buf), fmt, aInt);
  buf[sizeof(buf) - 1] = '\0';

  Append(NS_ConvertASCIItoUTF16(buf, len));
}

// Strings

#ifndef XPCOM_GLUE_AVOID_NSPR
PRInt32
nsAString::ToInteger(nsresult *aErrorCode, PRUint32 aRadix) const
{
  NS_ConvertUTF16toUTF8 narrow(*this);

  const char *fmt;
  switch (aRadix) {
  case 10:
    fmt = "%i";
    break;

  case 16:
    fmt = "%x";
    break;

  default:
    NS_ERROR("Unrecognized radix!");
    *aErrorCode = NS_ERROR_INVALID_ARG;
    return 0;
  }

  PRInt32 result = 0;
  if (PR_sscanf(narrow.get(), fmt, &result) == 1)
    *aErrorCode = NS_OK;
  else
    *aErrorCode = NS_ERROR_FAILURE;

  return result;
}
#endif // XPCOM_GLUE_AVOID_NSPR

// nsACString

PRUint32
nsACString::BeginReading(const char_type **begin, const char_type **end) const
{
  PRUint32 len = NS_CStringGetData(*this, begin);
  if (end)
    *end = *begin + len;

  return len;
}

const nsACString::char_type*
nsACString::BeginReading() const
{
  const char_type *data;
  NS_CStringGetData(*this, &data);
  return data;
}

const nsACString::char_type*
nsACString::EndReading() const
{
  const char_type *data;
  PRUint32 len = NS_CStringGetData(*this, &data);
  return data + len;
}

PRUint32
nsACString::BeginWriting(char_type **begin, char_type **end, PRUint32 newSize)
{
  PRUint32 len = NS_CStringGetMutableData(*this, newSize, begin);
  if (end)
    *end = *begin + len;

  return len;
}

nsACString::char_type*
nsACString::BeginWriting(PRUint32 aLen)
{
  char_type *data;
  NS_CStringGetMutableData(*this, aLen, &data);
  return data;
}

nsACString::char_type*
nsACString::EndWriting()
{
  char_type *data;
  PRUint32 len = NS_CStringGetMutableData(*this, PR_UINT32_MAX, &data);
  return data + len;
}

bool
nsACString::SetLength(PRUint32 aLen)
{
  char_type *data;
  NS_CStringGetMutableData(*this, aLen, &data);
  return data != nsnull;
}

void
nsACString::StripChars(const char *aSet)
{
  nsCString copy(*this);

  const char_type *source, *sourceEnd;
  copy.BeginReading(&source, &sourceEnd);

  char_type *dest;
  BeginWriting(&dest);
  if (!dest)
    return;

  char_type *curDest = dest;

  for (; source < sourceEnd; ++source) {
    const char *test;
    for (test = aSet; *test; ++test) {
      if (*source == char_type(*test))
        break;
    }

    if (!*test) {
      // not stripped, copy this char
      *curDest = *source;
      ++curDest;
    }
  }

  SetLength(curDest - dest);
}

void
nsACString::Trim(const char *aSet, bool aLeading, bool aTrailing)
{
  NS_ASSERTION(aLeading || aTrailing, "Ineffective Trim");

  const char *start, *end;
  PRUint32 cutLen;

  if (aLeading) {
    BeginReading(&start, &end);
    for (cutLen = 0; start < end; ++start, ++cutLen) {
      const char *test;
      for (test = aSet; *test; ++test) {
        if (*test == *start)
          break;
      }
      if (!*test)
        break;
    }
    if (cutLen) {
      NS_CStringCutData(*this, 0, cutLen);
    }
  }
  if (aTrailing) {
    PRUint32 len = BeginReading(&start, &end);
    --end;
    for (cutLen = 0; end >= start; --end, ++cutLen) {
      const char *test;
      for (test = aSet; *test; ++test) {
        if (*test == *end)
          break;
      }
      if (!*test)
        break;
    }
    if (cutLen) {
      NS_CStringCutData(*this, len - cutLen, cutLen);
    }
  }
}

PRInt32
nsACString::DefaultComparator(const char_type *a, const char_type *b,
                              PRUint32 len)
{
  return memcmp(a, b, len);
}

PRInt32
nsACString::Compare(const char_type *other, ComparatorFunc c) const
{
  const char_type *cself;
  PRUint32 selflen = NS_CStringGetData(*this, &cself);
  PRUint32 otherlen = strlen(other);
  PRUint32 comparelen = selflen <= otherlen ? selflen : otherlen;

  PRInt32 result = c(cself, other, comparelen);
  if (result == 0) {
    if (selflen < otherlen)
      return -1;
    else if (selflen > otherlen)
      return 1;
  }
  return result;
}

PRInt32
nsACString::Compare(const self_type &other, ComparatorFunc c) const
{
  const char_type *cself, *cother;
  PRUint32 selflen = NS_CStringGetData(*this, &cself);
  PRUint32 otherlen = NS_CStringGetData(other, &cother);
  PRUint32 comparelen = selflen <= otherlen ? selflen : otherlen;

  PRInt32 result = c(cself, cother, comparelen);
  if (result == 0) {
    if (selflen < otherlen)
      return -1;
    else if (selflen > otherlen)
      return 1;
  }
  return result;
}

bool
nsACString::Equals(const char_type *other, ComparatorFunc c) const
{
  const char_type *cself;
  PRUint32 selflen = NS_CStringGetData(*this, &cself);
  PRUint32 otherlen = strlen(other);

  if (selflen != otherlen)
    return false;

  return c(cself, other, selflen) == 0;
}

bool
nsACString::Equals(const self_type &other, ComparatorFunc c) const
{
  const char_type *cself;
  const char_type *cother;
  PRUint32 selflen = NS_CStringGetData(*this, &cself);
  PRUint32 otherlen = NS_CStringGetData(other, &cother);

  if (selflen != otherlen)
    return false;

  return c(cself, cother, selflen) == 0;
}

PRInt32
nsACString::Find(const self_type& aStr, PRUint32 aOffset,
                 ComparatorFunc c) const
{
  const char_type *begin, *end;
  PRUint32 selflen = BeginReading(&begin, &end);

  if (aOffset > selflen)
    return -1;

  const char_type *other;
  PRUint32 otherlen = aStr.BeginReading(&other);

  if (otherlen > selflen - aOffset)
    return -1;

  // We want to stop searching otherlen characters before the end of the string
  end -= otherlen;

  for (const char_type *cur = begin + aOffset; cur <= end; ++cur) {
    if (!c(cur, other, otherlen))
      return cur - begin;
  }
  return -1;
}

PRInt32
nsACString::Find(const char_type *aStr, ComparatorFunc c) const
{
  return Find(aStr, strlen(aStr), c);
}

PRInt32
nsACString::Find(const char_type *aStr, PRUint32 aLen, ComparatorFunc c) const
{
  const char_type *begin, *end;
  PRUint32 selflen = BeginReading(&begin, &end);

  if (aLen == 0) {
    NS_WARNING("Searching for zero-length string.");
    return -1;
  }

   if (aLen > selflen)
    return -1;

  // We want to stop searching otherlen characters before the end of the string
  end -= aLen;

  for (const char_type *cur = begin; cur <= end; ++cur) {
    if (!c(cur, aStr, aLen))
      return cur - begin;
  }
  return -1;
}

PRInt32 
nsACString::RFind(const self_type& aStr, PRInt32 aOffset, ComparatorFunc c) const
{
  const char_type *begin, *end;
  PRUint32 selflen = BeginReading(&begin, &end);

  const char_type *other;
  PRUint32 otherlen = aStr.BeginReading(&other);

  if (selflen < otherlen)
    return -1;

  if (aOffset < 0 || PRUint32(aOffset) > (selflen - otherlen))
    end -= otherlen;
  else
    end = begin + aOffset;

  for (const char_type *cur = end; cur >= begin; --cur) {
    if (!c(cur, other, otherlen))
      return cur - begin;
  }
  return -1;
}

PRInt32 
nsACString::RFind(const char_type *aStr, ComparatorFunc c) const
{
  return RFind(aStr, strlen(aStr), c);
}

PRInt32 
nsACString::RFind(const char_type *aStr, PRInt32 aLen, ComparatorFunc c) const
{
  const char_type *begin, *end;
  PRUint32 selflen = BeginReading(&begin, &end);

  if (aLen <= 0) {
    NS_WARNING("Searching for zero-length string.");
    return -1;
  }

  if (PRUint32(aLen) > selflen)
    return -1;

  // We want to start searching otherlen characters before the end of the string
  end -= aLen;

  for (const char_type *cur = end; cur >= begin; --cur) {
    if (!c(cur, aStr, aLen))
      return cur - begin;
  }
  return -1;
}

PRInt32
nsACString::FindChar(char_type aChar, PRUint32 aOffset) const
{
  const char_type *start, *end;
  PRUint32 len = BeginReading(&start, &end);
  if (aOffset > len)
    return -1;

  const char_type *cur;

  for (cur = start + aOffset; cur < end; ++cur) {
    if (*cur == aChar)
      return cur - start;
  }

  return -1;
}

PRInt32
nsACString::RFindChar(char_type aChar) const
{
  const char *start, *end;
  BeginReading(&start, &end);

  for (; end >= start; --end) {
    if (*end == aChar)
      return end - start;
  }

  return -1;
}

void
nsACString::AppendInt(int aInt, PRInt32 aRadix)
{
  const char *fmt;
  switch (aRadix) {
  case 8:
    fmt = "%o";
    break;

  case 10:
    fmt = "%d";
    break;

  case 16:
    fmt = "%x";
    break;

  default:
    NS_ERROR("Unrecognized radix");
    fmt = "";
  };

  char buf[20];
  int len = snprintf(buf, sizeof(buf), fmt, aInt);
  buf[sizeof(buf) - 1] = '\0';

  Append(buf, len);
}

#ifndef XPCOM_GLUE_AVOID_NSPR
PRInt32
nsACString::ToInteger(nsresult *aErrorCode, PRUint32 aRadix) const
{
  const char *fmt;
  switch (aRadix) {
  case 10:
    fmt = "%i";
    break;

  case 16:
    fmt = "%x";
    break;

  default:
    NS_ERROR("Unrecognized radix!");
    *aErrorCode = NS_ERROR_INVALID_ARG;
    return 0;
  }

  PRInt32 result = 0;
  if (PR_sscanf(nsCString(*this).get(), fmt, &result) == 1)
    *aErrorCode = NS_OK;
  else
    *aErrorCode = NS_ERROR_FAILURE;

  return result;
}
#endif // XPCOM_GLUE_AVOID_NSPR

// Substrings

nsDependentSubstring::nsDependentSubstring(const abstract_string_type& aStr,
                                           PRUint32 aStartPos)
{
  const PRUnichar* data;
  PRUint32 len = NS_StringGetData(aStr, &data);

  if (aStartPos > len)
    aStartPos = len;

  NS_StringContainerInit2(*this, data + aStartPos, len - aStartPos,
                          NS_STRING_CONTAINER_INIT_DEPEND |
                          NS_STRING_CONTAINER_INIT_SUBSTRING);
}

nsDependentSubstring::nsDependentSubstring(const abstract_string_type& aStr,
                                           PRUint32 aStartPos,
                                           PRUint32 aLength)
{
  const PRUnichar* data;
  PRUint32 len = NS_StringGetData(aStr, &data);

  if (aStartPos > len)
    aStartPos = len;

  if (aStartPos + aLength > len)
    aLength = len - aStartPos;

  NS_StringContainerInit2(*this, data + aStartPos, aLength,
                          NS_STRING_CONTAINER_INIT_DEPEND |
                            NS_STRING_CONTAINER_INIT_SUBSTRING);
}

nsDependentCSubstring::nsDependentCSubstring(const abstract_string_type& aStr,
                                             PRUint32 aStartPos)
{
  const char* data;
  PRUint32 len = NS_CStringGetData(aStr, &data);

  if (aStartPos > len)
    aStartPos = len;

  NS_CStringContainerInit2(*this, data + aStartPos, len - aStartPos,
                           NS_CSTRING_CONTAINER_INIT_DEPEND |
                           NS_CSTRING_CONTAINER_INIT_SUBSTRING);
}

nsDependentCSubstring::nsDependentCSubstring(const abstract_string_type& aStr,
                                             PRUint32 aStartPos,
                                             PRUint32 aLength)
{
  const char* data;
  PRUint32 len = NS_CStringGetData(aStr, &data);

  if (aStartPos > len)
    aStartPos = len;

  if (aStartPos + aLength > len)
    aLength = len - aStartPos;

  NS_CStringContainerInit2(*this, data + aStartPos, aLength,
                           NS_CSTRING_CONTAINER_INIT_DEPEND |
                           NS_CSTRING_CONTAINER_INIT_SUBSTRING);
}

// Utils

char*
ToNewUTF8String(const nsAString& aSource)
{
  nsCString temp;
  CopyUTF16toUTF8(aSource, temp);
  return NS_CStringCloneData(temp);
}

void
CompressWhitespace(nsAString& aString)
{
  PRUnichar *start;
  PRUint32 len = NS_StringGetMutableData(aString, PR_UINT32_MAX, &start);
  PRUnichar *end = start + len;
  PRUnichar *from = start, *to = start;

  // Skip any leading whitespace
  while (from < end && NS_IsAsciiWhitespace(*from))
    from++;

  while (from < end) {
    PRUnichar theChar = *from++;

    if (NS_IsAsciiWhitespace(theChar)) {
      // We found a whitespace char, so skip over any more 
      while (from < end && NS_IsAsciiWhitespace(*from))
        from++;  

      // Turn all whitespace into spaces
      theChar = ' ';
    }

    *to++ = theChar;
  }

  // Drop any trailing space
  if (to > start && to[-1] == ' ')
    to--;

  // Re-terminate the string
  *to = '\0';

  // Set the new length
  aString.SetLength(to - start);
}

PRUint32
ToLowerCase(nsACString& aStr)
{
  char *begin, *end;
  PRUint32 len = aStr.BeginWriting(&begin, &end);

  for (; begin < end; ++begin) {
    *begin = NS_ToLower(*begin);
  }

  return len;
}

PRUint32
ToUpperCase(nsACString& aStr)
{
  char *begin, *end;
  PRUint32 len = aStr.BeginWriting(&begin, &end);

  for (; begin < end; ++begin) {
    *begin = NS_ToUpper(*begin);
  }

  return len;
}

PRUint32
ToLowerCase(const nsACString& aSrc, nsACString& aDest)
{
  const char *begin,  *end;
  PRUint32 len = aSrc.BeginReading(&begin, &end);

  char *dest;
  NS_CStringGetMutableData(aDest, len, &dest);

  for (; begin < end; ++begin, ++dest) {
    *dest = NS_ToLower(*begin);
  }

  return len;
}

PRUint32
ToUpperCase(const nsACString& aSrc, nsACString& aDest)
{
  const char *begin,  *end;
  PRUint32 len = aSrc.BeginReading(&begin, &end);

  char *dest;
  NS_CStringGetMutableData(aDest, len, &dest);

  for (; begin < end; ++begin, ++dest) {
    *dest = NS_ToUpper(*begin);
  }

  return len;
}

PRInt32
CaseInsensitiveCompare(const char *a, const char *b,
                       PRUint32 len)
{
  for (const char *aend = a + len; a < aend; ++a, ++b) {
    char la = NS_ToLower(*a);
    char lb = NS_ToLower(*b);

    if (la == lb)
      continue;

    return la < lb ? -1 : 1;
  }

  return 0;
}

bool
ParseString(const nsACString& aSource, char aDelimiter, 
            nsTArray<nsCString>& aArray)
{
  PRInt32 start = 0;
  PRInt32 end = aSource.Length();

  PRUint32 oldLength = aArray.Length();

  for (;;) {
    PRInt32 delimiter = aSource.FindChar(aDelimiter, start);
    if (delimiter < 0) {
      delimiter = end;
    }

    if (delimiter != start) {
      if (!aArray.AppendElement(Substring(aSource, start, delimiter - start))) {
        aArray.RemoveElementsAt(oldLength, aArray.Length() - oldLength);
        return false;
      }
    }

    if (delimiter == end)
      break;
    start = ++delimiter;
    if (start == end)
      break;
  }

  return true;
}
