Index

    #ifndef __PTYPES_H__
    #define __PTYPES_H__

    #ifndef __PPORT_H__
    #include "pport.h"
    #endif

    #include <string.h>

    PTYPES_BEGIN

    #ifdef _MSC_VER
    #pragma pack(push, 4)
    // disable "non dll-interface class '...' used as base for dll-interface class '...'" warning
    #pragma warning(disable : 4275)
    // disable "conditional expression constant" warning
    #pragma warning(push)
    #pragma warning(disable : 4127)
    #endif

    int   __PFASTCALL pincrement(int* target);
    int   __PFASTCALL pdecrement(int* target);
    int   __PFASTCALL pexchange(int* target, int value);
    void* __PFASTCALL pexchange(void** target, void* value);

    template <class T> inline T* tpexchange(T** target, T* value)
        { return (T*)pexchange((void**)target, (void*)value); }

    #if (__GNUC__ == 3) && (__GNUC_MINOR__ >= 3)
    #  define VARIANT_TYPECAST_HACK
    #endif

    // -------------------------------------------------------------------- //
    // --- string class --------------------------------------------------- //
    // -------------------------------------------------------------------- //

    // dynamic string class with thread-safe ref-counted buffer

    struct _strrec
    {
        int refcount;
        int length;
    };
    typedef _strrec* _pstrrec;

    #define STR_BASE(x)      (_pstrrec(x)-1)
    #define STR_REFCOUNT(x)  (STR_BASE(x)->refcount)
    #define STR_LENGTH(x)    (STR_BASE(x)->length)

    #define PTR_TO_PSTRING(p)   (pstring(&(p)))
    #define PTR_TO_STRING(p)    (*PTR_TO_PSTRING(p))

    extern char* emptystr;

    class string
    {
        friend class variant;

    protected:
        char* data;

        static void idxerror();

        void _alloc(int);
        void _realloc(int);
        void _free();

        void initialize()  { data = emptystr; }
        void initialize(const char*, int);
        void initialize(const char*);
        void initialize(char);
        void initialize(const string& s);
        void initialize(const char*, int, const char*, int);
        void initialize(const variant&);
        void finalize();

        void assign(const char*, int);
        void assign(const char*);
        void assign(const string&);
        void assign(char);

    #ifdef CHECK_BOUNDS
        void idx(int index) const  { if (unsigned(index) >= unsigned(STR_LENGTH(data))) idxerror(); }
    #else
        void idx(int) const        { }
    #endif

        string(const char* s1, int len1, const char* s2, int len2)  { initialize(s1, len1, s2, len2); }

    public:
        friend int    length(const string& s);
        friend int    refcount(const string& s);
        friend void   assign(string& s, const char* buf, int len);
        friend void   clear(string& s);
        friend bool   isempty(const string& s);
        friend char*  setlength(string&, int);
        friend char*  unique(string&);
        friend void   concat(string& s, const char* sc, int catlen);
        friend void   concat(string& s, const char* s1);
        friend void   concat(string& s, char s1);
        friend void   concat(string& s, const string& s1);
        friend string copy(const string& s, int from, int cnt);
        friend string copy(const string& s, int from);
        friend void   ins(const char* s1, int s1len, string& s, int at);
        friend void   ins(const char* s1, string& s, int at);
        friend void   ins(char s1, string& s, int at);
        friend void   ins(const string& s1, string& s, int at);
        friend void   del(string& s, int at, int cnt);
        friend void   del(string& s, int at);
        friend int    pos(const char* s1, const string& s);
        friend int    pos(char s1, const string& s);
        friend int    pos(const string& s1, const string& s);
        friend int    rpos(char s1, const string& s);
        friend bool   contains(const char* s1, int len, const string& s, int at);
        friend bool   contains(const char* s1, const string& s, int at);
        friend bool   contains(char s1, const string& s, int at);
        friend bool   contains(const string& s1, const string& s, int at);
        friend string dup(const string& s);

        string()                                      { initialize(); }
        string(const char* sc, int initlen)           { initialize(sc, initlen); }
        string(const char* sc)                        { initialize(sc); }
        string(char c)                                { initialize(c); }
        string(const string& s)                       { initialize(s); }
        ~string()                                     { finalize(); }

    #ifdef VARIANT_TYPECAST_HACK
        string(const variant& v)                      { initialize(v); }
    #endif

        string& operator=  (const char* sc)           { assign(sc); return *this; }
        string& operator=  (char c)                   { assign(c); return *this; }
        string& operator=  (const string& s)          { assign(s); return *this; }
        string& operator+= (const char* sc)           { concat(*this, sc); return *this; }
        string& operator+= (char c)                   { concat(*this, c); return *this; }
        string& operator+= (const string& s)          { concat(*this, s); return *this; }

        string  operator+  (const char* sc) const;
        string  operator+  (char c) const;
        string  operator+  (const string& s) const;

        friend string operator+ (const char* sc, const string& s);
        friend string operator+ (char c, const string& s);

        bool operator== (const char* sc) const        { return strcmp(data, sc) == 0; }
        bool operator== (char) const;
        bool operator== (const string&) const;
        bool operator!= (const char* sc) const        { return !(*this == sc); }
        bool operator!= (char c) const                { return !(*this == c); }
        bool operator!= (const string& s) const       { return !(*this == s); }

        friend bool operator== (const char*, const string&);
        friend bool operator== (char, const string&);
        friend bool operator!= (const char*, const string&);
        friend bool operator!= (char, const string&);

        operator const char*() const                  { return data; }
        operator const uchar*() const                 { return (uchar*)data; }

        char&       operator[] (int i)                { idx(i); return unique(*this)[i]; }
        const char& operator[] (int i) const          { idx(i); return data[i]; }

        friend void initialize(string& s);
        friend void initialize(string& s, const string& s1);
        friend void initialize(string& s, const char* s1);
        friend void finalize(string& s);
    };

    typedef string* pstring;

    inline int  length(const string& s)                     { return STR_LENGTH(s.data); }
    inline int  refcount(const string& s)                   { return STR_REFCOUNT(s.data); }
    inline void assign(string& s, const char* buf, int len) { s.assign(buf, len); }
    inline void clear(string& s)                            { s.finalize(); }
    inline bool isempty(const string& s)                    { return length(s) == 0; }
    inline int  pos(const string& s1, const string& s)      { return pos(s1.data, s); }
    inline bool operator== (const char* sc, const string& s){ return s == sc; }
    inline bool operator== (char c, const string& s)        { return s == c; }
    inline bool operator!= (const char* sc, const string& s){ return s != sc; }
    inline bool operator!= (char c, const string& s)        { return s != c; }
    inline void initialize(string& s)                       { s.initialize(); }
    inline void initialize(string& s, const string& s1)     { s.initialize(s1); }
    inline void initialize(string& s, const char* s1)       { s.initialize(s1); }
    inline void finalize(string& s)                         { s.finalize(); }

    extern int stralloc;

    extern string nullstring;

    // -------------------------------------------------------------------- //
    // --- string utilities ----------------------------------------------- //
    // -------------------------------------------------------------------- //

    string fill(int width, char pad);
    string pad(const string& s, int width, char c, bool left = true);

    string itostring(large value, int base, int width = 0, char pad = 0);
    string itostring(ularge value, int base, int width = 0, char pad = 0);
    string itostring(int value, int base, int width = 0, char pad = 0);
    string itostring(unsigned value, int base, int width = 0, char pad = 0);
    string itostring(large v);
    string itostring(ularge v);
    string itostring(int v);
    string itostring(unsigned v);

    large  stringtoi(const char*);
    large  stringtoie(const char*);
    ularge stringtoue(const char*, int base);

    string lowercase(const char* s);
    string lowercase(const string& s);

    char hex4(char c);

    inline char locase(char c)
        { if (c >= 'A' && c <= 'Z') return char(c + 32); return c; }

    inline char upcase(char c)
        { if (c >= 'a' && c <= 'z') return char(c - 32); return c; }

    inline int hstrlen(const char* p) // some Unix systems do not accept NULL
        { return p == nil ? 0 : (int)strlen(p); }

    // -------------------------------------------------------------------- //
    // --- character set -------------------------------------------------- //
    // -------------------------------------------------------------------- //

    const int  _csetbits = 256;
    const int  _csetbytes = _csetbits / 8;
    const int  _csetwords = _csetbytes / sizeof(int);
    const char _csetesc = '~';

    class cset
    {
    protected:
        char data[_csetbytes];

        void assign(const cset& s)                  { memcpy(data, s.data, _csetbytes); }
        void assign(const char* setinit);
        void clear()                                { memset(data, 0, _csetbytes); }
        void fill()                                 { memset(data, -1, _csetbytes); }
        void include(char b)                        { data[uchar(b) / 8] |= uchar(1 << (uchar(b) % 8)); }
        void include(char min, char max);
        void exclude(char b)                        { data[uchar(b) / 8] &= uchar(~(1 << (uchar(b) % 8))); }
        void unite(const cset& s);
        void subtract(const cset& s);
        void intersect(const cset& s);
        void invert();
        bool contains(char b) const                 { return (data[uchar(b) / 8] & (1 << (uchar(b) % 8))) != 0; }
        bool eq(const cset& s) const                { return memcmp(data, s.data, _csetbytes) == 0; }
        bool le(const cset& s) const;

    public:
        cset()                                      { clear(); }
        cset(const cset& s)                         { assign(s); }
        cset(const char* setinit)                   { assign(setinit); }

        cset& operator=  (const cset& s)            { assign(s); return *this; }
        cset& operator+= (const cset& s)            { unite(s); return *this; }
        cset& operator+= (char b)                   { include(b); return *this; }
        cset  operator+  (const cset& s) const      { cset t = *this; return t += s; }
        cset  operator+  (char b) const             { cset t = *this; return t += b; }
        cset& operator-= (const cset& s)            { subtract(s); return *this; }
        cset& operator-= (char b)                   { exclude(b); return *this; }
        cset  operator-  (const cset& s) const      { cset t = *this; return t -= s; }
        cset  operator-  (char b) const             { cset t = *this; return t -= b; }
        cset& operator*= (const cset& s)            { intersect(s); return *this; }
        cset  operator*  (const cset& s) const      { cset t = *this; return t *= s; }
        cset  operator!  () const                   { cset t = *this; t.invert(); return t; }
        bool  operator== (const cset& s) const      { return eq(s); }
        bool  operator!= (const cset& s) const      { return !eq(s); }
        bool  operator<= (const cset& s) const      { return le(s); }
        bool  operator>= (const cset& s) const      { return s.le(*this); }

        friend cset operator+ (char b, const cset& s);
        friend bool operator& (char b, const cset& s);
        friend void assign(cset& s, const char* setinit);
        friend void clear(cset& s);
        friend void fill(cset& s);
        friend void include(cset& s, char b);
        friend void include(cset& s, char min, char max);
        friend void exclude(cset& s, char b);

        friend string asstring(const cset& s);
    };

    inline cset operator+ (char b, const cset& s)     { return s + b; }
    inline bool operator& (char b, const cset& s)     { return s.contains(b); }
    inline void assign(cset& s, const char* setinit)  { s.assign(setinit); }
    inline void clear(cset& s)                        { s.clear(); }
    inline void fill(cset& s)                         { s.fill(); }
    inline void include(cset& s, char b)              { s.include(b); }
    inline void include(cset& s, char min, char max)  { s.include(min, max); }
    inline void exclude(cset& s, char b)              { s.exclude(b); }

    // -------------------------------------------------------------------- //
    // --- basic abstract classes ----------------------------------------- //
    // -------------------------------------------------------------------- //

    // basic class with virtual destructor; historically was used as a base
    // for all list items. also helps to count the number of created and
    // destroyed objects in a program (objalloc global) in DEBUG mode, to
    // detect memory leaks. most classes in ptypes are derived from unknown.

    extern int objalloc;

    class unknown
    {
    private:
        // make all classes non-copyable by default
        unknown(const unknown&);
        const unknown& operator= (const unknown&);
    public:
    #ifdef COUNT_OBJALLOC
        unknown()           { pincrement(&objalloc); }
        virtual ~unknown()  { pdecrement(&objalloc); }
    #else
        unknown()           { }
        virtual ~unknown()  { }
    #endif
    };

    typedef unknown* punknown;

    // provide non-copyable base for all classes that are
    // not derived from 'unknown'

    class noncopyable
    {
    private:
        noncopyable(const noncopyable&);
        const noncopyable& operator= (const noncopyable&);
    public:
        noncopyable() {}
        ~noncopyable() {}
    };

    // -------------------------------------------------------------------- //
    // --- exception ------------------------------------------------------ //
    // -------------------------------------------------------------------- //

    // the basic exception class. NOTE: the library always throws dynamically
    // allocated exception objects.

    class exception: public unknown
    {
    protected:
        string message;
    public:
        exception(const char* imsg);
        exception(const string& imsg);
        virtual ~exception();
        virtual string get_message() { return message; }
    };

    // conversion exception class for stringtoie() and stringtoue()

    class econv: public exception
    {
    public:
        econv(const char* msg): exception(msg)  {}
        econv(const string& msg): exception(msg)  {}
        virtual ~econv();
    };

    // -------------------------------------------------------------------- //
    // --- tpodlist ------------------------------------------------------- //
    // -------------------------------------------------------------------- //

    // _podlist implements dynamic array of small POD structures; it serves
    // as a basis for all list types in the library. this class is undocumented.
    // tpodlist template must be used instead.

    class _podlist: public noncopyable
    {
    protected:
        void* list;                   // pointer to the array
        int   count;                  // number of items in the list
        int   capacity;               // allocated for the list
        int   itemsize;               // list item size

        static void idxerror();

        _podlist& operator =(const _podlist& t);

        void  grow();
        void* doins(int index);
        void  doins(int index, const _podlist&);
        void* doget(int index) const            { return (char*)list + index * itemsize; }
        void  dodel(int index);
        void  dodel(int index, int count);
        void  dopop();

    #ifdef CHECK_BOUNDS
        void idx(int index) const               { if (unsigned(index) >= unsigned(count)) idxerror(); }
        void idxa(int index) const              { if (unsigned(index) > unsigned(count)) idxerror(); }
    #else
        void idx(int) const                     { }
        void idxa(int) const                    { }
    #endif

    public:
        _podlist(int itemsize);
        ~_podlist();

        int   get_count() const                 { return count; }
        void  set_count(int newcount, bool zero = false);
        int   get_capacity() const              { return capacity; }
        void  set_capacity(int newcap);
        void  clear()                           { set_count(0); }
        void  pack()                            { set_capacity(count); }
        void* ins(int index)                    { idxa(index); return doins(index); }
        void  ins(int index, const _podlist& t) { idxa(index); doins(index, t); }
        void* add();
        void  add(const _podlist& t);
        void* operator [](int index)            { idx(index); return doget(index); }
        void* top()                             { return operator [](count - 1); }
        void  del(int index)                    { idx(index); dodel(index); }
        void  del(int index, int count)         { idx(index); dodel(index, count); }
        void  pop()                             { idx(0); dopop(); }
    };

    // tpodlist is a fully-inlined template based on _podlist

    template <class X, bool initzero = false> class tpodlist: public _podlist
    {
    protected:
        X&   dozero(X& t)                       { if (initzero) memset(&t, 0, sizeof(X)); return t; }
        X&   doget(int index) const             { return ((X*)list)[index]; }
        X&   doins(int index)                   { X& t = *(X*)_podlist::doins(index); return dozero(t); }
        void doins(int index, const X& item)    { *(X*)_podlist::doins(index) = item; }

    public:
        tpodlist(): _podlist(sizeof(X))         {}
        tpodlist<X, initzero>& operator =(const tpodlist<X, initzero>& t)
                                                { _podlist::operator =(t); return *this; }

        void set_count(int newcount)            { _podlist::set_count(newcount, initzero); }
        X&   ins(int index)                     { idxa(index); return doins(index); }
        void ins(int index, const X& item)      { idxa(index); doins(index, item); }
        void ins(int index, const tpodlist<X, initzero>& t)
                                                { _podlist::ins(index, t); }
        X&   add()                              { grow(); return dozero(doget(count++)); }
        void add(const X& item)                 { grow(); doget(count++) = item; }
        void add(const tpodlist<X, initzero>& t)
    					    { _podlist::add(t); }
        X&   operator [](int index)             { idx(index); return doget(index); }
        const X& operator [](int index) const   { idx(index); return doget(index); }
        X&   top()                              { idx(0); return doget(count - 1); }
    };

    // -------------------------------------------------------------------- //
    // --- tobjlist ------------------------------------------------------- //
    // -------------------------------------------------------------------- //

    // _objlist is a base for the tobjlist template, don't use it directly.
    // also, _objlist is a base for _strlist and derivatives.

    class _objlist: public unknown, protected tpodlist<void*, true>
    {
    protected:
        struct
        {
            unsigned ownobjects :1;   // list is responsible for destroying the items; used in _objlist
            unsigned ownslobjects :1; // same but for _strlist items (in _stritem structure)
            unsigned sorted :1;       // sorted list (_objlist+)
            unsigned duplicates :1;   // sorted: allows duplicate keys (_objlist+)
            unsigned casesens :1;     // sorted: string comparison is case sensitive (_strlist+)
            unsigned _reserved :27;
        } config;

        _objlist(bool ownobjects);	  // we hide this ctor, since _objlist actually can't free objects

        void* doget(int index) const            { return ((void**)list)[index]; }
        void  doput(int index, void* obj);
        void  dodel(int index);
        void  dodel(int index, int count);
        void* dopop();
        void  dofree(int index, int count);

        virtual void dofree(void* obj);        // pure method; defined in tobjlist instances
        virtual int compare(const void* key, const void* obj) const;  // pure method; defined in _strlist

    public:
        _objlist();
        virtual ~_objlist();

        int   get_count() const                 { return count; }
        void  set_count(int newcount);
        int   get_capacity() const              { return capacity; }
        void  set_capacity(int newcap)          { tpodlist<void*,true>::set_capacity(newcap); }
        void  clear()                           { set_count(0); }
        void  pack()                            { tpodlist<void*,true>::pack(); }
        void  ins(int index, void* obj)         { tpodlist<void*,true>::ins(index, obj); }
        void  add(void* obj)                    { tpodlist<void*,true>::add(obj); }
        void  put(int index, void* obj)         { idx(index); doput(index, obj); }
        void* operator [](int index) const      { idx(index); return doget(index); }
        void* top() const                       { idx(0); return doget(count - 1); }
        void* pop()                             { idx(0); return dopop(); }
        void  del(int index)                    { idx(index); dodel(index); }
        void  del(int index, int count)         { idx(index); dodel(index, count); }
        int   indexof(void* obj) const;
        bool  search(const void* key, int& index) const;
    };

    // the tobjlist template implements a list of pointers to arbitrary
    // structures. optionally can automatically free objects (ownobjects)
    // when removed from a list. only 2 virtual functions are being
    // instantiated by this template, the rest is static code in _objlist.

    template <class X> class tobjlist: public _objlist
    {
    protected:
        X* doget(int index) const               { return (X*)_objlist::doget(index); }
        virtual void dofree(void* obj);

    public:
        tobjlist(bool ownobjects = false): _objlist(ownobjects)  {}
        virtual ~tobjlist();

        bool  get_ownobjects() const            { return config.ownobjects; }
        void  set_ownobjects(bool newval)       { config.ownobjects = newval; }
        void  ins(int index, X* obj)            { _objlist::ins(index, obj); }
        void  add(X* obj)                       { _objlist::add(obj); }
        void  put(int index, X* obj)            { _objlist::put(index, obj); }
        X*    operator [](int index) const      { idx(index); return (X*)doget(index); }
        X*    top() const                       { return (X*)_objlist::top(); }
        X*    pop()                             { return (X*)_objlist::pop(); }
        int   indexof(X* obj) const             { return _objlist::indexof(obj); }

    #ifdef PTYPES19_COMPAT
        friend inline void ins(tobjlist& s, int i, X* obj)          { s.ins(i, obj); }
        friend inline int  add(tobjlist& s, X* obj)                 { s.add(obj); return s.get_count() - 1; }
        friend inline void put(tobjlist& s, int i, X* obj)          { s.put(i, obj); }
        friend inline int  indexof(const tobjlist& s, X* obj)       { return s.indexof(obj); }
        friend inline int  push(tobjlist& s, X* obj)                { s.add(obj); return s.get_count() - 1; }
        friend inline X*   pop(tobjlist& s)                         { return (X*)s.pop(); }
        friend inline X*   top(const tobjlist& s)                   { return (X*)s.top(); }
        friend inline X*   get(const tobjlist& s, int i)            { return (X*)s[i]; }
    #endif
    };

    template <class X> void tobjlist<X>::dofree(void* item)
    {
        delete (X*)item;
    }

    template <class X> tobjlist<X>::~tobjlist()
    {
        set_count(0);
    }

    // -------------------------------------------------------------------- //
    // --- tstrlist ------------------------------------------------------- //
    // -------------------------------------------------------------------- //

    // _strlist is a base for the tstrlist template

    enum slflags
    {
        SL_SORTED = 0x0001,
        SL_DUPLICATES = 0x0002,
        SL_CASESENS = 0x0004,
        SL_OWNOBJECTS = 0x0008,
    };

    struct _stritem
    {
        string key;
        void* obj;

        _stritem(const string& ikey, void* iobj)
            : key(ikey), obj(iobj)  {}
    };

    class _strlist: protected tobjlist<_stritem>
    {
    protected:
        static void sortederror();
        static void notsortederror();
        static void duperror();

        virtual void dofree(void* item);
        virtual int  compare(const void* key, const void* item) const;
        virtual void dofreeobj(void* obj);          // pure; tstrlist overrides it

        const string& dogetkey(int index) const             { return doget(index)->key; }
        void* dogetobj(int index) const                     { return doget(index)->obj; }
        void  doins(int index, const string& key, void* obj);
        void  doput(int index, const string& key, void* obj);
        void  doput(int index, void* obj);

    public:
        _strlist(int flags = 0);
        virtual ~_strlist();

        int   get_count() const                             { return count; }
        void  set_count(int newcount)                       { tobjlist<_stritem>::set_count(newcount); }
        int   get_capacity() const                          { return capacity; }
        void  set_capacity(int newcap)                      { tobjlist<_stritem>::set_capacity(newcap); }
        void  clear()                                       { tobjlist<_stritem>::clear(); }
        void  pack()                                        { tobjlist<_stritem>::pack(); }
        bool  get_sorted() const                            { return config.sorted; }
        bool  get_duplicates() const                        { return config.duplicates; }
        bool  get_casesens() const                          { return config.casesens; }
        bool  get_ownobjects() const                        { return config.ownslobjects; }
        void  set_ownobjects(bool newval)                   { config.ownslobjects = newval; }
        void  ins(int index, const string& key, void* obj)  { idxa(index); doins(index, key, obj); }
        void  put(int index, const string& key, void* obj)  { idx(index); doput(index, key, obj); }
        void  put(int index, void* obj)                     { idx(index); doput(index, obj); }
        int   put(const string& key, void* obj);
        int   add(const string& key, void* obj);
        void* operator [](int index) const                  { idx(index); return dogetobj(index); }
        void* operator [](const char* key) const;
        const string& getkey(int index) const               { idx(index); return dogetkey(index); }
        bool  search(const char* key, int& index) const     { return _objlist::search(key, index); }
        void  del(int index)                                { idx(index); dodel(index); }
        void  del(int index, int delcount)                  { idx(index); dodel(index, delcount); }
        void  del(const char* key)                          { put(key, nil); }
        int   indexof(const char* key) const;
        int   indexof(void* obj) const;
    };

    // the tstrlist template implements a list of string/object pairs,
    // optionally sorted for fast searching by string key.

    template <class X> class tstrlist: public _strlist
    {
    protected:
        virtual void dofreeobj(void* obj);

    public:
        tstrlist(int flags = 0): _strlist(flags)  {}
        virtual ~tstrlist();

        void  ins(int index, const string& key, X* obj)     { _strlist::ins(index, key, obj); }
        void  put(int index, const string& key, X* obj)     { _strlist::put(index, key, obj); }
        void  put(int index, X* obj)                        { _strlist::put(index, obj); }
        int   put(const string& key, X* obj)                { return _strlist::put(key, obj); }
        int   add(const string& key, X* obj)                { return _strlist::add(key, obj); }
        X*    operator [](int index) const                  { return (X*)_strlist::operator [](index); }
        X*    operator [](const char* key) const            { return (X*)_strlist::operator [](key); }
        int   indexof(X* obj) const                         { return _strlist::indexof(obj); }
        int   indexof(const char* key) const                { return _strlist::indexof(key); }

    #ifdef PTYPES19_COMPAT
        // pre-2.0 interface for backwards compatibility
        friend inline void ins(tstrlist& s, int i, const string& str, X* obj)  { s.ins(i, str, obj); }
        friend inline int  add(tstrlist& s, const string& str, X* obj)         { return s.add(str, obj); }
        friend inline void put(tstrlist& s, int i, const string& str, X* obj)  { s.put(i, str, obj); }
        friend inline void put(tstrlist& s, int i, X* obj)                     { s.put(i, obj); }
        friend inline int indexof(const tstrlist& s, X* obj)                   { return s.indexof(obj); }
        friend inline X* get(const tstrlist& s, int i)                         { return (X*)s[i]; }
    #endif
    };

    template <class X> void tstrlist<X>::dofreeobj(void* obj)
    {
        delete (X*)obj;
    }

    template <class X> tstrlist<X>::~tstrlist()
    {
        set_count(0);
    }

    // -------------------------------------------------------------------- //
    // --- textmap -------------------------------------------------------- //
    // -------------------------------------------------------------------- //

    // textmap is a list of string pairs (key/value)

    struct _textitem
    {
        string key;
        string value;

        _textitem(const string& ikey, const string& ivalue)
            : key(ikey), value(ivalue)  {}
    };

    class textmap: protected tobjlist<_textitem>
    {
    protected:
        virtual int compare(const void* key, const void* item) const;
        const string& dogetvalue(int index) const           { return doget(index)->value; }
        const string& dogetkey(int index) const             { return doget(index)->key; }

    public:
        textmap(bool casesens = false);
        virtual ~textmap();

        int   get_count() const                             { return tobjlist<_textitem>::get_count(); }
        void  pack()                                        { tobjlist<_textitem>::pack(); }
        void  clear()                                       { tobjlist<_textitem>::clear(); }
        int   put(const string& key, const string& value);
        void  del(int index)                                { idx(index); dodel(index); }
        void  del(const char* key)                          { put(key, nullstring); }
        const string& get(int index) const                  { idx(index); return dogetvalue(index); }
        const string& getkey(int index) const               { idx(index); return dogetkey(index); }
        const string& get(const char* key) const;
        const string& operator [](int index) const          { return get(index); }
        const string& operator [](const char* key) const    { return get(key); }
        int   indexof(const char* key) const;
    };

    // -------------------------------------------------------------------- //
    // --- component ------------------------------------------------------ //
    // -------------------------------------------------------------------- //

    // the component class is an abstract class that provides reference
    // counting and delete notification mechanisms. all stream classes
    // in ptypes are derived from component.

    // class ID's for all basic types: the first byte (least significant)
    // contains the base ID, the next is for the second level of inheritance,
    // etc. total of 4 levels allowed for basic types. call classid() for an
    // object, mask out first N bytes of interest and compare with a CLASS_XXX
    // value. f.ex. to determine whether an object is of type infile or any
    // derivative: (o->classid() & 0xffff) == CLASS2_INFILE. this scheme is for
    // internal use by PTypes and Objection; partly replaces the costly C++ RTTI
    // system.

    // first level of inheritance
    const int CLASS_UNDEFINED = 0x00000000;
    const int CLASS_INSTM     = 0x00000001;
    const int CLASS_OUTSTM    = 0x00000002;
    const int CLASS_UNIT      = 0x00000003;

    // second level of inheritance
    const int CLASS2_INFILE    = 0x00000100 | CLASS_INSTM;
    const int CLASS2_INMEMORY  = 0x00000200 | CLASS_INSTM;
    const int CLASS2_FDX       = 0x00000300 | CLASS_INSTM;
    const int CLASS2_OUTFILE   = 0x00000100 | CLASS_OUTSTM;
    const int CLASS2_OUTMEMORY = 0x00000200 | CLASS_OUTSTM;

    // third level of inheritance
    const int CLASS3_LOGFILE   = 0x00010000 | CLASS2_OUTFILE;
    const int CLASS3_IPSTM     = 0x00020000 | CLASS2_FDX;
    const int CLASS3_NPIPE     = 0x00030000 | CLASS2_FDX;

    class component: public unknown
    {
    protected:
        int                  refcount;     // reference counting, used by addref() and release()
        tobjlist<component>* freelist;     // list of components to notify about destruction, safer alternative to ref-counting
        void*                typeinfo;     // reserved for future use

        virtual void freenotify(component* sender);

    public:
        component();
        virtual ~component();
        void addnotification(component* obj);
        void delnotification(component* obj);

        friend component* addref(component*);
        friend bool release(component*);
        friend int refcount(component* c);

        virtual int classid();

        void  set_typeinfo(void* t) { typeinfo = t; }
        void* get_typeinfo()        { return typeinfo; }
    };

    typedef component* pcomponent;

    inline int refcount(component* c)  { return c->refcount; }

    template <class T> inline T* taddref(T* c)
        { return (T*)addref((component*)c); }

    template <class T> class compref
    {
    protected:
        T* ref;
    public:
        compref()                                   { ref = 0; }
        compref(const compref<T>& r)                { ref = taddref<T>(r.ref); }
        compref(T* c)                               { ref = taddref<T>(c); }
        ~compref()                                  { release(ref); }
        compref<T>& operator =(T* c);
        compref<T>& operator =(const compref<T>& r) { return operator =(r.ref); }
        T&   operator *() const                     { return *ref; }
        T*   operator ->() const                    { return ref; }
        bool operator ==(const compref<T>& r) const { return ref == r.ref; }
        bool operator ==(T* c) const                { return ref == c; }
        bool operator !=(const compref<T>& r) const { return ref != r.ref; }
        bool operator !=(T* c) const                { return ref != c; }
             operator T*() const                    { return ref; }
    };

    template <class T> compref<T>& compref<T>::operator =(T* c)
    {
        release(tpexchange<T>(&ref, taddref<T>(c)));
        return *this;
    }

    // -------------------------------------------------------------------- //
    // --- variant -------------------------------------------------------- //
    // -------------------------------------------------------------------- //

    enum {
        VAR_NULL,
        VAR_INT,
        VAR_BOOL,
        VAR_FLOAT,
        VAR_STRING,
        VAR_ARRAY,
        VAR_OBJECT,

        VAR_COMPOUND = VAR_STRING
    };

    class variant
    {
        friend class string;
        friend class _varray;

    protected:
        int tag;            // VAR_XXX
        union {
            large      i;   // 64-bit int value
            bool       b;   // bool value
            double     f;   // double value
            char*      s;   // string object; can't declare as string because of the union
            _varray*   a;   // pointer to a reference-counted _strlist object
            component* o;   // pointer to a reference-counted component object (or derivative)
        } value;            // we need this name to be able to copy the entire union in some situations

        void initialize()                       { tag = VAR_NULL; }
        void initialize(large v)                { tag = VAR_INT; value.i = v; }
        void initialize(bool v)                 { tag = VAR_BOOL; value.b = v; }
        void initialize(double v)               { tag = VAR_FLOAT; value.f = v; }
        void initialize(const char* v)          { tag = VAR_STRING; PTYPES_NAMESPACE::initialize(PTR_TO_STRING(value.s), v); }
        void initialize(const string& v)        { tag = VAR_STRING; PTYPES_NAMESPACE::initialize(PTR_TO_STRING(value.s), v); }
        void initialize(_varray* a);
        void initialize(component* o);
        void initialize(const variant& v);
        void finalize();

        void assign(large);
        void assign(bool);
        void assign(double);
        void assign(const char*);
        void assign(const string&);
        void assign(_varray*);
        void assign(component*);
        void assign(const variant&);

        bool equal(const variant& v) const;

        variant(_varray* a)                     { initialize(a); }

    public:
        // construction
        variant()                               { initialize(); }
        variant(int v)                          { initialize(large(v)); }
        variant(unsigned int v)                 { initialize(large(v)); }
        variant(large v)                        { initialize(v); }
        variant(bool v)                         { initialize(v); }
        variant(double v)                       { initialize(v); }
        variant(const char* v)                  { initialize(v); }
        variant(const string& v)                { initialize(v); }
        variant(component* v)                   { initialize(v); }
        variant(const variant& v)               { initialize(v); }
        ~variant()                              { finalize(); }

        // assignment
        variant& operator= (int v)              { assign(large(v)); return *this; }
        variant& operator= (unsigned int v)     { assign(large(v)); return *this; }
        variant& operator= (large v)            { assign(v); return *this; }
        variant& operator= (bool v)             { assign(v); return *this; }
        variant& operator= (double v)           { assign(v); return *this; }
        variant& operator= (const char* v)      { assign(v); return *this; }
        variant& operator= (const string& v)    { assign(v); return *this; }
        variant& operator= (component* v)       { assign(v); return *this; }
        variant& operator= (const variant& v)   { assign(v); return *this; }

        // typecast
        operator int() const;
        operator unsigned int() const;
        operator long() const;
        operator unsigned long() const;
        operator large() const;
        operator bool() const;
        operator double() const;
        operator string() const;
        operator component*() const;

        // comparison
        bool operator== (const variant& v) const  { return equal(v); }
        bool operator!= (const variant& v) const  { return !equal(v); }

        // typification
        friend void clear(variant&);
        friend int  vartype(const variant& v);
        friend bool isnull(const variant& v);
        friend bool isint(const variant& v);
        friend bool isbool(const variant& v);
        friend bool isfloat(const variant& v);
        friend bool isstring(const variant& v);
        friend bool isarray(const variant& v);
        friend bool isobject(const variant& v);
        friend bool iscompound(const variant& v);

        // array manipulation
        friend void aclear(variant&);
        friend variant aclone(const variant&);
        friend const variant& get(const variant&, const string& key);
        friend const variant& get(const variant&, large key);
        friend void put(variant&, const string& key, const variant& item);
        friend void put(variant&, large key, const variant& item);
        friend void del(variant&, const string& key);
        friend void del(variant&, large key);

        // indexed access to arrays
        friend int  alength(const variant&);
        friend void apack(variant&);
        friend bool anext(const variant& a, int&, variant& item);
        friend bool anext(const variant& a, int&, variant& item, string& key);
        friend int  aadd(variant&, const variant& item);
        friend void aput(variant&, int index, const variant& item);
        friend void ains(variant&, int index, const variant& item);
        friend void adel(variant&, int index);
        friend const variant& aget(const variant&, int index);
        friend string akey(const variant&, int index);

        const variant& operator[](const char* key) const    { return get(*this, string(key)); }
        const variant& operator[](const string& key) const  { return get(*this, key); }
        const variant& operator[](large key) const          { return get(*this, key); }

        // 'manual' initialization/finalization, undocumented. use with care!
        friend void initialize(variant& v);
        friend void initialize(variant& v, large i);
        friend void initialize(variant& v, int i);
        friend void initialize(variant& v, unsigned int i);
        friend void initialize(variant& v, bool i);
        friend void initialize(variant& v, double i);
        friend void initialize(variant& v, const char* i);
        friend void initialize(variant& v, const string& i);
        friend void initialize(variant& v, component* i);
        friend void initialize(variant& v, const variant& i);
        friend void finalize(variant& v);
    };

    typedef variant* pvariant;

    inline int  vartype(const variant& v)       { return v.tag; }
    inline bool isnull(const variant& v)        { return v.tag == VAR_NULL; }
    inline bool isint(const variant& v)         { return v.tag == VAR_INT; }
    inline bool isbool(const variant& v)        { return v.tag == VAR_BOOL; }
    inline bool isfloat(const variant& v)       { return v.tag == VAR_FLOAT; }
    inline bool isstring(const variant& v)      { return v.tag == VAR_STRING; }
    inline bool isarray(const variant& v)       { return v.tag == VAR_ARRAY; }
    inline bool isobject(const variant& v)      { return v.tag == VAR_OBJECT; }
    inline bool iscompound(const variant& v)    { return v.tag >= VAR_COMPOUND; }

    inline void initialize(variant& v)                   { v.initialize(); }
    inline void initialize(variant& v, large i)          { v.initialize(i); }
    inline void initialize(variant& v, int i)            { v.initialize(large(i)); }
    inline void initialize(variant& v, unsigned int i)   { v.initialize(large(i)); }
    inline void initialize(variant& v, bool i)           { v.initialize(i); }
    inline void initialize(variant& v, double i)         { v.initialize(i); }
    inline void initialize(variant& v, const char* i)    { v.initialize(i); }
    inline void initialize(variant& v, const string& i)  { v.initialize(i); }
    inline void initialize(variant& v, component* i)     { v.initialize(i); }
    inline void initialize(variant& v, const variant& i) { v.initialize(i); }
    inline void finalize(variant& v)                     { if (v.tag >= VAR_COMPOUND) v.finalize(); }

    extern const variant nullvar;

    // variant exception class; may be thrown when a variant
    // is being typecast'ed to 32-bit int and the value is
    // out of range

    class evariant: public exception
    {
    protected:
    public:
        evariant(const char* msg): exception(msg)  {}
        evariant(const string& msg): exception(msg)  {}
        virtual ~evariant();
    };

    // -------------------------------------------------------------------- //
    // --- pre-2.0 compatibility declarations ----------------------------- //
    // -------------------------------------------------------------------- //

    #ifdef PTYPES19_COMPAT

    // ptypes-1.9 objlist and strlist: accept only 'unknown' and
    // derivatives as a base type

    class objlist: public tobjlist<unknown>
    {
    public:
        objlist(bool ownobjects = false);
        virtual ~objlist();
    };

    inline int  length(const _objlist& s)                { return s.get_count(); }
    inline void setlength(_objlist& s, int newcount)     { s.set_count(newcount); }
    inline void pack(_objlist& s)                        { s.pack(); }
    inline void clear(_objlist& s)                       { s.clear(); }
    inline int  push(_objlist& s, unknown* obj)          { s.add(obj); return length(s) - 1; }
    inline unknown* top(const _objlist& s)               { return (unknown*)s.top(); }
    inline void ins(_objlist& s, int i, unknown* obj)    { s.ins(i, obj); }
    inline int  add(_objlist& s, unknown* obj)           { s.add(obj); return length(s) - 1; }
    inline void put(_objlist& s, int i, unknown* obj)    { s.put(i, obj); }
    inline unknown* get(const _objlist& s, int i)        { return (unknown*)s[i]; }
    inline unknown* pop(_objlist& s)                     { return (unknown*)s.pop(); }
    inline void del(_objlist& s, int i)                  { s.del(i); }
    inline int  indexof(const _objlist& s, unknown* obj) { return s.indexof(obj); }

    class strlist: public tstrlist<unknown>
    {
    public:
        strlist(int flags = 0);
        virtual ~strlist();
    };

    inline int  length(const _strlist& s)                                { return s.get_count(); }
    inline void clear(_strlist& s)                                       { s.clear(); }
    inline void pack(_strlist& s)                                        { s.pack(); }
    inline bool search(const _strlist& s, const char* key, int& i)       { return s.search(key, i); }
    inline void ins(_strlist& s, int i, const string& key, unknown* obj) { s.ins(i, key, obj); }
    inline int  add(_strlist& s, const string& key, unknown* obj)        { return s.add(key, obj); }
    inline void put(_strlist& s, int i, const string& key, unknown* obj) { s.put(i, key, obj); }
    inline void put(_strlist& s, int i, unknown* obj)                    { s.put(i, obj); }
    inline unknown* get(const _strlist& s, int i)                        { return (unknown*)s[i]; }
    inline const string& getstr(const _strlist& s, int i)                { return s.getkey(i); }
    inline void del(_strlist& s, int i)                                  { s.del(i); }
    inline int  find(const _strlist& s, const char* key)                 { return s.indexof(key); }
    inline int  indexof(const _strlist& s, unknown* obj)                 { return s.indexof(obj); }

    // ptypes-1.9 strmap: now replaced with _strlist(SL_SORTED)

    class strmap: public tstrlist<unknown>
    {
    public:
        strmap(int flags = 0);
        virtual ~strmap();
    };

    inline void     put(strmap& m, const string& key, unknown* obj)     { m.put(key, obj); }
    inline unknown* get(const strmap& m, const char* key)               { return m[key]; }
    inline void     del(strmap& m, const char* key)                     { m.del(key); }

    template <class X> class tstrmap: public strmap
    {
    public:
        tstrmap(): strmap()  {}
        tstrmap(int iflags): strmap(iflags)  {}
        friend inline X* get(const tstrmap& m, const char* str)         { return (X*)PTYPES_NAMESPACE::get((const strmap&)m, str); }
        friend inline void put(tstrmap& m, const string& str, X* obj)   { unknown* t = obj; PTYPES_NAMESPACE::put(m, str, t); }
        X* operator[] (const char* str) const                           { return (X*)PTYPES_NAMESPACE::get(*this, str); }
    };

    // ptypes-1.9 textmap interface

    inline int   length(const textmap& m)                           { return m.get_count(); }
    inline void  clear(textmap& m)                                  { m.clear(); }
    inline const string& get(const textmap& m, const string& k)     { return m.get(k); }
    inline void  put(textmap& m, const string& k, const string& v)  { m.put(k, v); }
    inline void  del(textmap& m, const string& k)                   { m.del(k); }

    #endif // PTYPES19_COMPAT

    #ifdef PTYPES18_COMPAT

    inline string itobase(large value, int base, int width = 0, char pad = 0)
        { return itostring(value, base, width, pad); }

    typedef exception exceptobj;

    #endif // PTYPES18_COMPAT

    #ifdef _MSC_VER
    #pragma warning(pop)
    #pragma pack(pop)
    #endif

    PTYPES_END

    #endif // __PTYPES_H__