/************************************************************************
 *
 * 21.strings.h - definitions of helpers used in clause 21 tests
 *
 * $Id$
 *
 ***************************************************************************
 *
 * 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.
 * 
 **************************************************************************/

#ifndef RW_21_STRINGS_H_INCLUDED
#define RW_21_STRINGS_H_INCLUDED

#include <rw_char.h>        // for rw_expand()
#include <rw_testdefs.h>
#include <rw_containers.h>  // for ContainerIds

/**************************************************************************/

// defines enumerations identifying basic_string template arguments,
// sets of overloaded functions, member types used in the declarations
// of their signatures, and specific overloads of such member functions
struct StringIds: ContainerIds {

    // identifiers for the charT template argument
    enum CharId { Char, WChar, UChar };

    // identifiers for the Traits template argument
    enum TraitsId { DefaultTraits, UserTraits };

    // identifies a set of overloaded member or non-member
    // string functions
    enum FuncId {
        // 6 bits, 64 functions max
        /*  0 */ fid_append,
        /*  1 */ fid_assign,
        /*  2 */ fid_erase,
        /*  3 */ fid_insert,
        /*  4 */ fid_replace,
        /*  5 */ fid_op_plus_eq,
        /*  6 */ fid_find,
        /*  7 */ fid_rfind,
        /*  8 */ fid_find_first_of,
        /*  9 */ fid_find_last_of,
        /* 10 */ fid_find_first_not_of,
        /* 11 */ fid_find_last_not_of,
        /* 12 */ fid_compare,
        /* 13 */ fid_substr,
        /* 14 */ fid_op_index,
        /*    */ fid_op_index_const = fid_op_index,
        /* 15 */ fid_at,
        /*    */ fid_at_const = fid_at,
        /* 16 */ fid_copy,
        /* 17 */ fid_ctor,
        /* 18 */ fid_op_set,
        /* 19 */ fid_swap,
        /* 20 */ fid_push_back,
        /* 21 */ fid_op_plus,
        /* 22 */ fid_op_equal,
        /* 23 */ fid_op_not_equal,
        /* 24 */ fid_op_less,
        /* 25 */ fid_op_less_equal,
        /* 26 */ fid_op_greater,
        /* 27 */ fid_op_greater_equal,
        /* 28 */ fid_size,
        /* 29 */ fid_length,
        /* 30 */ fid_max_size,
        /* 31 */ fid_resize,
        /* 32 */ fid_capacity,
        /* 33 */ fid_reserve,
        /* 34 */ fid_clear,
        /* 35 */ fid_empty,
        /* 36 */ fid_begin,
        /*    */ fid_begin_const = fid_begin,
        /* 37 */ fid_end,
        /*    */ fid_end_const = fid_end,
        /* 38 */ fid_rbegin,
        /*    */ fid_rbegin_const = fid_rbegin,
        /* 39 */ fid_rend,
        /*    */ fid_rend_const = fid_rend,
        /* 40 */ fid_c_str,
        /* 41 */ fid_data,
        /* 42 */ fid_get_allocator,
        /* 43 */ fid_extractor,
        /* 44 */ fid_inserter,
        /* 45 */ fid_getline,
        /* -- */ fid_bits = 6,
        /* -- */ fid_mask = 63
    };

    // identifies the type of a function argument, including
    // the implicit this
    enum ArgId {
        // 4 bits, 16 types max
        /*  0 */ arg_void,    // void
        /*  1 */ arg_size,    // size_type
        /*  2 */ arg_val,     // value_type
        /*  3 */ arg_ptr,     // pointer
        /*  4 */ arg_cptr,    // const_pointer
        /*  5 */ arg_ref,     // reference
        /*  6 */ arg_cref,    // const_reference
        /*  7 */ arg_iter,    // iterator
        /*  8 */ arg_citer,   // const_iterator
        /*  9 */ arg_range,   // Iterator, Iterator
        /* 10 */ arg_str,     // string& (or this for member functions)
        /* 11 */ arg_cstr,    // const string& (or const this for members)
        /* 12 */ arg_alloc,   // const allocator&
        /* 13 */ arg_istream, // istream&
        /* 14 */ arg_ostream, // ostream&
        /* -- */ arg_bits = 4,
        /* -- */ arg_mask = 15
    };

    enum {
        // bit designating a member function
        bit_member = 1 << (fid_bits + 6 * arg_bits)
    };

// define the helper macros
#include <rw_sigdefs.h>

