// -*- C++ -*-

// Testing character type and state type with char_traits and codecvt
// specializations for the C++ library testsuite.
//
// Copyright (C) 2003-2017 Free Software Foundation, Inc.
//
// This file is part of the GNU ISO 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 3, 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 this library; see the file COPYING3.  If not see
// <http://www.gnu.org/licenses/>.
//

#ifndef _GLIBCXX_TESTSUITE_CHARACTER_H
#define _GLIBCXX_TESTSUITE_CHARACTER_H

#include <climits>
#include <string> // for char_traits
#include <locale> // for codecvt
#include <algorithm> // for transform
#include <ext/pod_char_traits.h>

namespace __gnu_test
{
  struct pod_int
  {
    int value;

#if __cplusplus >= 201103L
    // For std::iota.
    pod_int&
    operator++()
    {
      ++value;
      return *this;
    }
#endif
  };

  // For 20.1 requirements for instantiable type: equality comparable
  // and less than comparable.
  inline bool
  operator==(const pod_int& lhs, const pod_int& rhs)
  { return lhs.value == rhs.value; }

  inline bool
  operator<(const pod_int& lhs, const pod_int& rhs)
  { return lhs.value < rhs.value; }

  // For 26 numeric algorithms requirements, need addable,
  // subtractable, multiplicable.
  inline pod_int
  operator+(const pod_int& lhs, const pod_int& rhs)
  {
    pod_int ret = { lhs.value + rhs.value };
    return ret;
  }

  inline pod_int
  operator-(const pod_int& lhs, const pod_int& rhs)
  {
    pod_int ret = { lhs.value - rhs.value };
    return ret;
  }

  inline pod_int
  operator*(const pod_int& lhs, const pod_int& rhs)
  {
    pod_int ret = { lhs.value * rhs.value };
    return ret;
  }

  struct pod_state
  {
    unsigned long value;
  };

  inline bool
  operator==(const pod_state& lhs, const pod_state& rhs)
  { return lhs.value == rhs.value; }

  inline bool
  operator<(const pod_state& lhs, const pod_state& rhs)
  { return lhs.value < rhs.value; }

  // Alternate character types.
  using __gnu_cxx::character;
  typedef character<unsigned char, pod_int, pod_state>  	pod_char;
  typedef character<unsigned char, unsigned int, pod_state>  	pod_uchar;
  typedef character<unsigned short, unsigned int>	   	pod_ushort;
  typedef character<unsigned int, unsigned long>	   	pod_uint;
}

namespace __gnu_cxx
{
  // Specializations.
  // pod_char
  template<>
    template<typename V2>
      inline __gnu_test::pod_char::char_type
      __gnu_test::pod_char::char_type::from(const V2& v)
      {
	char_type ret = { static_cast<value_type>(v.value) };
	return ret;
      }

  template<>
    template<typename V2>
      inline V2
      __gnu_test::pod_char::char_type::to(const char_type& c)
      {
	V2 ret = { c.value };
	return ret;
      }

  template<>
    template<typename V2>
      inline __gnu_test::pod_uchar::char_type
      __gnu_test::pod_uchar::char_type::from(const V2& v)
      {
	char_type ret;
	ret.value = (v >> 5);
	return ret;
      }

  template<>
    template<typename V2>
      inline V2
      __gnu_test::pod_uchar::char_type::to(const char_type& c)
      { return static_cast<V2>(c.value << 5); }
} // namespace __gnu_test

