/**************************************************************************
 *
 * rw_char.h - defines a User Defined Character type and its traits
 *
 * $Id: rw_char.h 448928 2006-09-22 13:43:18Z faridz $
 *
 ***************************************************************************
 *
 * Licensed to the Apache Software  Foundation (ASF) under one or more
 * contributor  license agreements.  See  the NOTICE  file distributed
 * with  this  work  for  additional information  regarding  copyright
 * ownership.   The ASF  licenses this  file to  you under  the Apache
 * License, Version  2.0 (the  "License"); you may  not use  this file
 * except in  compliance with the License.   You may obtain  a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the  License is distributed on an  "AS IS" BASIS,
 * WITHOUT  WARRANTIES OR CONDITIONS  OF ANY  KIND, either  express or
 * implied.   See  the License  for  the  specific language  governing
 * permissions and limitations under the License.
 *
 * Copyright 2004-2006 Rogue Wave Software.
 * 
 **************************************************************************/

#ifndef RW_CHAR_INCLUDED
#define RW_CHAR_INCLUDED


// avoid dependencies on any library headers
// to prevent the pollution of the namespace(s)
#include <rw/_traits.h>
#include <testdefs.h>


struct UserChar   // user-defined character type (must be POD)
{
#if    !defined (_RWSTD_NO_LONG_DOUBLE) \
    && !defined (__SUNPRO_CC) || __SUNPRO_CC > 0x540
    long double f;    // exercise correct alignment
#else
    // cannot use long double with SunPro due to a compiler
    // bug that prevents assignments of UserChar() (PR #28328)
    double f;
#endif   // _RWSTD_NO_LONG_DOUBLE

    unsigned char c;   // underlying character representation

    static UserChar eos () {
        // use UserChar::eos() instead of UserChar() to work around broken
        // compilers (e.g., MSVC 6) that do not zero out POD structs
        // on default construction
        const UserChar tmp = { 0, 0 };
        return tmp;
    }

    // definition of a POD-struct (9, p4):
    // A POD-struct is an aggregate class that has no non-static data
    // members of type pointer to member, non-POD-struct, non-POD-union
    // (or array of such types) or reference, and has no user-defined
    // copy assign-ment operator and no user-defined destructor.

    // definition of an aggregate (8.5.1, p1):
    // An aggregate is an array or a class (clause 9) with no user-declared
    // constructors (12.1), no private or protected non-static data members
    // (clause 11), no base classes (clause 10), and no virtual functions
    // (10.3).
};


inline UserChar make_char (char c, UserChar*)
{
    const UserChar ch = { 0.0, c };
    return ch;
}

inline char make_char (char c, char*)
{
    return c;
}

#ifndef _RWSTD_NO_WCHAR_T

inline wchar_t make_char (char c, wchar_t*)
{
    typedef unsigned char UChar;
    return wchar_t (UChar (c));
}

#endif   // _RWSTD_NO_WCHAR_T

///////////////////////////////////////////////////////////////////////////
// 21.1.2, p2 of C++ 03:
//
//   typedef INT_T int_type;
//
// -2- Requires: For a certain character container type char_type, a related
//     container type INT_T shall be a type or class which can represent all
//     of the valid characters converted from the corresponding char_type
//     values, as well as an end-of-file value, eof(). The type int_type
//     represents a character container type which can hold end-of-file to
//     be used as a return type of the iostream class member functions.224)
// ____________
// Footnote 224: If eof() can be held in char_type then some iostreams
//               operations may give surprising results.

class UserInt
{
    void* ptr_;   // to check validity
    int   i_;     // underlying int representation

    // valid values are 0 through 256 with 256 representing EOF
public:

    // default ctor creates a valid object but assigns it an invalid
    // value so that the only legal operations on it are assignment
    // and copy
    UserInt (): ptr_ (&i_ /* valid */), i_ (-1 /* invalid */) { /* empty */ }

    UserInt (const UserInt &rhs)
        : ptr_ (&i_), i_ (rhs.i_) {
        // verify rhs is valid 
        // const_cast used to avoid MSVC 7.0 error C2446:
        // '==' : no conversion from 'const int *' to 'void *const '
        RW_ASSERT (_RWSTD_CONST_CAST (const void*, rhs.ptr_) == &rhs.i_);
        RW_ASSERT (-1 <= rhs.i_  && rhs.i_ < 257);   // i may be invalid
    }