    // unique identifiers for all overloads of each member function
    //     6 bits for FuncId
    // 6 * 4 bits for ArgId (at most 6 arguments including this)
    //     1 bit for membership
    enum OverloadId {
        //////////////////////////////////////////////////////////////
        // append (const_pointer)
        MEMBER_1 (append, str, cptr),
        // append (const basic_string&)
        MEMBER_1 (append, str, cstr),
        // append (const_pointer, size_type)
        MEMBER_2 (append, str, cptr, size),
        // append (const basic_string&, size_type, size_type)
        MEMBER_3 (append, str, cstr, size, size),
        // append (size_type, value_type)
        MEMBER_2 (append, str, size, val),
        // append (InputIterator, InputIterator)
        MEMBER_1 (append, str, range),

        //////////////////////////////////////////////////////////////
        // assign (const_pointer)
        MEMBER_1 (assign, str, cptr),
        // assign (const basic_string&)
        MEMBER_1 (assign, str, cstr),
        // assign (const_pointer, size_type)
        MEMBER_2 (assign, str, cptr, size),
        // assign (const basic_string&, size_type, size_type)
        MEMBER_3 (assign, str, cstr, size, size),
        // assign (size_type, value_type)
        MEMBER_2 (assign, str, size, val),
        // assign (InputIterator, InputIterator)
        MEMBER_1 (assign, str, range),

        //////////////////////////////////////////////////////////////
        // erase ()
        MEMBER_0 (erase, str),
        // erase (size_type)
        MEMBER_1 (erase, str, size),
        // erase (size_type, size_type)
        MEMBER_2 (erase, str, size, size),
        // erase (iterator)
        MEMBER_1 (erase, str, iter),
        // erase (iterator, iterator)
        MEMBER_2 (erase, str, iter, iter),

        //////////////////////////////////////////////////////////////
        // insert (size_type, const_pointer)
        MEMBER_2 (insert, str, size, cptr),
        // insert (size_type, const basic_string&)
        MEMBER_2 (insert, str, size, cstr),
        // insert (size_type, const_pointer, size_type)
        MEMBER_3 (insert, str, size, cptr, size),
        // insert (size_type, const basic_string&, size_type, size_type)
        MEMBER_4 (insert, str, size, cstr, size, size),
        // insert (size_type, size_type, value_type)
        MEMBER_3 (insert, str, size, size, val),
        // insert (iterator, value_type)
        MEMBER_2 (insert, str, iter, val),
        // insert (iterator, size_type, value_type)
        MEMBER_3 (insert, str, iter, size, val),
        // insert (iterator, InputIterator, InputIterator)
        MEMBER_2 (insert, str, iter, range),

        //////////////////////////////////////////////////////////////
        // (size_type, size_type, const_pointer)
        MEMBER_3 (replace, str, size, size, cptr),
        // (size_type, size_type, const basic_string&)
        MEMBER_3 (replace, str, size, size, cstr),
        // (size_type, size_type, const_pointer, size_type)
        MEMBER_4 (replace, str, size, size, cptr, size),
        // (size_type, size_type, const basic_string&, size_type, size_type)
        MEMBER_5 (replace, str, size, size, cstr, size, size),
        // (size_type, size_type, size_type, value_type)
        MEMBER_4 (replace, str, size, size, size, val),
        // (iterator, iterator, const_pointer)
        MEMBER_3 (replace, str, iter, iter, cptr),
        // (iterator, iterator, const basic_string&)
        MEMBER_3 (replace, str, iter, iter, cstr),
        // (iterator, iterator, const_pointer, size_type)
        MEMBER_4 (replace, str, iter, iter, cptr, size),
        // (iterator, iterator, size_type, value_type)
        MEMBER_4 (replace, str, iter, iter, size, val),
        // (iterator, iterator, InputIterator, InputIterator)
        MEMBER_3 (replace, str, iter, iter, range),

        //////////////////////////////////////////////////////////////
        // operator+= (const_pointer)
        MEMBER_1 (op_plus_eq, str, cptr), 
        // operator+= (const basic_string&)
        MEMBER_1 (op_plus_eq, str, cstr),
        // operator+= (value_type)
        MEMBER_1 (op_plus_eq, str, val),

        //////////////////////////////////////////////////////////////
        // overloads of find, rfind, find_first_of, find_last_of, 
        // find_first_not_of, find_last_not_of

        // find (const_pointer) const
        MEMBER_1 (find, cstr, cptr),
        // find (const basic_string&) const
        MEMBER_1 (find, cstr, cstr),
        // find (const_pointer, size_type) const
        MEMBER_2 (find, cstr, cptr, size),
        // find (const_pointer, size_type, size_type) const
        MEMBER_3 (find, cstr, cptr, size, size),
        // find (const basic_string&, size_type) const
        MEMBER_2 (find, cstr, cstr, size),
        // find (value_type) const
        MEMBER_1 (find, cstr, val),
        // find (value_type, size_type) const
        MEMBER_2 (find, cstr, val, size),

