/************************************************************************
*
* 23.containers.h - definitions of helpers used in clause 23 tests
*
* $Id: 23.containers.h$
*
***************************************************************************
*
* 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_23_CONTAINERS_H_INCLUDED
#define RW_23_CONTAINERS_H_INCLUDED

#include <testdefs.h>
#include <rw_value.h>       // for UserClass, UserPOD
#include <rw_char.h>        // for rw_expand ()

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

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

    // identifiers for the T template argument
    enum ElemId { UserPOD, UserClass };

    // identifiers for the Allocator template argument
    enum AllocId { DefaultAlloc, UserAlloc };

    // identifiers for the Iterator template argument
    // used with member templates
    enum IteratorId {
        None,
        Input, Forward, Bidir, Random,
        Pointer, ConstPointer,
        Iterator, ConstIterator,
        ReverseIterator, ConstReverseIterator
    };

    enum ContainerId {
        List, Vector, Deque, Queue, Stack
    };

    // identifies a set of overloaded member or non-member
    // container functions
    enum FuncId {
        // 6 bits, 64 functions max
        // common
        /*  0 */ fid_ctor,
        /*  1 */ fid_op_set,
        /*  2 */ fid_assign,
        /*  3 */ fid_get_allocator,
        /*  4 */ fid_begin,
        /*    */ fid_begin_const = fid_begin,
        /*  5 */ fid_end,
        /*    */ fid_end_const = fid_end,
        /*  6 */ fid_rbegin,
        /*    */ fid_rbegin_const = fid_rbegin,
        /*  7 */ fid_rend,
        /*    */ fid_rend_const = fid_rend,
        /*  8 */ fid_empty,
        /*  9 */ fid_size,
        /* 10 */ fid_max_size,
        /* 11 */ fid_resize,
        /* 12 */ fid_insert,
        /* 13 */ fid_erase,
        /* 14 */ fid_swap,
        /* 15 */ fid_clear,
        /* 16 */ fid_op_equal,
        /* 17 */ fid_op_less,
        /* 18 */ fid_op_not_equal,
        /* 19 */ fid_op_greater,
        /* 20 */ fid_op_greater_equal,
        /* 21 */ fid_op_less_equal,
        /* 22 */ fid_push_back,

        // list, deque, vector
        /* 23 */ fid_front,
        /*    */ fid_front_const = fid_front,
        /* 24 */ fid_back,
        /*    */ fid_back_const = fid_back,
        /* 25 */ fid_pop_back,

        // list, deque
        /* 26 */ fid_push_front,
        /* 27 */ fid_pop_front,

        // list
        /* 28 */ fid_splice,
        /* 29 */ fid_remove,
        /* 30 */ fid_remove_if,
        /* 31 */ fid_unique,
        /* 32 */ fid_merge,
        /* 33 */ fid_sort,
        /* 34 */ fid_reverse,

        // vector, string, deque
        /* 35 */ fid_op_index,
        /*    */ fid_op_index_const = fid_op_index,
        /* 36 */ fid_at,
        /*    */ fid_at_const = fid_at,

        // vector, string
        /* 37 */ fid_capacity,
        /* 38 */ fid_reserve,

        // vector<bool>
        /* 39 */ fid_flip,

        /* -- */ 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_ref,     // reference
        /*  4 */ arg_cref,    // const_reference
        /*  5 */ arg_iter,    // iterator
        /*  6 */ arg_citer,   // const_iterator
        /*  7 */ arg_range,   // Iterator, Iterator
        /*  8 */ arg_cont,    // container& (or this for member functions)
        /*  9 */ arg_ccont,   // const container& (or const this for members)
        /* 10 */ arg_alloc,   // const allocator&
        /* 11 */ arg_pred,    // Predicate
        /* 12 */ arg_bpred,   // BinaryPredicate
        /* 13 */ arg_comp,    // Compare
        /* -- */ arg_bits = 4,
        /* -- */ arg_mask = 15
    };

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

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

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

struct ContainerFunc
{
    ContainerIds::ElemId      elem_id_;
    ContainerIds::AllocId     alloc_id_;
    ContainerIds::IteratorId  iter_id_;
    ContainerIds::ContainerId cont_id_;
    _RWSTD_SIZE_T             which_;
};


// describes a single test case for any overload of any container
// function (the same test case can be used to exercise more
// than one overload of the same function)
struct ContainerTestCase
{
    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

    int            bthrow;    // exception expected
};


// describes a set of test cases for a single overload of a function
struct ContainerTest
{
    // container function overload to exercise
    _RWSTD_SIZE_T which;

    // test cases to exercise overload with
    const ContainerTestCase *cases;

    // number of test cases
    _RWSTD_SIZE_T case_count;
};


typedef void ContainerTestFunc (const ContainerFunc&, const ContainerTestCase&);

_TEST_EXPORT int
rw_run_cont_test (int, char**, const char*, const char*,
                  ContainerIds::ContainerId, ContainerTestFunc*,
                  const ContainerTest*, _RWSTD_SIZE_T);

typedef void VoidFunc ();

_TEST_EXPORT int
rw_run_cont_test (int, char**, const char*, const char*,
                  ContainerIds::ContainerId, VoidFunc* const*,
                  const ContainerTest*, _RWSTD_SIZE_T);

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