namespace std
{
  // codecvt specialization
  //
  // The conversion performed by the specialization is not supposed to
  // be useful, rather it has been designed to demonstrate the
  // essential features of stateful conversions:
  // * Number and value of bytes for each internal character depends on the
  //   state in addition to the character itself.
  // * Unshift produces an unshift sequence and resets the state. On input
  //   the unshift sequence causes the state to be reset.
  //
  // The conversion for output is as follows:
  // 1. Calculate the value tmp by xor-ing the state and the internal
  //    character
  // 2. Split tmp into either two or three bytes depending on the value of
  //    state. Output those bytes.
  // 3. tmp becomes the new value of state.
  template<>
    class codecvt<__gnu_test::pod_uchar, char, __gnu_test::pod_state>
    : public __codecvt_abstract_base<__gnu_test::pod_uchar, char,
				     __gnu_test::pod_state>
    {
    public:
      typedef codecvt_base::result	result;
      typedef __gnu_test::pod_uchar 	intern_type;
      typedef char 			extern_type;
      typedef __gnu_test::pod_state 	state_type;
      typedef __codecvt_abstract_base<intern_type, extern_type, state_type>
      base_type;

      explicit codecvt(size_t refs = 0) : base_type(refs)
      { }

      static locale::id id;

    protected:
      ~codecvt()
      { }

      virtual result
      do_out(state_type& state, const intern_type* from,
	     const intern_type* from_end, const intern_type*& from_next,
	     extern_type* to, extern_type* to_limit,
	     extern_type*& to_next) const
      {
	while (from < from_end && to < to_limit)
	  {
	    unsigned char tmp = (state.value ^ from->value);
	    if (state.value & 0x8)
	      {
		if (to >= to_limit - 2)
		  break;
		*to++ = (tmp & 0x7);
		*to++ = ((tmp >> 3) & 0x7);
		*to++ = ((tmp >> 6) & 0x3);
	      }
	    else
	      {
		if (to >= to_limit - 1)
		  break;
		*to++ = (tmp & 0xf);
		*to++ = ((tmp >> 4) & 0xf);
	      }
	    state.value = tmp;
	    ++from;
	  }

	from_next = from;
	to_next = to;
	return (from < from_end) ? partial : ok;
      }

      virtual result
      do_in(state_type& state, const extern_type* from,
	    const extern_type* from_end, const extern_type*& from_next,
	    intern_type* to, intern_type* to_limit,
	    intern_type*& to_next) const
      {
	while (from < from_end && to < to_limit)
	  {
	    unsigned char c = *from;
	    if (c & 0xc0)
	      {
		// Unshift sequence
		state.value &= c;
		++from;
		continue;
	      }

	    unsigned char tmp;
	    if (state.value & 0x8)
	      {
		if (from >= from_end - 2)
		  break;
		tmp = (*from++ & 0x7);
		tmp |= ((*from++ << 3) & 0x38);
		tmp |= ((*from++ << 6) & 0xc0);
	      }
	    else
	      {
		if (from >= from_end - 1)
		  break;
		tmp = (*from++ & 0xf);
		tmp |= ((*from++ << 4) & 0xf0);
	      }
	    to->value = (tmp ^ state.value);
	    state.value = tmp;
	    ++to;
	  }

	from_next = from;
	to_next = to;
	return (from < from_end) ? partial : ok;
      }

      virtual result
      do_unshift(state_type& state, extern_type* to, extern_type* to_limit,
		 extern_type*& to_next) const
      {
	for (unsigned int i = 0; i < CHAR_BIT; ++i)
	  {
	    unsigned int mask = (1 << i);
	    if (state.value & mask)
	      {
		if (to == to_limit)
		  {
		    to_next = to;
		    return partial;
		  }

		state.value &= ~mask;
		*to++ = static_cast<unsigned char>(~mask);
	      }
	  }

	to_next = to;
	return state.value == 0 ? ok : error;
      }

      virtual int
      do_encoding() const throw()
      { return -1; }

      virtual bool
      do_always_noconv() const throw()
      { return false; }

      virtual int
      do_length(state_type& state, const extern_type* from,
		const extern_type* end, size_t max) const
      {
	const extern_type* beg = from;
	while (from < end)
	  {
	    unsigned char c = *from;
	    if (c & 0xc0)
	      {
		// Unshift sequence
		state.value &= c;
		++from;
		continue;
	      }

	    if (max == 0) break;

	    unsigned char tmp;
	    if (state.value & 0x8)
	      {
		if (from >= end - 2)
		  break;
		tmp = (*from++ & 0x7);
		tmp |= ((*from++ << 3) & 0x38);
		tmp |= ((*from++ << 6) & 0xc0);
	      }
	    else
	      {
		if (from >= end - 1)
		  break;
		tmp = (*from++ & 0xf);
		tmp |= ((*from++ << 4) & 0xf0);
	      }
	    state.value = tmp;
	    --max;
	  }
	return from - beg;
      }

      // Maximum 8 bytes unshift sequence followed by max 3 bytes for
      // one character.
      virtual int
      do_max_length() const throw()
      { return 11; }
    };