        //////////////////////////////////////////////////////////////
        // rfind (const_pointer) const
        MEMBER_1 (rfind, cstr, cptr),
        // rfind (const basic_string&) const
        MEMBER_1 (rfind, cstr, cstr),
        // rfind (const_pointer, size_type) const
        MEMBER_2 (rfind, cstr, cptr, size),
        // rfind (const_pointer, size_type, size_type) const
        MEMBER_3 (rfind, cstr, cptr, size, size),
        // rfind (const basic_string&, size_type) const
        MEMBER_2 (rfind, cstr, cstr, size),
        // rfind (value_type) const
        MEMBER_1 (rfind, cstr, val),
        // rfind (value_type, size_type) const
        MEMBER_2 (rfind, cstr, val, size),

        //////////////////////////////////////////////////////////////
        // find_first_of (const_pointer) const
        MEMBER_1 (find_first_of, cstr, cptr),
        // find_first_of (const basic_string&) const
        MEMBER_1 (find_first_of, cstr, cstr),
        // find_first_of (const_pointer, size_type) const
        MEMBER_2 (find_first_of, cstr, cptr, size),
        // find_first_of (const_pointer, size_type, size_type) const
        MEMBER_3 (find_first_of, cstr, cptr, size, size),
        // find_first_of (const basic_string&, size_type) const
        MEMBER_2 (find_first_of, cstr, cstr, size),
        // find_first_of (value_type) const
        MEMBER_1 (find_first_of, cstr, val),
        // find_first_of (value_type, size_type) const
        MEMBER_2 (find_first_of, cstr, val, size),

        //////////////////////////////////////////////////////////////
        // find_last_of (const_pointer) const
        MEMBER_1 (find_last_of, cstr, cptr),
        // find_last_of (const basic_string&) const
        MEMBER_1 (find_last_of, cstr, cstr),
        // find_last_of (const_pointer, size_type) const
        MEMBER_2 (find_last_of, cstr, cptr, size),
        // find_last_of (const_pointer, size_type, size_type) const
        MEMBER_3 (find_last_of, cstr, cptr, size, size),
        // find_last_of (const basic_string&, size_type) const
        MEMBER_2 (find_last_of, cstr, cstr, size),
        // find_last_of (value_type) const
        MEMBER_1 (find_last_of, cstr, val),
        // find_last_of (value_type, size_type) const
        MEMBER_2 (find_last_of, cstr, val, size),

        //////////////////////////////////////////////////////////////
        // find_first_not_of (const_pointer) const
        MEMBER_1 (find_first_not_of, cstr, cptr),
        // find_first_not_of (const basic_string&) const
        MEMBER_1 (find_first_not_of, cstr, cstr),
        // find_first_not_of (const_pointer, size_type) const
        MEMBER_2 (find_first_not_of, cstr, cptr, size),
        // find_first_not_of (const_pointer, size_type, size_type) const
        MEMBER_3 (find_first_not_of, cstr, cptr, size, size),
        // find_first_not_of (const basic_string&, size_type) const
        MEMBER_2 (find_first_not_of, cstr, cstr, size),
        // find_first_not_of (value_type) const
        MEMBER_1 (find_first_not_of, cstr, val),
        // find_first_not_of (value_type, size_type) const
        MEMBER_2 (find_first_not_of, cstr, val, size),

        //////////////////////////////////////////////////////////////
        // find_last_not_of (const_pointer) const
        MEMBER_1 (find_last_not_of, cstr, cptr),
        // find_last_not_of (const basic_string&) const
        MEMBER_1 (find_last_not_of, cstr, cstr),
        // find_last_not_of (const_pointer, size_type) const
        MEMBER_2 (find_last_not_of, cstr, cptr, size),
        // find_last_not_of (const_pointer, size_type, size_type) const
        MEMBER_3 (find_last_not_of, cstr, cptr, size, size),
        // find_last_not_of (const basic_string&, size_type) const
        MEMBER_2 (find_last_not_of, cstr, cstr, size),
        // find_last_not_of (value_type) const
        MEMBER_1 (find_last_not_of, cstr, val),
        // find_last_not_of (value_type, size_type) const
        MEMBER_2 (find_last_not_of, cstr, val, size),

        //////////////////////////////////////////////////////////////
        // compare (const_pointer) const
        MEMBER_1 (compare, cstr, cptr),
        // compare (const basic_string&) const
        MEMBER_1 (compare, cstr, cstr),
        // compare (size_type, size_type, const_pointer) const
        MEMBER_3 (compare, cstr, size, size, cptr),
        // compare (size_type, size_type, const basic_string&) const
        MEMBER_3 (compare, cstr, size, size, cstr),
        // compare (size_type, size_type, const_pointer, size_type) const
        MEMBER_4 (compare, cstr, size, size, cptr, size),
        // compare (size_type, size_type, const basic_string&,
        //          size_type, size_type) const
        MEMBER_5 (compare, cstr, size, size, cstr, size, size),