    // (almost) assignable; i.e., returns void instead of UserInt&
    // for extra robustness
    void operator= (const UserInt &rhs) {
        RW_ASSERT (ptr_     == &i_);                // verify *this is valid
        // verify rhs is valid
        RW_ASSERT (_RWSTD_CONST_CAST (const void*, rhs.ptr_) == &rhs.i_);
        RW_ASSERT (-1 <= i_     && i_     < 257);   // i may be invalid
        RW_ASSERT (-1 <  rhs.i_ && rhs.i_ < 257);   // rhs.i must ve valid

        i_ = rhs.i_;
    }

    static UserInt eof () {
        UserInt tmp;
        tmp.i_ = 256;   // EOF
        return tmp;
    }

    static UserInt from_char (const UserChar &c) {
        UserInt tmp;
        tmp.i_ = int (c.c);
        return tmp;
    }

    UserChar to_char () const {
        // working around an Intel C++ 9.1 bug #380635
        /* const */ UserChar tmp /* = { 0, i_ }*/;
        tmp.f = 0;
        tmp.c = (unsigned char)(i_);
        return tmp; 
    }

    ~UserInt () {
        RW_ASSERT (ptr_ == &i_);            // verify *this is valid
        RW_ASSERT (-1 <= i_ == i_ < 257);   // i may be invalid

        i_   = _RWSTD_INT_MIN;   // invalidate
        ptr_ = &ptr_;            // invalidate
    }

    bool equal (const UserInt &rhs) const {
        // verify *this is valid
        RW_ASSERT (_RWSTD_CONST_CAST (const void*, ptr_) == &i_);
        // verify rhs is valid
        RW_ASSERT (_RWSTD_CONST_CAST (const void*, rhs.ptr_) == &rhs.i_);
        RW_ASSERT (-1 < i_     && i_     < 257);   // i must ve valid
        RW_ASSERT (-1 < rhs.i_ && rhs.i_ < 257);   // rhs.i must be valid

        return i_ == rhs.i_;
    }
};


// user-defined character traits template
// (declared but not defined)
template <class charT>
struct UserTraits;


struct TraitsMemFunc
{
    enum {
        assign, eq, lt, compare, length, find, copy, move,
        assign2, not_eof, to_char_type, to_int_type, eq_int_type,
        eof,
        n_funs
    };
};


// required specialization on char
_RWSTD_SPECIALIZED_CLASS
struct _TEST_EXPORT UserTraits<char>: std::char_traits<char>
{
    typedef std::char_traits<char> Base;
    typedef Base::char_type        char_type;
    typedef Base::int_type         int_type;
    typedef TraitsMemFunc          MemFun;

    // avoid any dependency on the library
    typedef int                   off_type;     // std::streamoff
    typedef int                   state_type;   // std::mbstate_t
    typedef std::fpos<state_type> pos_type;     // std::fpos<state_type>

    static void
    assign (char_type&, const char_type&);

    static bool
    eq (const char_type&, const char_type&);

    static bool
    lt (const char_type&, const char_type&);

    static int
    compare (const char_type*, const char_type*, _RWSTD_SIZE_T);
        
    static _RWSTD_SIZE_T
    length (const char_type*);
 
    static const char_type*
    find (const char_type*, _RWSTD_SIZE_T, const char_type&);

    static char_type*
    copy (char_type*, const char_type*, _RWSTD_SIZE_T);

    static char_type*
    move (char_type*, const char_type*, _RWSTD_SIZE_T);

    static char_type*
    assign (char_type*, _RWSTD_SIZE_T, char_type);

    static int_type
    not_eof (const int_type&);

    static char_type
    to_char_type (const int_type&);
      
    static int_type
    to_int_type (const char_type&);

    static bool
    eq_int_type (const int_type&, const int_type&);

    static int_type
    eof ();

    static _RWSTD_SIZE_T n_calls_ [];

    static int_type eof_;

private:

    // not defined to detect bad assumptions made by the library
    UserTraits ();
    ~UserTraits ();
    void operator= (UserTraits&);
};


#ifndef _RWSTD_NO_WCHAR_T

// required specialization on wchar_t
_RWSTD_SPECIALIZED_CLASS
struct _TEST_EXPORT UserTraits<wchar_t>: std::char_traits<wchar_t>
{
    typedef std::char_traits<wchar_t> Base;
    typedef Base::char_type           char_type;
    typedef Base::int_type            int_type;
    typedef TraitsMemFunc             MemFun;

    // avoid any dependency on the library
    typedef int                   off_type;     // std::streamoff
    typedef int                   state_type;   // std::mbstate_t
    typedef std::fpos<state_type> pos_type;     // std::fpos<state_type>

    static void
    assign (char_type&, const char_type&);

    static bool
    eq (const char_type&, const char_type&);

    static bool
    lt (const char_type&, const char_type&);

    static int
    compare (const char_type*, const char_type*, _RWSTD_SIZE_T);
        
    static _RWSTD_SIZE_T
    length (const char_type*);
 
    static const char_type*
    find (const char_type*, _RWSTD_SIZE_T, const char_type&);

    static char_type*
    copy (char_type*, const char_type*, _RWSTD_SIZE_T);

    static char_type*
    move (char_type*, const char_type*, _RWSTD_SIZE_T);

    static char_type*
    assign (char_type*, _RWSTD_SIZE_T, char_type);

    static int_type
    not_eof (const int_type&);

    static char_type
    to_char_type (const int_type&);
      
    static int_type
    to_int_type (const char_type&);

    static bool
    eq_int_type (const int_type&, const int_type&);

    static int_type
    eof ();

    static _RWSTD_SIZE_T n_calls_ [];

    static int_type eof_;

private:

    // not defined to detect bad assumptions made by the library
    UserTraits ();
    ~UserTraits ();
    void operator= (UserTraits&);
};

#endif   // _RWSTD_NO_WCHAR_T


_RWSTD_SPECIALIZED_CLASS
struct _TEST_EXPORT UserTraits<UserChar>   // user-defined character traits
{
    typedef TraitsMemFunc MemFun;

    typedef UserChar char_type;
    typedef UserInt  int_type;

    // avoid any dependency on the library
    typedef int                   off_type;     // std::streamoff
    typedef int                   state_type;   // std::mbstate_t
    typedef std::fpos<state_type> pos_type;     // std::fpos<state_type>

    // accesses to the char_type::f member may trigger a SIGBUS
    // on some architectures (e.g., PA or SPARC) if the member
    // isn't appropriately aligned

    static void assign (char_type&, const char_type&);

    static bool eq (const char_type&, const char_type&);

    static bool lt (const char_type&, const char_type&);

    static int
    compare (const char_type*, const char_type*, _RWSTD_SIZE_T);
        
    static _RWSTD_SIZE_T length (const char_type*);
 
    static const char_type*
    find (const char_type*, _RWSTD_SIZE_T, const char_type&);

    static char_type*
    copy (char_type*, const char_type*, _RWSTD_SIZE_T);

    static char_type*
    move (char_type*, const char_type*, _RWSTD_SIZE_T);

    static char_type*
    assign (char_type*, _RWSTD_SIZE_T, char_type);

    static int_type not_eof (const int_type&);

    static char_type to_char_type (const int_type&);
      
    static int_type to_int_type (const char_type&);

    static bool eq_int_type (const int_type&, const int_type&);

    static int_type eof ();

    static _RWSTD_SIZE_T n_calls_ [];

private:

    // not defined to detect bad assumptions made by the library
    UserTraits ();
    ~UserTraits ();
    void operator= (UserTraits&);
};


// rw_widen() widens successive elements of src into the buffer dst
// when (dst != 0) the last character in the buffer is guaranteed to be NUL,
// regardless of the value of str [len]
// when (len == SIZE_MAX && src != 0), computes len as if by evaluating
// len = strlen(src)
// when (src == 0 && len < SIZE_MAX), initializes the first len elements
// of dst to NUL (i.e., such a call is the equivalent of calling
// memset(dst, 0, len * sizeof *dst))
// returns dst
_TEST_EXPORT char*
rw_widen (char*         /* dst */,
          const char*   /* src */,
          _RWSTD_SIZE_T /* len */ = _RWSTD_SIZE_MAX);

