// Member templates for the -*- C++ -*- string classes.
// Copyright (C) 1994 Free Software Foundation

// This file is part of the GNU ANSI C++ Library.  This library is free
// software; you can redistribute it and/or modify it under the
// terms of the GNU General Public License as published by the
// Free Software Foundation; either version 2, or (at your option)
// any later version.

// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with GNU CC; see the file COPYING.  If not, write to
// the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.

// As a special exception, if you link this library with files
// compiled with a GNU compiler to produce an executable, this does not cause
// the resulting executable to be covered by the GNU General Public License.
// This exception does not however invalidate any other reasons why
// the executable file might be covered by the GNU General Public License.

// Written by Jason Merrill based upon the specification by Takanori Adachi
// in ANSI X3J16/94-0013R2.

#include <stddef>
#include <bastring.hI>

template <class charT, class traits>
inline void * __bsrep <charT, traits>::
operator new (size_t s, size_t extra)
{
  return ::operator new (s + extra);
}

template <class charT, class traits>
inline size_t __bsrep <charT, traits>::
#ifdef _G_ALLOC_CONTROL
default_frob (size_t s)
#else
frob_size (size_t s)
#endif
{
  size_t i = 16;
  while (i < s) i *= 2;
  return i;
}

template <class charT, class traits>
inline __bsrep <charT, traits> * __bsrep <charT, traits>::
create (size_t extra)
{
  extra = frob_size (extra);
  Rep *p = new (extra) Rep;
  p->res = extra;
  p->ref = 1;
  return p;
}

template <class charT, class traits>
inline bool __bsrep <charT, traits>::
#ifdef _G_ALLOC_CONTROL
default_excess (size_t s, size_t r)
#else
excess_slop (size_t s, size_t r)
#endif
{
  return 2 * (s <= 16 ? 16 : s) < r;
}

template <class charT, class traits>
inline bool basic_string <charT, traits>::
check_realloc (size_t s)
{
  return (rep->ref > 1
	  || s > reserve ()
	  || Rep::excess_slop (s, reserve ()));
}

template <class charT, class traits>
void basic_string <charT, traits>::
alloc (size_t size, bool save)
{
  if (! check_realloc (size))
    return;

  Rep *p = Rep::create (size);

  if (save)
    {
      p->copy (0, data (), length ());
      p->len = length ();
    }
  else
    p->len = 0;

  rep->release ();
  rep = p;
}

template <class charT, class traits>
basic_string <charT, traits>::
basic_string (size_t size, capacity cap)
{
  switch (cap)
    {
    default_size:
      assign (eos (), size);
      break;
    reserve:
      rep = nilRep.grab ();
      reserve (size);
      break;
    default:
      ;
    }
}
      
template <class charT, class traits>
basic_string <charT, traits>& basic_string <charT, traits>::
replace (size_t pos1, size_t n1,
	 const basic_string& str, size_t pos2, size_t n2)
{
  const size_t len2 = str.length ();

  if (pos1 == 0 && n1 >= length () && pos2 == 0 && n2 >= len2)
    return operator= (str);

  OUTOFRANGE (pos2 > len2);

  if (n2 > len2 - pos2)
    n2 = len2 - pos2;

  return replace (pos1, n1, str.data () + pos2, n2);
}

template <class charT, class traits>
inline void __bsrep <charT, traits>::
copy (size_t pos, const charT *s, size_t n)
{
  if (n)
    traits::copy (data () + pos, s, n);
}

template <class charT, class traits>
inline void __bsrep <charT, traits>::
move (size_t pos, const charT *s, size_t n)
{
  if (n)
    traits::move (data () + pos, s, n);
}

template <class charT, class traits>
basic_string <charT, traits>& basic_string <charT, traits>::
replace (size_t pos, size_t n1, const charT* s, size_t n2)
{
  const size_t len = length ();
  OUTOFRANGE (pos > len);
  if (n1 > len - pos)
    n1 = len - pos;
  LENGTHERROR (len - n1 >= NPOS - n2);
  size_t newlen = len - n1 + n2;

  if (check_realloc (newlen))
    {
      Rep *p = Rep::create (newlen);
      p->copy (0, data (), pos);
      p->copy (pos + n2, data () + pos + n1, len - (pos + n1));
      p->copy (pos, s, n2);
      rep->release ();
      rep = p;
    }
  else
    {
      rep->move (pos + n2, data () + pos + n1, len - (pos + n1));
      rep->copy (pos, s, n2);
    }
  rep->len = newlen;

  return *this;
}