        //////////////////////////////////////////////////////////////
        // substr (void) const
        MEMBER_0 (substr, cstr),
        // substr (size_type) const
        MEMBER_1 (substr, cstr, size),
        // substr (size_type, size_type) const
        MEMBER_2 (substr, cstr, size, size),

        //////////////////////////////////////////////////////////////
        // operator[] (size_type)
        MEMBER_1 (op_index, str, size),
        // operator[] (size_type) const
        MEMBER_1 (op_index_const, cstr, size),
        // at (size_type)
        MEMBER_1 (at, str, size),
        // at (size_type) const
        MEMBER_1 (at_const, cstr, size),

        //////////////////////////////////////////////////////////////
        // copy (pointer, size_type) const
        MEMBER_2 (copy, cstr, ptr, size),
        // copy (pointer, size_type, size_type) const
        MEMBER_3 (copy, cstr, ptr, size, size),

        //////////////////////////////////////////////////////////////
        // basic_string (void)
        MEMBER_0 (ctor, str),
        // basic_string (const allocator_type&)
        MEMBER_1 (ctor, str, alloc),
        // basic_string (const_pointer)
        MEMBER_1 (ctor, str, cptr),
        // basic_string (const_pointer, const allocator_type&)
        MEMBER_2 (ctor, str, cptr, alloc),
        // basic_string (const basic_string&)
        MEMBER_1 (ctor, str, cstr),
        // basic_string (const_pointer, size_type)
        MEMBER_2 (ctor, str, cptr, size),
        // basic_string (const_pointer, size_type, const allocator_type&)
        MEMBER_3 (ctor, str, cptr, size, alloc),
        // basic_string (const basic_string&, size_type)
        MEMBER_2 (ctor, str, cstr, size),
        // basic_string (const basic_string&, size_type, const allocator&)
        MEMBER_3 (ctor, str, cstr, size, alloc),
        // basic_string (const basic_string&, size_type, size_type)
        MEMBER_3 (ctor, str, cstr, size, size),
        // basic_string (const basic_string&, size_type, size_type, allocator&)
        MEMBER_4 (ctor, str, cstr, size, size, alloc),
        // basic_string (size_type, value_type)
        MEMBER_2 (ctor, str, size, val),
        // basic_string (size_type, value_type, const allocator_type&)
        MEMBER_3 (ctor, str, size, val, alloc),
        // basic_string (InputIterator, InputIterator)
        MEMBER_1 (ctor, str, range),
        // basic_string (InputIterator, InputIterator, const allocator&)
        MEMBER_2 (ctor, str, range, alloc),

        //////////////////////////////////////////////////////////////
        // operator= (const_pointer)
        MEMBER_1 (op_set, str, cptr),
        // operator= (const basic_string&)
        MEMBER_1 (op_set, str, cstr),
        // operator= (value_type)
        MEMBER_1 (op_set, str, val),

        //////////////////////////////////////////////////////////////
        // swap (basic_string&)
        MEMBER_1 (swap, str, str),

        //////////////////////////////////////////////////////////////
        // push_back (value_type)
        MEMBER_1 (push_back, str, val),

        //////////////////////////////////////////////////////////////
        // operator+ (const_pointer, const basic_string&)
        NON_MEMBER_2 (op_plus, cptr, cstr),
        // operator+ (const basic_string&, const basic_string&)
        NON_MEMBER_2 (op_plus, cstr, cstr),
        // operator+ (const basic_string&, const_pointer)
        NON_MEMBER_2 (op_plus, cstr, cptr),
        // operator+ (const basic_string&, value_type)
        NON_MEMBER_2 (op_plus, cstr, val),
        // operator+ (value_type, const basic_string&)
        NON_MEMBER_2 (op_plus, val, cstr),

        //////////////////////////////////////////////////////////////
        // operator== (const_pointer, const basic_string&)
        NON_MEMBER_2 (op_equal, cptr, cstr),
        // operator== (const basic_string&, const basic_string&)
        NON_MEMBER_2 (op_equal, cstr, cstr),
        // operator== (const basic_string&, const_pointer)
        NON_MEMBER_2 (op_equal, cstr, cptr),

        //////////////////////////////////////////////////////////////
        // operator!= (const_pointer, const basic_string&)
        NON_MEMBER_2 (op_not_equal, cptr, cstr),
        // operator!= (const basic_string&, const basic_string&)
        NON_MEMBER_2 (op_not_equal, cstr, cstr),
        // operator!= (const basic_string&, const_pointer)
        NON_MEMBER_2 (op_not_equal, cstr, cptr),