// rw_expand() interprets string src as a sequence of directives and
// widens the result of their processing into the provided buffer dst
// when (dst != 0), otherwise into a dynamically allocated buffer of
// sufficient size
// each directive consists of a character C, optionally followed by
// the '@' sign followed by a non-negative decimal number N; the result
// of the processing of a directive is N consecutive copies of the
// character C where (N = 1) when the '@' is absent
// when (dst_len != 0) the function sets *dst_len to the number of
// produced characters not counting the terminating NUL
// the function returns the dynamically allocated buffer; the caller
// must deallocate the buffer using the delete expression
_TEST_EXPORT char*
rw_expand (char*          /* dst     */,
           const char*    /* src     */,
           _RWSTD_SIZE_T  /* src_len */ = _RWSTD_SIZE_MAX,
           _RWSTD_SIZE_T* /* dst_len */ = 0);


// rw_narrow() narrows successive elements of src into the buffer dst
// (see rw_widen() for details)
_TEST_EXPORT char*
rw_narrow (char*         /* dst */,
           const char*   /* src */,
           _RWSTD_SIZE_T /* len */ = _RWSTD_SIZE_MAX);

// rw_match() compares up to len successive elements of s1 and s2 for
// equality until it finds a pair that do not compare equal or until
// len comparisons has been made
// if (len == SIZE_MAX) is true elements are compared until the first
// mismatch is found or until the NUL character is encountered
// returns the number of matching elements
_TEST_EXPORT _RWSTD_SIZE_T 
rw_match (const char*   /* s1  */,
          const char*   /* s2  */,
          _RWSTD_SIZE_T /* len */ = _RWSTD_SIZE_MAX);

#ifndef _RWSTD_WCHAR_T

_TEST_EXPORT wchar_t*
rw_widen (wchar_t*, const char*, _RWSTD_SIZE_T = _RWSTD_SIZE_MAX);

_TEST_EXPORT wchar_t*
rw_expand (wchar_t*       /* dst     */,
           const char*    /* src     */,
           _RWSTD_SIZE_T  /* src_len */ = _RWSTD_SIZE_MAX,
           _RWSTD_SIZE_T* /* dst_len */ = 0);

_TEST_EXPORT char*
rw_narrow (char*, const wchar_t*, _RWSTD_SIZE_T = _RWSTD_SIZE_MAX);

_TEST_EXPORT _RWSTD_SIZE_T
rw_match (const char*, const wchar_t*,
          _RWSTD_SIZE_T = _RWSTD_SIZE_MAX);

#endif   // _RWSTD_WCHAR_T

_TEST_EXPORT UserChar*
rw_widen (UserChar*, const char*, _RWSTD_SIZE_T = _RWSTD_SIZE_MAX);

_TEST_EXPORT UserChar*
rw_expand (UserChar*      /* dst     */,
           const char*    /* src     */,
           _RWSTD_SIZE_T  /* src_len */ = _RWSTD_SIZE_MAX,
           _RWSTD_SIZE_T* /* dst_len */ = 0);

_TEST_EXPORT char*
rw_narrow (char*, const UserChar*, _RWSTD_SIZE_T = _RWSTD_SIZE_MAX);

_TEST_EXPORT _RWSTD_SIZE_T
rw_match (const char*, const UserChar*, _RWSTD_SIZE_T = _RWSTD_SIZE_MAX);


// accessors to n_calls array, if present
template <class charT> 
inline _RWSTD_SIZE_T*
rw_get_call_counters (std::char_traits<charT>*, charT*)
{
    return 0;
}

template <class charT> 
inline _RWSTD_SIZE_T*
rw_get_call_counters (UserTraits<charT>*, charT*)
{
    return UserTraits<charT>::n_calls_;
}


static const struct _TEST_EXPORT UserCharFmatInit {
    UserCharFmatInit ();
} _rw_user_char_fmat_init;

#endif   // RW_CHAR_INCLUDED