template <class charT, class traits>
inline void __bsrep <charT, traits>::
set (size_t pos, const charT c, size_t n)
{
  traits::set  (data () + pos, c, n);
}

template <class charT, class traits>
basic_string <charT, traits>& basic_string <charT, traits>::
replace (size_t pos, size_t n1, charT c, size_t n2)
{
  const size_t len = length ();
  OUTOFRANGE (pos > len);
  if (n1 > len - pos)
    n1 = len - pos;
  LENGTHERROR (len - n1 >= NPOS - n2);
  size_t newlen = len - n1 + n2;

  if (check_realloc (newlen))
    {
      Rep *p = Rep::create (newlen);
      p->copy (0, data (), pos);
      p->copy (pos + n2, data () + pos + n1, len - (pos + n1));
      p->set  (pos, c, n2);
      rep->release ();
      rep = p;
    }
  else
    {
      rep->move (pos + n2, data () + pos + n1, len - (pos + n1));
      rep->set  (pos, c, n2);
    }
  rep->len = newlen;

  return *this;
}

template <class charT, class traits>
void basic_string <charT, traits>::
resize (size_t n, charT c)
{
  LENGTHERROR (n == NPOS);

  if (n > length ())
    append (c, n - length ());
  else
    remove (n);
}

template <class charT, class traits>
size_t basic_string <charT, traits>::
copy (charT* s, size_t n, size_t pos)
{
  OUTOFRANGE (pos > length ());

  if (n > length () - pos)
    n = length () - pos;

  traits::copy (s, data () + pos, n);
  return n;
}

template <class charT, class traits>
size_t basic_string <charT, traits>::
find (const charT* s, size_t pos, size_t n)
{
  size_t xpos = pos;
  for (; xpos + n <= length (); ++xpos)
    if (traits::eq (data () [xpos], *s)
	&& traits::compare (data () + xpos, s, n) == 0)
      return xpos;
  return NPOS;
}

template <class charT, class traits>
inline size_t basic_string <charT, traits>::
_find (const charT* ptr, charT c, size_t xpos, size_t len)
{
  for (; xpos < len; ++xpos)
    if (traits::eq (ptr [xpos], c))
      return xpos;
  return NPOS;
}

template <class charT, class traits>
size_t basic_string <charT, traits>::
find (charT c, size_t pos)
{
  return _find (data (), c, pos, length ());
}

template <class charT, class traits>
size_t basic_string <charT, traits>::
rfind (const charT* s, size_t pos, size_t n)
{
  size_t xpos = length () - n;
  if (xpos > pos)
    xpos = pos;

  for (; xpos >= 0; --xpos)
    if (traits::eq (data () [xpos], *s)
	&& traits::compare (data () + xpos, s, n) == 0)
      return xpos;
  return NPOS;
}

template <class charT, class traits>
size_t basic_string <charT, traits>::
rfind (charT c, size_t pos)
{
  size_t xpos = length () - 1;
  if (xpos > pos)
    xpos = pos;

  for (; xpos >= 0; --xpos)
    if (traits::eq (data () [xpos], c))
      return xpos;
  return NPOS;
}

template <class charT, class traits>
size_t basic_string <charT, traits>::
find_first_of (const charT* s, size_t pos, size_t n)
{
  size_t xpos = pos;
  for (; xpos + n <= length (); ++xpos)
    if (_find (s, data () [xpos], 0, n) != NPOS)
      return xpos;
  return NPOS;
}

template <class charT, class traits>
size_t basic_string <charT, traits>::
find_last_of (const charT* s, size_t pos, size_t n)
{
  size_t xpos = length () - n;
  for (; xpos >= 0; --xpos)
    if (_find (s, data () [xpos], 0, n) != NPOS)
      return xpos;
  return NPOS;
}