        //////////////////////////////////////////////////////////////
        // operator< (const_pointer, const basic_string&)
        NON_MEMBER_2 (op_less, cptr, cstr),
        // operator< (const basic_string&, const basic_string&)
        NON_MEMBER_2 (op_less, cstr, cstr),
        // operator< (const basic_string&, const_pointer)
        NON_MEMBER_2 (op_less, cstr, cptr),

        //////////////////////////////////////////////////////////////
        // operator<= (const_pointer, const basic_string&)
        NON_MEMBER_2 (op_less_equal, cptr, cstr),
        // operator<= (const basic_string&, const basic_string&)
        NON_MEMBER_2 (op_less_equal, cstr, cstr),
        // operator<= (const basic_string&, const_pointer)
        NON_MEMBER_2 (op_less_equal, cstr, cptr),

        //////////////////////////////////////////////////////////////
        // operator> (const_pointer, const basic_string&)
        NON_MEMBER_2 (op_greater, cptr, cstr),
        // operator> (const basic_string&, const basic_string&)
        NON_MEMBER_2 (op_greater, cstr, cstr),
        // operator> (const basic_string&, const_pointer)
        NON_MEMBER_2 (op_greater, cstr, cptr),

        //////////////////////////////////////////////////////////////
        // operator>= (const_pointer, const basic_string&)
        NON_MEMBER_2 (op_greater_equal, cptr, cstr),
        // operator>= (const basic_string&, const basic_string&)
        NON_MEMBER_2 (op_greater_equal, cstr, cstr),
        // operator>= (const basic_string&, const_pointer)
        NON_MEMBER_2 (op_greater_equal, cstr, cptr),

        //////////////////////////////////////////////////////////////
        // size () const
        MEMBER_0 (size, cstr),

        //////////////////////////////////////////////////////////////
        // length () const
        MEMBER_0 (length, cstr),

        //////////////////////////////////////////////////////////////
        // max_size () const
        MEMBER_0 (max_size, cstr),

        //////////////////////////////////////////////////////////////
        // resize (size_type, value_type)
        MEMBER_2 (resize, str, size, val),
        // resize (size_type)
        MEMBER_1 (resize, str, size),

        //////////////////////////////////////////////////////////////
        // capacity () const
        MEMBER_0 (capacity, cstr),

        //////////////////////////////////////////////////////////////
        // reserve (size_type)
        MEMBER_1 (reserve, str, size),
        // reserve ()
        MEMBER_0 (reserve, str),

        //////////////////////////////////////////////////////////////
        // clear ()
        MEMBER_0 (clear, str),

        //////////////////////////////////////////////////////////////
        // empty () const
        MEMBER_0 (empty, cstr),

        //////////////////////////////////////////////////////////////
        // begin ()
        MEMBER_0 (begin, str),
        // begin () const
        MEMBER_0 (begin_const, cstr),
        // end ()
        MEMBER_0 (end, str),
        // end () const
        MEMBER_0 (end_const, cstr),

        //////////////////////////////////////////////////////////////
        // rbegin ()
        MEMBER_0 (rbegin, str),
        // rbegin () const
        MEMBER_0 (rbegin_const, cstr),
        // rend ()
        MEMBER_0 (rend, str),
        // rend () const
        MEMBER_0 (rend_const, cstr),

        //////////////////////////////////////////////////////////////
        // c_str () const
        MEMBER_0 (c_str, cstr),
        // data () const
        MEMBER_0 (data, cstr),

        //////////////////////////////////////////////////////////////
        // get_allocator () const
        MEMBER_0 (get_allocator, cstr),

        //////////////////////////////////////////////////////////////
        // operator>> (istream&, basic_string&)
        NON_MEMBER_2 (extractor, istream, str),
        // operator<< (ostream&, const basic_string&)
        NON_MEMBER_2 (inserter, ostream, cstr),

        //////////////////////////////////////////////////////////////
        // getline (istream&, basic_string&)
        NON_MEMBER_2 (getline, istream, str),
        // getline (istream&, basic_string&, value_type)
        NON_MEMBER_3 (getline, istream, str, val)
    };

// clean up helper macros used above
#include <rw_sigdefs.h>

    static ArgId arg_type (OverloadId id, int argno) {
        return ArgId (((int (id) >> fid_bits) >> argno * arg_bits) & arg_mask);
    }
};

/**************************************************************************/

static const _RWSTD_SIZE_T
NPOS = _RWSTD_SIZE_MAX;

/**************************************************************************/