template <class T>
class ContainerTestCaseData
{
private:

    // not defined, not copyable, not assignable
    ContainerTestCaseData (const ContainerTestCaseData&);
    void operator= (const ContainerTestCaseData&);

    // 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 container 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 T* str_;   // pointer to the expanded string
    const T* arg_;   // pointer to the expanded argument
    const T* res_;   // pointer to the expanded result

    const ContainerFunc     &func_;
    const ContainerTestCase &tcase_;

    // converts the narrow (and possibly) condensed strings to fully
    // expanded wide character arrays that can be used to construct
    // container objects
    ContainerTestCaseData (const ContainerFunc&,
                           const ContainerTestCase&);

    ~ContainerTestCaseData ();
};


template <class T>
ContainerTestCaseData<T>::
ContainerTestCaseData (const ContainerFunc     &func,
                       const ContainerTestCase &tcase)
    : func_ (func), tcase_ (tcase)
{
    char buf [256];

    strlen_ = sizeof (buf);
    char* str = rw_expand (buf, tcase.str, tcase.str_len, &strlen_);
    str_ = T::from_char (str, strlen_);
    if (str != buf)
        delete[] str;

    arglen_ = sizeof (buf);
    str = rw_expand (buf, tcase.arg, tcase.arg_len, &arglen_);
    arg_ = T::from_char (str, arglen_);
    if (str != buf)
        delete[] str;

    reslen_ = sizeof (buf);
    str = rw_expand (buf, tcase.res, tcase.nres, &reslen_);
    res_ = T::from_char (str, reslen_);
    if (str != buf)
        delete[] str;

    // compute the offset and extent of the container 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_;
}


template <class T>
ContainerTestCaseData<T>::
~ContainerTestCaseData ()
{
    // clean up dynamically allocated memory

    // cast away the constness of the pointers to work around
    // an HP aCC 6.16 and prior bug described in STDCXX-802
    delete[] _RWSTD_CONST_CAST (T*, str_);
    delete[] _RWSTD_CONST_CAST (T*, arg_);
    delete[] _RWSTD_CONST_CAST (T*, res_);
}

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

// base class-functor for the range template overloads testing
template <class Cont>
struct ContRangeBase {

    typedef typename Cont::value_type                 ContVal;
    typedef typename Cont::iterator                   ContIter;
    typedef typename Cont::const_iterator             ContConstIter;
    typedef typename Cont::reverse_iterator           ContRevIter;
    typedef typename Cont::const_reverse_iterator     ContConstRevIter;

    ContRangeBase () { }

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

    static ContIter
    begin (Cont &cont, ContIter*) {
        return cont.begin ();
    }

    static ContConstIter
    begin (const Cont &cont, ContConstIter*) {
        return cont.begin ();
    }

    static ContRevIter
    begin (Cont &cont, ContRevIter*) {
        return cont.rbegin ();
    }

    static ContConstRevIter
    begin (const Cont &cont, ContConstRevIter*) {
        return cont.rbegin ();
    }

    virtual Cont&
    operator() (Cont &cont, const ContainerTestCaseData<ContVal>&) const {
        RW_ASSERT (!"logic error: should be never called");
        return cont;
    }
};

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

#  define CONTAINER_TEST_DISPATCH(Alloc, fname, func, tcase)    \
    if (ContainerIds::UserPOD == func.elem_id_)                 \
        fname (UserPOD (), (Alloc<UserPOD>*)0, func, tcase);    \
    else                                                        \
        fname (UserClass (), (Alloc<UserClass>*)0, func, tcase)


#define DEFINE_CONTAINER_TEST_DISPATCH(fname)                             \
    static void                                                           \
    fname (const ContainerFunc     &func,                                 \
           const ContainerTestCase &tcase) {                              \
        if (ContainerIds::DefaultAlloc == func.alloc_id_) {               \
            CONTAINER_TEST_DISPATCH (std::allocator, fname, func, tcase); \
        }                                                                 \
        else if (ContainerIds::UserAlloc == func.alloc_id_) {             \
            CONTAINER_TEST_DISPATCH (UserAlloc, fname, func, tcase);      \
        }                                                                 \
        else                                                              \
            RW_ASSERT (!"logic error: bad allocator");                    \
    } typedef void rw_unused_typedef


#define CONTAINER_TFUNC(T, Allocator)         \
    void (*)(T*, Allocator<T>*,               \
             const ContainerTestCaseData<T>&)

#define CONTAINER_TFUNC_ADDR(fname, T, Allocator) \
    (VoidFunc*)(CONTAINER_TFUNC (T, Allocator))   \
        &fname<T, Allocator<T> >

#define DEFINE_CONTAINER_TEST_FUNCTIONS(fname)                 \
    static VoidFunc* const fname ## _func_array [] = {         \
      CONTAINER_TFUNC_ADDR (fname, UserPOD, std::allocator),   \
      CONTAINER_TFUNC_ADDR (fname, UserPOD, UserAlloc),        \
                                                               \
      CONTAINER_TFUNC_ADDR (fname, UserClass, std::allocator), \
      CONTAINER_TFUNC_ADDR (fname, UserClass, UserAlloc)       \
    }

#endif   // RW_23_CONTAINERS_H_INCLUDED