template <class charT, class traits>
size_t basic_string <charT, traits>::
find_first_not_of (const charT* s, size_t pos, size_t n)
{
  size_t xpos = pos;
  for (; xpos + n <= length (); ++xpos)
    if (_find (s, data () [xpos], 0, n) == NPOS)
      return xpos;
  return NPOS;
}

template <class charT, class traits>
size_t basic_string <charT, traits>::
find_first_not_of (charT c, size_t pos)
{
  size_t xpos = pos;
  for (; xpos < length (); ++xpos)
    if (traits::ne (data () [xpos], c))
      return xpos;
  return NPOS;
}

template <class charT, class traits>
size_t basic_string <charT, traits>::
find_last_not_of (const charT* s, size_t pos, size_t n)
{
  size_t xpos = length () - n;
  for (; xpos >= 0; --xpos)
    if (_find (s, data () [xpos], 0, n) == NPOS)
      return xpos;
  return NPOS;
}

template <class charT, class traits>
size_t basic_string <charT, traits>::
find_last_not_of (charT c, size_t pos)
{
  size_t xpos = length () - 1;
  for (; xpos >= 0; --xpos)
    if (traits::ne (data () [xpos], c))
      return xpos;
  return NPOS;
}

template <class charT, class traits>
int basic_string <charT, traits>::
compare (const basic_string& str, size_t pos, size_t n) const
{
  OUTOFRANGE (pos > length ());

  size_t rlen = length () - pos;
  if (rlen > n)
    rlen = n;
  if (rlen > str.length ())
    rlen = str.length ();
  int r = traits::compare (data () + pos, str.data (), rlen);
  if (r != 0)
    return r;
  if (rlen == n)
    return 0;
  return (length () - pos) - str.length ();
}

template <class charT, class traits>
int basic_string <charT, traits>::
compare (const charT* s, size_t pos, size_t n) const
{
  OUTOFRANGE (pos > length ());

  size_t rlen = length () - pos;
  if (rlen > n)
    rlen = n;
  int r = traits::compare (data () + pos, s, rlen);
  if (r != 0)
    return r;
  return (length () - pos) - n;
}

template <class charT, class traits>
int basic_string <charT, traits>::
compare (charT c, size_t pos, size_t n) const
{
  OUTOFRANGE (pos > length ());

  size_t end = pos + n;
  if (end > length ())
    end = length ();
  size_t xpos = pos;
  for (; xpos < end; ++xpos)
    if (traits::ne (data () [xpos], c))
      return traits::lt (data () [xpos], c) ? -1 : 1;
  return (length () - pos) - n;
}

#include <iostream.h>

// This function is complex to avoid unnecessary shrinking of the string
// argument.  Does this actually buy us anything?

template <class charT, class traits>
istream &
operator>> (istream &is, basic_string <charT, traits> &s)
{
  int w = is.width (0);
  if (is.ipfx0 ())
    {
      register size_t len = 0;
      const size_t oldalloc = s.reserve ();
      register streambuf *sb = is.rdbuf ();

      s.unique ();

      while (1)
	{
	  if (len == oldalloc)
	    goto next;

	  charT ch = sb->sbumpc ();
	  if (ch == EOF)
	    {
	      is.setstate (ios::eofbit);
	      break;
	    }
	  else if (traits::is_del (ch))
	    {
	      sb->sungetc ();
	      break;
	    }
	  (*s.rep)[len++] = ch;
	  if (--w == 1)
	    break;
	}
      s.resize (len);
      goto done;

    next:
      while (1)
	{
	  charT ch = sb->sbumpc ();
	  if (ch == EOF)
	    {
	      is.setstate (ios::eofbit);
	      break;
	    }
	  else if (traits::is_del (ch))
	    {
	      sb->sungetc ();
	      break;
	    }
	  s += ch;
	  if (--w == 1)
	    break;
	}
    }
 done:
  is.isfx ();
  if (s.length () == 0)
    is.setstate (ios::failbit);

  return is;
}

template <class charT, class traits>
ostream &
operator<< (ostream &o, basic_string <charT, traits> s)
{
  return o.write (s.data (), s.length ());
}