struct StringFunc
{
    StringIds::CharId     char_id_;
    StringIds::TraitsId   traits_id_;
    StringIds::AllocId    alloc_id_;
    StringIds::IteratorId iter_id_;
    StringIds::OverloadId which_;
};


// describes a single test case for any overload of any string
// function (the same test case can be used to exercise more
// than one overload of the same function)
struct StringTestCase
{
    int            line;      // test case line number

    int            off;       // offset (position argument)
    int            size;      // size (count argument)

    int            off2;      // offset 2 (position argument)
    int            size2;     // size 2 (count argument)

    int            val;       // value (single character to append)

    const char*    str;       // controlled sequence
    _RWSTD_SIZE_T  str_len;   // length of sequence

    const char*    arg;       // sequence to insert
    _RWSTD_SIZE_T  arg_len;   // length of sequence

    const char*    res;       // resulting sequence
    _RWSTD_SIZE_T  nres;      // length of sequence or expected result
                              // value for find, rfind, compare, etc

    int            bthrow;    // exception expected
};


// describes a set of test cases for a single overload of a function
struct StringTest
{
    // string function overload to exercise
    StringIds::OverloadId which;

    // test cases to exercise overload withh
    const StringTestCase *cases;

    // number of test cases
    _RWSTD_SIZE_T case_count;
};


// sets the {CLASS}, {FUNC}, {FUNCSIG}, and optionally {FUNCALL}
// environment variables as follows:
// CLASS:   the name of basic_string specialization
// FUNC:    the name of the basic_string function
// FUNCSIG: the name and signature of a specific overload
//          of the basic_string function
// FUNCALL: a string describing the call to the basic_string function
//          with function with function arguments expanded (as specified
//          by the TestCase argument)
_TEST_EXPORT void
rw_setvars (const StringFunc&, const StringTestCase* = 0);

typedef void StringTestFunc (const StringFunc&, const StringTestCase&);

_TEST_EXPORT int
rw_run_string_test (int, char**, const char*, const char*,
                    StringTestFunc*, const StringTest*, _RWSTD_SIZE_T);

typedef void VoidFunc ();

_TEST_EXPORT int
rw_run_string_test (int, char**, const char*, const char*,
                    VoidFunc* const*, const StringTest*, _RWSTD_SIZE_T);

/**************************************************************************/

template <class charT>
class StringTestCaseData
{
private:

    enum { BUFSIZE = 256 };

    // small buffers to avoid expensive dynamic memory allocation
    // in most test cases (will dynamically allocate sufficient
    // storage if necessary)
    charT str_buf_ [BUFSIZE];
    charT arg_buf_ [BUFSIZE];
    charT res_buf_ [BUFSIZE];

    // not defined, not copiable, not assignable
    StringTestCaseData (const StringTestCaseData&);
    void operator= (const StringTestCaseData&);

    // for convenience
    typedef _RWSTD_SIZE_T SizeType;

public:

    SizeType strlen_;   // the length of the expanded string
    SizeType arglen_;   // the length of the expanded argument
    SizeType reslen_;   // the length of the expanded result

    // the offset and extent (the number of elements) of
    // the first range into the string object being modified
    SizeType off1_;
    SizeType ext1_;

    // the offset and extent (the number of elements) of
    // the argument of the function call
    SizeType off2_;
    SizeType ext2_;

    const charT* const str_;   // pointer to the expanded string
    const charT* const arg_;   // pointer to the expanded argument
    const charT* const res_;   // pointer to the expanded result

    const StringFunc     &func_;
    const StringTestCase &tcase_;

    // converts the narrow (and possibly) condensed strings to fully
    // expanded wide character arrays that can be used to construct
    // basic_string objects
    StringTestCaseData (const StringFunc &func, const StringTestCase &tcase)
        : strlen_ (BUFSIZE), arglen_ (BUFSIZE), reslen_ (BUFSIZE),
          str_ (rw_expand (str_buf_, tcase.str, tcase.str_len, &strlen_)),
          arg_ (rw_expand (arg_buf_, tcase.arg, tcase.arg_len, &arglen_)),
          res_ (rw_expand (res_buf_, tcase.res, tcase.nres,    &reslen_)),
          func_ (func), tcase_ (tcase) {
        // compute the offset and extent of the string object
        // representing the controlled sequence and the offset
        // and extent of the argument of the function call
        const SizeType argl = tcase_.arg ? arglen_ : strlen_;

        off1_ = SizeType (tcase_.off) < strlen_ ?
            SizeType (tcase_.off) : strlen_;

        ext1_ = off1_ + tcase_.size < strlen_ ?
            SizeType (tcase_.size) : strlen_ - off1_;
        
        off2_ = SizeType (tcase_.off2) < argl ?
            SizeType (tcase_.off2) : argl;

        ext2_ = off2_ + tcase_.size2 < argl ?
            SizeType (tcase_.size2) : argl - off2_;
    }