  template<>
    class ctype<__gnu_test::pod_uchar>
    : public __ctype_abstract_base<__gnu_test::pod_uchar>
    {
    public:
      typedef __gnu_test::pod_uchar char_type;

      explicit ctype(size_t refs  = 0)
      : __ctype_abstract_base<__gnu_test::pod_uchar>(refs) { }

      static locale::id id;

    protected:
      ~ctype()
      { }

      virtual bool
      do_is(mask, char_type) const
      { return false; }

      virtual const char_type*
      do_is(const char_type* low, const char_type* high, mask* vec) const
      {
	fill_n(vec, high - low, mask());
	return high;
      }

      virtual const char_type*
      do_scan_is(mask, const char_type*, const char_type* high) const
      { return high; }

      virtual const char_type*
      do_scan_not(mask, const char_type* low, const char_type*) const
      { return low; }

      virtual char_type
      do_toupper(char_type c) const
      { return c; }

      virtual const char_type*
      do_toupper(char_type*, const char_type*  high) const
      { return high; }

      virtual char_type
      do_tolower(char_type c) const
      { return c; }

      virtual const char_type*
      do_tolower(char_type*, const char_type*  high) const
      { return high; }

      virtual char_type
      do_widen(char c) const
      { return __gnu_test::pod_uchar::from<char>(c); }

      virtual const char*
      do_widen(const char* low, const char* high, char_type* dest) const
      {
	transform(low, high, dest, &__gnu_test::pod_uchar::from<char>);
	return high;
      }

      virtual char
      do_narrow(char_type, char dfault) const
      { return dfault; }

      virtual const char_type*
      do_narrow(const char_type* low, const char_type* high,
		char dfault, char*  dest) const
      {
	fill_n(dest, high - low, dfault);
	return high;
      }
    };

  // numpunct specializations
  template<>
    class numpunct<__gnu_test::pod_uint>
    : public locale::facet
    {
    public:
      typedef __gnu_test::pod_uint    char_type;
      typedef basic_string<char_type> string_type;

      static locale::id id;

      explicit
      numpunct(size_t refs = 0)
      : locale::facet(refs)
      { }

      char_type
      decimal_point() const
      { return this->do_decimal_point(); }

      char_type
      thousands_sep() const
      { return this->do_thousands_sep(); }

      string
      grouping() const
      { return this->do_grouping(); }

      string_type
      truename() const
      { return this->do_truename(); }

      string_type
      falsename() const
      { return this->do_falsename(); }

    protected:
      ~numpunct()
      { }

      virtual char_type
      do_decimal_point() const
      { return char_type(); }

      virtual char_type
      do_thousands_sep() const
      { return char_type(); }

      virtual string
      do_grouping() const
      { return string(); }

      virtual string_type
      do_truename() const
      { return string_type(); }

      virtual string_type
      do_falsename() const
      { return string_type(); }
    };

  template<>
    class moneypunct<__gnu_test::pod_uint>
    : public locale::facet, public money_base
    {
    public:
      typedef __gnu_test::pod_uint    char_type;
      typedef basic_string<char_type> string_type;

      static locale::id id;
      static const bool intl = false;

      explicit
      moneypunct(size_t refs = 0)
      : locale::facet(refs)
      { }

      char_type
      decimal_point() const
      { return this->do_decimal_point(); }

      char_type
      thousands_sep() const
      { return this->do_thousands_sep(); }

      string
      grouping() const
      { return this->do_grouping(); }

      string_type
      curr_symbol() const
      { return this->do_curr_symbol(); }

      string_type
      positive_sign() const
      { return this->do_positive_sign(); }

      string_type
      negative_sign() const
      { return this->do_negative_sign(); }

      int
      frac_digits() const
      { return this->do_frac_digits(); }

      pattern
      pos_format() const
      { return this->do_pos_format(); }

      pattern
      neg_format() const
      { return this->do_neg_format(); }

    protected:
      ~moneypunct()
      { }

      virtual char_type
      do_decimal_point() const
      { return char_type(); }

      virtual char_type
      do_thousands_sep() const
      { return char_type(); }

      virtual string
      do_grouping() const
      { return string(); }

      virtual string_type
      do_curr_symbol() const
      { return string_type(); }

      string_type
      do_positive_sign() const
      { return string_type(); }

      string_type
      do_negative_sign() const
      { return string_type(); }

      int
      do_frac_digits() const
      { return 0; }

      pattern
      do_pos_format() const
      { return pattern(); }

      pattern
      do_neg_format() const
      { return pattern(); }
     };
} // namespace std

#endif // _GLIBCXX_TESTSUITE_CHARACTER_H