    ~StringTestCaseData () {
        // clean up dynamically allocated memory (if any)
        if (str_ != str_buf_)
            delete[] _RWSTD_CONST_CAST (charT*, str_);
        if (arg_ != arg_buf_)
            delete[] _RWSTD_CONST_CAST (charT*, arg_);
        if (res_ != res_buf_)
            delete[] _RWSTD_CONST_CAST (charT*, res_);
    }
};

/**************************************************************************/

// encapsulates the state of a string object without regard to type
// used in exception safety tests to determine changes to the state
// after a modifying operation throws an exception
struct _TEST_EXPORT StringState
{
    const void*   data_;
    _RWSTD_SIZE_T size_;
    _RWSTD_SIZE_T capacity_;

    // invokes rw_assert() to verify that two states are the same
    // returns 1 when the two states compare equal, and 0 otherwise
    int assert_equal (const StringState&, int, int, const char*) const;
};


// creates a StringState object from a basic_string
template <class String>
inline StringState
rw_get_string_state (const String &str)
{
    const StringState state = {
        str.data (), str.size (), str.capacity ()
    };

    return state;
}


/**************************************************************************/

// base class-functor for the range template overloads testing
template <class String>
struct RangeBase {

    typedef typename String::value_type                 StringChar;
    typedef typename String::pointer                    StringPtr;
    typedef typename String::const_pointer              StringConstPtr;
    typedef typename String::iterator                   StringIter;
    typedef typename String::const_iterator             StringConstIter;
    typedef typename String::reverse_iterator           StringRevIter;
    typedef typename String::const_reverse_iterator     StringConstRevIter;

    RangeBase () { }

    virtual ~RangeBase () { /* silence warnings */ }

    static StringPtr
    begin (String &str, StringPtr*) {
        return _RWSTD_CONST_CAST (StringPtr, str.data ());
    }

    static StringConstPtr
    begin (const String &str, StringConstPtr*) {
        return str.data ();
    }

#ifndef _RWSTD_NO_DEBUG_ITER

    // when debugging iterators are enabled string::iterator and
    // string::pointer are distinct types; otherwise they are the
    // same type

    static StringIter
    begin (String &str, StringIter*) {
        return str.begin ();
    }

    static StringConstIter
    begin (const String &str, StringConstIter*) {
        return str.begin ();
    }

#endif   // _RWSTD_NO_DEBUG_ITER

    static StringRevIter
    begin (String &str, StringRevIter*) {
        return str.rbegin ();
    }

    static StringConstRevIter
    begin (const String &str, StringConstRevIter*) {
        return str.rbegin ();
    }

    virtual String&
    operator() (String &str, const StringTestCaseData<StringChar>&) const {
        RW_ASSERT (!"logic error: should be never called");
        return str;
    }
};

/**************************************************************************/

#define Disabled(which)   \
    StringIds::opt_memfun_disabled [which & ~StringIds::fid_mask]


#ifndef _RWSTD_NO_WCHAR_T
#  define TEST_DISPATCH(Alloc, fname, func, tcase)              \
    if (StringIds::DefaultTraits == func.traits_id_) {          \
        if (StringIds::Char == func.char_id_)                   \
            fname (char (), (std::char_traits<char>*)0,         \
                   (Alloc<char>*)0, func, tcase);               \
        else if (StringIds::WChar == func.char_id_)             \
            fname (wchar_t (), (std::char_traits<wchar_t>*)0,   \
                   (Alloc<wchar_t>*)0, func, tcase);            \
        else                                                    \
            rw_note (0, 0, 0,                                   \
                     "%{$CLASS} tests not implemented");        \
    }                                                           \
    else {                                                      \
        if (StringIds::Char == func.char_id_)                   \
            fname (char (), (UserTraits<char>*)0,               \
                   (Alloc<char>*)0, func, tcase);               \
       else if (StringIds::WChar == func.char_id_)              \
            fname (wchar_t (), (UserTraits<wchar_t>*)0,         \
                   (Alloc<wchar_t>*)0, func, tcase);            \
       else                                                     \
           fname (UserChar (), (UserTraits<UserChar>*)0,        \
                  (Alloc<UserChar>*)0, func, tcase);            \
    }                                                           \
    (void)0

#else   // if defined (_RWSTD_NO_WCHAR_T)
#  define TEST_DISPATCH(Alloc, fname, func, tcase)              \
    if (StringIds::DefaultTraits == func.traits_id_) {          \
        if (StringIds::Char == func.char_id_)                   \
            fname (char (), (std::char_traits<char>*)0,         \
                   (Alloc<char>*)0, func, tcase);               \
        else if (StringIds::WChar == func.char_id_)             \
            RW_ASSERT (!"logic error: wchar_t disabled");       \
        else                                                    \
            rw_note (0, 0, 0,                                   \
                     "%{$CLASS} tests not implemented");        \
        }                                                       \
    }                                                           \
    else {                                                      \
        if (StringIds::Char == func.char_id_)                   \
            fname (char (), (UserTraits<char>*)0,               \
                   (Alloc<char>*)0, func, tcase);               \
        else if (StringIds::WChar == func.char_id_)             \
             RW_ASSERT (!"logic error: wchar_t disabled");      \
        else if (StringIds::UChar == func.char_id_)             \
            fname (UserChar (), (UserTraits<UserChar>*)0,       \
                   (Alloc<UserChar>*)0, func, tcase);           \
    }                                                           \
    (void)0

#endif   // _RWSTD_NO_WCHAR_T


#define DEFINE_STRING_TEST_DISPATCH(fname)                      \
    static void                                                 \
    fname (const StringFunc     &func,                          \
           const StringTestCase &tcase) {                       \
        if (StringIds::DefaultAlloc == func.alloc_id_) {        \
            TEST_DISPATCH (std::allocator, fname, func, tcase); \
        }                                                       \
        else if (StringIds::UserAlloc == func.alloc_id_) {      \
            TEST_DISPATCH (UserAlloc, fname, func, tcase);      \
        }                                                       \
        else                                                    \
            RW_ASSERT (!"logic error: bad allocator");          \
    } typedef void rw_unused_typedef


#define TFUNC(charT, Traits, Allocator)                 \
    void (*)(charT*, Traits<charT>*, Allocator<charT>*, \
             const StringTestCaseData<charT>&)

#define TFUNC_ADDR(fname, charT, Traits, Allocator)     \
    (VoidFunc*)(TFUNC (charT, Traits, Allocator))       \
        &fname<charT, Traits<charT>, Allocator<charT> >

#ifndef _RWSTD_NO_WCHAR_T
#  define DEFINE_STRING_TEST_FUNCTIONS(fname)                           \
    static VoidFunc* const fname ## _func_array [] = {                  \
      TFUNC_ADDR (fname, char, std::char_traits, std::allocator),       \
      TFUNC_ADDR (fname, char, std::char_traits, UserAlloc),            \
      TFUNC_ADDR (fname, char, UserTraits,       std::allocator),       \
      TFUNC_ADDR (fname, char, UserTraits,       UserAlloc),            \
                                                                        \
      TFUNC_ADDR (fname, wchar_t, std::char_traits, std::allocator),    \
      TFUNC_ADDR (fname, wchar_t, std::char_traits, UserAlloc),         \
      TFUNC_ADDR (fname, wchar_t, UserTraits,       std::allocator),    \
      TFUNC_ADDR (fname, wchar_t, UserTraits,       UserAlloc),         \
                                                                        \
      (VoidFunc*)0, /* std::char_traits<UserChar> not allowed */        \
      (VoidFunc*)0, /* std::char_traits<UserChar> not allowed */        \
      TFUNC_ADDR (fname, UserChar, UserTraits, std::allocator),         \
      TFUNC_ADDR (fname, UserChar, UserTraits, UserAlloc)               \
    }

#else   // if defined (_RWSTD_NO_WCHAR_T)
#  define DEFINE_STRING_TEST_FUNCTIONS(fname)                           \
    static VoidFunc* const fname ## _func_array [] = {                  \
      TFUNC_ADDR (fname, char, std::char_traits, std::allocator),       \
      TFUNC_ADDR (fname, char, std::char_traits, UserAlloc),            \
      TFUNC_ADDR (fname, char, UserTraits,       std::allocator),       \
      TFUNC_ADDR (fname, char, UserTraits,       UserAlloc),            \
                                                                        \
      (VoidFunc*)0, /* wchar_t disabled */                              \
      (VoidFunc*)0, /* wchar_t disabled */                              \
      (VoidFunc*)0, /* wchar_t disabled */                              \
      (VoidFunc*)0, /* wchar_t disabled */                              \
                                                                        \
      (VoidFunc*)0, /* std::char_traits<UserChar> not allowed */        \
      (VoidFunc*)0, /* std::char_traits<UserChar> not allowed */        \
      TFUNC_ADDR (fname, UserChar, UserTraits, std::allocator),         \
      TFUNC_ADDR (fname, UserChar, UserTraits, UserAlloc)               \
    }

#endif   // _RWSTD_NO_WCHAR_T


#endif   // RW_21_STRINGS_H_INCLUDED
