From ebbc153d450c27feece5c2e875d309656e920ed7 Mon Sep 17 00:00:00 2001 From: MatthieuDartiailh Date: Sun, 21 Oct 2018 17:26:49 -0400 Subject: [PATCH 01/16] py: drop support for Python < 3.5 and rely on cppy --- py/constraint.cpp | 55 +--- py/expression.cpp | 51 +-- py/kiwisolver.cpp | 67 ++-- py/pythonhelpers.h | 771 --------------------------------------------- py/solver.cpp | 22 +- py/symbolics.h | 72 ++--- py/term.cpp | 41 +-- py/util.h | 48 +-- py/variable.cpp | 65 +--- setup.py | 15 +- 10 files changed, 126 insertions(+), 1081 deletions(-) delete mode 100644 py/pythonhelpers.h diff --git a/py/constraint.cpp b/py/constraint.cpp index 2fbdd0ae..58461b5c 100644 --- a/py/constraint.cpp +++ b/py/constraint.cpp @@ -1,5 +1,5 @@ /*----------------------------------------------------------------------------- -| Copyright (c) 2013-2017, Nucleic Development Team. +| Copyright (c) 2013-2018, Nucleic Development Team. | | Distributed under the terms of the Modified BSD License. | @@ -8,8 +8,8 @@ #include #include #include +#include #include -#include "pythonhelpers.h" #include "types.h" #include "util.h" @@ -29,14 +29,14 @@ Constraint_new( PyTypeObject* type, PyObject* args, PyObject* kwargs ) &pyexpr, &pyop, &pystrength ) ) return 0; if( !Expression::TypeCheck( pyexpr ) ) - return py_expected_type_fail( pyexpr, "Expression" ); + return cppy::type_error( pyexpr, "Expression" ); kiwi::RelationalOperator op; if( !convert_to_relational_op( pyop, op ) ) return 0; double strength = kiwi::strength::required; if( pystrength && !convert_to_strength( pystrength, strength ) ) return 0; - PyObjectPtr pycn( PyType_GenericNew( type, args, kwargs ) ); + cppy::ptr pycn( PyType_GenericNew( type, args, kwargs ) ); if( !pycn ) return 0; Constraint* cn = reinterpret_cast( pycn.get() ); @@ -102,14 +102,14 @@ Constraint_repr( Constraint* self ) break; } stream << " | strength = " << self->constraint.strength(); - return FROM_STRING( stream.str().c_str() ); + return PyUnicode_FromString( stream.str().c_str() ); } static PyObject* Constraint_expression( Constraint* self ) { - return newref( self->expression ); + return cppy::incref( self->expression ); } @@ -120,13 +120,13 @@ Constraint_op( Constraint* self ) switch( self->constraint.op() ) { case kiwi::OP_EQ: - res = FROM_STRING( "==" ); + res = PyUnicode_FromString( "==" ); break; case kiwi::OP_LE: - res = FROM_STRING( "<=" ); + res = PyUnicode_FromString( "<=" ); break; case kiwi::OP_GE: - res = FROM_STRING( ">=" ); + res = PyUnicode_FromString( ">=" ); break; } return res; @@ -153,7 +153,7 @@ Constraint_or( PyObject* pyoldcn, PyObject* value ) return 0; Constraint* oldcn = reinterpret_cast( pyoldcn ); Constraint* newcn = reinterpret_cast( pynewcn ); - newcn->expression = newref( oldcn->expression ); + newcn->expression = cppy::incref( oldcn->expression ); new( &newcn->constraint ) kiwi::Constraint( oldcn->constraint, strength ); return pynewcn; } @@ -176,42 +176,25 @@ Constraint_as_number = { 0, /* nb_add */ 0, /* nb_subtract */ 0, /* nb_multiply */ -#if PY_MAJOR_VERSION < 3 - 0, /* nb_divide */ -#endif 0, /* nb_remainder */ 0, /* nb_divmod */ 0, /* nb_power */ 0, /* nb_negative */ 0, /* nb_positive */ 0, /* nb_absolute */ -#if PY_MAJOR_VERSION >= 3 0, /* nb_bool */ -#else - 0, /* nb_nonzero */ -#endif 0, /* nb_invert */ 0, /* nb_lshift */ 0, /* nb_rshift */ 0, /* nb_and */ 0, /* nb_xor */ (binaryfunc)Constraint_or, /* nb_or */ -#if PY_MAJOR_VERSION < 3 - 0, /* nb_coerce */ -#endif 0, /* nb_int */ 0, /* nb_long */ 0, /* nb_float */ -#if PY_MAJOR_VERSION < 3 - 0, /* nb_oct */ - 0, /* nb_hex */ -#endif 0, /* nb_inplace_add */ 0, /* nb_inplace_subtract */ 0, /* nb_inplace_multiply */ -#if PY_MAJOR_VERSION < 3 - 0, /* nb_inplace_divide */ -#endif 0, /* nb_inplace_remainder */ 0, /* nb_inplace_power */ 0, /* nb_inplace_lshift */ @@ -223,13 +206,9 @@ Constraint_as_number = { (binaryfunc)0, /* nb_true_divide */ 0, /* nb_inplace_floor_divide */ 0, /* nb_inplace_true_divide */ -#if PY_VERSION_HEX >= 0x02050000 (unaryfunc)0, /* nb_index */ -#endif -#if PY_VERSION_HEX >= 0x03050000 - (binaryfunc)0, /* nb_matrix_multiply */ - (binaryfunc)0, /* nb_inplace_matrix_multiply */ -#endif + (binaryfunc)0, /* nb_matrix_multiply */ + (binaryfunc)0, /* nb_inplace_matrix_multiply */ }; @@ -242,13 +221,7 @@ PyTypeObject Constraint_Type = { (printfunc)0, /* tp_print */ (getattrfunc)0, /* tp_getattr */ (setattrfunc)0, /* tp_setattr */ -#if PY_VERSION_HEX >= 0x03050000 ( PyAsyncMethods* )0, /* tp_as_async */ -#elif PY_VERSION_HEX >= 0x03000000 - ( void* ) 0, /* tp_reserved */ -#else - ( cmpfunc )0, /* tp_compare */ -#endif (reprfunc)Constraint_repr, /* tp_repr */ (PyNumberMethods*)&Constraint_as_number,/* tp_as_number */ (PySequenceMethods*)0, /* tp_as_sequence */ @@ -259,11 +232,7 @@ PyTypeObject Constraint_Type = { (getattrofunc)0, /* tp_getattro */ (setattrofunc)0, /* tp_setattro */ (PyBufferProcs*)0, /* tp_as_buffer */ -#if PY_MAJOR_VERSION >= 3 Py_TPFLAGS_DEFAULT|Py_TPFLAGS_HAVE_GC|Py_TPFLAGS_BASETYPE, /* tp_flags */ -#else - Py_TPFLAGS_DEFAULT|Py_TPFLAGS_HAVE_GC|Py_TPFLAGS_BASETYPE|Py_TPFLAGS_CHECKTYPES, /* tp_flags */ -#endif 0, /* Documentation string */ (traverseproc)Constraint_traverse, /* tp_traverse */ (inquiry)Constraint_clear, /* tp_clear */ diff --git a/py/expression.cpp b/py/expression.cpp index 5cd34e4c..dc636b8d 100644 --- a/py/expression.cpp +++ b/py/expression.cpp @@ -1,5 +1,5 @@ /*----------------------------------------------------------------------------- -| Copyright (c) 2013-2017, Nucleic Development Team. +| Copyright (c) 2013-2018, Nucleic Development Team. | | Distributed under the terms of the Modified BSD License. | @@ -7,7 +7,7 @@ |----------------------------------------------------------------------------*/ #include #include -#include "pythonhelpers.h" +#include #include "symbolics.h" #include "types.h" #include "util.h" @@ -26,7 +26,7 @@ Expression_new( PyTypeObject* type, PyObject* args, PyObject* kwargs ) args, kwargs, "O|O:__new__", const_cast( kwlist ), &pyterms, &pyconstant ) ) return 0; - PyObjectPtr terms( PySequence_Tuple( pyterms ) ); + cppy::ptr terms( PySequence_Tuple( pyterms ) ); if( !terms ) return 0; Py_ssize_t end = PyTuple_GET_SIZE( terms.get() ); @@ -34,7 +34,7 @@ Expression_new( PyTypeObject* type, PyObject* args, PyObject* kwargs ) { PyObject* item = PyTuple_GET_ITEM( terms.get(), i ); if( !Term::TypeCheck( item ) ) - return py_expected_type_fail( item, "Term" ); + return cppy::type_error( item, "Term" ); } double constant = 0.0; if( pyconstant && !convert_to_double( pyconstant, constant ) ) @@ -87,14 +87,14 @@ Expression_repr( Expression* self ) stream << " + "; } stream << self->constant; - return FROM_STRING( stream.str().c_str() ); + return PyUnicode_FromString( stream.str().c_str() ); } static PyObject* Expression_terms( Expression* self ) { - return newref( self->terms ); + return cppy::incref( self->terms ); } @@ -199,46 +199,25 @@ Expression_as_number = { (binaryfunc)Expression_add, /* nb_add */ (binaryfunc)Expression_sub, /* nb_subtract */ (binaryfunc)Expression_mul, /* nb_multiply */ -#if PY_MAJOR_VERSION < 3 - (binaryfunc)Expression_div, /* nb_divide */ -#endif 0, /* nb_remainder */ 0, /* nb_divmod */ 0, /* nb_power */ (unaryfunc)Expression_neg, /* nb_negative */ 0, /* nb_positive */ 0, /* nb_absolute */ -#if PY_MAJOR_VERSION >= 3 0, /* nb_bool */ -#else - 0, /* nb_nonzero */ -#endif 0, /* nb_invert */ 0, /* nb_lshift */ 0, /* nb_rshift */ 0, /* nb_and */ 0, /* nb_xor */ (binaryfunc)0, /* nb_or */ -#if PY_MAJOR_VERSION < 3 - 0, /* nb_coerce */ -#endif 0, /* nb_int */ -#if PY_MAJOR_VERSION >= 3 (void *)0, /* nb_reserved */ -#else - 0, /* nb_long */ -#endif 0, /* nb_float */ -#if PY_MAJOR_VERSION < 3 - 0, /* nb_oct */ - 0, /* nb_hex */ -#endif 0, /* nb_inplace_add */ 0, /* nb_inplace_subtract */ 0, /* nb_inplace_multiply */ -#if PY_MAJOR_VERSION < 3 - 0, /* nb_inplace_divide */ -#endif 0, /* nb_inplace_remainder */ 0, /* nb_inplace_power */ 0, /* nb_inplace_lshift */ @@ -250,13 +229,9 @@ Expression_as_number = { (binaryfunc)Expression_div, /* nb_true_divide */ 0, /* nb_inplace_floor_divide */ 0, /* nb_inplace_true_divide */ -#if PY_VERSION_HEX >= 0x02050000 (unaryfunc)0, /* nb_index */ -#endif -#if PY_VERSION_HEX >= 0x03050000 - (binaryfunc)0, /* nb_matrix_multiply */ - (binaryfunc)0, /* nb_inplace_matrix_multiply */ -#endif + (binaryfunc)0, /* nb_matrix_multiply */ + (binaryfunc)0, /* nb_inplace_matrix_multiply */ }; @@ -269,13 +244,7 @@ PyTypeObject Expression_Type = { (printfunc)0, /* tp_print */ (getattrfunc)0, /* tp_getattr */ (setattrfunc)0, /* tp_setattr */ -#if PY_VERSION_HEX >= 0x03050000 ( PyAsyncMethods* )0, /* tp_as_async */ -#elif PY_VERSION_HEX >= 0x03000000 - ( void* ) 0, /* tp_reserved */ -#else - ( cmpfunc )0, /* tp_compare */ -#endif (reprfunc)Expression_repr, /* tp_repr */ (PyNumberMethods*)&Expression_as_number,/* tp_as_number */ (PySequenceMethods*)0, /* tp_as_sequence */ @@ -286,11 +255,7 @@ PyTypeObject Expression_Type = { (getattrofunc)0, /* tp_getattro */ (setattrofunc)0, /* tp_setattro */ (PyBufferProcs*)0, /* tp_as_buffer */ -#if PY_MAJOR_VERSION >= 3 Py_TPFLAGS_DEFAULT|Py_TPFLAGS_HAVE_GC|Py_TPFLAGS_BASETYPE, -#else - Py_TPFLAGS_DEFAULT|Py_TPFLAGS_HAVE_GC|Py_TPFLAGS_BASETYPE|Py_TPFLAGS_CHECKTYPES, /* tp_flags */ -#endif 0, /* Documentation string */ (traverseproc)Expression_traverse, /* tp_traverse */ (inquiry)Expression_clear, /* tp_clear */ diff --git a/py/kiwisolver.cpp b/py/kiwisolver.cpp index 54b333a2..91699d51 100644 --- a/py/kiwisolver.cpp +++ b/py/kiwisolver.cpp @@ -1,25 +1,26 @@ /*----------------------------------------------------------------------------- -| Copyright (c) 2013-2017, Nucleic Development Team. +| Copyright (c) 2013-2018, Nucleic Development Team. | | Distributed under the terms of the Modified BSD License. | | The full license is in the file COPYING.txt, distributed with this software. |----------------------------------------------------------------------------*/ #include +#include #include -#include "pythonhelpers.h" #include "types.h" -#define PY_KIWI_VERSION "1.1.0" +#define PY_KIWI_VERSION "1.2.0.dev" -using namespace PythonHelpers; +struct module_state { + PyObject *error; +}; static PyMethodDef kiwisolver_methods[] = { { 0 } // Sentinel }; -#if PY_MAJOR_VERSION >= 3 static struct PyModuleDef kiwisolver_moduledef = { PyModuleDef_HEAD_INIT, "kiwisolver", @@ -31,56 +32,46 @@ static struct PyModuleDef kiwisolver_moduledef = { PyMODINIT_FUNC PyInit_kiwisolver( void ) -#else -PyMODINIT_FUNC -initkiwisolver( void ) -#endif { -#if PY_MAJOR_VERSION >= 3 PyObject *mod = PyModule_Create( &kiwisolver_moduledef ); -#else - PyObject* mod = Py_InitModule( "kiwisolver", kiwisolver_methods ); -#endif if( !mod ) - INITERROR; + return NULL; if( import_variable() < 0 ) - INITERROR; + return NULL; if( import_term() < 0 ) - INITERROR; + return NULL; if( import_expression() < 0 ) - INITERROR; + return NULL; if( import_constraint() < 0 ) - INITERROR; + return NULL; if( import_solver() < 0 ) - INITERROR; + return NULL; if( import_strength() < 0 ) - INITERROR; - PyObject* kiwiversion = FROM_STRING( KIWI_VERSION ); + return NULL; + PyObject* kiwiversion = PyUnicode_FromString( KIWI_VERSION ); if( !kiwiversion ) - INITERROR; - PyObject* pyversion = FROM_STRING( PY_KIWI_VERSION ); + return NULL; + PyObject* pyversion = PyUnicode_FromString( PY_KIWI_VERSION ); if( !pyversion ) - INITERROR; + return NULL; PyObject* pystrength = PyType_GenericNew( &strength_Type, 0, 0 ); if( !pystrength ) - INITERROR; + return NULL; PyModule_AddObject( mod, "__version__", pyversion ); PyModule_AddObject( mod, "__kiwi_version__", kiwiversion ); PyModule_AddObject( mod, "strength", pystrength ); - PyModule_AddObject( mod, "Variable", newref( pyobject_cast( &Variable_Type ) ) ); - PyModule_AddObject( mod, "Term", newref( pyobject_cast( &Term_Type ) ) ); - PyModule_AddObject( mod, "Expression", newref( pyobject_cast( &Expression_Type ) ) ); - PyModule_AddObject( mod, "Constraint", newref( pyobject_cast( &Constraint_Type ) ) ); - PyModule_AddObject( mod, "Solver", newref( pyobject_cast( &Solver_Type ) ) ); - PyModule_AddObject( mod, "DuplicateConstraint", newref( DuplicateConstraint ) ); - PyModule_AddObject( mod, "UnsatisfiableConstraint", newref( UnsatisfiableConstraint ) ); - PyModule_AddObject( mod, "UnknownConstraint", newref( UnknownConstraint ) ); - PyModule_AddObject( mod, "DuplicateEditVariable", newref( DuplicateEditVariable ) ); - PyModule_AddObject( mod, "UnknownEditVariable", newref( UnknownEditVariable ) ); - PyModule_AddObject( mod, "BadRequiredStrength", newref( BadRequiredStrength ) ); + PyModule_AddObject( mod, "Variable", cppy::incref( pyobject_cast( &Variable_Type ) ) ); + PyModule_AddObject( mod, "Term", cppy::incref( pyobject_cast( &Term_Type ) ) ); + PyModule_AddObject( mod, "Expression", cppy::incref( pyobject_cast( &Expression_Type ) ) ); + PyModule_AddObject( mod, "Constraint", cppy::incref( pyobject_cast( &Constraint_Type ) ) ); + PyModule_AddObject( mod, "Solver", cppy::incref( pyobject_cast( &Solver_Type ) ) ); + PyModule_AddObject( mod, "DuplicateConstraint", cppy::incref( DuplicateConstraint ) ); + PyModule_AddObject( mod, "UnsatisfiableConstraint", cppy::incref( UnsatisfiableConstraint ) ); + PyModule_AddObject( mod, "UnknownConstraint", cppy::incref( UnknownConstraint ) ); + PyModule_AddObject( mod, "DuplicateEditVariable", cppy::incref( DuplicateEditVariable ) ); + PyModule_AddObject( mod, "UnknownEditVariable", cppy::incref( UnknownEditVariable ) ); + PyModule_AddObject( mod, "BadRequiredStrength", cppy::incref( BadRequiredStrength ) ); -#if PY_MAJOR_VERSION >= 3 return mod; -#endif } diff --git a/py/pythonhelpers.h b/py/pythonhelpers.h deleted file mode 100644 index a9e0db63..00000000 --- a/py/pythonhelpers.h +++ /dev/null @@ -1,771 +0,0 @@ -/*----------------------------------------------------------------------------- -| Copyright (c) 2013-2017, Nucleic Development Team. -| -| Distributed under the terms of the Modified BSD License. -| -| The full license is in the file COPYING.txt, distributed with this software. -|----------------------------------------------------------------------------*/ -#pragma once -#include -#include -#include - -#if PY_MAJOR_VERSION >= 3 -#define FROM_STRING PyUnicode_FromString -#define INITERROR return NULL -#define MOD_INIT_FUNC(name) PyMODINIT_FUNC PyInit_##name(void) -#else -#define FROM_STRING PyString_FromString -#define INITERROR return -#define MOD_INIT_FUNC(name) PyMODINIT_FUNC init##name(void) -#endif - -#ifndef Py_RETURN_NOTIMPLEMENTED -#define Py_RETURN_NOTIMPLEMENTED \ - return Py_INCREF(Py_NotImplemented), Py_NotImplemented -#endif - -#define pyobject_cast( o ) ( reinterpret_cast( o ) ) -#define pytype_cast( o ) ( reinterpret_cast( o ) ) - -struct module_state { - PyObject *error; -}; - - -namespace PythonHelpers -{ - - -/*----------------------------------------------------------------------------- -| Exception Handling -|----------------------------------------------------------------------------*/ -inline PyObject* -py_bad_internal_call( const char* message ) -{ - PyErr_SetString( PyExc_SystemError, message ); - return 0; -} - - -inline PyObject* -py_type_fail( const char* message ) -{ - PyErr_SetString( PyExc_TypeError, message ); - return 0; -} - - -inline PyObject* -py_expected_type_fail( PyObject* pyobj, const char* expected_type ) -{ - PyErr_Format( - PyExc_TypeError, - "Expected object of type `%s`. Got object of type `%s` instead.", - expected_type, pyobj->ob_type->tp_name - ); - return 0; -} - - -inline PyObject* -py_value_fail( const char* message ) -{ - PyErr_SetString( PyExc_ValueError, message ); - return 0; -} - - -inline PyObject* -py_runtime_fail( const char* message ) -{ - PyErr_SetString( PyExc_RuntimeError, message ); - return 0; -} - - -inline PyObject* -py_attr_fail( const char* message ) -{ - PyErr_SetString( PyExc_AttributeError, message ); - return 0; -} - - -inline PyObject* -py_no_attr_fail( PyObject* pyobj, const char* attr ) -{ - PyErr_Format( - PyExc_AttributeError, - "'%s' object has no attribute '%s'", - pyobj->ob_type->tp_name, attr - ); - return 0; -} - - -/*----------------------------------------------------------------------------- -| Utilities -|----------------------------------------------------------------------------*/ -inline PyObject* -newref( PyObject* pyobj ) -{ - Py_INCREF( pyobj ); - return pyobj; -} - - -inline PyObject* -xnewref( PyObject* pyobj ) -{ - Py_XINCREF( pyobj ); - return pyobj; -} - - -inline PyObject* -py_bool( bool val ) -{ - return newref( val ? Py_True : Py_False ); -} - - -inline PyCFunction -lookup_method( PyTypeObject* type, const char* name ) -{ - PyMethodDef* method = type->tp_methods; - for( ; method->ml_name != 0; ++method ) - { - if( strcmp( method->ml_name, name ) == 0 ) - return method->ml_meth; - } - return 0; -} - - -/*----------------------------------------------------------------------------- -| Object Ptr -|----------------------------------------------------------------------------*/ -class PyObjectPtr { - -public: - - PyObjectPtr() : m_pyobj( 0 ) {} - - PyObjectPtr( const PyObjectPtr& objptr ) : - m_pyobj( PythonHelpers::xnewref( objptr.m_pyobj ) ) {} - - PyObjectPtr( PyObject* pyobj ) : m_pyobj( pyobj ) {} - - ~PyObjectPtr() - { - xdecref_release(); - } - - PyObject* get() const - { - return m_pyobj; - } - - void set( PyObject* pyobj ) - { - PyObject* old = m_pyobj; - m_pyobj = pyobj; - Py_XDECREF( old ); - } - - PyObject* release() - { - PyObject* pyobj = m_pyobj; - m_pyobj = 0; - return pyobj; - } - - PyObject* decref_release() - { - PyObject* pyobj = m_pyobj; - m_pyobj = 0; - Py_DECREF( pyobj ); - return pyobj; - } - - PyObject* xdecref_release() - { - PyObject* pyobj = m_pyobj; - m_pyobj = 0; - Py_XDECREF( pyobj ); - return pyobj; - } - - PyObject* incref_release() - { - PyObject* pyobj = m_pyobj; - m_pyobj = 0; - Py_INCREF( pyobj ); - return pyobj; - } - - PyObject* xincref_release() - { - PyObject* pyobj = m_pyobj; - m_pyobj = 0; - Py_XINCREF( pyobj ); - return pyobj; - } - - void incref() const - { - Py_INCREF( m_pyobj ); - } - - void decref() const - { - Py_DECREF( m_pyobj ); - } - - void xincref() const - { - Py_XINCREF( m_pyobj ); - } - - void xdecref() const - { - Py_XDECREF( m_pyobj ); - } - - PyObject* newref() const - { - Py_INCREF( m_pyobj ); - return m_pyobj; - } - - PyObject* xnewref() const - { - Py_XINCREF( m_pyobj ); - return m_pyobj; - } - - size_t refcount() const - { - if( m_pyobj ) - return m_pyobj->ob_refcnt; - return 0; - } - - bool is_true( bool clear_err=true ) const - { - int truth = PyObject_IsTrue( m_pyobj ); - if( truth == 1 ) - return true; - if( truth == 0 ) - return false; - if( clear_err ) - PyErr_Clear(); - return false; - } - - bool is_None() const - { - return m_pyobj == Py_None; - } - - bool is_True() const - { - return m_pyobj == Py_True; - } - - bool is_False() const - { - return m_pyobj == Py_False; - } - - bool load_dict( PyObjectPtr& out, bool forcecreate=false ) - { - PyObject** dict = _PyObject_GetDictPtr( m_pyobj ); - if( !dict ) - return false; - if( forcecreate && !*dict ) - *dict = PyDict_New(); - out = PythonHelpers::xnewref( *dict ); - return true; - } - - bool richcompare( PyObject* other, int opid, bool clear_err=true ) - { - int r = PyObject_RichCompareBool( m_pyobj, other, opid ); - if( r == 1 ) - return true; - if( r == 0 ) - return false; - if( clear_err && PyErr_Occurred() ) - PyErr_Clear(); - return false; - } - - bool richcompare( PyObjectPtr& other, int opid, bool clear_err=true ) - { - return richcompare( other.m_pyobj, opid, clear_err ); - } - - bool hasattr( PyObject* attr ) - { - return PyObject_HasAttr( m_pyobj, attr ) == 1; - } - - bool hasattr( PyObjectPtr& attr ) - { - return PyObject_HasAttr( m_pyobj, attr.get() ) == 1; - } - - bool hasattr( const char* attr ) - { - return PyObject_HasAttrString( m_pyobj, attr ) == 1; - } - - bool hasattr( std::string& attr ) - { - return hasattr( attr.c_str() ); - } - - PyObjectPtr getattr( PyObject* attr ) - { - return PyObjectPtr( PyObject_GetAttr( m_pyobj, attr ) ); - } - - PyObjectPtr getattr( PyObjectPtr& attr ) - { - return PyObjectPtr( PyObject_GetAttr( m_pyobj, attr.get() ) ); - } - - PyObjectPtr getattr( const char* attr ) - { - return PyObjectPtr( PyObject_GetAttrString( m_pyobj, attr ) ); - } - - PyObjectPtr getattr( std::string& attr ) - { - return getattr( attr.c_str() ); - } - - bool setattr( PyObject* attr, PyObject* value ) - { - return PyObject_SetAttr( m_pyobj, attr, value ) == 0; - } - - bool setattr( PyObjectPtr& attr, PyObjectPtr& value ) - { - return PyObject_SetAttr( m_pyobj, attr.get(), value.get() ) == 0; - } - - PyObjectPtr operator()( PyObjectPtr& args ) const - { - return PyObjectPtr( PyObject_Call( m_pyobj, args.get(), 0 ) ); - } - - PyObjectPtr operator()( PyObjectPtr& args, PyObjectPtr& kwargs ) const - { - return PyObjectPtr( PyObject_Call( m_pyobj, args.get(), kwargs.get() ) ); - } - - operator void*() const - { - return static_cast( m_pyobj ); - } - - PyObjectPtr& operator=( const PyObjectPtr& rhs ) - { - PyObject* old = m_pyobj; - m_pyobj = rhs.m_pyobj; - Py_XINCREF( m_pyobj ); - Py_XDECREF( old ); - return *this; - } - - PyObjectPtr& operator=( PyObject* rhs ) - { - PyObject* old = m_pyobj; - m_pyobj = rhs; - Py_XDECREF( old ); - return *this; - } - -protected: - - PyObject* m_pyobj; - -}; - - -inline bool -operator!=( const PyObjectPtr& lhs, const PyObjectPtr& rhs ) -{ - return lhs.get() != rhs.get(); -} - - -inline bool -operator!=( const PyObject* lhs, const PyObjectPtr& rhs ) -{ - return lhs != rhs.get(); -} - - -inline bool -operator!=( const PyObjectPtr& lhs, const PyObject* rhs ) -{ - return lhs.get() != rhs; -} - - -inline bool -operator==( const PyObjectPtr& lhs, const PyObjectPtr& rhs ) -{ - return lhs.get() == rhs.get(); -} - - -inline bool -operator==( const PyObject* lhs, const PyObjectPtr& rhs ) -{ - return lhs == rhs.get(); -} - - -inline bool -operator==( const PyObjectPtr& lhs, const PyObject* rhs ) -{ - return lhs.get() == rhs; -} - - -/*----------------------------------------------------------------------------- -| Tuple Ptr -|----------------------------------------------------------------------------*/ -class PyTuplePtr : public PyObjectPtr { - -public: - - PyTuplePtr() : PyObjectPtr() {} - - PyTuplePtr( const PyObjectPtr& objptr ) : PyObjectPtr( objptr ) {} - - PyTuplePtr( PyObject* pytuple ) : PyObjectPtr( pytuple ) {} - - bool check() - { - return PyTuple_Check( m_pyobj ); - } - - bool check_exact() - { - return PyTuple_CheckExact( m_pyobj ); - } - - Py_ssize_t size() const - { - return PyTuple_GET_SIZE( m_pyobj ); - } - - PyObjectPtr get_item( Py_ssize_t index ) const - { - return PyObjectPtr( PythonHelpers::newref( PyTuple_GET_ITEM( m_pyobj, index ) ) ); - } - - void set_item( Py_ssize_t index, PyObject* pyobj ) - { - PyObject* old_item = PyTuple_GET_ITEM( m_pyobj, index ); - PyTuple_SET_ITEM( m_pyobj, index, pyobj ); - Py_XDECREF( old_item ); - } - - void set_item( Py_ssize_t index, PyObjectPtr& item ) - { - PyObject* old_item = PyTuple_GET_ITEM( m_pyobj, index ); - PyTuple_SET_ITEM( m_pyobj, index, item.get() ); - Py_XINCREF( item.get() ); - Py_XDECREF( old_item ); - } - - // pyobj must not be null, only use to fill a new empty tuple - void initialize( Py_ssize_t index, PyObject* pyobj ) - { - PyTuple_SET_ITEM( m_pyobj, index, pyobj ); - } - - // ptr must not be empty, only use to fill a new empty tuple - void initialize( Py_ssize_t index, PyObjectPtr& item ) - { - PyTuple_SET_ITEM( m_pyobj, index, item.get() ); - Py_INCREF( item.get() ); - } - -}; - - -/*----------------------------------------------------------------------------- -| List Ptr -|----------------------------------------------------------------------------*/ -class PyListPtr : public PyObjectPtr { - -public: - - PyListPtr() : PyObjectPtr() {} - - PyListPtr( const PyObjectPtr& objptr ) : PyObjectPtr( objptr ) {} - - PyListPtr( PyObject* pylist ) : PyObjectPtr( pylist ) {} - - bool check() const - { - return PyList_Check( m_pyobj ); - } - - bool check_exact() const - { - return PyList_CheckExact( m_pyobj ); - } - - Py_ssize_t size() const - { - return PyList_GET_SIZE( m_pyobj ); - } - - PyObject* borrow_item( Py_ssize_t index ) const - { - return PyList_GET_ITEM( m_pyobj, index ); - } - - PyObjectPtr get_item( Py_ssize_t index ) const - { - return PyObjectPtr( PythonHelpers::newref( PyList_GET_ITEM( m_pyobj, index ) ) ); - } - - void set_item( Py_ssize_t index, PyObject* pyobj ) const - { - PyObject* old_item = PyList_GET_ITEM( m_pyobj, index ); - PyList_SET_ITEM( m_pyobj, index, pyobj ); - Py_XDECREF( old_item ); - } - - void set_item( Py_ssize_t index, PyObjectPtr& item ) const - { - PyObject* old_item = PyList_GET_ITEM( m_pyobj, index ); - PyList_SET_ITEM( m_pyobj, index, item.get() ); - Py_XINCREF( item.get() ); - Py_XDECREF( old_item ); - } - - bool del_item( Py_ssize_t index ) const - { - if( PySequence_DelItem( m_pyobj, index ) == -1 ) - return false; - return true; - } - - bool append( PyObjectPtr& pyobj ) const - { - if( PyList_Append( m_pyobj, pyobj.get() ) == 0 ) - return true; - return false; - } - - Py_ssize_t index( PyObjectPtr& item ) const - { - Py_ssize_t maxidx = size(); - for( Py_ssize_t idx = 0; idx < maxidx; idx++ ) - { - PyObjectPtr other( get_item( idx ) ); - if( item.richcompare( other, Py_EQ ) ) - return idx; - } - return -1; - } - -}; - - -/*----------------------------------------------------------------------------- -| Dict Ptr -|----------------------------------------------------------------------------*/ -class PyDictPtr : public PyObjectPtr { - -public: - - PyDictPtr() : PyObjectPtr() {} - - PyDictPtr( const PyObjectPtr& objptr ) : PyObjectPtr( objptr ) {} - - PyDictPtr( PyObject* pydict ) : PyObjectPtr( pydict ) {} - - bool check() - { - return PyDict_Check( m_pyobj ); - } - - bool check_exact() - { - return PyDict_CheckExact( m_pyobj ); - } - - Py_ssize_t size() const - { - return PyDict_Size( m_pyobj ); - } - - PyObjectPtr get_item( PyObject* key ) const - { - return PyObjectPtr( PythonHelpers::xnewref( PyDict_GetItem( m_pyobj, key ) ) ) ; - } - - PyObjectPtr get_item( PyObjectPtr& key ) const - { - return PyObjectPtr( PythonHelpers::xnewref( PyDict_GetItem( m_pyobj, key.get() ) ) ); - } - - PyObjectPtr get_item( const char* key ) const - { - return PyObjectPtr( PythonHelpers::xnewref( PyDict_GetItemString( m_pyobj, key ) ) ); - } - - PyObjectPtr get_item( std::string& key ) const - { - return get_item( key.c_str() ); - } - - bool set_item( PyObject* key, PyObject* value ) const - { - if( PyDict_SetItem( m_pyobj, key, value ) == 0 ) - return true; - return false; - } - - bool set_item( PyObject* key, PyObjectPtr& value ) const - { - if( PyDict_SetItem( m_pyobj, key, value.get() ) == 0 ) - return true; - return false; - } - - bool set_item( PyObjectPtr& key, PyObject* value ) const - { - if( PyDict_SetItem( m_pyobj, key.get(), value ) == 0 ) - return true; - return false; - } - - bool set_item( PyObjectPtr& key, PyObjectPtr& value ) const - { - if( PyDict_SetItem( m_pyobj, key.get(), value.get() ) == 0 ) - return true; - return false; - } - - bool set_item( const char* key, PyObjectPtr& value ) const - { - if( PyDict_SetItemString( m_pyobj, key, value.get() ) == 0 ) - return true; - return false; - } - - bool set_item( const char* key, PyObject* value ) const - { - if( PyDict_SetItemString( m_pyobj, key, value ) == 0 ) - return true; - return false; - } - - bool set_item( std::string& key, PyObjectPtr& value ) const - { - return set_item( key.c_str(), value ); - } - - bool del_item( PyObjectPtr& key ) const - { - if( PyDict_DelItem( m_pyobj, key.get() ) == 0 ) - return true; - return false; - } - - bool del_item( const char* key ) const - { - if( PyDict_DelItemString( m_pyobj, key ) == 0 ) - return true; - return false; - } - - bool del_item( std::string& key ) const - { - return del_item( key.c_str() ); - } - -}; - - -/*----------------------------------------------------------------------------- -| Method Ptr -|----------------------------------------------------------------------------*/ -class PyMethodPtr : public PyObjectPtr { - -public: - - PyMethodPtr() : PyObjectPtr() {} - - PyMethodPtr( const PyObjectPtr& objptr ) : PyObjectPtr( objptr ) {} - - PyMethodPtr( PyObject* pymethod ) : PyObjectPtr( pymethod ) {} - - bool check() - { - return PyMethod_Check( m_pyobj ); - } - - PyObjectPtr get_self() const - { - return PyObjectPtr( PythonHelpers::xnewref( PyMethod_GET_SELF( m_pyobj ) ) ); - } - - PyObjectPtr get_function() const - { - return PyObjectPtr( PythonHelpers::xnewref( PyMethod_GET_FUNCTION( m_pyobj ) ) ); - } - -#if PY_MAJOR_VERSION < 3 - PyObjectPtr get_class() const - { - return PyObjectPtr( PythonHelpers::xnewref( PyMethod_GET_CLASS( m_pyobj ) ) ); - } -#endif -}; - - -/*----------------------------------------------------------------------------- -| Weakref Ptr -|----------------------------------------------------------------------------*/ -class PyWeakrefPtr : public PyObjectPtr { - -public: - - PyWeakrefPtr() : PyObjectPtr() {} - - PyWeakrefPtr( const PyObjectPtr& objptr ) : PyObjectPtr( objptr ) {} - - PyWeakrefPtr( PyObject* pyweakref ) : PyObjectPtr( pyweakref ) {} - - bool check() - { - return PyWeakref_CheckRef( m_pyobj ); - } - - bool check_exact() - { - return PyWeakref_CheckRefExact( m_pyobj ); - } - - PyObjectPtr get_object() const - { - return PyObjectPtr( PythonHelpers::newref( PyWeakref_GET_OBJECT( m_pyobj ) ) ); - } - -}; - -} // namespace PythonHelpers diff --git a/py/solver.cpp b/py/solver.cpp index 7a2ef23c..cc8ec98a 100644 --- a/py/solver.cpp +++ b/py/solver.cpp @@ -1,5 +1,5 @@ /*----------------------------------------------------------------------------- -| Copyright (c) 2013-2017, Nucleic Development Team. +| Copyright (c) 2013-2018, Nucleic Development Team. | | Distributed under the terms of the Modified BSD License. | @@ -19,7 +19,7 @@ static PyObject* Solver_new( PyTypeObject* type, PyObject* args, PyObject* kwargs ) { if( PyTuple_GET_SIZE( args ) != 0 || ( kwargs && PyDict_Size( kwargs ) != 0 ) ) - return py_type_fail( "Solver.__new__ takes no arguments" ); + return cppy::type_error( "Solver.__new__ takes no arguments" ); PyObject* pysolver = PyType_GenericNew( type, args, kwargs ); if( !pysolver ) return 0; @@ -41,7 +41,7 @@ static PyObject* Solver_addConstraint( Solver* self, PyObject* other ) { if( !Constraint::TypeCheck( other ) ) - return py_expected_type_fail( other, "Constraint" ); + return cppy::type_error( other, "Constraint" ); Constraint* cn = reinterpret_cast( other ); try { @@ -65,7 +65,7 @@ static PyObject* Solver_removeConstraint( Solver* self, PyObject* other ) { if( !Constraint::TypeCheck( other ) ) - return py_expected_type_fail( other, "Constraint" ); + return cppy::type_error( other, "Constraint" ); Constraint* cn = reinterpret_cast( other ); try { @@ -84,9 +84,9 @@ static PyObject* Solver_hasConstraint( Solver* self, PyObject* other ) { if( !Constraint::TypeCheck( other ) ) - return py_expected_type_fail( other, "Constraint" ); + return cppy::type_error( other, "Constraint" ); Constraint* cn = reinterpret_cast( other ); - return newref( self->solver.hasConstraint( cn->constraint ) ? Py_True : Py_False ); + return cppy::incref( self->solver.hasConstraint( cn->constraint ) ? Py_True : Py_False ); } @@ -98,7 +98,7 @@ Solver_addEditVariable( Solver* self, PyObject* args ) if( !PyArg_ParseTuple( args, "OO", &pyvar, &pystrength ) ) return 0; if( !Variable::TypeCheck( pyvar ) ) - return py_expected_type_fail( pyvar, "Variable" ); + return cppy::type_error( pyvar, "Variable" ); double strength; if( !convert_to_strength( pystrength, strength ) ) return 0; @@ -125,7 +125,7 @@ static PyObject* Solver_removeEditVariable( Solver* self, PyObject* other ) { if( !Variable::TypeCheck( other ) ) - return py_expected_type_fail( other, "Variable" ); + return cppy::type_error( other, "Variable" ); Variable* var = reinterpret_cast( other ); try { @@ -144,9 +144,9 @@ static PyObject* Solver_hasEditVariable( Solver* self, PyObject* other ) { if( !Variable::TypeCheck( other ) ) - return py_expected_type_fail( other, "Variable" ); + return cppy::type_error( other, "Variable" ); Variable* var = reinterpret_cast( other ); - return newref( self->solver.hasEditVariable( var->variable ) ? Py_True : Py_False ); + return cppy::incref( self->solver.hasEditVariable( var->variable ) ? Py_True : Py_False ); } @@ -158,7 +158,7 @@ Solver_suggestValue( Solver* self, PyObject* args ) if( !PyArg_ParseTuple( args, "OO", &pyvar, &pyvalue ) ) return 0; if( !Variable::TypeCheck( pyvar ) ) - return py_expected_type_fail( pyvar, "Variable" ); + return cppy::type_error( pyvar, "Variable" ); double value; if( !convert_to_double( pyvalue, value ) ) return 0; diff --git a/py/symbolics.h b/py/symbolics.h index ac575537..37a77eed 100644 --- a/py/symbolics.h +++ b/py/symbolics.h @@ -1,5 +1,5 @@ /*----------------------------------------------------------------------------- -| Copyright (c) 2013-2017, Nucleic Development Team. +| Copyright (c) 2013-2018, Nucleic Development Team. | | Distributed under the terms of the Modified BSD License. | @@ -7,7 +7,7 @@ |----------------------------------------------------------------------------*/ #pragma once #include -#include "pythonhelpers.h" +#include #include "types.h" #include "util.h" @@ -61,10 +61,6 @@ struct BinaryInvoke return Invk()( primary, reinterpret_cast( secondary ) ); if( PyFloat_Check( secondary ) ) return Invk()( primary, PyFloat_AS_DOUBLE( secondary ) ); -#if PY_MAJOR_VERSION < 3 - if( PyInt_Check( secondary ) ) - return Invk()( primary, double( PyInt_AS_LONG( secondary ) ) ); -#endif if( PyLong_Check( secondary ) ) { double v = PyLong_AsDouble( secondary ); @@ -94,7 +90,7 @@ PyObject* BinaryMul::operator()( Variable* first, double second ) if( !pyterm ) return 0; Term* term = reinterpret_cast( pyterm ); - term->variable = PythonHelpers::newref( pyobject_cast( first ) ); + term->variable = cppy::incref( pyobject_cast( first ) ); term->coefficient = second; return pyterm; } @@ -107,7 +103,7 @@ PyObject* BinaryMul::operator()( Term* first, double second ) if( !pyterm ) return 0; Term* term = reinterpret_cast( pyterm ); - term->variable = PythonHelpers::newref( first->variable ); + term->variable = cppy::incref( first->variable ); term->coefficient = first->coefficient * second; return pyterm; } @@ -117,11 +113,11 @@ template<> inline PyObject* BinaryMul::operator()( Expression* first, double second ) { using namespace PythonHelpers; - PyObjectPtr pyexpr( PyType_GenericNew( &Expression_Type, 0, 0 ) ); + cppy::ptr pyexpr( PyType_GenericNew( &Expression_Type, 0, 0 ) ); if( !pyexpr ) return 0; Expression* expr = reinterpret_cast( pyexpr.get() ); - PyObjectPtr terms( PyTuple_New( PyTuple_GET_SIZE( first->terms ) ) ); + cppy::ptr terms( PyTuple_New( PyTuple_GET_SIZE( first->terms ) ) ); if( !terms ) return 0; Py_ssize_t end = PyTuple_GET_SIZE( first->terms ); @@ -252,7 +248,7 @@ struct BinaryAdd template<> inline PyObject* BinaryAdd::operator()( Expression* first, Expression* second ) { - PythonHelpers::PyObjectPtr pyexpr( PyType_GenericNew( &Expression_Type, 0, 0 ) ); + cppy::ptr pyexpr( PyType_GenericNew( &Expression_Type, 0, 0 ) ); if( !pyexpr ) return 0; Expression* expr = reinterpret_cast( pyexpr.get() ); @@ -268,7 +264,7 @@ template<> inline PyObject* BinaryAdd::operator()( Expression* first, Term* second ) { using namespace PythonHelpers; - PyObjectPtr pyexpr( PyType_GenericNew( &Expression_Type, 0, 0 ) ); + cppy::ptr pyexpr( PyType_GenericNew( &Expression_Type, 0, 0 ) ); if( !pyexpr ) return 0; PyObject* terms = PyTuple_New( PyTuple_GET_SIZE( first->terms ) + 1 ); @@ -278,9 +274,9 @@ PyObject* BinaryAdd::operator()( Expression* first, Term* second ) for( Py_ssize_t i = 0; i < end; ++i ) { PyObject* item = PyTuple_GET_ITEM( first->terms, i ); - PyTuple_SET_ITEM( terms, i, newref( item ) ); + PyTuple_SET_ITEM( terms, i, cppy::incref( item ) ); } - PyTuple_SET_ITEM( terms, end, newref( pyobject_cast( second ) ) ); + PyTuple_SET_ITEM( terms, end, cppy::incref( pyobject_cast( second ) ) ); Expression* expr = reinterpret_cast( pyexpr.get() ); expr->terms = terms; expr->constant = first->constant; @@ -291,7 +287,7 @@ PyObject* BinaryAdd::operator()( Expression* first, Term* second ) template<> inline PyObject* BinaryAdd::operator()( Expression* first, Variable* second ) { - PythonHelpers::PyObjectPtr temp( BinaryMul()( second, 1.0 ) ); + cppy::ptr temp( BinaryMul()( second, 1.0 ) ); if( !temp ) return 0; return operator()( first, reinterpret_cast( temp.get() ) ); @@ -302,11 +298,11 @@ template<> inline PyObject* BinaryAdd::operator()( Expression* first, double second ) { using namespace PythonHelpers; - PyObjectPtr pyexpr( PyType_GenericNew( &Expression_Type, 0, 0 ) ); + cppy::ptr pyexpr( PyType_GenericNew( &Expression_Type, 0, 0 ) ); if( !pyexpr ) return 0; Expression* expr = reinterpret_cast( pyexpr.get() ); - expr->terms = newref( first->terms ); + expr->terms = cppy::incref( first->terms ); expr->constant = first->constant + second; return pyexpr.release(); } @@ -315,7 +311,7 @@ PyObject* BinaryAdd::operator()( Expression* first, double second ) template<> inline PyObject* BinaryAdd::operator()( Term* first, double second ) { - PythonHelpers::PyObjectPtr pyexpr( PyType_GenericNew( &Expression_Type, 0, 0 ) ); + cppy::ptr pyexpr( PyType_GenericNew( &Expression_Type, 0, 0 ) ); if( !pyexpr ) return 0; Expression* expr = reinterpret_cast( pyexpr.get() ); @@ -337,7 +333,7 @@ PyObject* BinaryAdd::operator()( Term* first, Expression* second ) template<> inline PyObject* BinaryAdd::operator()( Term* first, Term* second ) { - PythonHelpers::PyObjectPtr pyexpr( PyType_GenericNew( &Expression_Type, 0, 0 ) ); + cppy::ptr pyexpr( PyType_GenericNew( &Expression_Type, 0, 0 ) ); if( !pyexpr ) return 0; Expression* expr = reinterpret_cast( pyexpr.get() ); @@ -352,7 +348,7 @@ PyObject* BinaryAdd::operator()( Term* first, Term* second ) template<> inline PyObject* BinaryAdd::operator()( Term* first, Variable* second ) { - PythonHelpers::PyObjectPtr temp( BinaryMul()( second, 1.0 ) ); + cppy::ptr temp( BinaryMul()( second, 1.0 ) ); if( !temp ) return 0; return BinaryAdd()( first, reinterpret_cast( temp.get() ) ); @@ -362,7 +358,7 @@ PyObject* BinaryAdd::operator()( Term* first, Variable* second ) template<> inline PyObject* BinaryAdd::operator()( Variable* first, double second ) { - PythonHelpers::PyObjectPtr temp( BinaryMul()( first, 1.0 ) ); + cppy::ptr temp( BinaryMul()( first, 1.0 ) ); if( !temp ) return 0; return operator()( reinterpret_cast( temp.get() ), second ); @@ -372,7 +368,7 @@ PyObject* BinaryAdd::operator()( Variable* first, double second ) template<> inline PyObject* BinaryAdd::operator()( Variable* first, Variable* second ) { - PythonHelpers::PyObjectPtr temp( BinaryMul()( first, 1.0 ) ); + cppy::ptr temp( BinaryMul()( first, 1.0 ) ); if( !temp ) return 0; return operator()( reinterpret_cast( temp.get() ), second ); @@ -382,7 +378,7 @@ PyObject* BinaryAdd::operator()( Variable* first, Variable* second ) template<> inline PyObject* BinaryAdd::operator()( Variable* first, Term* second ) { - PythonHelpers::PyObjectPtr temp( BinaryMul()( first, 1.0 ) ); + cppy::ptr temp( BinaryMul()( first, 1.0 ) ); if( !temp ) return 0; return operator()( reinterpret_cast( temp.get() ), second ); @@ -392,7 +388,7 @@ PyObject* BinaryAdd::operator()( Variable* first, Term* second ) template<> inline PyObject* BinaryAdd::operator()( Variable* first, Expression* second ) { - PythonHelpers::PyObjectPtr temp( BinaryMul()( first, 1.0 ) ); + cppy::ptr temp( BinaryMul()( first, 1.0 ) ); if( !temp ) return 0; return operator()( reinterpret_cast( temp.get() ), second ); @@ -440,7 +436,7 @@ PyObject* BinarySub::operator()( Variable* first, double second ) template<> inline PyObject* BinarySub::operator()( Variable* first, Variable* second ) { - PythonHelpers::PyObjectPtr temp( UnaryNeg()( second ) ); + cppy::ptr temp( UnaryNeg()( second ) ); if( !temp ) return 0; return BinaryAdd()( first, reinterpret_cast( temp.get() ) ); @@ -450,7 +446,7 @@ PyObject* BinarySub::operator()( Variable* first, Variable* second ) template<> inline PyObject* BinarySub::operator()( Variable* first, Term* second ) { - PythonHelpers::PyObjectPtr temp( UnaryNeg()( second ) ); + cppy::ptr temp( UnaryNeg()( second ) ); if( !temp ) return 0; return BinaryAdd()( first, reinterpret_cast( temp.get() ) ); @@ -460,7 +456,7 @@ PyObject* BinarySub::operator()( Variable* first, Term* second ) template<> inline PyObject* BinarySub::operator()( Variable* first, Expression* second ) { - PythonHelpers::PyObjectPtr temp( UnaryNeg()( second ) ); + cppy::ptr temp( UnaryNeg()( second ) ); if( !temp ) return 0; return BinaryAdd()( first, reinterpret_cast( temp.get() ) ); @@ -477,7 +473,7 @@ PyObject* BinarySub::operator()( Term* first, double second ) template<> inline PyObject* BinarySub::operator()( Term* first, Variable* second ) { - PythonHelpers::PyObjectPtr temp( UnaryNeg()( second ) ); + cppy::ptr temp( UnaryNeg()( second ) ); if( !temp ) return 0; return BinaryAdd()( first, reinterpret_cast( temp.get() ) ); @@ -487,7 +483,7 @@ PyObject* BinarySub::operator()( Term* first, Variable* second ) template<> inline PyObject* BinarySub::operator()( Term* first, Term* second ) { - PythonHelpers::PyObjectPtr temp( UnaryNeg()( second ) ); + cppy::ptr temp( UnaryNeg()( second ) ); if( !temp ) return 0; return BinaryAdd()( first, reinterpret_cast( temp.get() ) ); @@ -497,7 +493,7 @@ PyObject* BinarySub::operator()( Term* first, Term* second ) template<> inline PyObject* BinarySub::operator()( Term* first, Expression* second ) { - PythonHelpers::PyObjectPtr temp( UnaryNeg()( second ) ); + cppy::ptr temp( UnaryNeg()( second ) ); if( !temp ) return 0; return BinaryAdd()( first, reinterpret_cast( temp.get() ) ); @@ -514,7 +510,7 @@ PyObject* BinarySub::operator()( Expression* first, double second ) template<> inline PyObject* BinarySub::operator()( Expression* first, Variable* second ) { - PythonHelpers::PyObjectPtr temp( UnaryNeg()( second ) ); + cppy::ptr temp( UnaryNeg()( second ) ); if( !temp ) return 0; return BinaryAdd()( first, reinterpret_cast( temp.get() ) ); @@ -524,7 +520,7 @@ PyObject* BinarySub::operator()( Expression* first, Variable* second ) template<> inline PyObject* BinarySub::operator()( Expression* first, Term* second ) { - PythonHelpers::PyObjectPtr temp( UnaryNeg()( second ) ); + cppy::ptr temp( UnaryNeg()( second ) ); if( !temp ) return 0; return BinaryAdd()( first, reinterpret_cast( temp.get() ) ); @@ -534,7 +530,7 @@ PyObject* BinarySub::operator()( Expression* first, Term* second ) template<> inline PyObject* BinarySub::operator()( Expression* first, Expression* second ) { - PythonHelpers::PyObjectPtr temp( UnaryNeg()( second ) ); + cppy::ptr temp( UnaryNeg()( second ) ); if( !temp ) return 0; return BinaryAdd()( first, reinterpret_cast( temp.get() ) ); @@ -544,7 +540,7 @@ PyObject* BinarySub::operator()( Expression* first, Expression* second ) template<> inline PyObject* BinarySub::operator()( double first, Variable* second ) { - PythonHelpers::PyObjectPtr temp( UnaryNeg()( second ) ); + cppy::ptr temp( UnaryNeg()( second ) ); if( !temp ) return 0; return BinaryAdd()( first, reinterpret_cast( temp.get() ) ); @@ -554,7 +550,7 @@ PyObject* BinarySub::operator()( double first, Variable* second ) template<> inline PyObject* BinarySub::operator()( double first, Term* second ) { - PythonHelpers::PyObjectPtr temp( UnaryNeg()( second ) ); + cppy::ptr temp( UnaryNeg()( second ) ); if( !temp ) return 0; return BinaryAdd()( first, reinterpret_cast( temp.get() ) ); @@ -564,7 +560,7 @@ PyObject* BinarySub::operator()( double first, Term* second ) template<> inline PyObject* BinarySub::operator()( double first, Expression* second ) { - PythonHelpers::PyObjectPtr temp( UnaryNeg()( second ) ); + cppy::ptr temp( UnaryNeg()( second ) ); if( !temp ) return 0; return BinaryAdd()( first, reinterpret_cast( temp.get() ) ); @@ -574,10 +570,10 @@ PyObject* BinarySub::operator()( double first, Expression* second ) template PyObject* makecn( T first, U second, kiwi::RelationalOperator op ) { - PythonHelpers::PyObjectPtr pyexpr( BinarySub()( first, second ) ); + cppy::ptr pyexpr( BinarySub()( first, second ) ); if( !pyexpr ) return 0; - PythonHelpers::PyObjectPtr pycn( PyType_GenericNew( &Constraint_Type, 0, 0 ) ); + cppy::ptr pycn( PyType_GenericNew( &Constraint_Type, 0, 0 ) ); if( !pycn ) return 0; Constraint* cn = reinterpret_cast( pycn.get() ); diff --git a/py/term.cpp b/py/term.cpp index 4be64a1e..3416a356 100644 --- a/py/term.cpp +++ b/py/term.cpp @@ -1,5 +1,5 @@ /*----------------------------------------------------------------------------- -| Copyright (c) 2013-2017, Nucleic Development Team. +| Copyright (c) 2013-2018, Nucleic Development Team. | | Distributed under the terms of the Modified BSD License. | @@ -27,7 +27,7 @@ Term_new( PyTypeObject* type, PyObject* args, PyObject* kwargs ) &pyvar, &pycoeff ) ) return 0; if( !Variable::TypeCheck( pyvar ) ) - return py_expected_type_fail( pyvar, "Variable" ); + return cppy::type_error( pyvar, "Variable" ); double coefficient = 1.0; if( pycoeff && !convert_to_double( pycoeff, coefficient ) ) return 0; @@ -35,7 +35,7 @@ Term_new( PyTypeObject* type, PyObject* args, PyObject* kwargs ) if( !pyterm ) return 0; Term* self = reinterpret_cast( pyterm ); - self->variable = newref( pyvar ); + self->variable = cppy::incref( pyvar ); self->coefficient = coefficient; return pyterm; } @@ -71,14 +71,14 @@ Term_repr( Term* self ) std::stringstream stream; stream << self->coefficient << " * "; stream << reinterpret_cast( self->variable )->variable.name(); - return FROM_STRING( stream.str().c_str() ); + return PyUnicode_FromString( stream.str().c_str() ); } static PyObject* Term_variable( Term* self ) { - return newref( self->variable ); + return cppy::incref( self->variable ); } @@ -175,42 +175,25 @@ Term_as_number = { (binaryfunc)Term_add, /* nb_add */ (binaryfunc)Term_sub, /* nb_subtract */ (binaryfunc)Term_mul, /* nb_multiply */ -#if PY_MAJOR_VERSION < 3 - (binaryfunc)Term_div, /* nb_divide */ -#endif 0, /* nb_remainder */ 0, /* nb_divmod */ 0, /* nb_power */ (unaryfunc)Term_neg, /* nb_negative */ 0, /* nb_positive */ 0, /* nb_absolute */ -#if PY_MAJOR_VERSION >= 3 0, /* nb_bool */ -#else - 0, /* nb_nonzero */ -#endif 0, /* nb_invert */ 0, /* nb_lshift */ 0, /* nb_rshift */ 0, /* nb_and */ 0, /* nb_xor */ (binaryfunc)0, /* nb_or */ -#if PY_MAJOR_VERSION < 3 - 0, /* nb_coerce */ -#endif 0, /* nb_int */ 0, /* nb_long */ 0, /* nb_float */ -#if PY_MAJOR_VERSION < 3 - 0, /* nb_oct */ - 0, /* nb_hex */ -#endif 0, /* nb_inplace_add */ 0, /* nb_inplace_subtract */ 0, /* nb_inplace_multiply */ -#if PY_MAJOR_VERSION < 3 - 0, /* nb_inplace_divide */ -#endif 0, /* nb_inplace_remainder */ 0, /* nb_inplace_power */ 0, /* nb_inplace_lshift */ @@ -222,13 +205,9 @@ Term_as_number = { (binaryfunc)Term_div, /* nb_true_divide */ 0, /* nb_inplace_floor_divide */ 0, /* nb_inplace_true_divide */ -#if PY_VERSION_HEX >= 0x02050000 (unaryfunc)0, /* nb_index */ -#endif -#if PY_VERSION_HEX >= 0x03050000 (binaryfunc)0, /* nb_matrix_multiply */ (binaryfunc)0, /* nb_inplace_matrix_multiply */ -#endif }; @@ -241,13 +220,7 @@ PyTypeObject Term_Type = { (printfunc)0, /* tp_print */ (getattrfunc)0, /* tp_getattr */ (setattrfunc)0, /* tp_setattr */ -#if PY_VERSION_HEX >= 0x03050000 ( PyAsyncMethods* )0, /* tp_as_async */ -#elif PY_VERSION_HEX >= 0x03000000 - ( void* ) 0, /* tp_reserved */ -#else - ( cmpfunc )0, /* tp_compare */ -#endif (reprfunc)Term_repr, /* tp_repr */ (PyNumberMethods*)&Term_as_number, /* tp_as_number */ (PySequenceMethods*)0, /* tp_as_sequence */ @@ -258,11 +231,7 @@ PyTypeObject Term_Type = { (getattrofunc)0, /* tp_getattro */ (setattrofunc)0, /* tp_setattro */ (PyBufferProcs*)0, /* tp_as_buffer */ -#if PY_MAJOR_VERSION >= 3 Py_TPFLAGS_DEFAULT|Py_TPFLAGS_HAVE_GC|Py_TPFLAGS_BASETYPE, /* tp_flags */ -#else - Py_TPFLAGS_DEFAULT|Py_TPFLAGS_HAVE_GC|Py_TPFLAGS_BASETYPE|Py_TPFLAGS_CHECKTYPES, /* tp_flags */ -#endif 0, /* Documentation string */ (traverseproc)Term_traverse, /* tp_traverse */ (inquiry)Term_clear, /* tp_clear */ diff --git a/py/util.h b/py/util.h index 78e9cbd0..683d6db6 100644 --- a/py/util.h +++ b/py/util.h @@ -1,5 +1,5 @@ /*----------------------------------------------------------------------------- -| Copyright (c) 2013-2017, Nucleic Development Team. +| Copyright (c) 2013-2019, Nucleic Development Team. | | Distributed under the terms of the Modified BSD License. | @@ -9,8 +9,8 @@ #include #include #include +#include #include -#include "pythonhelpers.h" #include "types.h" @@ -22,13 +22,6 @@ convert_to_double( PyObject* obj, double& out ) out = PyFloat_AS_DOUBLE( obj ); return true; } -#if PY_MAJOR_VERSION < 3 - if( PyInt_Check( obj ) ) - { - out = double( PyInt_AsLong( obj ) ); - return true; - } -#endif if( PyLong_Check( obj ) ) { out = PyLong_AsDouble( obj ); @@ -36,7 +29,7 @@ convert_to_double( PyObject* obj, double& out ) return false; return true; } - PythonHelpers::py_expected_type_fail( obj, "float, int, or long" ); + cppy::type_error( obj, "float, int, or long" ); return false; } @@ -44,19 +37,7 @@ convert_to_double( PyObject* obj, double& out ) inline bool convert_pystr_to_str( PyObject* value, std::string& out ) { -#if PY_MAJOR_VERSION >= 3 out = PyUnicode_AsUTF8( value ); -#else - if( PyUnicode_Check( value ) ) - { - PythonHelpers::PyObjectPtr py_str( PyUnicode_AsUTF8String( value ) ); - if( !py_str ) - return false; // LCOV_EXCL_LINE - out = PyString_AS_STRING( py_str.get() ); - } - else - out = PyString_AS_STRING( value ); -#endif return true; } @@ -64,13 +45,8 @@ convert_pystr_to_str( PyObject* value, std::string& out ) inline bool convert_to_strength( PyObject* value, double& out ) { -#if PY_MAJOR_VERSION >= 3 if( PyUnicode_Check( value ) ) { -#else - if( PyString_Check( value ) | PyUnicode_Check( value )) - { -#endif std::string str; if( !convert_pystr_to_str( value, str ) ) return false; @@ -92,7 +68,7 @@ convert_to_strength( PyObject* value, double& out ) ); return false; } - return true; + return true; } if( !convert_to_double( value, out ) ) return false; @@ -103,19 +79,11 @@ convert_to_strength( PyObject* value, double& out ) inline bool convert_to_relational_op( PyObject* value, kiwi::RelationalOperator& out ) { -#if PY_MAJOR_VERSION >= 3 if( !PyUnicode_Check( value ) ) { - PythonHelpers::py_expected_type_fail( value, "unicode" ); - return false; - } -#else - if( !(PyString_Check( value ) | PyUnicode_Check( value ) ) ) - { - PythonHelpers::py_expected_type_fail( value, "str or unicode" ); + cppy::type_error( value, "str" ); return false; } -#endif std::string str; if( !convert_pystr_to_str( value, str ) ) return false; @@ -142,7 +110,7 @@ inline PyObject* make_terms( const std::map& coeffs ) { typedef std::map::const_iterator iter_t; - PythonHelpers::PyObjectPtr terms( PyTuple_New( coeffs.size() ) ); + cppy::ptr terms( PyTuple_New( coeffs.size() ) ); if( !terms ) return 0; Py_ssize_t size = PyTuple_GET_SIZE( terms.get() ); @@ -157,7 +125,7 @@ make_terms( const std::map& coeffs ) if( !pyterm ) return 0; Term* term = reinterpret_cast( pyterm ); - term->variable = PythonHelpers::newref( it->first ); + term->variable = cppy::incref( it->first ); term->coefficient = it->second; PyTuple_SET_ITEM( terms.get(), i, pyterm ); } @@ -177,7 +145,7 @@ reduce_expression( PyObject* pyexpr ) // pyexpr must be an Expression Term* term = reinterpret_cast( item ); coeffs[ term->variable ] += term->coefficient; } - PythonHelpers::PyObjectPtr terms( make_terms( coeffs ) ); + cppy::ptr terms( make_terms( coeffs ) ); if( !terms ) return 0; PyObject* pynewexpr = PyType_GenericNew( &Expression_Type, 0, 0 ); diff --git a/py/variable.cpp b/py/variable.cpp index a622e852..d419913a 100644 --- a/py/variable.cpp +++ b/py/variable.cpp @@ -1,13 +1,13 @@ /*----------------------------------------------------------------------------- -| Copyright (c) 2013-2017, Nucleic Development Team. +| Copyright (c) 2013-2018, Nucleic Development Team. | | Distributed under the terms of the Modified BSD License. | | The full license is in the file COPYING.txt, distributed with this software. |----------------------------------------------------------------------------*/ #include +#include #include -#include "pythonhelpers.h" #include "symbolics.h" #include "types.h" #include "util.h" @@ -28,24 +28,17 @@ Variable_new( PyTypeObject* type, PyObject* args, PyObject* kwargs ) &name, &context ) ) return 0; - PyObjectPtr pyvar( PyType_GenericNew( type, args, kwargs ) ); + cppy::ptr pyvar( PyType_GenericNew( type, args, kwargs ) ); if( !pyvar ) return 0; Variable* self = reinterpret_cast( pyvar.get() ); - self->context = xnewref( context ); + self->context = cppy::xincref( context ); if( name != 0 ) { -#if PY_MAJOR_VERSION >= 3 if( !PyUnicode_Check( name ) ) - return py_expected_type_fail( name, "unicode" ); -#else - if( !( PyString_Check( name ) | PyUnicode_Check( name ) ) ) - { - return py_expected_type_fail( name, "str or unicode" ); - } -#endif + return cppy::type_error( name, "str" ); std::string c_name; if( !convert_pystr_to_str(name, c_name) ) return 0; // LCOV_EXCL_LINE @@ -88,29 +81,22 @@ Variable_dealloc( Variable* self ) static PyObject* Variable_repr( Variable* self ) { - return FROM_STRING( self->variable.name().c_str() ); + return PyUnicode_FromString( self->variable.name().c_str() ); } static PyObject* Variable_name( Variable* self ) { - return FROM_STRING( self->variable.name().c_str() ); + return PyUnicode_FromString( self->variable.name().c_str() ); } static PyObject* Variable_setName( Variable* self, PyObject* pystr ) { -#if PY_MAJOR_VERSION >= 3 if( !PyUnicode_Check( pystr ) ) - return py_expected_type_fail( pystr, "unicode" ); -#else - if( !(PyString_Check( pystr ) | PyUnicode_Check( pystr ) ) ) - { - return py_expected_type_fail( pystr, "str or unicode" ); - } -#endif + return cppy::type_error( pystr, "str" ); std::string str; if( !convert_pystr_to_str( pystr, str ) ) return 0; @@ -123,7 +109,7 @@ static PyObject* Variable_context( Variable* self ) { if( self->context ) - return newref( self->context ); + return cppy::incref( self->context ); Py_RETURN_NONE; } @@ -134,7 +120,7 @@ Variable_setContext( Variable* self, PyObject* value ) if( value != self->context ) { PyObject* temp = self->context; - self->context = newref( value ); + self->context = cppy::incref( value ); Py_XDECREF( temp ); } Py_RETURN_NONE; @@ -230,42 +216,25 @@ Variable_as_number = { (binaryfunc)Variable_add, /* nb_add */ (binaryfunc)Variable_sub, /* nb_subtract */ (binaryfunc)Variable_mul, /* nb_multiply */ -#if PY_MAJOR_VERSION < 3 - (binaryfunc)Variable_div, /* nb_divide */ -#endif 0, /* nb_remainder */ 0, /* nb_divmod */ 0, /* nb_power */ (unaryfunc)Variable_neg, /* nb_negative */ 0, /* nb_positive */ 0, /* nb_absolute */ -#if PY_MAJOR_VERSION >= 3 0, /* nb_bool */ -#else - 0, /* nb_nonzero */ -#endif 0, /* nb_invert */ 0, /* nb_lshift */ 0, /* nb_rshift */ 0, /* nb_and */ 0, /* nb_xor */ (binaryfunc)0, /* nb_or */ -#if PY_MAJOR_VERSION < 3 - 0, /* nb_coerce */ -#endif 0, /* nb_int */ 0, /* nb_long */ 0, /* nb_float */ -#if PY_MAJOR_VERSION < 3 - 0, /* nb_oct */ - 0, /* nb_hex */ -#endif 0, /* nb_inplace_add */ 0, /* nb_inplace_subtract */ 0, /* nb_inplace_multiply */ -#if PY_MAJOR_VERSION < 3 - 0, /* nb_inplace_divide */ -#endif 0, /* nb_inplace_remainder */ 0, /* nb_inplace_power */ 0, /* nb_inplace_lshift */ @@ -277,13 +246,9 @@ Variable_as_number = { (binaryfunc)Variable_div, /* nb_true_divide */ 0, /* nb_inplace_floor_divide */ 0, /* nb_inplace_true_divide */ -#if PY_VERSION_HEX >= 0x02050000 (unaryfunc)0, /* nb_index */ -#endif -#if PY_VERSION_HEX >= 0x03050000 (binaryfunc)0, /* nb_matrix_multiply */ (binaryfunc)0, /* nb_inplace_matrix_multiply */ -#endif }; @@ -296,13 +261,7 @@ PyTypeObject Variable_Type = { (printfunc)0, /* tp_print */ (getattrfunc)0, /* tp_getattr */ (setattrfunc)0, /* tp_setattr */ -#if PY_VERSION_HEX >= 0x03050000 ( PyAsyncMethods* )0, /* tp_as_async */ -#elif PY_VERSION_HEX >= 0x03000000 - ( void* ) 0, /* tp_reserved */ -#else - ( cmpfunc )0, /* tp_compare */ -#endif (reprfunc)Variable_repr, /* tp_repr */ (PyNumberMethods*)&Variable_as_number, /* tp_as_number */ (PySequenceMethods*)0, /* tp_as_sequence */ @@ -313,11 +272,7 @@ PyTypeObject Variable_Type = { (getattrofunc)0, /* tp_getattro */ (setattrofunc)0, /* tp_setattro */ (PyBufferProcs*)0, /* tp_as_buffer */ -#if PY_MAJOR_VERSION >= 3 Py_TPFLAGS_DEFAULT|Py_TPFLAGS_HAVE_GC|Py_TPFLAGS_BASETYPE, /* tp_flags */ -#else - Py_TPFLAGS_DEFAULT|Py_TPFLAGS_HAVE_GC|Py_TPFLAGS_BASETYPE|Py_TPFLAGS_CHECKTYPES, /* tp_flags */ -#endif 0, /* Documentation string */ (traverseproc)Variable_traverse, /* tp_traverse */ (inquiry)Variable_clear, /* tp_clear */ diff --git a/setup.py b/setup.py index fce7d4e5..0b69345d 100644 --- a/setup.py +++ b/setup.py @@ -39,16 +39,21 @@ class BuildExt(build_ext): } def build_extensions(self): + + # Delayed import of cppy to let setup_requires install it if necessary + import cppy + ct = self.compiler.compiler_type opts = self.c_opts.get(ct, []) for ext in self.extensions: + ext.include_dirs.insert(0, cppy.get_include()) ext.extra_compile_args = opts build_ext.build_extensions(self) setup( name='kiwisolver', - version='1.1.0', + version='1.2.0.dev', author='The Nucleic Development Team', author_email='sccolbert@gmail.com', url='https://github.com/nucleic/kiwi', @@ -58,17 +63,15 @@ def build_extensions(self): classifiers=[ # https://pypi.org/pypi?%3Aaction=list_classifiers 'Programming Language :: Python', - 'Programming Language :: Python :: 2', - 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.4', 'Programming Language :: Python :: 3.5', 'Programming Language :: Python :: 3.6', 'Programming Language :: Python :: 3.7', 'Programming Language :: Python :: Implementation :: CPython', ], - python_requires='>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*', - install_requires=[], + python_requires='>=3.5', + install_requires=['setuptools'], + setup_requires=['cppy'], ext_modules=ext_modules, cmdclass={'build_ext': BuildExt}, ) From b91496f8e42838a0bb3f922f4b734fcd799b5d97 Mon Sep 17 00:00:00 2001 From: MatthieuDartiailh Date: Thu, 25 Oct 2018 18:01:22 -0400 Subject: [PATCH 02/16] kiwi: c++11 update --- kiwi/AssocVector.h | 5109 +++++++++++++++++++++++++++++++++++++++++--- kiwi/maptype.h | 2 +- kiwi/solverimpl.h | 12 +- kiwi/variable.h | 2 +- 4 files changed, 4863 insertions(+), 262 deletions(-) diff --git a/kiwi/AssocVector.h b/kiwi/AssocVector.h index 0d5eb288..74f69ad2 100644 --- a/kiwi/AssocVector.h +++ b/kiwi/AssocVector.h @@ -1,354 +1,4955 @@ -//////////////////////////////////////////////////////////////////////////////// -// The Loki Library -// Copyright (c) 2001 by Andrei Alexandrescu -// This code accompanies the book: -// Alexandrescu, Andrei. "Modern C++ Design: Generic Programming and Design -// Patterns Applied". Copyright (c) 2001. Addison-Wesley. -// Permission to use, copy, modify, distribute and sell this software for any -// purpose is hereby granted without fee, provided that the above copyright -// notice appear in all copies and that both that copyright notice and this -// permission notice appear in supporting documentation. -// The author or Addison-Wesley Longman make no representations about the -// suitability of this software for any purpose. It is provided "as is" -// without express or implied warranty. -//////////////////////////////////////////////////////////////////////////////// -#pragma once - -// $Id: AssocVector.h 765 2006-10-18 13:55:32Z syntheticpp $ +/* + * Copyright (C) 2012 �ukasz Czerwi�ski + * + * GitHub: https://github.com/wo3kie/AssocVector + * Website: http://www.lukaszczerwinski.pl/assoc_vector.en.html + * + * Distributed under the BSD Software License (see file license) + */ +#ifndef ASSOC_VECTOR_HPP +#define ASSOC_VECTOR_HPP + +#ifndef __GXX_EXPERIMENTAL_CXX0X__ + #error C++11 is required to run this code, please use AssocVector 1.0.x instead. +#endif + +// includes.begin #include #include -#include -#include +#include +#include + +#include +#include + +// includes.end + +// configuration.begin + +#if ( __GNUC__ >= 4 && __GNUC_MINOR__ >= 8 ) + #define AV_HAS_TRIVIAL_DESTRUCTOR( type ) std::is_trivially_destructible< type >::value + #define AV_MOVE_IF_NOEXCEPT std::move_if_noexcept +#else + #define AV_HAS_TRIVIAL_DESTRUCTOR( type ) __has_trivial_destructor( type ) + #define AV_MOVE_IF_NOEXCEPT std::move +#endif + +// configuration.end + +#ifndef NDEBUG + #define AV_DEBUG +#endif + +#ifdef AV_DEBUG + #define AV_PRECONDITION( condition ) if( (bool)( condition ) == false ){int*i=0;*i=0;} + #define AV_CHECK( condition ) if( (bool)( condition ) == false ){int*i=0;*i=0;} + #define AV_POSTCONDITION( condition ) if( (bool)( condition ) == false ){int*i=0;*i=0;} + #define AV_ERROR() if( (true) ){int*i=0;*i=0;} +#else + #define AV_PRECONDITION( condition ) (void)( 0 ); + #define AV_CHECK( condition ) (void)( 0 ); + #define AV_POSTCONDITION( condition ) (void)( 0 ); + #define AV_ERROR() (void)( 0 ); +#endif + +namespace util +{ + // + // CmpByFirst + // + template< typename _Pair, typename _Cmp > + struct CmpByFirst + { + CmpByFirst( _Cmp const & cmp = _Cmp() ) + : _cmp( cmp ) + { + } + + bool operator()( _Pair const & lhs, _Pair const & rhs )const + { + return _cmp( lhs.first, rhs.first ); + } + + bool operator()( _Pair const & pair, typename _Pair::first_type const & value )const + { + return _cmp( pair.first, value ); + } + + bool operator()( typename _Pair::first_type const & value, _Pair const & pair )const + { + return _cmp( value, pair.first ); + } + + private: + _Cmp _cmp; + }; +} + +namespace util +{ + // + // equal + // + template< + typename _T1 + , typename _T2 + > + inline bool equal( + _T1 const & first + , _T2 const & second + ) + { + if( first < second ){ + return false; + } + + if( second < first ){ + return false; + } -namespace Loki + return true; + } + + // + // less_equal + // + template< + typename _T1 + , typename _T2 + > + inline bool less_equal( + _T1 const & first + , _T2 const & second + ) + { + return ( second < first ) == false; + } +} + +namespace util { -//////////////////////////////////////////////////////////////////////////////// -// class template AssocVectorCompare -// Used by AssocVector -//////////////////////////////////////////////////////////////////////////////// + // + // is_between + // + template< + typename _T1 + , typename _T2 + > + inline bool is_between( + _T1 const & first + , _T2 const & value + , _T1 const & last + ) + { + AV_PRECONDITION( less_equal( first, last ) ); - namespace Private + return ( value < first ) == false && ( last < value ) == false; + } +} + +namespace util +{ + // + // destroy_range + // + namespace detail { - template - class AssocVectorCompare : public C + template< bool _HasTrivialDestructor > + struct DestroyRangeImpl { - typedef std::pair - Data; - typedef typename C::first_argument_type first_argument_type; + }; - public: - AssocVectorCompare() - {} + template<> + struct DestroyRangeImpl< true > + { + template< typename _Ptr > + static + void destroy( _Ptr, _Ptr ) + { + } + }; + + template<> + struct DestroyRangeImpl< false > + { + template< typename _Ptr > + static + void destroy( _Ptr first, _Ptr const last ) + { + AV_PRECONDITION( less_equal( first, last ) ); + + typedef typename std::iterator_traits< _Ptr >::value_type T; + + for( /*empty*/ ; first != last ; ++ first ){ + first -> T::~T(); + } + } + }; + } + + template< typename _Ptr > + inline void destroy_range( + _Ptr first + , _Ptr const last + ) + { + typedef typename std::iterator_traits< _Ptr >::value_type T; + + detail::DestroyRangeImpl< AV_HAS_TRIVIAL_DESTRUCTOR( T ) >::destroy( first, last ); + } +} + +namespace util +{ + // + // move + // + template< + typename _InputPtr + , typename _OutputPtr + > + inline void move( + _InputPtr first + , _InputPtr last + , _OutputPtr first2 + ) + { + if( first < first2 ){ + std::move_backward( first, last, first2 + ( last - first ) ); + } + else if( first > first2 ){ + std::move( first, last, first2 ); + } + else{ + // first == first2 -> do nothing + } + } + + template< + typename _InputPtr + , typename _OutputPtr + > + inline void copy( + _InputPtr first + , _InputPtr last + , _OutputPtr first2 + ) + { + if( first < first2 ){ + std::copy_backward( first, last, first2 + ( last - first ) ); + } + else if( first > first2 ){ + std::copy( first, last, first2 ); + } + else{ + // first == first2 -> do nothing + } + } - AssocVectorCompare(const C& src) : C(src) - {} - bool operator()(const first_argument_type& lhs, - const first_argument_type& rhs) const - { return C::operator()(lhs, rhs); } - bool operator()(const Data& lhs, const Data& rhs) const - { return operator()(lhs.first, rhs.first); } + namespace detail + { + + template< bool _MoveDoesNotThrow > + struct MoveIfNoExcept + { + }; - bool operator()(const Data& lhs, - const first_argument_type& rhs) const - { return operator()(lhs.first, rhs); } + template<> + struct MoveIfNoExcept< true > + { + template< + typename _InputPtr + , typename _OutputPtr + > + static + void move( _InputPtr first, _InputPtr const last, _OutputPtr first2 ) + { + move( first, last, first2 ); + } + }; - bool operator()(const first_argument_type& lhs, - const Data& rhs) const - { return operator()(lhs, rhs.first); } + template<> + struct MoveIfNoExcept< false > + { + template< + typename _InputPtr + , typename _OutputPtr + > + static + void move( _InputPtr first, _InputPtr const last, _OutputPtr first2 ) + { + copy( first, last, first2 ); + } }; } -//////////////////////////////////////////////////////////////////////////////// -// class template AssocVector -// An associative vector built as a syntactic drop-in replacement for std::map -// BEWARE: AssocVector doesn't respect all map's guarantees, the most important -// being: -// * iterators are invalidated by insert and erase operations -// * the complexity of insert/erase is O(N) not O(log N) -// * value_type is std::pair not std::pair -// * iterators are random -//////////////////////////////////////////////////////////////////////////////// - - - template - < - class K, - class V, - class C = std::less, - class A = std::allocator< std::pair > + template< + typename _InputPtr + , typename _OutputPtr > - class AssocVector - : private std::vector< std::pair, A > - , private Private::AssocVectorCompare + inline void move_if_noexcept( + _InputPtr first + , _InputPtr const last + , _OutputPtr first2 + ) { - typedef std::vector, A> Base; - typedef Private::AssocVectorCompare MyCompare; + typedef typename std::iterator_traits< _InputPtr >::value_type T; - public: - typedef K key_type; - typedef V mapped_type; - typedef typename Base::value_type value_type; - - typedef C key_compare; - typedef A allocator_type; - typedef typename A::reference reference; - typedef typename A::const_reference const_reference; - typedef typename Base::iterator iterator; - typedef typename Base::const_iterator const_iterator; - typedef typename Base::size_type size_type; - typedef typename Base::difference_type difference_type; - typedef typename A::pointer pointer; - typedef typename A::const_pointer const_pointer; - typedef typename Base::reverse_iterator reverse_iterator; - typedef typename Base::const_reverse_iterator const_reverse_iterator; - - class value_compare - : public std::binary_function - , private key_compare - { - friend class AssocVector; - - protected: - value_compare(key_compare pred) : key_compare(pred) - {} + detail::MoveIfNoExcept< true >::move( first, last, first2 ); + } + +} + +namespace util +{ + +template< + typename _Iterator + , typename _T + , typename _Cmp +> +_Iterator +last_less_equal( + _Iterator first + , _Iterator last + , _T const & t + , _Cmp cmp +) +{ + AV_PRECONDITION( less_equal( first, last ) ); + + if( first == last ){ + return last; + } + + _Iterator greaterEqual = std::lower_bound( first, last, t, cmp ); + + if( greaterEqual != last ) + { + // lower_bound returns first greater_than/equal_to but we need last less_than + + bool const isEqual = cmp( t, * greaterEqual ) == false; + + if( isEqual ) + { + // that is OK, request item was found + return greaterEqual; + } + } + + if( greaterEqual == first ) + { + // requested item does not exist in container + + // 6 8 10 13 17 19 20 21 22 24 end + // ^ lower_bound( 1 ): + // requested: ^ + + return last; + } + else + { + // we need to go one item backward + + // 6 8 10 13 17 19 20 21 22 24 + // lower_bound( 23 ): ^ + // requested: ^ + + return -- greaterEqual; + } +} + +} + +namespace util +{ + +template< + typename _InputIterator1 + , typename _InputIterator2 + , typename _Cmp +> +bool has_intersection( + _InputIterator1 first1 + , _InputIterator1 const last1 + , _InputIterator2 first2 + , _InputIterator2 const last2 + , _Cmp const & cmp +) +{ + while( first1 != last1 && first2 != last2 ) + { + if( cmp( * first1, * first2 ) ){ + ++first1; + } + else if( cmp( * first2, * first1 ) ){ + ++first2; + } + else{ + return true; + } + } + + return false; +} + +} + +namespace array +{ + // + // I need much more control over Array inside AssocVector than std::vector offers + // + + // C' style array with some useful methods and functions + + // + // Array + // + template< + typename _T + , typename _Alloc = std::allocator< _T > + > + struct ArrayBase + { + struct ArrayImpl + : public _Alloc + { + ArrayImpl( _Alloc const & alloc = _Alloc() ) + : _Alloc( alloc ) + , _data( nullptr ) + , _capacity( 0 ) + , _size( 0 ) + { + } + + ArrayImpl( std::size_t capacity, _Alloc const & alloc = _Alloc() ) + : _Alloc( alloc ) + , _data( capacity ? this->allocate( capacity ) : nullptr ) + , _capacity( capacity ) + , _size( 0 ) + { + AV_PRECONDITION( _capacity < this->max_size() ); + } + + ~ArrayImpl() + { + util::destroy_range( _data, _data + _size ); + + this->deallocate( _data, _capacity ); + } + + void swap( ArrayImpl & other )noexcept + { + std::swap( _data, other._data ); + std::swap( _capacity, other._capacity ); + std::swap( _size, other._size ); + } public: - bool operator()(const value_type& lhs, const value_type& rhs) const - { return key_compare::operator()(lhs.first, rhs.first); } + _T * _data; + + std::size_t _capacity; + std::size_t _size; }; - // 23.3.1.1 construct/copy/destroy + typedef _Alloc allocator_type; - explicit AssocVector(const key_compare& comp = key_compare(), - const A& alloc = A()) - : Base(alloc), MyCompare(comp) - {} + allocator_type + get_allocator() const { + return * static_cast< allocator_type const * >( & this->_impl ); + } - template - AssocVector(InputIterator first, InputIterator last, - const key_compare& comp = key_compare(), - const A& alloc = A()) - : Base(first, last, alloc), MyCompare(comp) + public: + ArrayBase( _Alloc const & alloc = _Alloc() ) + : _impl( alloc ) { - MyCompare& me = *this; - std::sort(begin(), end(), me); } - AssocVector& operator=(const AssocVector& rhs) + ArrayBase( std::size_t capacity, _Alloc const & alloc = _Alloc() ) + : _impl( capacity, alloc ) { - AssocVector(rhs).swap(*this); - return *this; } - // iterators: - // The following are here because MWCW gets 'using' wrong - iterator begin() { return Base::begin(); } - const_iterator begin() const { return Base::begin(); } - iterator end() { return Base::end(); } - const_iterator end() const { return Base::end(); } - reverse_iterator rbegin() { return Base::rbegin(); } - const_reverse_iterator rbegin() const { return Base::rbegin(); } - reverse_iterator rend() { return Base::rend(); } - const_reverse_iterator rend() const { return Base::rend(); } + ~ArrayBase() = default; + + ArrayBase( ArrayBase const & other ) = delete; + ArrayBase( ArrayBase && other ) = delete; + + ArrayBase & operator=( ArrayBase const & other ) = delete; + ArrayBase & operator=( ArrayBase && other ) = delete; + + protected: + ArrayImpl _impl; + }; + + template< + typename _T + , typename _Alloc = std::allocator< _T > + > + struct Array + : protected ArrayBase< _T, _Alloc > + { + private: + typedef ArrayBase< _T, _Alloc > Base; + + public: + using Base::get_allocator; - // capacity: - bool empty() const { return Base::empty(); } - size_type size() const { return Base::size(); } - size_type max_size() { return Base::max_size(); } + public: + typedef _T value_type; - // 23.3.1.2 element access: - mapped_type& operator[](const key_type& key) - { return insert(value_type(key, mapped_type())).first->second; } + typedef _T * iterator; + typedef _T const * const_iterator; - // modifiers: - std::pair insert(const value_type& val) + public: + Array( _Alloc const & alloc = _Alloc() ) + : Base( alloc ) { - bool found(true); - iterator i(lower_bound(val.first)); + } - if (i == end() || this->operator()(val.first, i->first)) - { - i = Base::insert(i, val); - found = false; + Array( std::size_t capacity, _Alloc const & alloc = _Alloc() ) + : Base( capacity, alloc ) + { + } + + Array( Array const & other ) + // In std::vector new vector's capacity is equal to old vector's size. + // Array's capacity is equal to old array's capacity to ensure invariant that: + // sqrt( storage.capacity ) == buffer.capacity + // sqrt( storage.capacity ) == erased.capacity + : Base( other.capacity(), other.get_allocator() ) + { + for( /*empty*/ ; this->_impl._size < other.size() ; ++ this->_impl._size ){ + get_allocator().construct( + this->_impl._data + this->_impl._size + , * ( other._impl._data + this->_impl._size ) + ); } - return std::make_pair(i, !found); } - //Section [23.1.2], Table 69 - //http://developer.apple.com/documentation/DeveloperTools/gcc-3.3/libstdc++/23_containers/howto.html#4 - iterator insert(iterator pos, const value_type& val) + + Array( Array && other ) + : Base( 0, other.get_allocator() ) { - if( (pos == begin() || this->operator()(*(pos-1),val)) && - (pos == end() || this->operator()(val, *pos)) ) - { - return Base::insert(pos, val); + this->_impl.swap( other._impl ); + } + + ~Array() = default; + + Array & operator=( Array const & other ) + { + Array temp( other ); + + swap( temp ); + + return * this; + } + + Array & operator=( Array && other ) + { + this->_impl.swap( other._impl ); + + return * this; + } + + void swap( Array & other )noexcept + { + this->_impl.swap( other._impl ); + } + + void reserve( std::size_t capacity ) + { + if( get_allocator().max_size() < capacity ){ + throw std::length_error( "Array::reserve" ); + } + + if( capacity <= getCapacity() ){ + return; } - return insert(val).first; + + Array< _T, _Alloc > temp( capacity, get_allocator() ); + + std::uninitialized_copy( + std::make_move_iterator( begin() ) + , std::make_move_iterator( end() ) + , temp.begin() + ); + + swap( temp ); + } + + iterator begin()noexcept + { + return getData(); + } + + const_iterator begin()const noexcept + { + return getData(); } - template - void insert(InputIterator first, InputIterator last) - { for (; first != last; ++first) insert(*first); } + const_iterator cbegin()const noexcept + { + return getData(); + } - void erase(iterator pos) - { Base::erase(pos); } + iterator end()noexcept + { + return getData() + getSize(); + } - size_type erase(const key_type& k) + const_iterator end()const noexcept { - iterator i(find(k)); - if (i == end()) return 0; - erase(i); - return 1; + return getData() + getSize(); } - void erase(iterator first, iterator last) - { Base::erase(first, last); } + const_iterator cend()const noexcept + { + return getData() + getSize(); + } - void swap(AssocVector& other) + bool empty()const noexcept { - Base::swap(other); - MyCompare& me = *this; - MyCompare& rhs = other; - std::swap(me, rhs); + return getSize() == 0; } - void clear() - { Base::clear(); } + bool full()const noexcept + { + return size() == capacity(); + } - // observers: - key_compare key_comp() const - { return *this; } + std::size_t size()const noexcept + { + return this->_impl._size; + } - value_compare value_comp() const + std::size_t getSize()const noexcept { - const key_compare& comp = *this; - return value_compare(comp); + return size(); } - // 23.3.1.3 map operations: - iterator find(const key_type& k) + void setSize( std::size_t newSize ) noexcept { - iterator i(lower_bound(k)); - if (i != end() && this->operator()(k, i->first)) - { - i = end(); - } - return i; + AV_PRECONDITION( getData() != 0 || newSize == 0 ); + + this->_impl._size = newSize; } - const_iterator find(const key_type& k) const + std::size_t capacity()const noexcept { - const_iterator i(lower_bound(k)); - if (i != end() && this->operator()(k, i->first)) - { - i = end(); - } - return i; + return this->_impl._capacity; } - size_type count(const key_type& k) const - { return find(k) != end(); } + std::size_t getCapacity()const noexcept + { + return capacity(); + } + + value_type & front() noexcept + { + AV_PRECONDITION( getData() != 0 ); + AV_PRECONDITION( empty() == false ); + + return getData()[ 0 ]; + } + + value_type const & front()const noexcept + { + AV_PRECONDITION( getData() != 0 ); + AV_PRECONDITION( empty() == false ); + + return getData()[ 0 ]; + } + + value_type & back() noexcept + { + AV_PRECONDITION( getData() != 0 ); + AV_PRECONDITION( empty() == false ); + + return getData()[ getSize() - 1 ]; + } + + value_type const & back()const noexcept + { + AV_PRECONDITION( getData() != 0 ); + AV_PRECONDITION( empty() == false ); + + return getData()[ getSize() - 1 ]; + } - iterator lower_bound(const key_type& k) + _T const * data()const noexcept { - MyCompare& me = *this; - return std::lower_bound(begin(), end(), k, me); + return this->_impl._data; } - const_iterator lower_bound(const key_type& k) const + _T const * getData()const noexcept { - const MyCompare& me = *this; - return std::lower_bound(begin(), end(), k, me); + return data(); } - iterator upper_bound(const key_type& k) + _T * data() noexcept { - MyCompare& me = *this; - return std::upper_bound(begin(), end(), k, me); + return this->_impl._data; } - const_iterator upper_bound(const key_type& k) const + _T * getData() noexcept { - const MyCompare& me = *this; - return std::upper_bound(begin(), end(), k, me); + return data(); } - std::pair equal_range(const key_type& k) + value_type & operator[]( std::size_t index ) noexcept { - MyCompare& me = *this; - return std::equal_range(begin(), end(), k, me); + AV_PRECONDITION( getData() != 0 ); + AV_PRECONDITION( index < size() ); + + return getData()[ index ]; } - std::pair equal_range( - const key_type& k) const + value_type const & operator[]( std::size_t index )const noexcept { - const MyCompare& me = *this; - return std::equal_range(begin(), end(), k, me); + AV_PRECONDITION( getData() != 0 ); + AV_PRECONDITION( index < size() ); + + return getData()[ index ]; } - template - friend bool operator==(const AssocVector& lhs, - const AssocVector& rhs); + template< typename _T2 > + void + insert( + typename Array< _T >::iterator const pos + , _T2 && t + ) + { + AV_PRECONDITION( util::less_equal( size() + 1, capacity() ) ); + AV_PRECONDITION( util::is_between( begin(), pos, end() ) ); + + iterator const oldEnd = end(); + + get_allocator().construct( end() ); + setSize( getSize() + 1 ); + + if( pos != oldEnd ) + { + util::move( pos, oldEnd, pos + 1 ); + } + + * pos = AV_MOVE_IF_NOEXCEPT( t ); + } - bool operator<(const AssocVector& rhs) const + // Array::push_back is not implemented to ensure invariant that: + // sqrt( storage.capacity ) == buffer.capacity + // sqrt( storage.capacity ) == erased.capacity + template< typename __T2 > + void place_back( __T2 && value ) { - const Base& me = *this; - const Base& yo = rhs; - return me < yo; + AV_CHECK( getData() ); + AV_CHECK( capacity() > 0 ); + AV_CHECK( getSize() < capacity() ); + + get_allocator().construct( end(), std::forward< __T2 >( value ) ); + setSize( getSize() + 1 ); } - template - friend bool operator!=(const AssocVector& lhs, - const AssocVector& rhs); + void + erase( typename Array< _T >::iterator pos ) + { + AV_PRECONDITION( empty() == false ); + AV_PRECONDITION( util::less_equal( begin(), pos ) ); + AV_PRECONDITION( pos < end() ); + + util::move( pos + 1, end(), pos ); + get_allocator().destroy( end() - 1 ); + setSize( getSize() - 1 ); + } - template - friend bool operator>(const AssocVector& lhs, - const AssocVector& rhs); + private: + void setCapacity( std::size_t newCapacity ) noexcept + { + AV_PRECONDITION( getData() != 0 || newCapacity == 0 ); - template - friend bool operator>=(const AssocVector& lhs, - const AssocVector& rhs); + this->_impl._capacity = newCapacity; + } - template - friend bool operator<=(const AssocVector& lhs, - const AssocVector& rhs); + void setData( _T * t ) noexcept + { + this->_impl._data = t; + } }; +} - template - inline bool operator==(const AssocVector& lhs, - const AssocVector& rhs) +namespace array +{ + template< + typename _Iterator + , typename _T + , typename _Cmp + > + _Iterator + binary_search( + _Iterator first + , _Iterator last + , _T const & t + , _Cmp cmp + ) { - const std::vector, A>& me = lhs; - return me == rhs; - } + AV_PRECONDITION( util::less_equal( first, last ) ); + + _Iterator const greaterEqual = std::lower_bound( first, last, t, cmp ); - template - inline bool operator!=(const AssocVector& lhs, - const AssocVector& rhs) - { return !(lhs == rhs); } + if( greaterEqual == last ){ + return last; + } + + bool const isEqual = cmp( t, * greaterEqual ) == false; + + if( isEqual ){ + return greaterEqual; + } - template - inline bool operator>(const AssocVector& lhs, - const AssocVector& rhs) - { return rhs < lhs; } + return last; + } - template - inline bool operator>=(const AssocVector& lhs, - const AssocVector& rhs) - { return !(lhs < rhs); } + template< + typename _T + , typename _T2 + , typename _Cmp + > + std::pair< typename Array< _T >::iterator, bool > + insert_in_sorted( + Array< _T > & array + , _T2 && t + , _Cmp cmp + ) + { + AV_PRECONDITION( util::less_equal( array.size() + 1, array.capacity() ) ); - template - inline bool operator<=(const AssocVector& lhs, - const AssocVector& rhs) - { return !(rhs < lhs); } + typename Array< _T >::iterator const greaterEqual + = std::lower_bound( array.begin(), array.end(), t, cmp ); + if( greaterEqual != array.end() ) + { + bool const isEqual = cmp( t, * greaterEqual ) == false; + + if( isEqual ){ + return std::make_pair( greaterEqual, false ); + } + } + + array.insert( greaterEqual, std::forward< _T2 >( t ) ); + + return std::make_pair( greaterEqual, true ); + } + + template< typename _T > + void + erase_removed( + array::Array< _T > & storage + , array::Array< typename array::Array< _T >::const_iterator > const & erased + ) + { + AV_PRECONDITION( util::less_equal( erased.size(), storage.size() ) ); + + if( erased.empty() ){ + return; + } + + typedef typename array::Array< _T >::const_iterator StorageConstIterator; + typedef typename array::Array< _T >::iterator StorageIterator; + typedef typename array::Array< StorageConstIterator >::const_iterator ErasedConstIterator; + + StorageIterator currentInStorage = const_cast< StorageIterator >( erased.front() ); + AV_CHECK( util::is_between( storage.begin(), currentInStorage, storage.end() ) ); + + StorageIterator const endInStorage = storage.end(); + + StorageIterator whereInsertInStorage = const_cast< StorageIterator >( erased.front() ); + AV_CHECK( util::is_between( storage.begin(), whereInsertInStorage, storage.end() ) ); + + ErasedConstIterator currentInErased = erased.begin(); + ErasedConstIterator const endInErased = erased.end(); + + while( currentInStorage != endInStorage ) + { + AV_CHECK( util::is_between( storage.begin(), whereInsertInStorage, storage.end() ) ); + + if( + currentInErased != endInErased + && currentInStorage == ( * currentInErased ) + ) + { + ++ currentInStorage; + ++ currentInErased; + } + else + { + ( * whereInsertInStorage ) = AV_MOVE_IF_NOEXCEPT( * currentInStorage ); + + ++ whereInsertInStorage; + ++ currentInStorage; + } + } + + AV_POSTCONDITION( currentInErased == endInErased ); + + storage.setSize( storage.size() - erased.size() ); + } + + template< + typename _T + , typename _Cmp + > + void + move_merge( + array::Array< _T > & storage + , array::Array< _T > & buffer + , _Cmp const & cmp = _Cmp() + ) + { + AV_PRECONDITION( util::less_equal( storage.size() + buffer.size(), storage.capacity() ) ); + + typedef typename array::Array< _T >::iterator Iterator; + + Iterator rWhereInsertInStorage = storage.begin() + storage.size() + buffer.size() - 1; + + Iterator rCurrentInStorage = storage.begin() + storage.size() - 1; + Iterator const rEndInStorage = storage.begin() - 1; + + Iterator rCurrentInBuffer = buffer.begin() + buffer.size() - 1; + Iterator const rEndInBuffer = buffer.begin() - 1; + + std::size_t numberOfItemsToCreateByPlacementNew = buffer.size(); + + while( + rCurrentInBuffer != rEndInBuffer + && numberOfItemsToCreateByPlacementNew != 0 + ) + { + AV_CHECK( rWhereInsertInStorage != 0 ); + AV_CHECK( rCurrentInStorage != 0 ); + AV_CHECK( rCurrentInBuffer != 0 ); + + if( + rCurrentInStorage == rEndInStorage + || cmp( * rCurrentInStorage, * rCurrentInBuffer ) + ) + { + new ( static_cast< void * >( rWhereInsertInStorage ) ) + _T( AV_MOVE_IF_NOEXCEPT( * rCurrentInBuffer ) ); + + -- rCurrentInBuffer; + } + else + { + new ( static_cast< void * >( rWhereInsertInStorage ) ) + _T( AV_MOVE_IF_NOEXCEPT( * rCurrentInStorage ) ); + + -- rCurrentInStorage; + } + + -- numberOfItemsToCreateByPlacementNew; + -- rWhereInsertInStorage; + } + + AV_CHECK( numberOfItemsToCreateByPlacementNew == 0 ); + + while( rCurrentInBuffer != rEndInBuffer ) + { + AV_CHECK( rWhereInsertInStorage != 0 ); + AV_CHECK( rCurrentInStorage != 0 ); + AV_CHECK( rCurrentInBuffer != 0 ); + + if( + rCurrentInStorage == rEndInStorage + || cmp( * rCurrentInStorage, * rCurrentInBuffer ) + ) + { + * rWhereInsertInStorage = AV_MOVE_IF_NOEXCEPT( * rCurrentInBuffer ); + + -- rCurrentInBuffer; + } + else + { + * rWhereInsertInStorage = AV_MOVE_IF_NOEXCEPT( * rCurrentInStorage ); + + -- rCurrentInStorage; + } + + -- rWhereInsertInStorage; + } + + storage.setSize( storage.size() + buffer.size() ); + } +} + +namespace util +{ + +template< + typename _InputPtr1 + , typename _InputPtr2 + , typename _OutputPtr + , typename _Cmp +> +_OutputPtr +move_merge_into_uninitialized( + _InputPtr1 first1 + , _InputPtr1 last1 + , _InputPtr2 first2 + , _InputPtr2 last2 + , _OutputPtr output + , _Cmp cmp = _Cmp() +) +{ + AV_PRECONDITION( util::less_equal( first1, last1 ) ); + AV_PRECONDITION( util::less_equal( first2, last2 ) ); + + while( first1 != last1 && first2 != last2 ) + { + AV_CHECK( first1 != 0 ); + AV_CHECK( first2 != 0 ); + AV_CHECK( output != 0 ); + + if( cmp( * first1, * first2 ) ) + { + new ( static_cast< void * >( output ) ) + typename std::iterator_traits< _OutputPtr >::value_type( std::move( * first1 ) ); + + ++ output; + ++ first1; + } + else + { + new ( static_cast< void * >( output ) ) + typename std::iterator_traits< _OutputPtr >::value_type( std::move( * first2 ) ); + + ++ output; + ++ first2; + } + } + + if( first1 == last1 ){ + return std::uninitialized_copy( + std::make_move_iterator( first2 ) + , std::make_move_iterator( last2 ) + , output + ); + } + + if( first2 == last2 ){ + return std::uninitialized_copy( + std::make_move_iterator( first1 ) + , std::make_move_iterator( last1 ) + , output + ); + } + + return output; +} + +} + +namespace detail +{ + template< typename _Iterator > + bool equal( _Iterator const & lhs, _Iterator const & rhs ) + { + if( lhs.getContainer() != rhs.getContainer() ){ + return false; + } + + // for empty container returns that begin == end + // despite on fact they are not + if( lhs.getContainer()->empty() ){ + return true; + } + + return lhs.getCurrent() == rhs.getCurrent(); + } + + // + // AssocVectorLazyIterator + // + + template< + typename _Iterator + , typename _Container + > + struct AssocVectorLazyIterator + { + public: + typedef typename std::iterator_traits< _Iterator >::pointer pointer_mutable; + + public: + typedef std::bidirectional_iterator_tag iterator_category; + typedef typename std::iterator_traits< _Iterator >::value_type value_type; + typedef typename std::iterator_traits< _Iterator >::difference_type difference_type; + + // make key const + typedef std::pair< + typename value_type::first_type const + , typename value_type::second_type + > & reference; + + // make key const + typedef std::pair< + typename value_type::first_type const + , typename value_type::second_type + > * pointer; + + private: + struct _CurrentInErased + { + _CurrentInErased( typename _Container::_Erased::const_iterator current ) + : _current( current ) + { + } + + _CurrentInErased( _CurrentInErased const & other ) + : _current( other._current ) + { + } + + _CurrentInErased & operator=( _CurrentInErased const & other ) + { + _current = other._current; + + return * this; + } + + _CurrentInErased & operator=( typename _Container::_Erased::const_iterator current ) + { + _current = current; + + return * this; + } + + bool is_end( _Container const * container )const + { + return _current == container->erased().end(); + } + + bool is_not_end( _Container const * container )const + { + return ! is_end( container ); + } + + bool is_begin( _Container const * container )const + { + return _current == container->erased().begin(); + } + + bool is_not_begin( _Container const * container )const + { + return ! is_begin( container ); + } + + void increment( _Container const * container ) + { + AV_PRECONDITION( is_not_end( container ) ); + + ++ _current; + } + + void try_increment( _Container const * container ) + { + if( is_end( container ) ){ + return; + } + + increment( container ); + } + + void decrement( _Container const * container ) + { + AV_PRECONDITION( is_not_begin( container ) ); + + -- _current; + } + + void try_decrement( _Container const * container ) + { + if( is_begin( container ) ){ + return; + } + + decrement( container ); + } + + bool validate( _Container const * container )const + { + bool const result + = util::is_between( + container->erased().begin() + , _current + , container->erased().end() + ); + + if( result ){ + return true; + } + + AV_ERROR(); + + return false; + } + + typename _Container::_Erased::value_type const & + get( _Container const * container )const noexcept + { + AV_PRECONDITION( _current ); + AV_PRECONDITION( is_not_end( container ) ); + AV_PRECONDITION( validate( container ) ); + + return * _current; + } + + typename _Container::_Erased::const_iterator data()const noexcept + { + return _current; + } + + operator bool()const noexcept + { + return _current != 0; + } + + private: + typename _Container::_Erased::const_iterator _current; + }; + + struct _CurrentInBuffer + { + _CurrentInBuffer( pointer_mutable current ) + : _current( current ) + { + } + + _CurrentInBuffer( _CurrentInBuffer const & other ) + : _current( other._current ) + { + } + + _CurrentInBuffer & operator=( _CurrentInBuffer const & other ) + { + _current = other._current; + + return * this; + } + + _CurrentInBuffer & operator=( pointer_mutable current ) + { + _current = current; + + return * this; + } + + bool is_begin( _Container const * container )const + { + return _current == container->buffer().begin(); + } + + bool is_not_begin( _Container const * container )const + { + return ! is_begin( container ); + } + + bool is_end( _Container const * container )const + { + return _current == container->buffer().end(); + } + + bool is_not_end( _Container const * container )const + { + return ! is_end( container ); + } + + void increment( _Container const * container ) + { + AV_PRECONDITION( is_not_end( container ) ); + + ++ _current; + } + + void try_increment( _Container const * container ) + { + if( is_end( container ) ){ + return; + } + + increment( container ); + } + + void decrement( _Container const * container ) + { + AV_PRECONDITION( is_not_begin( container ) ); + + -- _current; + } + + void try_decrement( _Container const * container ) + { + if( is_begin( container ) ){ + return; + } + + decrement( container ); + } + + bool validate( _Container const * container )const + { + bool const result + = util::is_between( + container->buffer().begin() + , _current + , container->buffer().end() + ); + + if( result ){ + return true; + } + + AV_ERROR(); + + return false; + } + + typename _Container::_Storage::value_type const & + get( _Container const * container )const noexcept + { + AV_PRECONDITION( _current ); + AV_PRECONDITION( is_not_end( container ) ); + AV_PRECONDITION( validate( container ) ); + + return * _current; + } + + operator bool()const noexcept + { + return _current != 0; + } + + pointer_mutable data()const noexcept + { + return _current; + } + private: + pointer_mutable _current; + }; + + struct _CurrentInStorage + { + // begin is always set at first not erased item, marked with ^ + // end is always set at the end of container, marked with $ + + // Case 1: no item erased + // storage: ^1 3 5 7$ + // erased : + + // Case 2: item erased from front + // storage: 1 ^3 5 7$ + // erased : 1 + + // Case 3: item erased from back + // storage: ^1 3 5 7$ + // erased : 7 + + // Case 4: item erased from front and back + // storage: 1 ^3 5 7$ + // erased : 1 7 + + _CurrentInStorage( pointer_mutable current ) + : _dir( 1 ) + , _current( current ) + { + } + + _CurrentInStorage( _CurrentInStorage const & other ) + : _dir( other._dir ) + , _current( other._current ) + { + } + + _CurrentInStorage & operator=( _CurrentInStorage const & other ) + { + _dir = other._dir; + _current = other._current; + + return * this; + } + + bool operator==( _CurrentInStorage const & other )const + { + return _current == other._current; + } + + bool operator!=( _CurrentInStorage const & other )const + { + return _current != other._current; + } + + bool operator==( typename _Container::_Erased::value_type inErased )const + { + return _current == inErased; + } + + bool operator!=( typename _Container::_Erased::value_type inErased )const + { + return _current != inErased; + } + + bool is_begin( _Container const * container )const + { + _CurrentInStorage currentInStorage = const_cast< pointer_mutable >( container->storage().begin() ); + _CurrentInErased currentInErased = container->erased().begin(); + + currentInStorage.setOnNotErased( currentInErased, container ); + + return data() == currentInStorage.data(); + } + + bool is_not_begin( _Container const * container )const + { + return ! is_begin( container ); + } + + bool _is_begin( _Container const * container )const + { + return _current == container->storage().begin(); + } + + bool _is_not_begin( _Container const * container )const + { + return ! _is_begin( container ); + } + + bool is_end( _Container const * container )const + { + return _current == container->storage().end(); + } + + bool is_not_end( _Container const * container )const + { + return ! is_end( container ); + } + + void + increment( + _CurrentInErased & currentInErased + , _Container const * container + ) + { + AV_PRECONDITION( is_not_end( container ) ); + + increment( container ); + + if( _dir == -1 ) + { + _dir = 1; + currentInErased.try_increment( container ); + } + + setOnNotErased( currentInErased, container ); + } + + void + try_increment( + _CurrentInErased & currentInErased + , _Container const * container + ) + { + if( is_end( container ) ){ + return; + } + + increment( currentInErased, container ); + } + + void + decrement( + _CurrentInErased & currentInErased + , _Container const * container + ) + { + AV_PRECONDITION( is_not_begin( container ) ); + + decrement( container ); + + if( _dir == 1 ) + { + _dir = -1; + currentInErased.try_decrement( container ); + } + + setOnNotErasedBackward( currentInErased, container ); + } + + void + try_decrement( + _CurrentInErased & currentInErased + , _Container const * container + ) + { + if( _is_begin( container ) ){ + return; + } + + decrement( currentInErased, container ); + } + + void + setOnNotErased( + _CurrentInErased & currentInErased + , _Container const * container + ) + { + if( is_end( container ) ) + { + if( !currentInErased ) + { + currentInErased = container->erased().end(); + } + + return; + } + + if( !currentInErased ) + { + currentInErased = std::lower_bound( + container->erased().begin() + , container->erased().end() + , data() + , std::less< typename _Container::_Storage::const_iterator >() + ); + } + + if( _dir == -1 ) + { + _dir = 1; + currentInErased.try_increment( container ); + } + + while( + is_not_end( container ) + && currentInErased.is_not_end( container ) + && data() == currentInErased.get( container ) + ) + { + increment( container ); + currentInErased.increment( container ); + } + + AV_POSTCONDITION( currentInErased ); + + AV_POSTCONDITION( + ( + is_end( container ) + && currentInErased.is_end( container ) + ) + || currentInErased.is_end( container ) + || *this != currentInErased.get( container ) + ); + } + + void + setOnNotErasedBackward( + _CurrentInErased & currentInErased + , _Container const * container + ) + { + AV_CHECK( is_not_end( container ) ); + + if( _dir == 1 ) + { + _dir = -1; + currentInErased.try_decrement( container ); + } + + while( + is_not_begin( container ) + && currentInErased.is_not_end( container ) + && data() == currentInErased.get( container ) + ) + { + decrement( container ); + currentInErased.try_decrement( container ); + } + + AV_POSTCONDITION( validate( container ) ); + + AV_POSTCONDITION( + currentInErased.is_end( container ) + || *this != currentInErased.get( container ) + ); + } + + operator bool()const noexcept + { + return _current != 0; + } + + bool validate( _Container const * container )const + { + bool const result = util::is_between( + container->storage().begin() + , _current + , container->storage().end() + ); + + if( result ){ + return true; + } + + AV_ERROR(); + + return false; + } + + typename _Container::_Storage::value_type const & + get( _Container const * container )const noexcept + { + AV_PRECONDITION( _current ); + AV_PRECONDITION( is_not_end( container ) ); + + return * _current; + } + + pointer_mutable data()const noexcept + { + return _current; + } + + private: + void increment( _Container const * container ) + { + AV_PRECONDITION( is_not_end( container ) ); + + ++ _current; + } + + void decrement( _Container const * container ) + { + AV_PRECONDITION( is_not_begin( container ) ); + + -- _current; + } + + private: + int _dir; + + pointer_mutable _current; + }; + + struct _Current + { + _Current( pointer_mutable current ) + : _current( current ) + { + } + + _Current( _CurrentInStorage const & inStorage ) + : _current( inStorage.data() ) + { + } + + _Current( _CurrentInBuffer const & inBuffer ) + : _current( inBuffer.data() ) + { + } + + _Current & operator=( _Current const & other ) + { + _current = other._current; + + return * this; + } + + _Current & operator=( _CurrentInStorage const & inStorage ) + { + _current = inStorage.data(); + + return * this; + } + + _Current & operator=( _CurrentInBuffer const & inBuffer ) + { + _current = inBuffer.data(); + + return * this; + } + + bool operator==( _Current const & other )const + { + return _current == other._current; + } + + bool operator==( _CurrentInStorage const & inStorage )const + { + return _current == inStorage.data(); + } + + bool operator==( _CurrentInBuffer const & inBuffer )const + { + return _current == inBuffer.data(); + } + + bool validate( + _CurrentInStorage currentInStorage + , _CurrentInBuffer currentInBuffer + , _Container const * container + )const + { + AV_PRECONDITION( currentInStorage || currentInBuffer ); + AV_PRECONDITION( container != 0 ); + + if( !currentInStorage ) + { + if( _current == currentInBuffer.data() ){ + return true; + } + + AV_ERROR(); + + return false; + } + + if( !currentInBuffer ) + { + if( _current == currentInStorage.data() ){ + return true; + } + + AV_ERROR(); + + return false; + } + + // if 'setLower' does not work 'validateCurrent' does not work as well :O( + bool const result + = _current == getLower( currentInStorage, currentInBuffer, container ).data(); + + if( result ){ + return true; + } + + AV_ERROR(); + + return false; + } + + void setLower( + _CurrentInStorage currentInStorage + , _CurrentInBuffer currentInBuffer + , _Container const * container + ) + { + _current = getLower( currentInStorage, currentInBuffer, container ).data(); + } + + _Current getLower( + _CurrentInStorage currentInStorage + , _CurrentInBuffer currentInBuffer + , _Container const * container + )const + { + AV_CHECK( currentInStorage ); + AV_CHECK( currentInBuffer ); + + if( currentInStorage.is_end( container ) ) + { + if( currentInBuffer.is_end( container ) ){ + return _Current( 0 ); + } + else{ + return _Current( currentInBuffer ); + } + } + else + { + if( currentInBuffer.is_end( container ) ){ + return _Current( currentInStorage ); + } + else + { + if( container->value_comp()( + currentInStorage.get( container ) + , currentInBuffer.get( container ) + ) + ){ + return _Current( currentInStorage ); + } + else{ + return _Current( currentInBuffer ); + } + } + } + } + + operator bool()const noexcept + { + return _current != 0; + } + + typename _Container::_Storage::value_type const & + get( _Container const * container )const noexcept + { + return * _current; + } + + pointer_mutable data()const noexcept + { + return _current; + } + + private: + pointer_mutable _current; + }; + + public: + AssocVectorLazyIterator( + typename _Container::value_compare const & cmp = typename _Container::value_compare() + ) + : _container( 0 ) + + , _currentInStorage( 0 ) + , _currentInBuffer( 0 ) + , _currentInErased( 0 ) + + , _current( 0 ) + { + } + + template< typename _Iter > + AssocVectorLazyIterator( AssocVectorLazyIterator< _Iter, _Container > const & other ) + : _container( other.getContainer() ) + + , _currentInStorage( other.getCurrentInStorage() ) + , _currentInBuffer( other.getCurrentInBuffer() ) + , _currentInErased( other.getCurrentInErased() ) + + , _current( other.getCurrent() ) + { + } + + AssocVectorLazyIterator( + _Container const * container + , pointer_mutable currentInStorage + , pointer_mutable currentInBuffer + , typename _Container::_Erased::const_iterator currentInErased + , pointer_mutable current + ) + : _container( container ) + + , _currentInStorage( currentInStorage ) + , _currentInBuffer( currentInBuffer ) + , _currentInErased( currentInErased ) + + , _current( current ) + { + AV_PRECONDITION( container != 0 ); + AV_PRECONDITION( validate() ); + + if( _currentInStorage && _currentInBuffer && !_currentInErased && !_current ) + { + // not found in storage, insert to buffer + // erase from buffer + find in storage + // lower_bound + // upper_bound + + // _currentInStorage <- fix against '!_currentInErased' + _currentInStorage.setOnNotErased( _currentInErased, _container ); + + // _current <- get it right now + _current.setLower( _currentInStorage, _currentInBuffer, _container ); + } + else + if( _currentInStorage && _currentInBuffer && !_currentInErased && _current ) + { + // not found in storage, found in buffer + // not found in storage, inserted to buffer + // erased from storage's back + + // _currentInStorage <- fix against '!_currentInErased' + _currentInStorage.setOnNotErased( _currentInErased, _container ); + } + else + if( _currentInStorage && _currentInBuffer && _currentInErased && !_current ) + { + // begin iterator + // end iterator + // erase from storage, not merged + find in buffer + // erase from storage, merged + find in buffer + find in storage + + // _currentInStorage <- check against _currentInErased + _currentInStorage.setOnNotErased( _currentInErased, _container ); + + // _current <- get it right now + _current.setLower( _currentInStorage, _currentInBuffer, _container ); + } + else + if( ! _currentInStorage && ! _currentInBuffer && ! _currentInErased && !_current ) + { + // begin iterator on empty AssocVector + // end iterator on empty AssocVector + + // return, do not make validation + return; + } + + AV_POSTCONDITION( _current.validate( _currentInStorage, _currentInBuffer, _container ) ); + AV_POSTCONDITION( _currentInStorage || _currentInBuffer ); + AV_POSTCONDITION( _currentInErased ); + AV_POSTCONDITION( _container != 0 ); + } + + AssocVectorLazyIterator & + operator=( AssocVectorLazyIterator const & other ) + { + _container = other._container; + + _currentInStorage = other._currentInStorage; + _currentInBuffer = other._currentInBuffer; + _currentInErased = other._currentInErased; + + _current = other._current; + + return * this; + } + + bool operator==( AssocVectorLazyIterator const & other )const + { + this->resolveLazyValues(); + other.resolveLazyValues(); + + if( isEmpty() && other.isEmpty() ){ + return getContainer() == other.getContainer(); + } + + AV_PRECONDITION( _current.validate( _currentInStorage, _currentInBuffer, _container ) ); + + return equal( *this, other ); + } + + bool operator!=( AssocVectorLazyIterator const & other )const + { + this->resolveLazyValues(); + other.resolveLazyValues(); + + return ! ( ( * this ) == other ); + } + + AssocVectorLazyIterator & operator++() + { + AV_PRECONDITION( isEmpty() == false ); + + resolveLazyValues(); + + AV_PRECONDITION( _current.validate( _currentInStorage, _currentInBuffer, _container ) ); + + if( _current == _Current( 0 ) ){ + return * this; + } + else if( _current == _currentInStorage ){ + _currentInStorage.try_increment( _currentInErased, _container ); + } + else if( _current == _currentInBuffer ){ + _currentInBuffer.try_increment( _container ); + } + else{ + AV_ERROR(); + } + + _current.setLower( _currentInStorage, _currentInBuffer, _container ); + + AV_POSTCONDITION( _current.validate( _currentInStorage, _currentInBuffer, _container ) ); + + return * this; + } + + AssocVectorLazyIterator operator++( int ) + { + AssocVectorLazyIterator result( * this ); + + ( * this ).operator++(); + + return result; + } + + AssocVectorLazyIterator & operator--() + { + AV_PRECONDITION( isEmpty() == false ); + + resolveLazyValues(); + + AV_PRECONDITION( _current.validate( _currentInStorage, _currentInBuffer, _container ) ); + + if( + _currentInStorage.is_begin( _container ) + && _currentInBuffer.is_begin( _container ) + ) + { + AV_POSTCONDITION( _current.validate( _currentInStorage, _currentInBuffer, _container ) ); + + return * this; + } + + if( _currentInStorage.is_begin( _container ) ) + { + _currentInBuffer.decrement( _container ); + + _current = _currentInBuffer; + + AV_POSTCONDITION( _current.validate( _currentInStorage, _currentInBuffer, _container ) ); + + return * this; + } + + if( _currentInBuffer.is_begin( _container ) ) + { + _currentInStorage.decrement( _currentInErased, _container ); + + _current = _currentInStorage; + + AV_POSTCONDITION( _current.validate( _currentInStorage, _currentInBuffer, _container ) ); + + return * this; + } + + _CurrentInStorage currentInStorage = _currentInStorage; + _CurrentInBuffer currentInBuffer = _currentInBuffer; + + _CurrentInErased currentInErased = _currentInErased; + + currentInStorage.decrement( currentInErased, _container ); + currentInBuffer.decrement( _container ); + + if( + _container->value_comp()( + currentInStorage.get( _container ) + , currentInBuffer.get( _container ) + ) + ) + { + _currentInBuffer = currentInBuffer; + + _current = _currentInBuffer; + } + else + { + _currentInStorage = currentInStorage; + _currentInErased = currentInErased; + + _current = _currentInStorage; + } + + AV_POSTCONDITION( _current.validate( _currentInStorage, _currentInBuffer, _container ) ); + + return * this; + } + + AssocVectorLazyIterator operator--( int ) + { + AssocVectorLazyIterator result( * this ); + + ( * this ).operator--(); + + return result; + } + + reference operator*()const + { + return * get(); + } + + pointer operator->()const + { + return get(); + } + + pointer get()const + { + AV_PRECONDITION( isEmpty() == false ); + AV_PRECONDITION( _current ); + AV_PRECONDITION( _current.validate( _currentInStorage, _currentInBuffer, _container ) ); + + // make key const + // pair< T1, T2 > * -> pair< T1 const, T2 > * + //return reinterpret_cast< pointer >( _current ); + + return + reinterpret_cast< pointer >( + const_cast< void * >( + reinterpret_cast< void const * >( _current.data() ) + ) + ); + } + + // public for copy constructor only : Iterator -> ConstIterator + _Container const * getContainer()const noexcept + { + return _container; + } + + pointer_mutable getCurrentInStorage()const noexcept + { + return _currentInStorage.data(); + } + + pointer_mutable getCurrentInBuffer()const noexcept + { + return _currentInBuffer.data(); + } + + typename _Container::_Erased::const_iterator getCurrentInErased()const noexcept + { + return _currentInErased.data(); + } + + pointer_mutable getCurrent()const noexcept + { + return _current.data(); + } + + private: + bool isEmpty()const + { + if( _currentInStorage ){ + return false; + } + if( _currentInBuffer ){ + return false; + } + if( _currentInErased ){ + return false; + } + if( _current ){ + return false; + } + + return true; + } + + /*const function*/ + static + void + resolveLazyCurrentInBuffer( + _CurrentInBuffer & currentInBuffer + , _Current const current + , _Container const * container + ) + { + if( currentInBuffer ){ + return; + } + + currentInBuffer = const_cast< pointer_mutable >( + std::lower_bound( + container->buffer().begin() + , container->buffer().end() + , current.get( container ) + , container->value_comp() + ) + ); + } + + static + void + resolveLazyCurrentInStorage( + _CurrentInStorage & currentInStorage + , _CurrentInErased & currentInErased + , _Current const current + , _Container const * container + ) + { + if( currentInStorage ){ + return; + } + + currentInStorage = const_cast< pointer_mutable >( + std::lower_bound( + container->storage().begin() + , container->storage().end() + , current.get( container ) + , container->value_comp() + ) + ); + + currentInStorage.setOnNotErased( currentInErased, container ); + } + + void + resolveLazyValues()const + { + resolveLazyCurrentInBuffer( _currentInBuffer, _current, _container ); + resolveLazyCurrentInStorage( _currentInStorage, _currentInErased, _current, _container ); + } + + /*pure function*/ + bool + validate()const + { + if( !_currentInStorage && _currentInBuffer && _currentInErased && _current ) + { + // not found in storage, inserted to buffer, buffer merged to storage + + AV_CHECK( _currentInBuffer.validate( _container ) ); + AV_CHECK( _currentInErased.is_end( _container ) ); + AV_CHECK( _current == _currentInBuffer ); + + // _currentInStorage <- lazy in operator++/operator--/operator==/operator!= + } + else + if( _currentInStorage && !_currentInBuffer && _currentInErased && !_current ) + { + AV_ERROR(); + + return false; + } + else + if( _currentInStorage && !_currentInBuffer && _currentInErased && _current ) + { + // found in storage, not found in erased + + AV_CHECK( _currentInStorage.validate( _container ) ); + AV_CHECK( + std::binary_search( + _container->erased().begin() + , _container->erased().end() + , _currentInStorage.data() + ) == false + ); + AV_CHECK( _currentInErased.validate( _container ) ); + AV_CHECK( _current == _currentInStorage ); + + // _currentInBuffer <- lazy in operator++/operator--/operator==/operator!= + } + else + if( _currentInStorage && _currentInBuffer && !_currentInErased && !_current ) + { + // not found in storage, insert to buffer + // erase from buffer + find in storage + // lower_bound + // upper_bound + + AV_CHECK( _currentInStorage.validate( _container ) ); + AV_CHECK( _currentInBuffer.validate( _container ) ); + + // _currentInStorage <- fix against '!_currentInErased' + // _current <- get it right now + } + else + if( _currentInStorage && _currentInBuffer && !_currentInErased && _current ) + { + // not found in storage, found in buffer + // not found in storage, inserted to buffer + // erased from storage's back + + AV_CHECK( _currentInStorage.validate( _container ) ); + AV_CHECK( _currentInBuffer.validate( _container ) ); + AV_CHECK( _current == _currentInBuffer ); + + // _currentInStorage <- fix against '!_currentInErased' + // _currentInErased <- get it right now + } + else + if( _currentInStorage && _currentInBuffer && _currentInErased && !_current ) + { + // begin iterator + // end iterator + // erase from storage, not merged + find in buffer + // erase from storage, merged + find in buffer + find in storage + + AV_CHECK( _currentInStorage.validate( _container ) ); + AV_CHECK( _currentInBuffer.validate( _container ) ); + AV_CHECK( _currentInErased.validate( _container ) ); + + // _currentInStorage <- check against _currentInErased + // _current <- get it right now + } + else + if( ! _currentInStorage && ! _currentInBuffer && ! _currentInErased && !_current ) + { + // begin iterator on empty AssocVector + // end iterator on empty AssocVector + } + else + { + AV_ERROR(); + + return false; + } + + return true; + } + + private: + _Container const * _container; + + // mutable since lazy values in operator==()const / operator!=()const + mutable _CurrentInStorage _currentInStorage; + mutable _CurrentInBuffer _currentInBuffer; + mutable _CurrentInErased _currentInErased; + + _Current _current; + }; + + template< + typename _Iterator + , typename _Container + > + std::ostream & + operator<<( + std::ostream & out + , AssocVectorLazyIterator< _Iterator, _Container > const & iter + ) + { + out << "S: " << iter.getCurrentInStorage(); + + if( iter.getCurrentInStorage() == 0 ){ + out << " (null)"; + } + else if( iter.getContainer()->storage().end() == iter.getCurrentInStorage() ){ + out << " (end)"; + } + else{ + out << " " << * iter.getCurrentInStorage(); + } + + out << "\nB: " << iter.getCurrentInBuffer(); + + if( iter.getCurrentInBuffer() == 0 ){ + out << " (null)"; + } + else if( iter.getContainer()->buffer().end() == iter.getCurrentInBuffer() ){ + out << " (end)"; + } + else{ + out << " " << * iter.getCurrentInBuffer(); + } + + out << "\nE: " << iter.getCurrentInErased(); + + if( iter.getCurrentInErased() == 0 ){ + out << " (null)"; + } + else if( iter.getContainer()->erased().end() == iter.getCurrentInErased() ){ + out << " (end)"; + } + else{ + AV_CHECK( * iter.getCurrentInErased() ); + + out << " " << * * iter.getCurrentInErased(); + } + + out << "\nC: " << iter.getCurrent(); + + if( iter.getCurrent() == 0 ){ + out << " (null)"; + } + else{ + out << " " << * iter.getCurrent(); + } + + std::flush( out ); + + return out; + } + + // + // _AssocVectorIterator, simplified version of AssocVectorLazyIterator, works with _find and _end + // + template< + typename _Iterator + , typename _Container + > + struct _AssocVectorIterator + { + private: + typedef typename std::iterator_traits< _Iterator >::pointer pointer_mutable; + + public: + typedef typename std::iterator_traits< _Iterator >::value_type value_type; + + // make key const + typedef std::pair< + typename value_type::first_type const + , typename value_type::second_type + > & reference; + + // make key const + typedef std::pair< + typename value_type::first_type const + , typename value_type::second_type + > * pointer; + + public: + _AssocVectorIterator( + typename _Container::value_compare const & cmp = typename _Container::value_compare() + ) + : _current( 0 ) + { + } + + template< typename _Iter > + _AssocVectorIterator( _AssocVectorIterator< _Iter, _Container > const & other ) + : _current( other.getCurrent() ) + { + } + + _AssocVectorIterator( pointer_mutable current ) + : _current( current ) + { + } + + _AssocVectorIterator & + operator=( _AssocVectorIterator const & other ) + { + _current = other._current; + + return * this; + } + + bool operator==( _AssocVectorIterator const & other )const + { + return _current == other.getCurrent(); + } + + bool operator!=( _AssocVectorIterator const & other )const + { + return ! ( ( * this ) == other ); + } + + reference operator*()const + { + AV_PRECONDITION( _current != 0 ); + + return * get(); + } + + pointer operator->()const + { + AV_PRECONDITION( _current != 0 ); + + return get(); + } + + pointer get()const + { + AV_PRECONDITION( _current != 0 ); + + // make key const + // pair< T1, T2 > * -> pair< T1 const, T2 > * + //return reinterpret_cast< pointer >( _current ); + + return + reinterpret_cast< pointer >( + const_cast< void * >( + reinterpret_cast< void const * >( _current ) + ) + ); + } + + operator bool()const noexcept + { + return _current != 0; + } + + // public for copy constructor only : Iterator -> ConstIterator + pointer_mutable getCurrent()const noexcept + { + return _current; + } + + private: + pointer_mutable _current; + }; + + template< + typename _Iterator + , typename _Container + > + std::ostream & operator<<( + std::ostream & out + , _AssocVectorIterator< _Iterator, _Container > const & iter + ) + { + out << "S: " << iter.getCurrent(); + + if( iter.getCurrent() == 0 ){ + out << " (null)(end)"; + } + else{ + out << " " << * iter.get(); + } + + return out; + } + +} // namespace detail + +template< + typename _Key + , typename _Mapped + , typename _Cmp = std::less< _Key > + , typename _Allocator = std::allocator< std::pair< _Key, _Mapped > > +> +struct AssocVector +{ +private: + typedef std::pair< _Key, _Mapped > value_type_mutable; + typedef std::pair< _Key const, _Mapped > value_type_key_const; + +public: + typedef _Key key_type; + typedef _Mapped mapped_type; + + typedef value_type_key_const value_type; + + typedef typename _Allocator::size_type size_type; + typedef typename _Allocator::difference_type difference_type; + + typedef typename _Allocator::pointer pointer; + typedef typename _Allocator::const_pointer const_pointer; + + typedef _Cmp key_compare; + typedef util::CmpByFirst< value_type_mutable, _Cmp > value_compare; + + typedef _Allocator allocator_type; + + typedef mapped_type & reference; + typedef mapped_type const & const_reference; + + typedef detail::AssocVectorLazyIterator< value_type_mutable *, AssocVector > iterator; + typedef detail::AssocVectorLazyIterator< value_type_mutable const *, AssocVector > const_iterator; + + typedef std::reverse_iterator< iterator > reverse_iterator; + typedef std::reverse_iterator< const_iterator > const_reverse_iterator; + + typedef array::Array< value_type_mutable > _Storage; + typedef array::Array< typename _Storage::const_iterator > _Erased; + +#ifdef AV_ENABLE_EXTENSIONS + public: +#else + private: +#endif + + // + // extension, faster, non STL compatible version of iterator, working with _find end _end + // + typedef detail::_AssocVectorIterator< value_type_mutable *, AssocVector > _iterator; + typedef detail::_AssocVectorIterator< value_type_mutable const *, AssocVector > _const_iterator; + +private: + struct _FindOrInsertToBufferResult + { + typename _Storage::iterator _inBuffer; + bool _isInserted; + bool _isReallocated; + }; + + struct _TryRemoveBackResult + { + bool _anyItemRemoved; + bool _erasedItemRemoved; + }; + + struct _FindImplResult + { + typename _Storage::iterator _inStorage; + typename _Storage::iterator _inBuffer; + typename _Erased::iterator _inErased; + typename _Storage::iterator _current; + + bool validate()const + { + return + ( _current == 0 && _inStorage == 0 && _inBuffer == 0 && _inErased == 0 ) + || _inStorage != 0; + } + }; + + struct _InsertImplResult + { + bool _isInserted; + + typename _Storage::iterator _inStorage; + typename _Storage::iterator _inBuffer; + typename _Erased::iterator _inErased; + typename _Storage::iterator _current; + + bool validate()const + { + if( _current == 0 ) + { + AV_ERROR(); + + return false; + } + + if( _inStorage == 0 && ( _inBuffer == 0 || _inErased == 0 ) ) + { + AV_ERROR(); + + return false; + } + + return true; + } + }; + + struct _TryEraseFromStorageResult + { + typename _Erased::iterator _inErased; + bool _isErased; + bool _isMerged; + }; + +public: + // + // constructor + // + explicit + AssocVector( + _Cmp const & cmp = _Cmp() + , _Allocator const & allocator = _Allocator() + ); + + explicit + AssocVector( _Allocator const & allocator ); + + template< typename __InputIterator > + AssocVector( + __InputIterator first + , __InputIterator last + , _Cmp const & cmp = _Cmp() + , _Allocator const & allocator = _Allocator() + ); + + AssocVector( AssocVector< _Key, _Mapped, _Cmp, _Allocator > const & other ); + AssocVector( + AssocVector< _Key, _Mapped, _Cmp, _Allocator > const & other + , _Allocator const & allocator + ); + + AssocVector( AssocVector< _Key, _Mapped, _Cmp, _Allocator > && other ); + AssocVector( + AssocVector< _Key, _Mapped, _Cmp, _Allocator > && other + , _Allocator const & allocator + ); + + AssocVector( + std::initializer_list< value_type > list + , _Cmp const & cmp = _Cmp() + , _Allocator const & allocator = _Allocator() + ); + + // + // destructor + // + inline ~AssocVector(); + + // + // clear + // + inline void clear() noexcept; + + // + // operator= + // + AssocVector & operator=( AssocVector const & other ); + AssocVector & operator=( AssocVector && other ); + + // + // methods + // + void reserve( std::size_t newCapacity ); + void swap( AssocVector & other ) noexcept; + + // + // iterators + // + inline iterator begin(); + inline const_iterator begin()const; + inline const_iterator cbegin()const; + + inline reverse_iterator rbegin(); + inline const_reverse_iterator rbegin()const; + inline const_reverse_iterator crbegin()const; + + inline iterator end(); + inline const_iterator end()const; + inline const_iterator cend()const; + + inline reverse_iterator rend(); + inline const_reverse_iterator rend()const; + inline const_reverse_iterator crend()const; + + // + // size + // + inline bool empty()const noexcept; + inline std::size_t size()const noexcept; + inline std::size_t capacity()const noexcept; + inline std::size_t max_size()const noexcept; + + // + // insert + // + std::pair< iterator, bool > insert( value_type const & value ); + + template< typename __ValueType > + std::pair< iterator, bool > insert( __ValueType && value ); + + iterator insert( const_iterator hint, value_type const & value ); + + template< typename __ValueType > + iterator insert( const_iterator hint, __ValueType && value ); + + template< typename _Iterator > + inline void insert( _Iterator first, _Iterator last ); + + inline void insert( std::initializer_list< value_type > list ); + + // + // emplace + // + template< class... __Args > + std::pair< iterator, bool > emplace( __Args... args ); + + template< class... __Args > + std::pair< iterator, bool > emplace_hint( const_iterator hint, __Args... args ); + + // + // find + // + iterator find( key_type const & k ); + const_iterator find( key_type const & k )const; + + iterator lower_bound( key_type const & k ); + const_iterator lower_bound( key_type const & k )const; + + iterator upper_bound( key_type const & k ); + const_iterator upper_bound( key_type const & k )const; + + std::pair< iterator, iterator > equal_range( key_type const & k ); + std::pair< const_iterator, const_iterator > equal_range( key_type const & k )const; + + // + // count + // + inline std::size_t count( key_type const & k )const; + + // + // operator[] + // + reference operator[]( key_type const & k ); + reference operator[]( key_type && k ); + + // + // at + // + reference at( key_type const & k ); + const_reference at( key_type const & k )const; + + // + // erase + // + std::size_t erase( key_type const & k ); + iterator erase( iterator pos ); + + // + // observers + // + key_compare key_comp()const + { + return _cmp; + } + + value_compare value_comp()const + { + return value_compare( _cmp ); + } + + //allocator_type get_allocator()const + //{ + // return _allocator; + //} + +#ifdef AV_ENABLE_EXTENSIONS + public: +#else + private: +#endif + + // + // extension, flatenize container, enforce merge of _storage with _erased and with _buffer + // + void _merge(); + + // + // extension, faster, non STL compatible version of insert + // + bool _insert( value_type const & value ); + + template< typename __ValueType > + bool _insert( __ValueType && value ); + + // + // extension, faster, non STL compatible version of end, works with _find + // + inline _iterator _end(); + inline _const_iterator _end()const; + + // + // extension, faster, non STL compatible version of find, works with _end + // + _iterator _find( key_type const & k ); + _const_iterator _find( key_type const & k )const; + + // + // extension, faster, non STL compatible version of erase + // + bool _erase( iterator pos ); + +private: + bool validateStorage()const; + bool validateBuffer()const; + bool validateErased()const; + bool validate()const; + + // + // merge + // + void mergeStorageWithBuffer(); + void mergeStorageWithErased(); + + // + // insert + // + template< typename __ValueType > + void pushBack( __ValueType && value ); + + template< typename __ValueType > + bool shouldBePushBack( __ValueType && value )const; + + template< typename __ValueType > + _FindOrInsertToBufferResult + findOrInsertToBuffer( __ValueType && value ); + + // + // insertImpl, function does as little as needed but returns as much data as possible + // + template< typename __ValueType > + _InsertImplResult + insertImpl( __ValueType && value ); + + // + // emplace_impl + // + template< class __Head, class... __Tail > + std::pair< iterator, bool > emplaceImpl( __Head && head, __Tail... tail ); + + // + // erase + // + _TryRemoveBackResult + tryRemoveStorageBack( typename _Storage::iterator pos ); + + // + // tryEraseFromStorage + // + _TryEraseFromStorageResult + tryEraseFromStorage( typename _Storage::iterator pos ); + + // + // isErased + // + bool isErased( typename _Storage::const_iterator iterator )const; + + // + // findImpl, function does as little as needed but returns as much data as possible + // + _FindImplResult + findImpl( key_type const & key ); + + // + // getAllocator (method specialization) + // + //_Allocator getAllocator( _Storage const & ){ return _allocator; } + // + //typename _Allocator::template rebind< typename _Storage::const_iterator >::other + //getAllocator( _Erased const & ) + //{return typename _Allocator::template rebind< typename _Storage::const_iterator >::other( _allocator );} + +public: // public for unit tests only + void dump( int width = -1 )const; + + std::size_t bufferSize()const{ return _buffer.size(); } + std::size_t bufferCapacity()const{ return _buffer.capacity(); } + _Storage const & storage()const{ return _storage; } + + std::size_t storageSize()const{ return _storage.size(); } + std::size_t storageCapacity()const{ return _storage.capacity(); } + _Storage const & buffer()const{ return _buffer; } + + std::size_t erasedSize()const{ return _erased.size(); } + std::size_t erasedCapacity()const{ return _erased.capacity(); } + _Erased const & erased()const{ return _erased; } + + static std::size_t calculateNewBufferCapacity( std::size_t storageSize ); + static std::size_t calculateNewErasedCapacity( std::size_t storageSize ); + static std::size_t calculateNewStorageCapacity( std::size_t storageSize ); + +private: + _Storage _storage; + _Storage _buffer; + _Erased _erased; + + _Cmp _cmp; +}; + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +bool operator==( + AssocVector< _Key, _Mapped, _Cmp, _Allocator > const & lhs + , AssocVector< _Key, _Mapped, _Cmp, _Allocator > const & rhs +) +{ + if( lhs.size() != rhs.size() ){ + return false; + } + + typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::const_iterator begin = lhs.begin(); + typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::const_iterator const end = lhs.end(); + + typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::const_iterator begin2 = rhs.begin(); + + for( /*empty*/ ; begin != end ; ++ begin, ++ begin2 ) + { + if( begin->first != begin2->first ){ + return false; + } + + if( begin->second != begin2->second ){ + return false; + } + } + + return true; +} + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +bool operator!=( + AssocVector< _Key, _Mapped, _Cmp, _Allocator > const & lhs + , AssocVector< _Key, _Mapped, _Cmp, _Allocator > const & rhs +) +{ + return ! ( lhs == rhs ); +} + +// +// Method Definitions +// + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +AssocVector< _Key, _Mapped, _Cmp, _Allocator >::AssocVector( + _Cmp const & cmp + , _Allocator const & allocator +) + : _cmp( cmp ) +{ +} + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +AssocVector< _Key, _Mapped, _Cmp, _Allocator >::AssocVector( _Allocator const & allocator ) +{ +} + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +template< + typename __InputIterator +> +AssocVector< _Key, _Mapped, _Cmp, _Allocator >::AssocVector( + __InputIterator first + , __InputIterator last + , _Cmp const & cmp + , _Allocator const & allocator +) + : _cmp( cmp ) +{ + AV_PRECONDITION( std::distance( first, last ) >= 0 ); + + std::size_t const size = std::distance( first, last ); + + if( size > 0 ) + { + reserve( size ); + + for( /*empty*/ ; first != last ; ++ first ){ + insert( * first ); + } + } + + AV_POSTCONDITION( validate() ); +} + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +AssocVector< _Key, _Mapped, _Cmp, _Allocator >::AssocVector( + AssocVector< _Key, _Mapped, _Cmp, _Allocator > const & other +) + : _storage( other._storage ) + , _buffer( other._buffer ) + , _erased( other._erased ) + , _cmp( other._cmp ) +{ +} + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +AssocVector< _Key, _Mapped, _Cmp, _Allocator >::AssocVector( + AssocVector< _Key, _Mapped, _Cmp, _Allocator > const & other + , _Allocator const & allocator +) + : _storage( other._storage, allocator ) + , _buffer( other._buffer, allocator ) + , _erased( other._erased, allocator ) + , _cmp( other._cmp, allocator ) +{ +} + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +AssocVector< _Key, _Mapped, _Cmp, _Allocator >::AssocVector( + AssocVector< _Key, _Mapped, _Cmp, _Allocator > && other +) + : _storage( std::move( other._storage ) ) + , _buffer( std::move( other._buffer ) ) + , _erased( std::move( other._erased ) ) + , _cmp( other._cmp ) +{ +} + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +AssocVector< _Key, _Mapped, _Cmp, _Allocator >::AssocVector( + AssocVector< _Key, _Mapped, _Cmp, _Allocator > && other + , _Allocator const & allocator +) + : _storage( std::move( other._storage ) ) + , _buffer( std::move( other._buffer ) ) + , _erased( std::move( other._erased ) ) + , _cmp( other._cmp ) +{ +} + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +AssocVector< _Key, _Mapped, _Cmp, _Allocator >::AssocVector( + std::initializer_list< typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::value_type > list + , _Cmp const & cmp + , _Allocator const & allocator +) + : _cmp( cmp ) +{ + reserve( list.size() ); + + insert( list ); +} + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +AssocVector< _Key, _Mapped, _Cmp, _Allocator >::~AssocVector() +{ +} + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +void +AssocVector< _Key, _Mapped, _Cmp, _Allocator >::clear() noexcept +{ + util::destroy_range( _storage.begin(), _storage.end() ); + util::destroy_range( _buffer.begin(), _buffer.end() ); + + _storage.setSize( 0 ); + _buffer.setSize( 0 ); + _erased.setSize( 0 ); +} + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +AssocVector< _Key, _Mapped, _Cmp, _Allocator > & +AssocVector< _Key, _Mapped, _Cmp, _Allocator >::operator=( AssocVector const & other ) +{ + AssocVector temp( other ); + temp.swap( * this ); + + return * this; +} + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +AssocVector< _Key, _Mapped, _Cmp, _Allocator > & +AssocVector< _Key, _Mapped, _Cmp, _Allocator >::operator=( AssocVector && other ) +{ + AssocVector temp( std::move( other ) ); + + temp.swap( * this ); + + return * this; +} + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +void +AssocVector< _Key, _Mapped, _Cmp, _Allocator >::reserve( std::size_t newStorageCapacity ) +{ + if( _storage.get_allocator().max_size() < newStorageCapacity ){ + throw std::length_error( "AssocVector< _K, _M, _C, _A >::reserve" ); + } + + if( util::less_equal( newStorageCapacity, _storage.capacity() ) ){ + return; + } + + {// _erased + if( _erased.empty() == false ){ + mergeStorageWithErased(); + } + + _erased.reserve( calculateNewErasedCapacity( newStorageCapacity ) ); + + AV_CHECK( _erased.empty() ); + } + + std::size_t const newBufferCapacity + = calculateNewBufferCapacity( newStorageCapacity ); + + std::size_t const newStorageSize = _storage.size() + _buffer.size(); + + { + _Storage newStorage( newStorageCapacity, _storage.get_allocator() ); + _Storage newBuffer( newBufferCapacity, _buffer.get_allocator() ); + + util::move_merge_into_uninitialized( + _storage.begin() + , _storage.end() + , _buffer.begin() + , _buffer.end() + , newStorage.begin() + , value_comp() + ); + + newStorage.swap( _storage ); + newBuffer.swap( _buffer ); + }// call newStorage newBuffer destructors + + _storage.setSize( newStorageSize ); + + AV_POSTCONDITION( _buffer.empty() ); + AV_POSTCONDITION( validate() ); +} + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::iterator +AssocVector< _Key, _Mapped, _Cmp, _Allocator >::begin() +{ + return iterator( this, _storage.begin(), _buffer.begin(), _erased.begin(), 0 ); +} + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::reverse_iterator +AssocVector< _Key, _Mapped, _Cmp, _Allocator >::rbegin() +{ + return reverse_iterator( end() ); +} + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::const_iterator +AssocVector< _Key, _Mapped, _Cmp, _Allocator >::begin()const +{ + return const_iterator( this, _storage.begin(), _buffer.begin(), _erased.begin(), 0 ); +} + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::const_iterator +AssocVector< _Key, _Mapped, _Cmp, _Allocator >::cbegin()const +{ + return begin(); +} + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::const_reverse_iterator +AssocVector< _Key, _Mapped, _Cmp, _Allocator >::rbegin()const +{ + return const_reverse_iterator( end() ); +} + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::const_reverse_iterator +AssocVector< _Key, _Mapped, _Cmp, _Allocator >::crbegin()const +{ + return rbegin(); +} + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::iterator +AssocVector< _Key, _Mapped, _Cmp, _Allocator >::end() +{ + return iterator( this, _storage.end(), _buffer.end(), _erased.end(), 0 ); +} + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::reverse_iterator +AssocVector< _Key, _Mapped, _Cmp, _Allocator >::rend() +{ + return reverse_iterator( begin() ); +} + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::const_iterator +AssocVector< _Key, _Mapped, _Cmp, _Allocator >::end()const +{ + return const_iterator( this, _storage.end(), _buffer.end(), _erased.end(), 0 ); +} + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::const_iterator +AssocVector< _Key, _Mapped, _Cmp, _Allocator >::cend()const +{ + return end(); +} + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::const_reverse_iterator +AssocVector< _Key, _Mapped, _Cmp, _Allocator >::rend()const +{ + return const_reverse_iterator( begin() ); +} + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::const_reverse_iterator +AssocVector< _Key, _Mapped, _Cmp, _Allocator >::crend()const +{ + return rend(); +} + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::_iterator +AssocVector< _Key, _Mapped, _Cmp, _Allocator >::_end() +{ + return _iterator( 0 ); +} + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::_const_iterator +AssocVector< _Key, _Mapped, _Cmp, _Allocator >::_end()const +{ + return _const_iterator( 0 ); +} + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +bool +AssocVector< _Key, _Mapped, _Cmp, _Allocator >::empty()const noexcept +{ + return size() == 0; +} + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +std::size_t +AssocVector< _Key, _Mapped, _Cmp, _Allocator >::size()const noexcept +{ + return _storage.size() + _buffer.size() - _erased.size(); +} + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +std::size_t +AssocVector< _Key, _Mapped, _Cmp, _Allocator >::capacity()const noexcept +{ + return _storage.capacity() + _buffer.capacity(); +} + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +std::size_t +AssocVector< _Key, _Mapped, _Cmp, _Allocator >::max_size()const noexcept +{ + return _storage.get_allocator().max_size(); +} + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +std::pair< typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::iterator, bool > +AssocVector< _Key, _Mapped, _Cmp, _Allocator >::insert( value_type const & value ) +{ + _InsertImplResult const result = insertImpl( value ); + + return std::make_pair( + iterator( + this + , result._inStorage + , result._inBuffer + , result._inErased + , result._current + ) + , result._isInserted + ); +} + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +template< + typename __ValueType +> +std::pair< typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::iterator, bool > +AssocVector< _Key, _Mapped, _Cmp, _Allocator >::insert( __ValueType && value ) +{ + _InsertImplResult const result = insertImpl( std::forward< __ValueType >( value ) ); + + return std::make_pair( + iterator( + this + , result._inStorage + , result._inBuffer + , result._inErased + , result._current + ) + , result._isInserted + ); +} + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::iterator +AssocVector< _Key, _Mapped, _Cmp, _Allocator >::insert( + typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::const_iterator hint + , value_type const & value +) +{ + ( void )( hint ); + + _InsertImplResult const result = insertImpl( value ); + + return iterator( + this + , result._inStorage + , result._inBuffer + , result._inErased + , result._current + ); +} + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +template< + typename __ValueType +> +typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::iterator +AssocVector< _Key, _Mapped, _Cmp, _Allocator >::insert( + const_iterator hint + , __ValueType && value +) +{ + ( void )( hint ); + + _InsertImplResult const result = insertImpl( std::forward< __ValueType >( value ) ); + + return iterator( + this + , result._inStorage + , result._inBuffer + , result._inErased + , result._current + ); +} + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +void +AssocVector< _Key, _Mapped, _Cmp, _Allocator >::insert( + std::initializer_list< value_type > list +) +{ + insert( list.begin(), list.end() ); +} + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +bool +AssocVector< _Key, _Mapped, _Cmp, _Allocator >::_insert( value_type const & value ) +{ + return insertImpl( value )._isInserted; +} + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +template< + typename __ValueType +> +bool +AssocVector< _Key, _Mapped, _Cmp, _Allocator >::_insert( __ValueType && value ) +{ + return insertImpl( std::forward< __ValueType >( value ) )._isInserted; +} + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +template< + typename __ValueType +> +typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::_InsertImplResult +AssocVector< _Key, _Mapped, _Cmp, _Allocator >::insertImpl( __ValueType && value ) +{ + _Key const & k = value.first; + _Mapped const & m = value.second; + + {//push back to storage + if( shouldBePushBack( value ) ) + { + pushBack( std::forward< __ValueType >( value ) ); + + _InsertImplResult result; + + { + AV_CHECK( _storage.empty() == false ); + + result._inStorage = ( _storage.end() - 1 ); + result._current = ( _storage.end() - 1 ); + } + + result._isInserted = true; + result._inBuffer = 0; + result._inErased = _erased.end(); + + AV_POSTCONDITION( result.validate() ); + AV_POSTCONDITION( validate() ); + + return result; + } + } + + typename _Storage::iterator const greaterEqualInStorage + = std::lower_bound( _storage.begin(), _storage.end(), k, value_comp() ); + + bool const notPresentInStorage + = greaterEqualInStorage == _storage.end() + || key_comp()( k, greaterEqualInStorage->first ); + + {//find or insert to buffer + if( notPresentInStorage ) + { + _FindOrInsertToBufferResult const findOrInsertToBufferResult + = findOrInsertToBuffer( std::forward< __ValueType >( value ) ); + + _InsertImplResult result; + result._isInserted = findOrInsertToBufferResult._isInserted; + + if( findOrInsertToBufferResult._isReallocated ) + { + result._inStorage = 0; + result._inErased = _erased.end(); + } + else + { + result._inStorage = greaterEqualInStorage; + result._inErased = 0; + } + + result._inBuffer = findOrInsertToBufferResult._inBuffer; + result._current = findOrInsertToBufferResult._inBuffer; + + AV_POSTCONDITION( result.validate() ); + AV_POSTCONDITION( validate() ); + + return result; + } + } + + {// check if not erased + typename _Erased::iterator const greaterEqualInErased = std::lower_bound( + _erased.begin() + , _erased.end() + , greaterEqualInStorage + , std::less< typename _Storage::const_iterator >() + ); + + bool const itemNotMarkedAsErased + = greaterEqualInErased == _erased.end() + || std::less< typename _Storage::const_iterator >() + ( greaterEqualInStorage, * greaterEqualInErased ); + + if( itemNotMarkedAsErased ) + {// item is in storage and is not marked as erased + _InsertImplResult result; + result._isInserted = false; + result._inStorage = greaterEqualInStorage; + result._inBuffer = 0; + result._inErased = greaterEqualInErased; + result._current = greaterEqualInStorage; + + AV_POSTCONDITION( result.validate() ); + AV_POSTCONDITION( validate() ); + + return result; + } + else + {// item is in storage but is marked as erased + _erased.erase( greaterEqualInErased ); + + greaterEqualInStorage->second = m; + + _InsertImplResult result; + result._isInserted = true; + result._inStorage = greaterEqualInStorage; + result._inBuffer = 0; + + // greaterEqualInErased is after 'Array::erase' but still valid + result._inErased = greaterEqualInErased; + + result._current = greaterEqualInStorage; + + AV_POSTCONDITION( validate() ); + AV_POSTCONDITION( result.validate() ); + + return result; + } + } +} + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +template< + typename _Iterator +> +void +AssocVector< _Key, _Mapped, _Cmp, _Allocator >::insert( _Iterator const begin, _Iterator const end ) +{ + for( _Iterator current = begin ; current != end ; ++ current ){ + insert( * current ); + } +} + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +template< + class... __Args +> +std::pair< typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::iterator, bool > +AssocVector< _Key, _Mapped, _Cmp, _Allocator >::emplace( __Args... args ) +{ + return emplaceImpl( args... ); +} + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +template< + class... __Args +> +std::pair< typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::iterator, bool > +AssocVector< _Key, _Mapped, _Cmp, _Allocator >::emplace_hint( const_iterator hint, __Args... args ) +{ + ( void )( hint ); + + return emplaceImpl( args... ); +} + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +template< + class __Head + , class... __Tail +> +std::pair< typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::iterator, bool > +AssocVector< _Key, _Mapped, _Cmp, _Allocator >::emplaceImpl( __Head && head, __Tail... tail ) +{ + _InsertImplResult const result + = insertImpl( value_type_mutable( key_type( head ), mapped_type( tail... ) ) ); + + return std::make_pair( + iterator( + this + , result._inStorage + , result._inBuffer + , result._inErased + , result._current + ) + , result._isInserted + ); +} + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +template< + typename __ValueType +> +bool +AssocVector< _Key, _Mapped, _Cmp, _Allocator >::shouldBePushBack( __ValueType && value )const +{ + bool pushBackToStorage = false; + + {// should be pushed back + if( _storage.empty() ) + { + if( _buffer.empty() ){ + pushBackToStorage = true; + } + else + { + if( _cmp( _buffer.back().first, value.first ) ){ + pushBackToStorage = true; + } + } + } + else + { + if( _buffer.empty() ) + { + if( _cmp( _storage.back().first, value.first ) ){ + pushBackToStorage = true; + } + } + else + { + if( _cmp( _storage.back().first, value.first ) + && _cmp( _buffer.back().first, value.first ) + ){ + pushBackToStorage = true; + } + } + } + } + + return pushBackToStorage; +} + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +template< + typename __ValueType +> +void +AssocVector< _Key, _Mapped, _Cmp, _Allocator >::pushBack( __ValueType && value ) +{ + if( _storage.size() != _storage.capacity() ){ + _storage.place_back( std::forward< __ValueType >( value ) ); + + AV_POSTCONDITION( validate() ); + + return; + } + + std::size_t const newStorageCapacity = calculateNewStorageCapacity( _storage.capacity() ); + std::size_t const newBufferCapacity = calculateNewBufferCapacity( newStorageCapacity ); + std::size_t const newErasedCapacity = calculateNewErasedCapacity( newStorageCapacity ); + + if( util::less_equal( newStorageCapacity, _storage.capacity() ) ){ + return; + } + + if( _storage.get_allocator().max_size() < newStorageCapacity ){ + throw std::length_error( "AssocVector::reserve" ); + } + + _Storage newStorage( newStorageCapacity, _storage.get_allocator() ); + _Storage newBuffer( newBufferCapacity, _buffer.get_allocator() ); + _Erased newErased( newErasedCapacity, _erased.get_allocator() ); + + {// may throw + iterator current = begin(); + iterator const end = this->end(); + + while( current != end ) + { + // for '++ current' object has still exist and can not be moved before + typename iterator::pointer_mutable const current_raw_ptr = current.getCurrent(); + + ++ current; + + newStorage.place_back( AV_MOVE_IF_NOEXCEPT( * current_raw_ptr ) ); + } + } + + // may throw an exception in __ValueType copy constructor, not exception safe + newStorage.place_back( std::forward< __ValueType >( value ) ); + + newStorage.swap( _storage ); + newBuffer.swap( _buffer ); + newErased.swap( _erased ); + + AV_POSTCONDITION( _buffer.empty() ); + AV_POSTCONDITION( _erased.empty() ); + + AV_POSTCONDITION( validate() ); +} + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::_TryRemoveBackResult +AssocVector< _Key, _Mapped, _Cmp, _Allocator >::tryRemoveStorageBack( + typename _Storage::iterator pos +) +{ + if( pos + 1 != _storage.end() ) + { + _TryRemoveBackResult result; + result._anyItemRemoved = false; + result._erasedItemRemoved = false; + + return result; + } + + _storage.get_allocator().destroy( pos ); + + _storage.setSize( _storage.size() - 1 ); + + if( + _erased.empty() == false + && _erased.back() == pos + ) + { + _erased.setSize( _erased.size() - 1 ); + + _TryRemoveBackResult result; + result._anyItemRemoved = true; + result._erasedItemRemoved = true; + + AV_POSTCONDITION( validate() ); + + return result; + } + + _TryRemoveBackResult result; + result._anyItemRemoved = true; + result._erasedItemRemoved = false; + + AV_POSTCONDITION( validate() ); + + return result; +} + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::_TryEraseFromStorageResult +AssocVector< _Key, _Mapped, _Cmp, _Allocator >::tryEraseFromStorage( + typename _Storage::iterator pos +) +{ + std::pair< typename _Erased::iterator, bool > const insertInSortedResult + = array::insert_in_sorted( + _erased + , typename _Storage::const_iterator( pos ) + , std::less< typename _Storage::const_iterator >() + ); + + if( _erased.full() ) + { + mergeStorageWithErased(); + + _TryEraseFromStorageResult result; + result._inErased = _erased.end(); + result._isErased = true; + result._isMerged = true; + + AV_POSTCONDITION( validate() ); + + return result; + } + else + { + _TryEraseFromStorageResult result; + result._inErased = insertInSortedResult.first; + result._isErased = insertInSortedResult.second; + result._isMerged = false; + + AV_POSTCONDITION( validate() ); + + return result; + } +} + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +bool +AssocVector< _Key, _Mapped, _Cmp, _Allocator >::isErased( + typename AssocVector::_Storage::const_iterator iterator +)const +{ + typename _Erased::const_iterator const foundInErased = array::binary_search( + _erased.begin() + , _erased.end() + , iterator + , std::less< typename _Storage::const_iterator >() + ); + + return foundInErased != _erased.end(); +} + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::_FindImplResult +AssocVector< _Key, _Mapped, _Cmp, _Allocator >::findImpl( _Key const & k ) +{ + typename _Storage::iterator const greaterEqualInStorage + = std::lower_bound( _storage.begin(), _storage.end(), k, value_comp() ); + + bool const presentInStorage + = greaterEqualInStorage != _storage.end() + && key_comp()( k, greaterEqualInStorage->first ) == false; + + {// item is in storage, check in erased + if( presentInStorage ) + { + typename _Erased::iterator greaterEqualInErased = std::lower_bound( + _erased.begin() + , _erased.end() + , greaterEqualInStorage + , std::less< typename _Storage::const_iterator >() + ); + + bool const itemNotMarkedAsErased + = greaterEqualInErased == _erased.end() + || std::less< typename _Storage::const_iterator >()( + greaterEqualInStorage + , * greaterEqualInErased + ); + + if( itemNotMarkedAsErased ) + { + _FindImplResult result; + result._inStorage = greaterEqualInStorage; + result._inBuffer = 0; + result._inErased = greaterEqualInErased; + result._current = greaterEqualInStorage; + + AV_POSTCONDITION( result.validate() ); + + return result; + } + else + { + _FindImplResult result; + result._inStorage = 0; + result._inBuffer = 0; + result._inErased = 0; + result._current = 0; + + AV_POSTCONDITION( result.validate() ); + + return result; + } + } + } + + {// check in buffer + typename _Storage::iterator const greaterEqualInBuffer + = std::lower_bound( _buffer.begin(), _buffer.end(), k, value_comp() ); + + bool const presentInBuffer + = greaterEqualInBuffer != _buffer.end() + && key_comp()( k, greaterEqualInBuffer->first ) == false; + + if( presentInBuffer ) + { + _FindImplResult result; + result._inStorage = greaterEqualInStorage; + result._inBuffer = greaterEqualInBuffer; + result._inErased = 0; + result._current = greaterEqualInBuffer; + + AV_POSTCONDITION( result.validate() ); + + return result; + } + else + { + _FindImplResult result; + result._inStorage = 0; + result._inBuffer = 0; + result._inErased = 0; + result._current = 0; + + AV_POSTCONDITION( result.validate() ); + + return result; + } + } +} + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::iterator +AssocVector< _Key, _Mapped, _Cmp, _Allocator >::find( _Key const & k ) +{ + _FindImplResult const result = findImpl( k ); + + if( result._current == 0 ){ + return end(); + } + else + { + return iterator( + this + , result._inStorage + , result._inBuffer + , result._inErased + , result._current + ); + } +} + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::reference +AssocVector< _Key, _Mapped, _Cmp, _Allocator >::at( _Key const & k ) +{ + _FindImplResult const result = findImpl( k ); + + if( result._current == 0 ){ + throw std::out_of_range( "AssocVector::at" ); + } + else + { + return result._current->second; + } +} + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::const_reference +AssocVector< _Key, _Mapped, _Cmp, _Allocator >::at( _Key const & k )const +{ + typedef AssocVector< _Key, _Mapped, _Cmp, _Allocator > * NonConstThis; + + return const_cast< NonConstThis >( this )->at( k ); +} + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::const_iterator +AssocVector< _Key, _Mapped, _Cmp, _Allocator >::find( _Key const & k )const +{ + typedef AssocVector< _Key, _Mapped, _Cmp, _Allocator > * NonConstThis; + + return const_cast< NonConstThis >( this )->find( k ); +} + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::iterator +AssocVector< _Key, _Mapped, _Cmp, _Allocator >::lower_bound( _Key const & k ) +{ + typename _Storage::iterator const greaterEqualInStorage + = std::lower_bound( _storage.begin(), _storage.end(), k, value_comp() ); + + typename _Storage::iterator const greaterEqualInBuffer + = std::lower_bound( _buffer.begin(), _buffer.end(), k, value_comp() ); + + return iterator( this, greaterEqualInStorage, greaterEqualInBuffer, 0, 0 ); +} + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::const_iterator +AssocVector< _Key, _Mapped, _Cmp, _Allocator >::lower_bound( _Key const & k )const +{ + typedef AssocVector< _Key, _Mapped, _Cmp, _Allocator > * NonConstThis; + + return const_cast< NonConstThis >( this )->lower_bound( k ); +} + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::iterator +AssocVector< _Key, _Mapped, _Cmp, _Allocator >::upper_bound( _Key const & k ) +{ + typename _Storage::iterator const greaterInStorage + = std::upper_bound( _storage.begin(), _storage.end(), k, value_comp() ); + + typename _Storage::iterator const greaterInBuffer + = std::upper_bound( _buffer.begin(), _buffer.end(), k, value_comp() ); + + return iterator( this, greaterInStorage, greaterInBuffer, 0, 0 ); +} + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::const_iterator +AssocVector< _Key, _Mapped, _Cmp, _Allocator >::upper_bound( _Key const & k )const +{ + typedef AssocVector< _Key, _Mapped, _Cmp, _Allocator > * NonConstThis; + + return const_cast< NonConstThis >( this )->upper_bound( k ); +} + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +std::pair< + typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::iterator + , typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::iterator +> +AssocVector< _Key, _Mapped, _Cmp, _Allocator >::equal_range( _Key const & k ) +{ + typename _Storage::iterator const greaterEqualInStorage + = std::lower_bound( _storage.begin(), _storage.end(), k, value_comp() ); + + bool const notPresentInStorage + = greaterEqualInStorage == _storage.end() + || key_comp()( k, greaterEqualInStorage->first ); + + typename _Storage::iterator const greaterEqualInBuffer + = std::lower_bound( _buffer.begin(), _buffer.end(), k, value_comp() ); + + bool const notPresentInBuffer + = greaterEqualInBuffer == _buffer.end() + || key_comp()( k, greaterEqualInBuffer->first ); + + if( notPresentInStorage == false ) + { + return std::make_pair( + iterator( this, greaterEqualInStorage, greaterEqualInBuffer, 0, 0 ) + , iterator( this, greaterEqualInStorage + 1, greaterEqualInBuffer, 0, 0 ) + ); + } + + if( notPresentInBuffer == false ) + { + return std::make_pair( + iterator( this, greaterEqualInStorage, greaterEqualInBuffer, 0, 0 ) + , iterator( this, greaterEqualInStorage, greaterEqualInBuffer + 1, 0, 0 ) + ); + } + + return std::make_pair( + iterator( this, greaterEqualInStorage, greaterEqualInBuffer, 0, 0 ) + , iterator( this, greaterEqualInStorage, greaterEqualInBuffer, 0, 0 ) + ); +} + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +std::pair< + typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::const_iterator + , typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::const_iterator +> +AssocVector< _Key, _Mapped, _Cmp, _Allocator >::equal_range( _Key const & k )const +{ + typedef AssocVector< _Key, _Mapped, _Cmp, _Allocator > * NonConstThis; + + return const_cast< NonConstThis >( this )->equal_range( k ); +} + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::_iterator +AssocVector< _Key, _Mapped, _Cmp, _Allocator >::_find( _Key const & k ) +{ + return _iterator( findImpl( k )._current ); +} + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::_const_iterator +AssocVector< _Key, _Mapped, _Cmp, _Allocator >::_find( _Key const & k )const +{ + typedef AssocVector< _Key, _Mapped, _Cmp, _Allocator > * NonConstThis; + + return const_cast< NonConstThis >( this )->_find( k ); +} + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +bool +AssocVector< _Key, _Mapped, _Cmp, _Allocator >::validateStorage()const +{ + if( _storage.size() > _storage.capacity() ) + { + AV_ERROR(); + + return false; + } + + if( std::is_sorted( _storage.begin(), _storage.end(), value_comp() ) == false ) + { + AV_ERROR(); + + return false; + } + + return true; +} + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +bool +AssocVector< _Key, _Mapped, _Cmp, _Allocator >::validateBuffer()const +{ + if( _buffer.size() > _buffer.capacity() ) + { + AV_ERROR(); + + return false; + } + + if( _buffer.empty() ){ + return true; + } + + if( std::is_sorted( _buffer.begin(), _buffer.end(), value_comp() ) == false ) + { + AV_ERROR(); + + return false; + } + + if( + util::has_intersection( + _buffer.begin() + , _buffer.end() + , _storage.begin() + , _storage.end() + , value_comp() + ) + ) + { + AV_ERROR(); + + return false; + } + + return true; +} + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +bool +AssocVector< _Key, _Mapped, _Cmp, _Allocator >::validateErased()const +{ + if( _erased.size() > _erased.capacity() ) + { + AV_ERROR(); + + return false; + } + + if( _erased.empty() ){ + return true; + } + + if( + std::is_sorted( + _erased.begin() + , _erased.end() + , std::less< typename _Storage::const_iterator >() + ) == false + ) + { + AV_ERROR(); + + return false; + } + + AV_CHECK( _erased.front() >= _storage.begin() ); + AV_CHECK( _erased.back() < _storage.end() ); + + return true; +} + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +bool +AssocVector< _Key, _Mapped, _Cmp, _Allocator >::validate()const +{ + if( calculateNewBufferCapacity( _storage.capacity() ) != _buffer.capacity() ){ + return false; + } + + if( calculateNewErasedCapacity( _storage.capacity() ) != _erased.capacity() ){ + return false; + } + + return validateStorage() && validateBuffer() && validateErased(); +} + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +void +AssocVector< _Key, _Mapped, _Cmp, _Allocator >::_merge() +{ + if( size() > _storage.capacity() ) + { + reserve( calculateNewStorageCapacity( _storage.capacity() ) ); + + return; + } + + if( _erased.empty() == false ){ + mergeStorageWithErased(); + } + + mergeStorageWithBuffer(); +} + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::reference +AssocVector< _Key, _Mapped, _Cmp, _Allocator >::operator[]( key_type const & k ) +{ + return insert( value_type( k, mapped_type() ) ).first->second; +} + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::reference +AssocVector< _Key, _Mapped, _Cmp, _Allocator >::operator[]( key_type && k ) +{ + return insert( value_type( std::move( k ), mapped_type() ) ).first->second; +} + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +std::size_t +AssocVector< _Key, _Mapped, _Cmp, _Allocator >::count( key_type const & k )const +{ + return _find( k ) ? 1 : 0; +} + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +std::size_t +AssocVector< _Key, _Mapped, _Cmp, _Allocator >::erase( key_type const & k ) +{ + typename _Storage::iterator const foundInStorage + = array::binary_search( _storage.begin(), _storage.end(), k, value_comp() ); + + {//erase from _buffer + if( foundInStorage == _storage.end() ) + { + typename _Storage::iterator const foundInBuffer + = array::binary_search( _buffer.begin(), _buffer.end(), k, value_comp() ); + + if( foundInBuffer == _buffer.end() ) + { + AV_POSTCONDITION( validate() ); + + return 0; + } + else + { + _buffer.erase( foundInBuffer ); + + AV_POSTCONDITION( validate() ); + + return 1; + } + } + } + + {//erase from back + _TryRemoveBackResult const result = tryRemoveStorageBack( foundInStorage ); + + if( result._anyItemRemoved ) + { + if( result._erasedItemRemoved ) + { + AV_POSTCONDITION( validate() ); + + return 0; + } + else + { + AV_POSTCONDITION( validate() ); + + return 1; + } + } + } + + {//erase from _storage + bool const result = tryEraseFromStorage( foundInStorage )._isErased ? 1 : 0; + + AV_POSTCONDITION( validate() ); + + return result; + } +} + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::iterator +AssocVector< _Key, _Mapped, _Cmp, _Allocator >::erase( iterator pos ) +{ + if( pos == end() ){ + return end(); + } + + // iterator::get converts : pair< T1, T2 > * -> pair< T1 const, T2 > * + // revert real iterator type: pair< T1 const, T2 > * -> pair< T1, T2 > * + value_type_mutable * const posBase = reinterpret_cast< value_type_mutable * >( pos.get() ); + + _Key const key = pos->first; + + {//erase from _buffer + if( util::is_between( _buffer.begin(), posBase, _buffer.end() ) ) + { + _buffer.erase( posBase ); + + typename _Storage::iterator const greaterEqualInStorage + = pos.getCurrentInStorage() + ? pos.getCurrentInStorage() + : std::lower_bound( _storage.begin(), _storage.end(), key, value_comp() ); + + AV_POSTCONDITION( validate() ); + + return iterator( this, greaterEqualInStorage, posBase, 0, 0 ); + } + } + + {//item not present in container + if( util::is_between( _storage.begin(), posBase, _storage.end() ) == false ){ + return end(); + } + } + + {//erase from back + _TryRemoveBackResult const result = tryRemoveStorageBack( posBase ); + + if( result._anyItemRemoved ) + { + typename _Storage::iterator const greaterEqualInBuffer + = pos.getCurrentInBuffer() + ? pos.getCurrentInBuffer() + : std::lower_bound( _buffer.begin(), _buffer.end(), key, value_comp() ); + + if( greaterEqualInBuffer == _buffer.end() ) + { + AV_POSTCONDITION( validate() ); + + return end(); + } + else + { + AV_POSTCONDITION( validate() ); + + return iterator( this, _storage.end(), greaterEqualInBuffer, 0, 0 ); + } + } + } + + {//erase from _storage + _TryEraseFromStorageResult const result = tryEraseFromStorage( posBase ); + + if( result._isErased == false ){ + return end(); + } + + typename _Storage::iterator const greaterEqualInBuffer + = pos.getCurrentInBuffer() + ? pos.getCurrentInBuffer() + : std::lower_bound( _buffer.begin(), _buffer.end(), key, value_comp() ); + + if( result._isMerged == false ) + { + AV_POSTCONDITION( validate() ); + + return iterator( this, posBase + 1, greaterEqualInBuffer, result._inErased + 1, 0 ); + } + + typename _Storage::iterator const greaterEqualInStorage + = std::lower_bound( _storage.begin(), _storage.end(), key, value_comp() ); + + AV_POSTCONDITION( validate() ); + + return iterator( this, greaterEqualInStorage, greaterEqualInBuffer, _erased.end(), 0 ); + } +} + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +bool +AssocVector< _Key, _Mapped, _Cmp, _Allocator >::_erase( iterator pos ) +{ + // iterator::get converts : pair< T1, T2 > * -> pair< T1 const, T2 > * + // revert real iterator type: pair< T1 const, T2 > * -> pair< T1, T2 > * + value_type_mutable * const posBase = reinterpret_cast< value_type_mutable * >( pos.get() ); + + {//erase from _buffer + if( util::is_between( _buffer.begin(), posBase, _buffer.end() ) ) + { + _buffer.erase( posBase ); + + AV_POSTCONDITION( validate() ); + + return true; + } + } + + {//item not present in container + if( util::is_between( _storage.begin(), posBase, _storage.end() ) == false ){ + return false; + } + } + + {//erase from back + _TryRemoveBackResult const result = tryRemoveStorageBack( posBase ); + + if( result._anyItemRemoved ) + { + AV_POSTCONDITION( validate() ); + + return true; + } + } + + {//erase from _storage + bool const result = tryEraseFromStorage( posBase )._isErased; + + AV_POSTCONDITION( validate() ); + + return result; + } +} + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +void +AssocVector< _Key, _Mapped, _Cmp, _Allocator >::swap( + AssocVector< _Key, _Mapped, _Cmp, _Allocator > & other +) noexcept +{ + std::swap( _storage, other._storage ); + std::swap( _buffer, other._buffer ); + std::swap( _erased, other._erased ); + + std::swap( _cmp, other._cmp ); +} + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +void +AssocVector< _Key, _Mapped, _Cmp, _Allocator >::mergeStorageWithBuffer() +{ + AV_PRECONDITION( _erased.empty() ); + + array::move_merge( _storage, _buffer, value_comp() ); + + util::destroy_range( _buffer.begin(), _buffer.end() ); + + _buffer.setSize( 0 ); + + AV_POSTCONDITION( _buffer.empty() ); + AV_POSTCONDITION( validateStorage() ); +} + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +void +AssocVector< _Key, _Mapped, _Cmp, _Allocator >::mergeStorageWithErased() +{ + typename _Storage::iterator const end = _storage.end(); + + array::erase_removed( _storage, _erased ); + + util::destroy_range( _storage.end(), end ); + + _erased.setSize( 0 ); + + AV_POSTCONDITION( _erased.empty() ); + AV_POSTCONDITION( validateStorage() ); +} + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +template< + typename __ValueType +> +typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::_FindOrInsertToBufferResult +AssocVector< _Key, _Mapped, _Cmp, _Allocator >::findOrInsertToBuffer( __ValueType && value ) +{ + typename _Storage::iterator const greaterEqualInBuffer + = std::lower_bound( _buffer.begin(), _buffer.end(), value.first, value_comp() ); + + if( greaterEqualInBuffer != _buffer.end() ) + { + bool const isEqual = _cmp( value.first, greaterEqualInBuffer->first ) == false; + + if( isEqual ) + { + _FindOrInsertToBufferResult result; + result._inBuffer = greaterEqualInBuffer; + result._isInserted = false; + result._isReallocated = false; + + return result; + } + } + + if( _buffer.full() ) + { + _merge(); + + AV_CHECK( _buffer.empty() ); + + _buffer.insert( + _buffer.begin() + , value_type_mutable( std::forward< __ValueType >( value ) ) + ); + + _FindOrInsertToBufferResult result; + result._inBuffer = _buffer.begin(); + result._isInserted = true; + result._isReallocated = true; + + AV_POSTCONDITION( validate() ); + + return result; + } + else + { + _buffer.insert( + greaterEqualInBuffer + , value_type_mutable( std::forward< __ValueType >( value ) ) + ); + + _FindOrInsertToBufferResult result; + result._inBuffer = greaterEqualInBuffer; + result._isInserted = true; + result._isReallocated = false; + + AV_POSTCONDITION( validate() ); + + return result; + } +} + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +std::size_t AssocVector< _Key, _Mapped, _Cmp, _Allocator >::calculateNewBufferCapacity( + std::size_t storageSize +) +{ + return static_cast< std::size_t >( 1.0 * sqrt( storageSize )); +} + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +std::size_t AssocVector< _Key, _Mapped, _Cmp, _Allocator >::calculateNewErasedCapacity( + std::size_t storageSize +) +{ + return calculateNewBufferCapacity( storageSize ); +} + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +std::size_t AssocVector< _Key, _Mapped, _Cmp, _Allocator >::calculateNewStorageCapacity( + std::size_t storageSize +) +{ + if( storageSize == 0 ){ + return 1; + } + else if( storageSize == 1 ){ + // size=1 size=2 + // capacity=1 capacity=2 + // S:[a] S:[ab] + // B:[b] -> reserve -> B:[ ] + // E:[ ] E:[ ] + + return 4; + } + else{ + return 2 * storageSize; + } +} + +template< + typename _Key + , typename _Mapped + , typename _Cmp + , typename _Allocator +> +void AssocVector< _Key, _Mapped, _Cmp, _Allocator >::dump( int width )const +{ + std::cout << "storage: "; + for( unsigned i = 0 ; i < _storage.size() ; ++ i ) + { + if( i > 0 && width > 0 && ( i % width == 0 ) ){ + std::cout << "\n "; + } + + std::cout << " (" << _storage[i].first << "," << _storage[i].second << ")"; + } + + std::cout << std::endl << "buffer: "; + for( unsigned i = 0 ; i < _buffer.size() ; ++ i ) + { + if( i > 0 && width > 0 && ( i % width == 0 ) ){ + std::cout << "\n "; + } + + std::cout << " (" << _buffer[i].first << "," << _buffer[i].second << ")"; + } + + std::cout << std::endl << "erased: "; + for( unsigned i = 0 ; i < _erased.size() ; ++ i ) + { + if( i > 0 && width > 0 && ( i % width == 0 ) ){ + std::cout << "\n "; + } + + std::cout << " (" << (*_erased[i]).first << "," << (*_erased[i]).second << ")"; + } - // specialized algorithms: - template - void swap(AssocVector& lhs, AssocVector& rhs) - { lhs.swap(rhs); } + std::cout << "." << std::endl; +} -} // namespace Loki +#endif diff --git a/kiwi/maptype.h b/kiwi/maptype.h index d125b07c..cf10ff98 100644 --- a/kiwi/maptype.h +++ b/kiwi/maptype.h @@ -27,7 +27,7 @@ template< class MapType { public: - typedef Loki::AssocVector Type; + typedef AssocVector Type; //typedef std::map Type; private: MapType(); diff --git a/kiwi/solverimpl.h b/kiwi/solverimpl.h index 9b56ee28..72214f28 100644 --- a/kiwi/solverimpl.h +++ b/kiwi/solverimpl.h @@ -88,7 +88,7 @@ class SolverImpl // constraints and since exceptional conditions are uncommon, // i'm not too worried about aggressive cleanup of the var map. Tag tag; - std::auto_ptr rowptr( createRow( constraint, tag ) ); + std::unique_ptr rowptr( createRow( constraint, tag ) ); Symbol subject( chooseSubject( *rowptr, tag ) ); // If chooseSubject could not find a valid entering symbol, one @@ -155,7 +155,7 @@ class SolverImpl RowMap::iterator row_it = m_rows.find( tag.marker ); if( row_it != m_rows.end() ) { - std::auto_ptr rowptr( row_it->second ); + std::unique_ptr rowptr( row_it->second ); m_rows.erase( row_it ); } else @@ -164,7 +164,7 @@ class SolverImpl if( row_it == m_rows.end() ) throw InternalSolverError( "failed to find leaving row" ); Symbol leaving( row_it->first ); - std::auto_ptr rowptr( row_it->second ); + std::unique_ptr rowptr( row_it->second ); m_rows.erase( row_it ); rowptr->solveFor( leaving, tag.marker ); substitute( tag.marker, *rowptr ); @@ -511,7 +511,7 @@ class SolverImpl RowMap::iterator it = m_rows.find( art ); if( it != m_rows.end() ) { - std::auto_ptr rowptr( it->second ); + std::unique_ptr rowptr( it->second ); m_rows.erase( it ); if( rowptr->cells().empty() ) return success; @@ -830,8 +830,8 @@ class SolverImpl VarMap m_vars; EditMap m_edits; std::vector m_infeasible_rows; - std::auto_ptr m_objective; - std::auto_ptr m_artificial; + std::unique_ptr m_objective; + std::unique_ptr m_artificial; Symbol::Id m_id_tick; }; diff --git a/kiwi/variable.h b/kiwi/variable.h index eaee93f2..11862f89 100644 --- a/kiwi/variable.h +++ b/kiwi/variable.h @@ -100,7 +100,7 @@ class Variable ~VariableData() {} std::string m_name; - std::auto_ptr m_context; + std::unique_ptr m_context; double m_value; private: From 7895663d86e4736450eedc0ecbf5005a8ad2affe Mon Sep 17 00:00:00 2001 From: MatthieuDartiailh Date: Fri, 26 Oct 2018 12:14:29 -0400 Subject: [PATCH 03/16] kiwi: fix compilation and tests --- py/constraint.cpp | 3 --- py/expression.cpp | 3 --- py/solver.cpp | 5 +---- py/strength.cpp | 9 +++++++-- py/symbolics.h | 3 --- py/term.cpp | 5 +---- py/variable.cpp | 3 --- setup.py | 3 ++- 8 files changed, 11 insertions(+), 23 deletions(-) diff --git a/py/constraint.cpp b/py/constraint.cpp index 58461b5c..6726deff 100644 --- a/py/constraint.cpp +++ b/py/constraint.cpp @@ -14,9 +14,6 @@ #include "util.h" -using namespace PythonHelpers; - - static PyObject* Constraint_new( PyTypeObject* type, PyObject* args, PyObject* kwargs ) { diff --git a/py/expression.cpp b/py/expression.cpp index dc636b8d..2c682975 100644 --- a/py/expression.cpp +++ b/py/expression.cpp @@ -13,9 +13,6 @@ #include "util.h" -using namespace PythonHelpers; - - static PyObject* Expression_new( PyTypeObject* type, PyObject* args, PyObject* kwargs ) { diff --git a/py/solver.cpp b/py/solver.cpp index cc8ec98a..561ec7cc 100644 --- a/py/solver.cpp +++ b/py/solver.cpp @@ -6,15 +6,12 @@ | The full license is in the file COPYING.txt, distributed with this software. |----------------------------------------------------------------------------*/ #include +#include #include -#include "pythonhelpers.h" #include "types.h" #include "util.h" -using namespace PythonHelpers; - - static PyObject* Solver_new( PyTypeObject* type, PyObject* args, PyObject* kwargs ) { diff --git a/py/strength.cpp b/py/strength.cpp index df1552d1..0160530d 100644 --- a/py/strength.cpp +++ b/py/strength.cpp @@ -6,13 +6,18 @@ | The full license is in the file COPYING.txt, distributed with this software. |----------------------------------------------------------------------------*/ #include +#include #include -#include "pythonhelpers.h" #include "util.h" -using namespace PythonHelpers; +#ifdef __clang__ +#pragma clang diagnostic ignored "-Wdeprecated-writable-strings" +#endif +#ifdef __GNUC__ +#pragma GCC diagnostic ignored "-Wwrite-strings" +#endif struct strength { diff --git a/py/symbolics.h b/py/symbolics.h index 37a77eed..eb81c511 100644 --- a/py/symbolics.h +++ b/py/symbolics.h @@ -112,7 +112,6 @@ PyObject* BinaryMul::operator()( Term* first, double second ) template<> inline PyObject* BinaryMul::operator()( Expression* first, double second ) { - using namespace PythonHelpers; cppy::ptr pyexpr( PyType_GenericNew( &Expression_Type, 0, 0 ) ); if( !pyexpr ) return 0; @@ -263,7 +262,6 @@ PyObject* BinaryAdd::operator()( Expression* first, Expression* second ) template<> inline PyObject* BinaryAdd::operator()( Expression* first, Term* second ) { - using namespace PythonHelpers; cppy::ptr pyexpr( PyType_GenericNew( &Expression_Type, 0, 0 ) ); if( !pyexpr ) return 0; @@ -297,7 +295,6 @@ PyObject* BinaryAdd::operator()( Expression* first, Variable* second ) template<> inline PyObject* BinaryAdd::operator()( Expression* first, double second ) { - using namespace PythonHelpers; cppy::ptr pyexpr( PyType_GenericNew( &Expression_Type, 0, 0 ) ); if( !pyexpr ) return 0; diff --git a/py/term.cpp b/py/term.cpp index 3416a356..e9ec02b0 100644 --- a/py/term.cpp +++ b/py/term.cpp @@ -7,15 +7,12 @@ |----------------------------------------------------------------------------*/ #include #include -#include "pythonhelpers.h" +#include #include "symbolics.h" #include "types.h" #include "util.h" -using namespace PythonHelpers; - - static PyObject* Term_new( PyTypeObject* type, PyObject* args, PyObject* kwargs ) { diff --git a/py/variable.cpp b/py/variable.cpp index d419913a..855729d6 100644 --- a/py/variable.cpp +++ b/py/variable.cpp @@ -13,9 +13,6 @@ #include "util.h" -using namespace PythonHelpers; - - static PyObject* Variable_new( PyTypeObject* type, PyObject* args, PyObject* kwargs ) { diff --git a/setup.py b/setup.py index 0b69345d..368a44bf 100644 --- a/setup.py +++ b/setup.py @@ -35,7 +35,8 @@ class BuildExt(build_ext): """ c_opts = { - 'msvc': ['/EHsc'] + 'msvc': ['/EHsc', '/std:c++11'], + 'unix': ['-std=c++11'] } def build_extensions(self): From 6eddbfeff33fe3f896eac79a9f0588a8c32a8b3d Mon Sep 17 00:00:00 2001 From: MatthieuDartiailh Date: Fri, 26 Oct 2018 12:18:20 -0400 Subject: [PATCH 04/16] travis: update travis --- .travis.yml | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/.travis.yml b/.travis.yml index c3671df4..24068f98 100644 --- a/.travis.yml +++ b/.travis.yml @@ -16,12 +16,6 @@ branches: jobs: include: - - name: "run test suite with python 2.7" - python: 2.7 - dist: trusty - - name: "run test suite with python 3.4" - python: 3.4 - dist: trusty - name: "run test suite with python 3.5" python: 3.5 dist: trusty @@ -34,6 +28,8 @@ env: - CPPFLAGS=--coverage before_install: - pip install --upgrade pip + - pip install pytest-cov + - pip install https://github.com/nucleic/cppy/tarball/nucleic-migration install: - python setup.py develop script: From 14f23203b4eb5b202cb402233b7e165aa9b788e6 Mon Sep 17 00:00:00 2001 From: MatthieuDartiailh Date: Mon, 25 Mar 2019 21:49:03 -0400 Subject: [PATCH 05/16] kiwi: add benchmark and make Loki::AssocVector c++11 compliant --- benchmarks/README.rst | 9 + benchmarks/enaml_like_benchmark.py | 197 ++ docs/source/use_cases/enaml.rst | 4 +- kiwi/AssocVector.h | 5109 ++-------------------------- kiwi/maptype.h | 18 +- kiwi/row.h | 2 +- kiwi/solverimpl.h | 8 +- py/solver.cpp | 2 +- py/tests/test_constraint.py | 2 +- 9 files changed, 480 insertions(+), 4871 deletions(-) create mode 100644 benchmarks/README.rst create mode 100644 benchmarks/enaml_like_benchmark.py diff --git a/benchmarks/README.rst b/benchmarks/README.rst new file mode 100644 index 00000000..08438abb --- /dev/null +++ b/benchmarks/README.rst @@ -0,0 +1,9 @@ +Benchmarks for Kiwi +------------------- + +Those benchmarks are mostly used to check the performance of kiwi depending on +different c++ data structure. + +Running these benchmarks require to install the perf module:: + + >>> python enaml_like_benchmarks.py diff --git a/benchmarks/enaml_like_benchmark.py b/benchmarks/enaml_like_benchmark.py new file mode 100644 index 00000000..f474b3ce --- /dev/null +++ b/benchmarks/enaml_like_benchmark.py @@ -0,0 +1,197 @@ +#------------------------------------------------------------------------------ +# Copyright (c) 2019, Nucleic Development Team. +# +# Distributed under the terms of the Modified BSD License. +# +# The full license is in the file COPYING.txt, distributed with this software. +#------------------------------------------------------------------------------ +"""Time updating an EditVariable in a set of constraints typical of enaml use. + +""" +import perf +from kiwisolver import Variable, Solver, strength + +solver = Solver() + +# Create custom strength +mmedium = strength.create(0, 1, 0, 1.25) +smedium = strength.create(0, 100, 0) + +# Create the variable +left = Variable('left') +height = Variable('height') +top = Variable('top') +width = Variable('width') +contents_top = Variable('contents_top') +contents_bottom = Variable('contents_bottom') +contents_left = Variable('contents_left') +contents_right = Variable('contents_right') +midline = Variable('midline') +ctleft = Variable('ctleft') +ctheight = Variable('ctheight') +cttop = Variable('cttop') +ctwidth = Variable('ctwidth') +lb1left = Variable('lb1left') +lb1height = Variable('lb1height') +lb1top = Variable('lb1top') +lb1width = Variable('lb1width') +lb2left = Variable('lb2left') +lb2height = Variable('lb2height') +lb2top = Variable('lb2top') +lb2width = Variable('lb2width') +lb3left = Variable('lb3left') +lb3height = Variable('lb3height') +lb3top = Variable('lb3top') +lb3width = Variable('lb3width') +fl1left = Variable('fl1left') +fl1height = Variable('fl1height') +fl1top = Variable('fl1top') +fl1width = Variable('fl1width') +fl2left = Variable('fl2left') +fl2height = Variable('fl2height') +fl2top = Variable('fl2top') +fl2width = Variable('fl2width') +fl3left = Variable('fl3left') +fl3height = Variable('fl3height') +fl3top = Variable('fl3top') +fl3width = Variable('fl3width') + +# Add the edit variables +solver.addEditVariable(width, 'strong') +solver.addEditVariable(height, 'strong') + +# Add the constraints +for c in [(left + -0 >= 0) | "required", + (height + 0 == 0) | "medium", + (top + -0 >= 0) | "required", + (width + -0 >= 0) | "required", + (height + -0 >= 0) | "required", + (- top + contents_top + -10 == 0) | "required", + (lb3height + -16 == 0) | "strong", + (lb3height + -16 >= 0) | "strong", + (ctleft + -0 >= 0) | "required", + (cttop + -0 >= 0) | "required", + (ctwidth + -0 >= 0) | "required", + (ctheight + -0 >= 0) | "required", + (fl3left + -0 >= 0) | "required", + (ctheight + -24 >= 0) | smedium, + (ctwidth + -1.67772e+07 <= 0) | smedium, + (ctheight + -24 <= 0) | smedium, + (fl3top + -0 >= 0) | "required", + (fl3width + -0 >= 0) | "required", + (fl3height + -0 >= 0) | "required", + (lb1width + -67 == 0) | "weak", + (lb2width + -0 >= 0) | "required", + (lb2height + -0 >= 0) | "required", + (fl2height + -0 >= 0) | "required", + (lb3left + -0 >= 0) | "required", + (fl2width + -125 >= 0) | "strong", + (fl2height + -21 == 0) | "strong", + (fl2height + -21 >= 0) | "strong", + (lb3top + -0 >= 0) | "required", + (lb3width + -0 >= 0) | "required", + (fl1left + -0 >= 0) | "required", + (fl1width + -0 >= 0) | "required", + (lb1width + -67 >= 0) | "strong", + (fl2left + -0 >= 0) | "required", + (lb2width + -66 == 0) | "weak", + (lb2width + -66 >= 0) | "strong", + (lb2height + -16 == 0) | "strong", + (fl1height + -0 >= 0) | "required", + (fl1top + -0 >= 0) | "required", + (lb2top + -0 >= 0) | "required", + (- lb2top + lb3top + - lb2height + -10 == 0) | mmedium, + (- lb3top + - lb3height + fl3top + -10 >= 0) | "required", + (- lb3top + - lb3height + fl3top + -10 == 0) | mmedium, + (contents_bottom + - fl3height + - fl3top + -0 == 0) | mmedium, + (fl1top + - contents_top + 0 >= 0) | "required", + (fl1top + - contents_top + 0 == 0) | mmedium, + (contents_bottom + - fl3height + - fl3top + -0 >= 0) | "required", + (- left + - width + contents_right + 10 == 0) | "required", + (- top + - height + contents_bottom + 10 == 0) | "required", + (- left + contents_left + -10 == 0) | "required", + (lb3left + - contents_left + 0 == 0) | mmedium, + (fl1left + - midline + 0 == 0) | "strong", + (fl2left + - midline + 0 == 0) | "strong", + (ctleft + - midline + 0 == 0) | "strong", + (fl1top + 0.5 * fl1height + - lb1top + -0.5 * lb1height + 0 == 0) | "strong", + (lb1left + - contents_left + 0 >= 0) | "required", + (lb1left + - contents_left + 0 == 0) | mmedium, + (- lb1left + fl1left + - lb1width + -10 >= 0) | "required", + (- lb1left + fl1left + - lb1width + -10 == 0) | mmedium, + (- fl1left + contents_right + - fl1width + -0 >= 0) | "required", + (width + 0 == 0) | "medium", + (- fl1top + fl2top + - fl1height + -10 >= 0) | "required", + (- fl1top + fl2top + - fl1height + -10 == 0) | mmedium, + (cttop + - fl2top + - fl2height + -10 >= 0) | "required", + (- ctheight + - cttop + fl3top + -10 >= 0) | "required", + (contents_bottom + - fl3height + - fl3top + -0 >= 0) | "required", + (cttop + - fl2top + - fl2height + -10 == 0) | mmedium, + (- fl1left + contents_right + - fl1width + -0 == 0) | mmedium, + (- lb2top + -0.5 * lb2height + fl2top + 0.5 * fl2height + 0 == 0) | "strong", + (- contents_left + lb2left + 0 >= 0) | "required", + (- contents_left + lb2left + 0 == 0) | mmedium, + (fl2left + - lb2width + - lb2left + -10 >= 0) | "required", + (- ctheight + - cttop + fl3top + -10 == 0) | mmedium, + (contents_bottom + - fl3height + - fl3top + -0 == 0) | mmedium, + (lb1top + -0 >= 0) | "required", + (lb1width + -0 >= 0) | "required", + (lb1height + -0 >= 0) | "required", + (fl2left + - lb2width + - lb2left + -10 == 0) | mmedium, + (- fl2left + - fl2width + contents_right + -0 == 0) | mmedium, + (- fl2left + - fl2width + contents_right + -0 >= 0) | "required", + (lb3left + - contents_left + 0 >= 0) | "required", + (lb1left + -0 >= 0) | "required", + (0.5 * ctheight + cttop + - lb3top + -0.5 * lb3height + 0 == 0) | "strong", + (ctleft + - lb3left + - lb3width + -10 >= 0) | "required", + (- ctwidth + - ctleft + contents_right + -0 >= 0) | "required", + (ctleft + - lb3left + - lb3width + -10 == 0) | mmedium, + (fl3left + - contents_left + 0 >= 0) | "required", + (fl3left + - contents_left + 0 == 0) | mmedium, + (- ctwidth + - ctleft + contents_right + -0 == 0) | mmedium, + (- fl3left + contents_right + - fl3width + -0 == 0) | mmedium, + (- contents_top + lb1top + 0 >= 0) | "required", + (- contents_top + lb1top + 0 == 0) | mmedium, + (- fl3left + contents_right + - fl3width + -0 >= 0) | "required", + (lb2top + - lb1top + - lb1height + -10 >= 0) | "required", + (- lb2top + lb3top + - lb2height + -10 >= 0) | "required", + (lb2top + - lb1top + - lb1height + -10 == 0) | mmedium, + (fl1height + -21 == 0) | "strong", + (fl1height + -21 >= 0) | "strong", + (lb2left + -0 >= 0) | "required", + (lb2height + -16 >= 0) | "strong", + (fl2top + -0 >= 0) | "required", + (fl2width + -0 >= 0) | "required", + (lb1height + -16 >= 0) | "strong", + (lb1height + -16 == 0) | "strong", + (fl3width + -125 >= 0) | "strong", + (fl3height + -21 == 0) | "strong", + (fl3height + -21 >= 0) | "strong", + (lb3height + -0 >= 0) | "required", + (ctwidth + -119 >= 0) | smedium, + (lb3width + -24 == 0) | "weak", + (lb3width + -24 >= 0) | "strong", + (fl1width + -125 >= 0) | "strong", + ]: + solver.addConstraint(c) + + +def bench_update_variables(loops, solver): + """Suggest new values and update variables. + + This mimic the use of kiwi in enaml in the case of a resizing. + + """ + t0 = perf.perf_counter() + for w, h in [(400, 600), (600, 400), (800, 1200), (1200, 800), + (400, 800), (800, 400)]*loops: + solver.suggestValue(width, w) + solver.suggestValue(height, h) + solver.updateVariables() + + return perf.perf_counter() - t0 + + +runner = perf.Runner() +runner.bench_time_func('kiwi.suggestValue', bench_update_variables, + solver, inner_loops=1) diff --git a/docs/source/use_cases/enaml.rst b/docs/source/use_cases/enaml.rst index b534e449..f6486fad 100644 --- a/docs/source/use_cases/enaml.rst +++ b/docs/source/use_cases/enaml.rst @@ -82,7 +82,9 @@ widgets should be in contact. When generating the constraints, `hbox` will be passed the container and use the spacers to generate the constraints by simply glueing the anchors of surrounding widgets. Each spacer can generate multiple constraints which gives -this process a lot of flexibility. +this process a lot of flexibility. Furthermore, those helpers define the same +variable as the widgets allowing for to position groups with respect to one +another. .. note:: diff --git a/kiwi/AssocVector.h b/kiwi/AssocVector.h index 74f69ad2..e4c5903c 100644 --- a/kiwi/AssocVector.h +++ b/kiwi/AssocVector.h @@ -1,4955 +1,356 @@ -/* - * Copyright (C) 2012 �ukasz Czerwi�ski - * - * GitHub: https://github.com/wo3kie/AssocVector - * Website: http://www.lukaszczerwinski.pl/assoc_vector.en.html - * - * Distributed under the BSD Software License (see file license) - */ +//////////////////////////////////////////////////////////////////////////////// +// The Loki Library +// Copyright (c) 2001 by Andrei Alexandrescu +// This code accompanies the book: +// Alexandrescu, Andrei. "Modern C++ Design: Generic Programming and Design +// Patterns Applied". Copyright (c) 2001. Addison-Wesley. +// Permission to use, copy, modify, distribute and sell this software for any +// purpose is hereby granted without fee, provided that the above copyright +// notice appear in all copies and that both that copyright notice and this +// permission notice appear in supporting documentation. +// The author or Addison-Wesley Longman make no representations about the +// suitability of this software for any purpose. It is provided "as is" +// without express or implied warranty. +//////////////////////////////////////////////////////////////////////////////// +// Updated 2019 by Matthieu Dartiialh for C++11 compliancy +//////////////////////////////////////////////////////////////////////////////// +#pragma once + +// $Id: AssocVector.h 765 2006-10-18 13:55:32Z syntheticpp $ -#ifndef ASSOC_VECTOR_HPP -#define ASSOC_VECTOR_HPP - -#ifndef __GXX_EXPERIMENTAL_CXX0X__ - #error C++11 is required to run this code, please use AssocVector 1.0.x instead. -#endif - -// includes.begin #include #include -#include -#include - -#include -#include - -// includes.end - -// configuration.begin - -#if ( __GNUC__ >= 4 && __GNUC_MINOR__ >= 8 ) - #define AV_HAS_TRIVIAL_DESTRUCTOR( type ) std::is_trivially_destructible< type >::value - #define AV_MOVE_IF_NOEXCEPT std::move_if_noexcept -#else - #define AV_HAS_TRIVIAL_DESTRUCTOR( type ) __has_trivial_destructor( type ) - #define AV_MOVE_IF_NOEXCEPT std::move -#endif - -// configuration.end - -#ifndef NDEBUG - #define AV_DEBUG -#endif - -#ifdef AV_DEBUG - #define AV_PRECONDITION( condition ) if( (bool)( condition ) == false ){int*i=0;*i=0;} - #define AV_CHECK( condition ) if( (bool)( condition ) == false ){int*i=0;*i=0;} - #define AV_POSTCONDITION( condition ) if( (bool)( condition ) == false ){int*i=0;*i=0;} - #define AV_ERROR() if( (true) ){int*i=0;*i=0;} -#else - #define AV_PRECONDITION( condition ) (void)( 0 ); - #define AV_CHECK( condition ) (void)( 0 ); - #define AV_POSTCONDITION( condition ) (void)( 0 ); - #define AV_ERROR() (void)( 0 ); -#endif - -namespace util -{ - // - // CmpByFirst - // - template< typename _Pair, typename _Cmp > - struct CmpByFirst - { - CmpByFirst( _Cmp const & cmp = _Cmp() ) - : _cmp( cmp ) - { - } - - bool operator()( _Pair const & lhs, _Pair const & rhs )const - { - return _cmp( lhs.first, rhs.first ); - } - - bool operator()( _Pair const & pair, typename _Pair::first_type const & value )const - { - return _cmp( pair.first, value ); - } - - bool operator()( typename _Pair::first_type const & value, _Pair const & pair )const - { - return _cmp( value, pair.first ); - } - - private: - _Cmp _cmp; - }; -} - -namespace util -{ - // - // equal - // - template< - typename _T1 - , typename _T2 - > - inline bool equal( - _T1 const & first - , _T2 const & second - ) - { - if( first < second ){ - return false; - } - - if( second < first ){ - return false; - } +#include +#include - return true; - } - - // - // less_equal - // - template< - typename _T1 - , typename _T2 - > - inline bool less_equal( - _T1 const & first - , _T2 const & second - ) - { - return ( second < first ) == false; - } -} - -namespace util +namespace Loki { - // - // is_between - // - template< - typename _T1 - , typename _T2 - > - inline bool is_between( - _T1 const & first - , _T2 const & value - , _T1 const & last - ) - { - AV_PRECONDITION( less_equal( first, last ) ); +//////////////////////////////////////////////////////////////////////////////// +// class template AssocVectorCompare +// Used by AssocVector +//////////////////////////////////////////////////////////////////////////////// - return ( value < first ) == false && ( last < value ) == false; - } -} - -namespace util -{ - // - // destroy_range - // - namespace detail + namespace Private { - template< bool _HasTrivialDestructor > - struct DestroyRangeImpl - { - }; - - template<> - struct DestroyRangeImpl< true > - { - template< typename _Ptr > - static - void destroy( _Ptr, _Ptr ) - { - } - }; - - template<> - struct DestroyRangeImpl< false > + template + class AssocVectorCompare : public C { - template< typename _Ptr > - static - void destroy( _Ptr first, _Ptr const last ) - { - AV_PRECONDITION( less_equal( first, last ) ); - - typedef typename std::iterator_traits< _Ptr >::value_type T; - - for( /*empty*/ ; first != last ; ++ first ){ - first -> T::~T(); - } - } - }; - } - - template< typename _Ptr > - inline void destroy_range( - _Ptr first - , _Ptr const last - ) - { - typedef typename std::iterator_traits< _Ptr >::value_type T; - - detail::DestroyRangeImpl< AV_HAS_TRIVIAL_DESTRUCTOR( T ) >::destroy( first, last ); - } -} - -namespace util -{ - // - // move - // - template< - typename _InputPtr - , typename _OutputPtr - > - inline void move( - _InputPtr first - , _InputPtr last - , _OutputPtr first2 - ) - { - if( first < first2 ){ - std::move_backward( first, last, first2 + ( last - first ) ); - } - else if( first > first2 ){ - std::move( first, last, first2 ); - } - else{ - // first == first2 -> do nothing - } - } - - template< - typename _InputPtr - , typename _OutputPtr - > - inline void copy( - _InputPtr first - , _InputPtr last - , _OutputPtr first2 - ) - { - if( first < first2 ){ - std::copy_backward( first, last, first2 + ( last - first ) ); - } - else if( first > first2 ){ - std::copy( first, last, first2 ); - } - else{ - // first == first2 -> do nothing - } - } + typedef std::pair + Data; + typedef typename C::first_argument_type first_argument_type; + public: + AssocVectorCompare() + {} + AssocVectorCompare(const C& src) : C(src) + {} - namespace detail - { + bool operator()(const first_argument_type& lhs, + const first_argument_type& rhs) const + { return C::operator()(lhs, rhs); } - template< bool _MoveDoesNotThrow > - struct MoveIfNoExcept - { - }; + bool operator()(const Data& lhs, const Data& rhs) const + { return operator()(lhs.first, rhs.first); } - template<> - struct MoveIfNoExcept< true > - { - template< - typename _InputPtr - , typename _OutputPtr - > - static - void move( _InputPtr first, _InputPtr const last, _OutputPtr first2 ) - { - move( first, last, first2 ); - } - }; + bool operator()(const Data& lhs, + const first_argument_type& rhs) const + { return operator()(lhs.first, rhs); } - template<> - struct MoveIfNoExcept< false > - { - template< - typename _InputPtr - , typename _OutputPtr - > - static - void move( _InputPtr first, _InputPtr const last, _OutputPtr first2 ) - { - copy( first, last, first2 ); - } + bool operator()(const first_argument_type& lhs, + const Data& rhs) const + { return operator()(lhs, rhs.first); } }; } - template< - typename _InputPtr - , typename _OutputPtr - > - inline void move_if_noexcept( - _InputPtr first - , _InputPtr const last - , _OutputPtr first2 - ) - { - typedef typename std::iterator_traits< _InputPtr >::value_type T; - - detail::MoveIfNoExcept< true >::move( first, last, first2 ); - } - -} - -namespace util -{ - -template< - typename _Iterator - , typename _T - , typename _Cmp -> -_Iterator -last_less_equal( - _Iterator first - , _Iterator last - , _T const & t - , _Cmp cmp -) -{ - AV_PRECONDITION( less_equal( first, last ) ); - - if( first == last ){ - return last; - } - - _Iterator greaterEqual = std::lower_bound( first, last, t, cmp ); - - if( greaterEqual != last ) - { - // lower_bound returns first greater_than/equal_to but we need last less_than - - bool const isEqual = cmp( t, * greaterEqual ) == false; - - if( isEqual ) - { - // that is OK, request item was found - return greaterEqual; - } - } - - if( greaterEqual == first ) - { - // requested item does not exist in container - - // 6 8 10 13 17 19 20 21 22 24 end - // ^ lower_bound( 1 ): - // requested: ^ - - return last; - } - else - { - // we need to go one item backward - - // 6 8 10 13 17 19 20 21 22 24 - // lower_bound( 23 ): ^ - // requested: ^ - - return -- greaterEqual; - } -} - -} - -namespace util -{ - -template< - typename _InputIterator1 - , typename _InputIterator2 - , typename _Cmp -> -bool has_intersection( - _InputIterator1 first1 - , _InputIterator1 const last1 - , _InputIterator2 first2 - , _InputIterator2 const last2 - , _Cmp const & cmp -) -{ - while( first1 != last1 && first2 != last2 ) - { - if( cmp( * first1, * first2 ) ){ - ++first1; - } - else if( cmp( * first2, * first1 ) ){ - ++first2; - } - else{ - return true; - } - } - - return false; -} - -} - -namespace array -{ - // - // I need much more control over Array inside AssocVector than std::vector offers - // - - // C' style array with some useful methods and functions - - // - // Array - // - template< - typename _T - , typename _Alloc = std::allocator< _T > +//////////////////////////////////////////////////////////////////////////////// +// class template AssocVector +// An associative vector built as a syntactic drop-in replacement for std::map +// BEWARE: AssocVector doesn't respect all map's guarantees, the most important +// being: +// * iterators are invalidated by insert and erase operations +// * the complexity of insert/erase is O(N) not O(log N) +// * value_type is std::pair not std::pair +// * iterators are random +//////////////////////////////////////////////////////////////////////////////// + + + template + < + class K, + class V, + class C = std::less, + class A = std::allocator< std::pair > > - struct ArrayBase + class AssocVector + : private std::vector< std::pair, A > + , private Private::AssocVectorCompare { - struct ArrayImpl - : public _Alloc - { - ArrayImpl( _Alloc const & alloc = _Alloc() ) - : _Alloc( alloc ) - , _data( nullptr ) - , _capacity( 0 ) - , _size( 0 ) - { - } - - ArrayImpl( std::size_t capacity, _Alloc const & alloc = _Alloc() ) - : _Alloc( alloc ) - , _data( capacity ? this->allocate( capacity ) : nullptr ) - , _capacity( capacity ) - , _size( 0 ) - { - AV_PRECONDITION( _capacity < this->max_size() ); - } - - ~ArrayImpl() - { - util::destroy_range( _data, _data + _size ); - - this->deallocate( _data, _capacity ); - } + typedef std::vector, A> Base; + typedef Private::AssocVectorCompare MyCompare; - void swap( ArrayImpl & other )noexcept - { - std::swap( _data, other._data ); - std::swap( _capacity, other._capacity ); - std::swap( _size, other._size ); - } + public: + typedef K key_type; + typedef V mapped_type; + typedef typename Base::value_type value_type; + + typedef C key_compare; + typedef A allocator_type; + typedef typename A::reference reference; + typedef typename A::const_reference const_reference; + typedef typename Base::iterator iterator; + typedef typename Base::const_iterator const_iterator; + typedef typename Base::size_type size_type; + typedef typename Base::difference_type difference_type; + typedef typename A::pointer pointer; + typedef typename A::const_pointer const_pointer; + typedef typename Base::reverse_iterator reverse_iterator; + typedef typename Base::const_reverse_iterator const_reverse_iterator; + + class value_compare + : public std::function + , private key_compare + { + friend class AssocVector; + + protected: + value_compare(key_compare pred) : key_compare(pred) + {} public: - _T * _data; - - std::size_t _capacity; - std::size_t _size; + bool operator()(const value_type& lhs, const value_type& rhs) const + { return key_compare::operator()(lhs.first, rhs.first); } }; - typedef _Alloc allocator_type; + // 23.3.1.1 construct/copy/destroy - allocator_type - get_allocator() const { - return * static_cast< allocator_type const * >( & this->_impl ); - } + explicit AssocVector(const key_compare& comp = key_compare(), + const A& alloc = A()) + : Base(alloc), MyCompare(comp) + {} - public: - ArrayBase( _Alloc const & alloc = _Alloc() ) - : _impl( alloc ) + template + AssocVector(InputIterator first, InputIterator last, + const key_compare& comp = key_compare(), + const A& alloc = A()) + : Base(first, last, alloc), MyCompare(comp) { + MyCompare& me = *this; + std::sort(begin(), end(), me); } - ArrayBase( std::size_t capacity, _Alloc const & alloc = _Alloc() ) - : _impl( capacity, alloc ) + AssocVector& operator=(const AssocVector& rhs) { + AssocVector(rhs).swap(*this); + return *this; } - ~ArrayBase() = default; - - ArrayBase( ArrayBase const & other ) = delete; - ArrayBase( ArrayBase && other ) = delete; - - ArrayBase & operator=( ArrayBase const & other ) = delete; - ArrayBase & operator=( ArrayBase && other ) = delete; - - protected: - ArrayImpl _impl; - }; - - template< - typename _T - , typename _Alloc = std::allocator< _T > - > - struct Array - : protected ArrayBase< _T, _Alloc > - { - private: - typedef ArrayBase< _T, _Alloc > Base; - - public: - using Base::get_allocator; + // iterators: + // The following are here because MWCW gets 'using' wrong + iterator begin() { return Base::begin(); } + const_iterator begin() const { return Base::begin(); } + iterator end() { return Base::end(); } + const_iterator end() const { return Base::end(); } + reverse_iterator rbegin() { return Base::rbegin(); } + const_reverse_iterator rbegin() const { return Base::rbegin(); } + reverse_iterator rend() { return Base::rend(); } + const_reverse_iterator rend() const { return Base::rend(); } - public: - typedef _T value_type; - - typedef _T * iterator; - typedef _T const * const_iterator; + // capacity: + bool empty() const { return Base::empty(); } + size_type size() const { return Base::size(); } + size_type max_size() { return Base::max_size(); } - public: - Array( _Alloc const & alloc = _Alloc() ) - : Base( alloc ) - { - } + // 23.3.1.2 element access: + mapped_type& operator[](const key_type& key) + { return insert(value_type(key, mapped_type())).first->second; } - Array( std::size_t capacity, _Alloc const & alloc = _Alloc() ) - : Base( capacity, alloc ) + // modifiers: + std::pair insert(const value_type& val) { - } + bool found(true); + iterator i(lower_bound(val.first)); - Array( Array const & other ) - // In std::vector new vector's capacity is equal to old vector's size. - // Array's capacity is equal to old array's capacity to ensure invariant that: - // sqrt( storage.capacity ) == buffer.capacity - // sqrt( storage.capacity ) == erased.capacity - : Base( other.capacity(), other.get_allocator() ) - { - for( /*empty*/ ; this->_impl._size < other.size() ; ++ this->_impl._size ){ - get_allocator().construct( - this->_impl._data + this->_impl._size - , * ( other._impl._data + this->_impl._size ) - ); + if (i == end() || this->operator()(val.first, i->first)) + { + i = Base::insert(i, val); + found = false; } + return std::make_pair(i, !found); } - - Array( Array && other ) - : Base( 0, other.get_allocator() ) - { - this->_impl.swap( other._impl ); - } - - ~Array() = default; - - Array & operator=( Array const & other ) - { - Array temp( other ); - - swap( temp ); - - return * this; - } - - Array & operator=( Array && other ) - { - this->_impl.swap( other._impl ); - - return * this; - } - - void swap( Array & other )noexcept - { - this->_impl.swap( other._impl ); - } - - void reserve( std::size_t capacity ) + //Section [23.1.2], Table 69 + //http://developer.apple.com/documentation/DeveloperTools/gcc-3.3/libstdc++/23_containers/howto.html#4 + iterator insert(iterator pos, const value_type& val) { - if( get_allocator().max_size() < capacity ){ - throw std::length_error( "Array::reserve" ); - } - - if( capacity <= getCapacity() ){ - return; + if( (pos == begin() || this->operator()(*(pos-1),val)) && + (pos == end() || this->operator()(val, *pos)) ) + { + return Base::insert(pos, val); } - - Array< _T, _Alloc > temp( capacity, get_allocator() ); - - std::uninitialized_copy( - std::make_move_iterator( begin() ) - , std::make_move_iterator( end() ) - , temp.begin() - ); - - swap( temp ); - } - - iterator begin()noexcept - { - return getData(); - } - - const_iterator begin()const noexcept - { - return getData(); - } - - const_iterator cbegin()const noexcept - { - return getData(); - } - - iterator end()noexcept - { - return getData() + getSize(); - } - - const_iterator end()const noexcept - { - return getData() + getSize(); - } - - const_iterator cend()const noexcept - { - return getData() + getSize(); + return insert(val).first; } - bool empty()const noexcept - { - return getSize() == 0; - } + template + void insert(InputIterator first, InputIterator last) + { for (; first != last; ++first) insert(*first); } - bool full()const noexcept - { - return size() == capacity(); - } + void erase(iterator pos) + { Base::erase(pos); } - std::size_t size()const noexcept + size_type erase(const key_type& k) { - return this->_impl._size; + iterator i(find(k)); + if (i == end()) return 0; + erase(i); + return 1; } - std::size_t getSize()const noexcept - { - return size(); - } + void erase(iterator first, iterator last) + { Base::erase(first, last); } - void setSize( std::size_t newSize ) noexcept + void swap(AssocVector& other) { - AV_PRECONDITION( getData() != 0 || newSize == 0 ); - - this->_impl._size = newSize; + Base::swap(other); + MyCompare& me = *this; + MyCompare& rhs = other; + std::swap(me, rhs); } - std::size_t capacity()const noexcept - { - return this->_impl._capacity; - } + void clear() + { Base::clear(); } - std::size_t getCapacity()const noexcept - { - return capacity(); - } + // observers: + key_compare key_comp() const + { return *this; } - value_type & front() noexcept + value_compare value_comp() const { - AV_PRECONDITION( getData() != 0 ); - AV_PRECONDITION( empty() == false ); - - return getData()[ 0 ]; + const key_compare& comp = *this; + return value_compare(comp); } - value_type const & front()const noexcept + // 23.3.1.3 map operations: + iterator find(const key_type& k) { - AV_PRECONDITION( getData() != 0 ); - AV_PRECONDITION( empty() == false ); - - return getData()[ 0 ]; + iterator i(lower_bound(k)); + if (i != end() && this->operator()(k, i->first)) + { + i = end(); + } + return i; } - value_type & back() noexcept + const_iterator find(const key_type& k) const { - AV_PRECONDITION( getData() != 0 ); - AV_PRECONDITION( empty() == false ); - - return getData()[ getSize() - 1 ]; + const_iterator i(lower_bound(k)); + if (i != end() && this->operator()(k, i->first)) + { + i = end(); + } + return i; } - value_type const & back()const noexcept - { - AV_PRECONDITION( getData() != 0 ); - AV_PRECONDITION( empty() == false ); - - return getData()[ getSize() - 1 ]; - } + size_type count(const key_type& k) const + { return find(k) != end(); } - _T const * data()const noexcept + iterator lower_bound(const key_type& k) { - return this->_impl._data; + MyCompare& me = *this; + return std::lower_bound(begin(), end(), k, me); } - _T const * getData()const noexcept + const_iterator lower_bound(const key_type& k) const { - return data(); + const MyCompare& me = *this; + return std::lower_bound(begin(), end(), k, me); } - _T * data() noexcept + iterator upper_bound(const key_type& k) { - return this->_impl._data; + MyCompare& me = *this; + return std::upper_bound(begin(), end(), k, me); } - _T * getData() noexcept + const_iterator upper_bound(const key_type& k) const { - return data(); + const MyCompare& me = *this; + return std::upper_bound(begin(), end(), k, me); } - value_type & operator[]( std::size_t index ) noexcept + std::pair equal_range(const key_type& k) { - AV_PRECONDITION( getData() != 0 ); - AV_PRECONDITION( index < size() ); - - return getData()[ index ]; + MyCompare& me = *this; + return std::equal_range(begin(), end(), k, me); } - value_type const & operator[]( std::size_t index )const noexcept + std::pair equal_range( + const key_type& k) const { - AV_PRECONDITION( getData() != 0 ); - AV_PRECONDITION( index < size() ); - - return getData()[ index ]; + const MyCompare& me = *this; + return std::equal_range(begin(), end(), k, me); } - template< typename _T2 > - void - insert( - typename Array< _T >::iterator const pos - , _T2 && t - ) - { - AV_PRECONDITION( util::less_equal( size() + 1, capacity() ) ); - AV_PRECONDITION( util::is_between( begin(), pos, end() ) ); - - iterator const oldEnd = end(); - - get_allocator().construct( end() ); - setSize( getSize() + 1 ); - - if( pos != oldEnd ) - { - util::move( pos, oldEnd, pos + 1 ); - } - - * pos = AV_MOVE_IF_NOEXCEPT( t ); - } + template + friend bool operator==(const AssocVector& lhs, + const AssocVector& rhs); - // Array::push_back is not implemented to ensure invariant that: - // sqrt( storage.capacity ) == buffer.capacity - // sqrt( storage.capacity ) == erased.capacity - template< typename __T2 > - void place_back( __T2 && value ) + bool operator<(const AssocVector& rhs) const { - AV_CHECK( getData() ); - AV_CHECK( capacity() > 0 ); - AV_CHECK( getSize() < capacity() ); - - get_allocator().construct( end(), std::forward< __T2 >( value ) ); - setSize( getSize() + 1 ); + const Base& me = *this; + const Base& yo = rhs; + return me < yo; } - void - erase( typename Array< _T >::iterator pos ) - { - AV_PRECONDITION( empty() == false ); - AV_PRECONDITION( util::less_equal( begin(), pos ) ); - AV_PRECONDITION( pos < end() ); - - util::move( pos + 1, end(), pos ); - get_allocator().destroy( end() - 1 ); - setSize( getSize() - 1 ); - } + template + friend bool operator!=(const AssocVector& lhs, + const AssocVector& rhs); - private: - void setCapacity( std::size_t newCapacity ) noexcept - { - AV_PRECONDITION( getData() != 0 || newCapacity == 0 ); + template + friend bool operator>(const AssocVector& lhs, + const AssocVector& rhs); - this->_impl._capacity = newCapacity; - } + template + friend bool operator>=(const AssocVector& lhs, + const AssocVector& rhs); - void setData( _T * t ) noexcept - { - this->_impl._data = t; - } + template + friend bool operator<=(const AssocVector& lhs, + const AssocVector& rhs); }; -} -namespace array -{ - template< - typename _Iterator - , typename _T - , typename _Cmp - > - _Iterator - binary_search( - _Iterator first - , _Iterator last - , _T const & t - , _Cmp cmp - ) + template + inline bool operator==(const AssocVector& lhs, + const AssocVector& rhs) { - AV_PRECONDITION( util::less_equal( first, last ) ); - - _Iterator const greaterEqual = std::lower_bound( first, last, t, cmp ); - - if( greaterEqual == last ){ - return last; - } - - bool const isEqual = cmp( t, * greaterEqual ) == false; - - if( isEqual ){ - return greaterEqual; - } - - return last; + const std::vector, A>& me = lhs; + return me == rhs; } - template< - typename _T - , typename _T2 - , typename _Cmp - > - std::pair< typename Array< _T >::iterator, bool > - insert_in_sorted( - Array< _T > & array - , _T2 && t - , _Cmp cmp - ) - { - AV_PRECONDITION( util::less_equal( array.size() + 1, array.capacity() ) ); + template + inline bool operator!=(const AssocVector& lhs, + const AssocVector& rhs) + { return !(lhs == rhs); } - typename Array< _T >::iterator const greaterEqual - = std::lower_bound( array.begin(), array.end(), t, cmp ); + template + inline bool operator>(const AssocVector& lhs, + const AssocVector& rhs) + { return rhs < lhs; } - if( greaterEqual != array.end() ) - { - bool const isEqual = cmp( t, * greaterEqual ) == false; + template + inline bool operator>=(const AssocVector& lhs, + const AssocVector& rhs) + { return !(lhs < rhs); } - if( isEqual ){ - return std::make_pair( greaterEqual, false ); - } - } - - array.insert( greaterEqual, std::forward< _T2 >( t ) ); - - return std::make_pair( greaterEqual, true ); - } - - template< typename _T > - void - erase_removed( - array::Array< _T > & storage - , array::Array< typename array::Array< _T >::const_iterator > const & erased - ) - { - AV_PRECONDITION( util::less_equal( erased.size(), storage.size() ) ); - - if( erased.empty() ){ - return; - } - - typedef typename array::Array< _T >::const_iterator StorageConstIterator; - typedef typename array::Array< _T >::iterator StorageIterator; - typedef typename array::Array< StorageConstIterator >::const_iterator ErasedConstIterator; - - StorageIterator currentInStorage = const_cast< StorageIterator >( erased.front() ); - AV_CHECK( util::is_between( storage.begin(), currentInStorage, storage.end() ) ); - - StorageIterator const endInStorage = storage.end(); - - StorageIterator whereInsertInStorage = const_cast< StorageIterator >( erased.front() ); - AV_CHECK( util::is_between( storage.begin(), whereInsertInStorage, storage.end() ) ); - - ErasedConstIterator currentInErased = erased.begin(); - ErasedConstIterator const endInErased = erased.end(); - - while( currentInStorage != endInStorage ) - { - AV_CHECK( util::is_between( storage.begin(), whereInsertInStorage, storage.end() ) ); - - if( - currentInErased != endInErased - && currentInStorage == ( * currentInErased ) - ) - { - ++ currentInStorage; - ++ currentInErased; - } - else - { - ( * whereInsertInStorage ) = AV_MOVE_IF_NOEXCEPT( * currentInStorage ); - - ++ whereInsertInStorage; - ++ currentInStorage; - } - } - - AV_POSTCONDITION( currentInErased == endInErased ); - - storage.setSize( storage.size() - erased.size() ); - } - - template< - typename _T - , typename _Cmp - > - void - move_merge( - array::Array< _T > & storage - , array::Array< _T > & buffer - , _Cmp const & cmp = _Cmp() - ) - { - AV_PRECONDITION( util::less_equal( storage.size() + buffer.size(), storage.capacity() ) ); - - typedef typename array::Array< _T >::iterator Iterator; - - Iterator rWhereInsertInStorage = storage.begin() + storage.size() + buffer.size() - 1; - - Iterator rCurrentInStorage = storage.begin() + storage.size() - 1; - Iterator const rEndInStorage = storage.begin() - 1; - - Iterator rCurrentInBuffer = buffer.begin() + buffer.size() - 1; - Iterator const rEndInBuffer = buffer.begin() - 1; - - std::size_t numberOfItemsToCreateByPlacementNew = buffer.size(); - - while( - rCurrentInBuffer != rEndInBuffer - && numberOfItemsToCreateByPlacementNew != 0 - ) - { - AV_CHECK( rWhereInsertInStorage != 0 ); - AV_CHECK( rCurrentInStorage != 0 ); - AV_CHECK( rCurrentInBuffer != 0 ); - - if( - rCurrentInStorage == rEndInStorage - || cmp( * rCurrentInStorage, * rCurrentInBuffer ) - ) - { - new ( static_cast< void * >( rWhereInsertInStorage ) ) - _T( AV_MOVE_IF_NOEXCEPT( * rCurrentInBuffer ) ); - - -- rCurrentInBuffer; - } - else - { - new ( static_cast< void * >( rWhereInsertInStorage ) ) - _T( AV_MOVE_IF_NOEXCEPT( * rCurrentInStorage ) ); - - -- rCurrentInStorage; - } - - -- numberOfItemsToCreateByPlacementNew; - -- rWhereInsertInStorage; - } - - AV_CHECK( numberOfItemsToCreateByPlacementNew == 0 ); - - while( rCurrentInBuffer != rEndInBuffer ) - { - AV_CHECK( rWhereInsertInStorage != 0 ); - AV_CHECK( rCurrentInStorage != 0 ); - AV_CHECK( rCurrentInBuffer != 0 ); - - if( - rCurrentInStorage == rEndInStorage - || cmp( * rCurrentInStorage, * rCurrentInBuffer ) - ) - { - * rWhereInsertInStorage = AV_MOVE_IF_NOEXCEPT( * rCurrentInBuffer ); - - -- rCurrentInBuffer; - } - else - { - * rWhereInsertInStorage = AV_MOVE_IF_NOEXCEPT( * rCurrentInStorage ); - - -- rCurrentInStorage; - } - - -- rWhereInsertInStorage; - } - - storage.setSize( storage.size() + buffer.size() ); - } -} - -namespace util -{ - -template< - typename _InputPtr1 - , typename _InputPtr2 - , typename _OutputPtr - , typename _Cmp -> -_OutputPtr -move_merge_into_uninitialized( - _InputPtr1 first1 - , _InputPtr1 last1 - , _InputPtr2 first2 - , _InputPtr2 last2 - , _OutputPtr output - , _Cmp cmp = _Cmp() -) -{ - AV_PRECONDITION( util::less_equal( first1, last1 ) ); - AV_PRECONDITION( util::less_equal( first2, last2 ) ); - - while( first1 != last1 && first2 != last2 ) - { - AV_CHECK( first1 != 0 ); - AV_CHECK( first2 != 0 ); - AV_CHECK( output != 0 ); - - if( cmp( * first1, * first2 ) ) - { - new ( static_cast< void * >( output ) ) - typename std::iterator_traits< _OutputPtr >::value_type( std::move( * first1 ) ); - - ++ output; - ++ first1; - } - else - { - new ( static_cast< void * >( output ) ) - typename std::iterator_traits< _OutputPtr >::value_type( std::move( * first2 ) ); - - ++ output; - ++ first2; - } - } - - if( first1 == last1 ){ - return std::uninitialized_copy( - std::make_move_iterator( first2 ) - , std::make_move_iterator( last2 ) - , output - ); - } - - if( first2 == last2 ){ - return std::uninitialized_copy( - std::make_move_iterator( first1 ) - , std::make_move_iterator( last1 ) - , output - ); - } - - return output; -} - -} - -namespace detail -{ - template< typename _Iterator > - bool equal( _Iterator const & lhs, _Iterator const & rhs ) - { - if( lhs.getContainer() != rhs.getContainer() ){ - return false; - } - - // for empty container returns that begin == end - // despite on fact they are not - if( lhs.getContainer()->empty() ){ - return true; - } - - return lhs.getCurrent() == rhs.getCurrent(); - } - - // - // AssocVectorLazyIterator - // - - template< - typename _Iterator - , typename _Container - > - struct AssocVectorLazyIterator - { - public: - typedef typename std::iterator_traits< _Iterator >::pointer pointer_mutable; - - public: - typedef std::bidirectional_iterator_tag iterator_category; - typedef typename std::iterator_traits< _Iterator >::value_type value_type; - typedef typename std::iterator_traits< _Iterator >::difference_type difference_type; - - // make key const - typedef std::pair< - typename value_type::first_type const - , typename value_type::second_type - > & reference; - - // make key const - typedef std::pair< - typename value_type::first_type const - , typename value_type::second_type - > * pointer; - - private: - struct _CurrentInErased - { - _CurrentInErased( typename _Container::_Erased::const_iterator current ) - : _current( current ) - { - } - - _CurrentInErased( _CurrentInErased const & other ) - : _current( other._current ) - { - } - - _CurrentInErased & operator=( _CurrentInErased const & other ) - { - _current = other._current; - - return * this; - } - - _CurrentInErased & operator=( typename _Container::_Erased::const_iterator current ) - { - _current = current; - - return * this; - } - - bool is_end( _Container const * container )const - { - return _current == container->erased().end(); - } - - bool is_not_end( _Container const * container )const - { - return ! is_end( container ); - } - - bool is_begin( _Container const * container )const - { - return _current == container->erased().begin(); - } - - bool is_not_begin( _Container const * container )const - { - return ! is_begin( container ); - } - - void increment( _Container const * container ) - { - AV_PRECONDITION( is_not_end( container ) ); - - ++ _current; - } - - void try_increment( _Container const * container ) - { - if( is_end( container ) ){ - return; - } - - increment( container ); - } - - void decrement( _Container const * container ) - { - AV_PRECONDITION( is_not_begin( container ) ); - - -- _current; - } - - void try_decrement( _Container const * container ) - { - if( is_begin( container ) ){ - return; - } - - decrement( container ); - } - - bool validate( _Container const * container )const - { - bool const result - = util::is_between( - container->erased().begin() - , _current - , container->erased().end() - ); - - if( result ){ - return true; - } - - AV_ERROR(); - - return false; - } - - typename _Container::_Erased::value_type const & - get( _Container const * container )const noexcept - { - AV_PRECONDITION( _current ); - AV_PRECONDITION( is_not_end( container ) ); - AV_PRECONDITION( validate( container ) ); - - return * _current; - } - - typename _Container::_Erased::const_iterator data()const noexcept - { - return _current; - } - - operator bool()const noexcept - { - return _current != 0; - } - - private: - typename _Container::_Erased::const_iterator _current; - }; - - struct _CurrentInBuffer - { - _CurrentInBuffer( pointer_mutable current ) - : _current( current ) - { - } - - _CurrentInBuffer( _CurrentInBuffer const & other ) - : _current( other._current ) - { - } - - _CurrentInBuffer & operator=( _CurrentInBuffer const & other ) - { - _current = other._current; - - return * this; - } - - _CurrentInBuffer & operator=( pointer_mutable current ) - { - _current = current; - - return * this; - } - - bool is_begin( _Container const * container )const - { - return _current == container->buffer().begin(); - } - - bool is_not_begin( _Container const * container )const - { - return ! is_begin( container ); - } - - bool is_end( _Container const * container )const - { - return _current == container->buffer().end(); - } - - bool is_not_end( _Container const * container )const - { - return ! is_end( container ); - } - - void increment( _Container const * container ) - { - AV_PRECONDITION( is_not_end( container ) ); - - ++ _current; - } - - void try_increment( _Container const * container ) - { - if( is_end( container ) ){ - return; - } - - increment( container ); - } - - void decrement( _Container const * container ) - { - AV_PRECONDITION( is_not_begin( container ) ); - - -- _current; - } - - void try_decrement( _Container const * container ) - { - if( is_begin( container ) ){ - return; - } - - decrement( container ); - } - - bool validate( _Container const * container )const - { - bool const result - = util::is_between( - container->buffer().begin() - , _current - , container->buffer().end() - ); - - if( result ){ - return true; - } - - AV_ERROR(); - - return false; - } - - typename _Container::_Storage::value_type const & - get( _Container const * container )const noexcept - { - AV_PRECONDITION( _current ); - AV_PRECONDITION( is_not_end( container ) ); - AV_PRECONDITION( validate( container ) ); - - return * _current; - } - - operator bool()const noexcept - { - return _current != 0; - } - - pointer_mutable data()const noexcept - { - return _current; - } - private: - pointer_mutable _current; - }; - - struct _CurrentInStorage - { - // begin is always set at first not erased item, marked with ^ - // end is always set at the end of container, marked with $ - - // Case 1: no item erased - // storage: ^1 3 5 7$ - // erased : - - // Case 2: item erased from front - // storage: 1 ^3 5 7$ - // erased : 1 - - // Case 3: item erased from back - // storage: ^1 3 5 7$ - // erased : 7 - - // Case 4: item erased from front and back - // storage: 1 ^3 5 7$ - // erased : 1 7 - - _CurrentInStorage( pointer_mutable current ) - : _dir( 1 ) - , _current( current ) - { - } - - _CurrentInStorage( _CurrentInStorage const & other ) - : _dir( other._dir ) - , _current( other._current ) - { - } - - _CurrentInStorage & operator=( _CurrentInStorage const & other ) - { - _dir = other._dir; - _current = other._current; - - return * this; - } - - bool operator==( _CurrentInStorage const & other )const - { - return _current == other._current; - } - - bool operator!=( _CurrentInStorage const & other )const - { - return _current != other._current; - } - - bool operator==( typename _Container::_Erased::value_type inErased )const - { - return _current == inErased; - } - - bool operator!=( typename _Container::_Erased::value_type inErased )const - { - return _current != inErased; - } - - bool is_begin( _Container const * container )const - { - _CurrentInStorage currentInStorage = const_cast< pointer_mutable >( container->storage().begin() ); - _CurrentInErased currentInErased = container->erased().begin(); - - currentInStorage.setOnNotErased( currentInErased, container ); - - return data() == currentInStorage.data(); - } - - bool is_not_begin( _Container const * container )const - { - return ! is_begin( container ); - } - - bool _is_begin( _Container const * container )const - { - return _current == container->storage().begin(); - } - - bool _is_not_begin( _Container const * container )const - { - return ! _is_begin( container ); - } - - bool is_end( _Container const * container )const - { - return _current == container->storage().end(); - } - - bool is_not_end( _Container const * container )const - { - return ! is_end( container ); - } - - void - increment( - _CurrentInErased & currentInErased - , _Container const * container - ) - { - AV_PRECONDITION( is_not_end( container ) ); - - increment( container ); - - if( _dir == -1 ) - { - _dir = 1; - currentInErased.try_increment( container ); - } - - setOnNotErased( currentInErased, container ); - } - - void - try_increment( - _CurrentInErased & currentInErased - , _Container const * container - ) - { - if( is_end( container ) ){ - return; - } - - increment( currentInErased, container ); - } - - void - decrement( - _CurrentInErased & currentInErased - , _Container const * container - ) - { - AV_PRECONDITION( is_not_begin( container ) ); - - decrement( container ); - - if( _dir == 1 ) - { - _dir = -1; - currentInErased.try_decrement( container ); - } - - setOnNotErasedBackward( currentInErased, container ); - } - - void - try_decrement( - _CurrentInErased & currentInErased - , _Container const * container - ) - { - if( _is_begin( container ) ){ - return; - } - - decrement( currentInErased, container ); - } - - void - setOnNotErased( - _CurrentInErased & currentInErased - , _Container const * container - ) - { - if( is_end( container ) ) - { - if( !currentInErased ) - { - currentInErased = container->erased().end(); - } - - return; - } - - if( !currentInErased ) - { - currentInErased = std::lower_bound( - container->erased().begin() - , container->erased().end() - , data() - , std::less< typename _Container::_Storage::const_iterator >() - ); - } - - if( _dir == -1 ) - { - _dir = 1; - currentInErased.try_increment( container ); - } - - while( - is_not_end( container ) - && currentInErased.is_not_end( container ) - && data() == currentInErased.get( container ) - ) - { - increment( container ); - currentInErased.increment( container ); - } - - AV_POSTCONDITION( currentInErased ); - - AV_POSTCONDITION( - ( - is_end( container ) - && currentInErased.is_end( container ) - ) - || currentInErased.is_end( container ) - || *this != currentInErased.get( container ) - ); - } - - void - setOnNotErasedBackward( - _CurrentInErased & currentInErased - , _Container const * container - ) - { - AV_CHECK( is_not_end( container ) ); - - if( _dir == 1 ) - { - _dir = -1; - currentInErased.try_decrement( container ); - } - - while( - is_not_begin( container ) - && currentInErased.is_not_end( container ) - && data() == currentInErased.get( container ) - ) - { - decrement( container ); - currentInErased.try_decrement( container ); - } - - AV_POSTCONDITION( validate( container ) ); - - AV_POSTCONDITION( - currentInErased.is_end( container ) - || *this != currentInErased.get( container ) - ); - } - - operator bool()const noexcept - { - return _current != 0; - } - - bool validate( _Container const * container )const - { - bool const result = util::is_between( - container->storage().begin() - , _current - , container->storage().end() - ); - - if( result ){ - return true; - } - - AV_ERROR(); - - return false; - } - - typename _Container::_Storage::value_type const & - get( _Container const * container )const noexcept - { - AV_PRECONDITION( _current ); - AV_PRECONDITION( is_not_end( container ) ); - - return * _current; - } - - pointer_mutable data()const noexcept - { - return _current; - } - - private: - void increment( _Container const * container ) - { - AV_PRECONDITION( is_not_end( container ) ); - - ++ _current; - } - - void decrement( _Container const * container ) - { - AV_PRECONDITION( is_not_begin( container ) ); - - -- _current; - } - - private: - int _dir; - - pointer_mutable _current; - }; - - struct _Current - { - _Current( pointer_mutable current ) - : _current( current ) - { - } - - _Current( _CurrentInStorage const & inStorage ) - : _current( inStorage.data() ) - { - } - - _Current( _CurrentInBuffer const & inBuffer ) - : _current( inBuffer.data() ) - { - } - - _Current & operator=( _Current const & other ) - { - _current = other._current; - - return * this; - } - - _Current & operator=( _CurrentInStorage const & inStorage ) - { - _current = inStorage.data(); - - return * this; - } - - _Current & operator=( _CurrentInBuffer const & inBuffer ) - { - _current = inBuffer.data(); - - return * this; - } - - bool operator==( _Current const & other )const - { - return _current == other._current; - } - - bool operator==( _CurrentInStorage const & inStorage )const - { - return _current == inStorage.data(); - } - - bool operator==( _CurrentInBuffer const & inBuffer )const - { - return _current == inBuffer.data(); - } - - bool validate( - _CurrentInStorage currentInStorage - , _CurrentInBuffer currentInBuffer - , _Container const * container - )const - { - AV_PRECONDITION( currentInStorage || currentInBuffer ); - AV_PRECONDITION( container != 0 ); - - if( !currentInStorage ) - { - if( _current == currentInBuffer.data() ){ - return true; - } - - AV_ERROR(); - - return false; - } - - if( !currentInBuffer ) - { - if( _current == currentInStorage.data() ){ - return true; - } - - AV_ERROR(); - - return false; - } - - // if 'setLower' does not work 'validateCurrent' does not work as well :O( - bool const result - = _current == getLower( currentInStorage, currentInBuffer, container ).data(); - - if( result ){ - return true; - } - - AV_ERROR(); - - return false; - } - - void setLower( - _CurrentInStorage currentInStorage - , _CurrentInBuffer currentInBuffer - , _Container const * container - ) - { - _current = getLower( currentInStorage, currentInBuffer, container ).data(); - } - - _Current getLower( - _CurrentInStorage currentInStorage - , _CurrentInBuffer currentInBuffer - , _Container const * container - )const - { - AV_CHECK( currentInStorage ); - AV_CHECK( currentInBuffer ); - - if( currentInStorage.is_end( container ) ) - { - if( currentInBuffer.is_end( container ) ){ - return _Current( 0 ); - } - else{ - return _Current( currentInBuffer ); - } - } - else - { - if( currentInBuffer.is_end( container ) ){ - return _Current( currentInStorage ); - } - else - { - if( container->value_comp()( - currentInStorage.get( container ) - , currentInBuffer.get( container ) - ) - ){ - return _Current( currentInStorage ); - } - else{ - return _Current( currentInBuffer ); - } - } - } - } - - operator bool()const noexcept - { - return _current != 0; - } - - typename _Container::_Storage::value_type const & - get( _Container const * container )const noexcept - { - return * _current; - } - - pointer_mutable data()const noexcept - { - return _current; - } - - private: - pointer_mutable _current; - }; - - public: - AssocVectorLazyIterator( - typename _Container::value_compare const & cmp = typename _Container::value_compare() - ) - : _container( 0 ) - - , _currentInStorage( 0 ) - , _currentInBuffer( 0 ) - , _currentInErased( 0 ) - - , _current( 0 ) - { - } - - template< typename _Iter > - AssocVectorLazyIterator( AssocVectorLazyIterator< _Iter, _Container > const & other ) - : _container( other.getContainer() ) - - , _currentInStorage( other.getCurrentInStorage() ) - , _currentInBuffer( other.getCurrentInBuffer() ) - , _currentInErased( other.getCurrentInErased() ) - - , _current( other.getCurrent() ) - { - } - - AssocVectorLazyIterator( - _Container const * container - , pointer_mutable currentInStorage - , pointer_mutable currentInBuffer - , typename _Container::_Erased::const_iterator currentInErased - , pointer_mutable current - ) - : _container( container ) - - , _currentInStorage( currentInStorage ) - , _currentInBuffer( currentInBuffer ) - , _currentInErased( currentInErased ) - - , _current( current ) - { - AV_PRECONDITION( container != 0 ); - AV_PRECONDITION( validate() ); - - if( _currentInStorage && _currentInBuffer && !_currentInErased && !_current ) - { - // not found in storage, insert to buffer - // erase from buffer + find in storage - // lower_bound - // upper_bound - - // _currentInStorage <- fix against '!_currentInErased' - _currentInStorage.setOnNotErased( _currentInErased, _container ); - - // _current <- get it right now - _current.setLower( _currentInStorage, _currentInBuffer, _container ); - } - else - if( _currentInStorage && _currentInBuffer && !_currentInErased && _current ) - { - // not found in storage, found in buffer - // not found in storage, inserted to buffer - // erased from storage's back - - // _currentInStorage <- fix against '!_currentInErased' - _currentInStorage.setOnNotErased( _currentInErased, _container ); - } - else - if( _currentInStorage && _currentInBuffer && _currentInErased && !_current ) - { - // begin iterator - // end iterator - // erase from storage, not merged + find in buffer - // erase from storage, merged + find in buffer + find in storage - - // _currentInStorage <- check against _currentInErased - _currentInStorage.setOnNotErased( _currentInErased, _container ); - - // _current <- get it right now - _current.setLower( _currentInStorage, _currentInBuffer, _container ); - } - else - if( ! _currentInStorage && ! _currentInBuffer && ! _currentInErased && !_current ) - { - // begin iterator on empty AssocVector - // end iterator on empty AssocVector - - // return, do not make validation - return; - } - - AV_POSTCONDITION( _current.validate( _currentInStorage, _currentInBuffer, _container ) ); - AV_POSTCONDITION( _currentInStorage || _currentInBuffer ); - AV_POSTCONDITION( _currentInErased ); - AV_POSTCONDITION( _container != 0 ); - } - - AssocVectorLazyIterator & - operator=( AssocVectorLazyIterator const & other ) - { - _container = other._container; - - _currentInStorage = other._currentInStorage; - _currentInBuffer = other._currentInBuffer; - _currentInErased = other._currentInErased; - - _current = other._current; - - return * this; - } - - bool operator==( AssocVectorLazyIterator const & other )const - { - this->resolveLazyValues(); - other.resolveLazyValues(); - - if( isEmpty() && other.isEmpty() ){ - return getContainer() == other.getContainer(); - } - - AV_PRECONDITION( _current.validate( _currentInStorage, _currentInBuffer, _container ) ); - - return equal( *this, other ); - } - - bool operator!=( AssocVectorLazyIterator const & other )const - { - this->resolveLazyValues(); - other.resolveLazyValues(); - - return ! ( ( * this ) == other ); - } - - AssocVectorLazyIterator & operator++() - { - AV_PRECONDITION( isEmpty() == false ); - - resolveLazyValues(); - - AV_PRECONDITION( _current.validate( _currentInStorage, _currentInBuffer, _container ) ); - - if( _current == _Current( 0 ) ){ - return * this; - } - else if( _current == _currentInStorage ){ - _currentInStorage.try_increment( _currentInErased, _container ); - } - else if( _current == _currentInBuffer ){ - _currentInBuffer.try_increment( _container ); - } - else{ - AV_ERROR(); - } - - _current.setLower( _currentInStorage, _currentInBuffer, _container ); - - AV_POSTCONDITION( _current.validate( _currentInStorage, _currentInBuffer, _container ) ); - - return * this; - } - - AssocVectorLazyIterator operator++( int ) - { - AssocVectorLazyIterator result( * this ); - - ( * this ).operator++(); - - return result; - } - - AssocVectorLazyIterator & operator--() - { - AV_PRECONDITION( isEmpty() == false ); - - resolveLazyValues(); - - AV_PRECONDITION( _current.validate( _currentInStorage, _currentInBuffer, _container ) ); - - if( - _currentInStorage.is_begin( _container ) - && _currentInBuffer.is_begin( _container ) - ) - { - AV_POSTCONDITION( _current.validate( _currentInStorage, _currentInBuffer, _container ) ); - - return * this; - } - - if( _currentInStorage.is_begin( _container ) ) - { - _currentInBuffer.decrement( _container ); - - _current = _currentInBuffer; - - AV_POSTCONDITION( _current.validate( _currentInStorage, _currentInBuffer, _container ) ); - - return * this; - } - - if( _currentInBuffer.is_begin( _container ) ) - { - _currentInStorage.decrement( _currentInErased, _container ); - - _current = _currentInStorage; - - AV_POSTCONDITION( _current.validate( _currentInStorage, _currentInBuffer, _container ) ); - - return * this; - } - - _CurrentInStorage currentInStorage = _currentInStorage; - _CurrentInBuffer currentInBuffer = _currentInBuffer; - - _CurrentInErased currentInErased = _currentInErased; - - currentInStorage.decrement( currentInErased, _container ); - currentInBuffer.decrement( _container ); - - if( - _container->value_comp()( - currentInStorage.get( _container ) - , currentInBuffer.get( _container ) - ) - ) - { - _currentInBuffer = currentInBuffer; - - _current = _currentInBuffer; - } - else - { - _currentInStorage = currentInStorage; - _currentInErased = currentInErased; - - _current = _currentInStorage; - } - - AV_POSTCONDITION( _current.validate( _currentInStorage, _currentInBuffer, _container ) ); - - return * this; - } - - AssocVectorLazyIterator operator--( int ) - { - AssocVectorLazyIterator result( * this ); - - ( * this ).operator--(); - - return result; - } - - reference operator*()const - { - return * get(); - } - - pointer operator->()const - { - return get(); - } - - pointer get()const - { - AV_PRECONDITION( isEmpty() == false ); - AV_PRECONDITION( _current ); - AV_PRECONDITION( _current.validate( _currentInStorage, _currentInBuffer, _container ) ); - - // make key const - // pair< T1, T2 > * -> pair< T1 const, T2 > * - //return reinterpret_cast< pointer >( _current ); - - return - reinterpret_cast< pointer >( - const_cast< void * >( - reinterpret_cast< void const * >( _current.data() ) - ) - ); - } - - // public for copy constructor only : Iterator -> ConstIterator - _Container const * getContainer()const noexcept - { - return _container; - } - - pointer_mutable getCurrentInStorage()const noexcept - { - return _currentInStorage.data(); - } - - pointer_mutable getCurrentInBuffer()const noexcept - { - return _currentInBuffer.data(); - } - - typename _Container::_Erased::const_iterator getCurrentInErased()const noexcept - { - return _currentInErased.data(); - } - - pointer_mutable getCurrent()const noexcept - { - return _current.data(); - } - - private: - bool isEmpty()const - { - if( _currentInStorage ){ - return false; - } - if( _currentInBuffer ){ - return false; - } - if( _currentInErased ){ - return false; - } - if( _current ){ - return false; - } - - return true; - } - - /*const function*/ - static - void - resolveLazyCurrentInBuffer( - _CurrentInBuffer & currentInBuffer - , _Current const current - , _Container const * container - ) - { - if( currentInBuffer ){ - return; - } - - currentInBuffer = const_cast< pointer_mutable >( - std::lower_bound( - container->buffer().begin() - , container->buffer().end() - , current.get( container ) - , container->value_comp() - ) - ); - } - - static - void - resolveLazyCurrentInStorage( - _CurrentInStorage & currentInStorage - , _CurrentInErased & currentInErased - , _Current const current - , _Container const * container - ) - { - if( currentInStorage ){ - return; - } - - currentInStorage = const_cast< pointer_mutable >( - std::lower_bound( - container->storage().begin() - , container->storage().end() - , current.get( container ) - , container->value_comp() - ) - ); - - currentInStorage.setOnNotErased( currentInErased, container ); - } - - void - resolveLazyValues()const - { - resolveLazyCurrentInBuffer( _currentInBuffer, _current, _container ); - resolveLazyCurrentInStorage( _currentInStorage, _currentInErased, _current, _container ); - } - - /*pure function*/ - bool - validate()const - { - if( !_currentInStorage && _currentInBuffer && _currentInErased && _current ) - { - // not found in storage, inserted to buffer, buffer merged to storage - - AV_CHECK( _currentInBuffer.validate( _container ) ); - AV_CHECK( _currentInErased.is_end( _container ) ); - AV_CHECK( _current == _currentInBuffer ); - - // _currentInStorage <- lazy in operator++/operator--/operator==/operator!= - } - else - if( _currentInStorage && !_currentInBuffer && _currentInErased && !_current ) - { - AV_ERROR(); - - return false; - } - else - if( _currentInStorage && !_currentInBuffer && _currentInErased && _current ) - { - // found in storage, not found in erased - - AV_CHECK( _currentInStorage.validate( _container ) ); - AV_CHECK( - std::binary_search( - _container->erased().begin() - , _container->erased().end() - , _currentInStorage.data() - ) == false - ); - AV_CHECK( _currentInErased.validate( _container ) ); - AV_CHECK( _current == _currentInStorage ); - - // _currentInBuffer <- lazy in operator++/operator--/operator==/operator!= - } - else - if( _currentInStorage && _currentInBuffer && !_currentInErased && !_current ) - { - // not found in storage, insert to buffer - // erase from buffer + find in storage - // lower_bound - // upper_bound - - AV_CHECK( _currentInStorage.validate( _container ) ); - AV_CHECK( _currentInBuffer.validate( _container ) ); - - // _currentInStorage <- fix against '!_currentInErased' - // _current <- get it right now - } - else - if( _currentInStorage && _currentInBuffer && !_currentInErased && _current ) - { - // not found in storage, found in buffer - // not found in storage, inserted to buffer - // erased from storage's back - - AV_CHECK( _currentInStorage.validate( _container ) ); - AV_CHECK( _currentInBuffer.validate( _container ) ); - AV_CHECK( _current == _currentInBuffer ); - - // _currentInStorage <- fix against '!_currentInErased' - // _currentInErased <- get it right now - } - else - if( _currentInStorage && _currentInBuffer && _currentInErased && !_current ) - { - // begin iterator - // end iterator - // erase from storage, not merged + find in buffer - // erase from storage, merged + find in buffer + find in storage - - AV_CHECK( _currentInStorage.validate( _container ) ); - AV_CHECK( _currentInBuffer.validate( _container ) ); - AV_CHECK( _currentInErased.validate( _container ) ); - - // _currentInStorage <- check against _currentInErased - // _current <- get it right now - } - else - if( ! _currentInStorage && ! _currentInBuffer && ! _currentInErased && !_current ) - { - // begin iterator on empty AssocVector - // end iterator on empty AssocVector - } - else - { - AV_ERROR(); - - return false; - } - - return true; - } - - private: - _Container const * _container; - - // mutable since lazy values in operator==()const / operator!=()const - mutable _CurrentInStorage _currentInStorage; - mutable _CurrentInBuffer _currentInBuffer; - mutable _CurrentInErased _currentInErased; - - _Current _current; - }; - - template< - typename _Iterator - , typename _Container - > - std::ostream & - operator<<( - std::ostream & out - , AssocVectorLazyIterator< _Iterator, _Container > const & iter - ) - { - out << "S: " << iter.getCurrentInStorage(); - - if( iter.getCurrentInStorage() == 0 ){ - out << " (null)"; - } - else if( iter.getContainer()->storage().end() == iter.getCurrentInStorage() ){ - out << " (end)"; - } - else{ - out << " " << * iter.getCurrentInStorage(); - } - - out << "\nB: " << iter.getCurrentInBuffer(); - - if( iter.getCurrentInBuffer() == 0 ){ - out << " (null)"; - } - else if( iter.getContainer()->buffer().end() == iter.getCurrentInBuffer() ){ - out << " (end)"; - } - else{ - out << " " << * iter.getCurrentInBuffer(); - } - - out << "\nE: " << iter.getCurrentInErased(); - - if( iter.getCurrentInErased() == 0 ){ - out << " (null)"; - } - else if( iter.getContainer()->erased().end() == iter.getCurrentInErased() ){ - out << " (end)"; - } - else{ - AV_CHECK( * iter.getCurrentInErased() ); - - out << " " << * * iter.getCurrentInErased(); - } - - out << "\nC: " << iter.getCurrent(); - - if( iter.getCurrent() == 0 ){ - out << " (null)"; - } - else{ - out << " " << * iter.getCurrent(); - } - - std::flush( out ); - - return out; - } - - // - // _AssocVectorIterator, simplified version of AssocVectorLazyIterator, works with _find and _end - // - template< - typename _Iterator - , typename _Container - > - struct _AssocVectorIterator - { - private: - typedef typename std::iterator_traits< _Iterator >::pointer pointer_mutable; - - public: - typedef typename std::iterator_traits< _Iterator >::value_type value_type; - - // make key const - typedef std::pair< - typename value_type::first_type const - , typename value_type::second_type - > & reference; - - // make key const - typedef std::pair< - typename value_type::first_type const - , typename value_type::second_type - > * pointer; - - public: - _AssocVectorIterator( - typename _Container::value_compare const & cmp = typename _Container::value_compare() - ) - : _current( 0 ) - { - } - - template< typename _Iter > - _AssocVectorIterator( _AssocVectorIterator< _Iter, _Container > const & other ) - : _current( other.getCurrent() ) - { - } - - _AssocVectorIterator( pointer_mutable current ) - : _current( current ) - { - } - - _AssocVectorIterator & - operator=( _AssocVectorIterator const & other ) - { - _current = other._current; - - return * this; - } - - bool operator==( _AssocVectorIterator const & other )const - { - return _current == other.getCurrent(); - } - - bool operator!=( _AssocVectorIterator const & other )const - { - return ! ( ( * this ) == other ); - } - - reference operator*()const - { - AV_PRECONDITION( _current != 0 ); - - return * get(); - } - - pointer operator->()const - { - AV_PRECONDITION( _current != 0 ); - - return get(); - } - - pointer get()const - { - AV_PRECONDITION( _current != 0 ); - - // make key const - // pair< T1, T2 > * -> pair< T1 const, T2 > * - //return reinterpret_cast< pointer >( _current ); - - return - reinterpret_cast< pointer >( - const_cast< void * >( - reinterpret_cast< void const * >( _current ) - ) - ); - } - - operator bool()const noexcept - { - return _current != 0; - } - - // public for copy constructor only : Iterator -> ConstIterator - pointer_mutable getCurrent()const noexcept - { - return _current; - } - - private: - pointer_mutable _current; - }; - - template< - typename _Iterator - , typename _Container - > - std::ostream & operator<<( - std::ostream & out - , _AssocVectorIterator< _Iterator, _Container > const & iter - ) - { - out << "S: " << iter.getCurrent(); - - if( iter.getCurrent() == 0 ){ - out << " (null)(end)"; - } - else{ - out << " " << * iter.get(); - } - - return out; - } - -} // namespace detail - -template< - typename _Key - , typename _Mapped - , typename _Cmp = std::less< _Key > - , typename _Allocator = std::allocator< std::pair< _Key, _Mapped > > -> -struct AssocVector -{ -private: - typedef std::pair< _Key, _Mapped > value_type_mutable; - typedef std::pair< _Key const, _Mapped > value_type_key_const; - -public: - typedef _Key key_type; - typedef _Mapped mapped_type; - - typedef value_type_key_const value_type; - - typedef typename _Allocator::size_type size_type; - typedef typename _Allocator::difference_type difference_type; - - typedef typename _Allocator::pointer pointer; - typedef typename _Allocator::const_pointer const_pointer; - - typedef _Cmp key_compare; - typedef util::CmpByFirst< value_type_mutable, _Cmp > value_compare; - - typedef _Allocator allocator_type; - - typedef mapped_type & reference; - typedef mapped_type const & const_reference; - - typedef detail::AssocVectorLazyIterator< value_type_mutable *, AssocVector > iterator; - typedef detail::AssocVectorLazyIterator< value_type_mutable const *, AssocVector > const_iterator; - - typedef std::reverse_iterator< iterator > reverse_iterator; - typedef std::reverse_iterator< const_iterator > const_reverse_iterator; - - typedef array::Array< value_type_mutable > _Storage; - typedef array::Array< typename _Storage::const_iterator > _Erased; - -#ifdef AV_ENABLE_EXTENSIONS - public: -#else - private: -#endif - - // - // extension, faster, non STL compatible version of iterator, working with _find end _end - // - typedef detail::_AssocVectorIterator< value_type_mutable *, AssocVector > _iterator; - typedef detail::_AssocVectorIterator< value_type_mutable const *, AssocVector > _const_iterator; - -private: - struct _FindOrInsertToBufferResult - { - typename _Storage::iterator _inBuffer; - bool _isInserted; - bool _isReallocated; - }; - - struct _TryRemoveBackResult - { - bool _anyItemRemoved; - bool _erasedItemRemoved; - }; - - struct _FindImplResult - { - typename _Storage::iterator _inStorage; - typename _Storage::iterator _inBuffer; - typename _Erased::iterator _inErased; - typename _Storage::iterator _current; - - bool validate()const - { - return - ( _current == 0 && _inStorage == 0 && _inBuffer == 0 && _inErased == 0 ) - || _inStorage != 0; - } - }; - - struct _InsertImplResult - { - bool _isInserted; - - typename _Storage::iterator _inStorage; - typename _Storage::iterator _inBuffer; - typename _Erased::iterator _inErased; - typename _Storage::iterator _current; - - bool validate()const - { - if( _current == 0 ) - { - AV_ERROR(); - - return false; - } - - if( _inStorage == 0 && ( _inBuffer == 0 || _inErased == 0 ) ) - { - AV_ERROR(); - - return false; - } - - return true; - } - }; - - struct _TryEraseFromStorageResult - { - typename _Erased::iterator _inErased; - bool _isErased; - bool _isMerged; - }; - -public: - // - // constructor - // - explicit - AssocVector( - _Cmp const & cmp = _Cmp() - , _Allocator const & allocator = _Allocator() - ); - - explicit - AssocVector( _Allocator const & allocator ); - - template< typename __InputIterator > - AssocVector( - __InputIterator first - , __InputIterator last - , _Cmp const & cmp = _Cmp() - , _Allocator const & allocator = _Allocator() - ); - - AssocVector( AssocVector< _Key, _Mapped, _Cmp, _Allocator > const & other ); - AssocVector( - AssocVector< _Key, _Mapped, _Cmp, _Allocator > const & other - , _Allocator const & allocator - ); - - AssocVector( AssocVector< _Key, _Mapped, _Cmp, _Allocator > && other ); - AssocVector( - AssocVector< _Key, _Mapped, _Cmp, _Allocator > && other - , _Allocator const & allocator - ); - - AssocVector( - std::initializer_list< value_type > list - , _Cmp const & cmp = _Cmp() - , _Allocator const & allocator = _Allocator() - ); - - // - // destructor - // - inline ~AssocVector(); - - // - // clear - // - inline void clear() noexcept; - - // - // operator= - // - AssocVector & operator=( AssocVector const & other ); - AssocVector & operator=( AssocVector && other ); - - // - // methods - // - void reserve( std::size_t newCapacity ); - void swap( AssocVector & other ) noexcept; - - // - // iterators - // - inline iterator begin(); - inline const_iterator begin()const; - inline const_iterator cbegin()const; - - inline reverse_iterator rbegin(); - inline const_reverse_iterator rbegin()const; - inline const_reverse_iterator crbegin()const; - - inline iterator end(); - inline const_iterator end()const; - inline const_iterator cend()const; - - inline reverse_iterator rend(); - inline const_reverse_iterator rend()const; - inline const_reverse_iterator crend()const; - - // - // size - // - inline bool empty()const noexcept; - inline std::size_t size()const noexcept; - inline std::size_t capacity()const noexcept; - inline std::size_t max_size()const noexcept; - - // - // insert - // - std::pair< iterator, bool > insert( value_type const & value ); - - template< typename __ValueType > - std::pair< iterator, bool > insert( __ValueType && value ); - - iterator insert( const_iterator hint, value_type const & value ); - - template< typename __ValueType > - iterator insert( const_iterator hint, __ValueType && value ); - - template< typename _Iterator > - inline void insert( _Iterator first, _Iterator last ); - - inline void insert( std::initializer_list< value_type > list ); - - // - // emplace - // - template< class... __Args > - std::pair< iterator, bool > emplace( __Args... args ); - - template< class... __Args > - std::pair< iterator, bool > emplace_hint( const_iterator hint, __Args... args ); - - // - // find - // - iterator find( key_type const & k ); - const_iterator find( key_type const & k )const; - - iterator lower_bound( key_type const & k ); - const_iterator lower_bound( key_type const & k )const; - - iterator upper_bound( key_type const & k ); - const_iterator upper_bound( key_type const & k )const; - - std::pair< iterator, iterator > equal_range( key_type const & k ); - std::pair< const_iterator, const_iterator > equal_range( key_type const & k )const; - - // - // count - // - inline std::size_t count( key_type const & k )const; - - // - // operator[] - // - reference operator[]( key_type const & k ); - reference operator[]( key_type && k ); - - // - // at - // - reference at( key_type const & k ); - const_reference at( key_type const & k )const; - - // - // erase - // - std::size_t erase( key_type const & k ); - iterator erase( iterator pos ); - - // - // observers - // - key_compare key_comp()const - { - return _cmp; - } - - value_compare value_comp()const - { - return value_compare( _cmp ); - } - - //allocator_type get_allocator()const - //{ - // return _allocator; - //} - -#ifdef AV_ENABLE_EXTENSIONS - public: -#else - private: -#endif - - // - // extension, flatenize container, enforce merge of _storage with _erased and with _buffer - // - void _merge(); - - // - // extension, faster, non STL compatible version of insert - // - bool _insert( value_type const & value ); - - template< typename __ValueType > - bool _insert( __ValueType && value ); - - // - // extension, faster, non STL compatible version of end, works with _find - // - inline _iterator _end(); - inline _const_iterator _end()const; - - // - // extension, faster, non STL compatible version of find, works with _end - // - _iterator _find( key_type const & k ); - _const_iterator _find( key_type const & k )const; - - // - // extension, faster, non STL compatible version of erase - // - bool _erase( iterator pos ); - -private: - bool validateStorage()const; - bool validateBuffer()const; - bool validateErased()const; - bool validate()const; - - // - // merge - // - void mergeStorageWithBuffer(); - void mergeStorageWithErased(); - - // - // insert - // - template< typename __ValueType > - void pushBack( __ValueType && value ); - - template< typename __ValueType > - bool shouldBePushBack( __ValueType && value )const; - - template< typename __ValueType > - _FindOrInsertToBufferResult - findOrInsertToBuffer( __ValueType && value ); - - // - // insertImpl, function does as little as needed but returns as much data as possible - // - template< typename __ValueType > - _InsertImplResult - insertImpl( __ValueType && value ); - - // - // emplace_impl - // - template< class __Head, class... __Tail > - std::pair< iterator, bool > emplaceImpl( __Head && head, __Tail... tail ); - - // - // erase - // - _TryRemoveBackResult - tryRemoveStorageBack( typename _Storage::iterator pos ); - - // - // tryEraseFromStorage - // - _TryEraseFromStorageResult - tryEraseFromStorage( typename _Storage::iterator pos ); - - // - // isErased - // - bool isErased( typename _Storage::const_iterator iterator )const; - - // - // findImpl, function does as little as needed but returns as much data as possible - // - _FindImplResult - findImpl( key_type const & key ); - - // - // getAllocator (method specialization) - // - //_Allocator getAllocator( _Storage const & ){ return _allocator; } - // - //typename _Allocator::template rebind< typename _Storage::const_iterator >::other - //getAllocator( _Erased const & ) - //{return typename _Allocator::template rebind< typename _Storage::const_iterator >::other( _allocator );} - -public: // public for unit tests only - void dump( int width = -1 )const; - - std::size_t bufferSize()const{ return _buffer.size(); } - std::size_t bufferCapacity()const{ return _buffer.capacity(); } - _Storage const & storage()const{ return _storage; } - - std::size_t storageSize()const{ return _storage.size(); } - std::size_t storageCapacity()const{ return _storage.capacity(); } - _Storage const & buffer()const{ return _buffer; } - - std::size_t erasedSize()const{ return _erased.size(); } - std::size_t erasedCapacity()const{ return _erased.capacity(); } - _Erased const & erased()const{ return _erased; } - - static std::size_t calculateNewBufferCapacity( std::size_t storageSize ); - static std::size_t calculateNewErasedCapacity( std::size_t storageSize ); - static std::size_t calculateNewStorageCapacity( std::size_t storageSize ); - -private: - _Storage _storage; - _Storage _buffer; - _Erased _erased; - - _Cmp _cmp; -}; - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -bool operator==( - AssocVector< _Key, _Mapped, _Cmp, _Allocator > const & lhs - , AssocVector< _Key, _Mapped, _Cmp, _Allocator > const & rhs -) -{ - if( lhs.size() != rhs.size() ){ - return false; - } - - typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::const_iterator begin = lhs.begin(); - typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::const_iterator const end = lhs.end(); - - typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::const_iterator begin2 = rhs.begin(); - - for( /*empty*/ ; begin != end ; ++ begin, ++ begin2 ) - { - if( begin->first != begin2->first ){ - return false; - } - - if( begin->second != begin2->second ){ - return false; - } - } - - return true; -} - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -bool operator!=( - AssocVector< _Key, _Mapped, _Cmp, _Allocator > const & lhs - , AssocVector< _Key, _Mapped, _Cmp, _Allocator > const & rhs -) -{ - return ! ( lhs == rhs ); -} - -// -// Method Definitions -// - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -AssocVector< _Key, _Mapped, _Cmp, _Allocator >::AssocVector( - _Cmp const & cmp - , _Allocator const & allocator -) - : _cmp( cmp ) -{ -} - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -AssocVector< _Key, _Mapped, _Cmp, _Allocator >::AssocVector( _Allocator const & allocator ) -{ -} - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -template< - typename __InputIterator -> -AssocVector< _Key, _Mapped, _Cmp, _Allocator >::AssocVector( - __InputIterator first - , __InputIterator last - , _Cmp const & cmp - , _Allocator const & allocator -) - : _cmp( cmp ) -{ - AV_PRECONDITION( std::distance( first, last ) >= 0 ); - - std::size_t const size = std::distance( first, last ); - - if( size > 0 ) - { - reserve( size ); - - for( /*empty*/ ; first != last ; ++ first ){ - insert( * first ); - } - } - - AV_POSTCONDITION( validate() ); -} - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -AssocVector< _Key, _Mapped, _Cmp, _Allocator >::AssocVector( - AssocVector< _Key, _Mapped, _Cmp, _Allocator > const & other -) - : _storage( other._storage ) - , _buffer( other._buffer ) - , _erased( other._erased ) - , _cmp( other._cmp ) -{ -} - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -AssocVector< _Key, _Mapped, _Cmp, _Allocator >::AssocVector( - AssocVector< _Key, _Mapped, _Cmp, _Allocator > const & other - , _Allocator const & allocator -) - : _storage( other._storage, allocator ) - , _buffer( other._buffer, allocator ) - , _erased( other._erased, allocator ) - , _cmp( other._cmp, allocator ) -{ -} - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -AssocVector< _Key, _Mapped, _Cmp, _Allocator >::AssocVector( - AssocVector< _Key, _Mapped, _Cmp, _Allocator > && other -) - : _storage( std::move( other._storage ) ) - , _buffer( std::move( other._buffer ) ) - , _erased( std::move( other._erased ) ) - , _cmp( other._cmp ) -{ -} - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -AssocVector< _Key, _Mapped, _Cmp, _Allocator >::AssocVector( - AssocVector< _Key, _Mapped, _Cmp, _Allocator > && other - , _Allocator const & allocator -) - : _storage( std::move( other._storage ) ) - , _buffer( std::move( other._buffer ) ) - , _erased( std::move( other._erased ) ) - , _cmp( other._cmp ) -{ -} - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -AssocVector< _Key, _Mapped, _Cmp, _Allocator >::AssocVector( - std::initializer_list< typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::value_type > list - , _Cmp const & cmp - , _Allocator const & allocator -) - : _cmp( cmp ) -{ - reserve( list.size() ); - - insert( list ); -} - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -AssocVector< _Key, _Mapped, _Cmp, _Allocator >::~AssocVector() -{ -} - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -void -AssocVector< _Key, _Mapped, _Cmp, _Allocator >::clear() noexcept -{ - util::destroy_range( _storage.begin(), _storage.end() ); - util::destroy_range( _buffer.begin(), _buffer.end() ); - - _storage.setSize( 0 ); - _buffer.setSize( 0 ); - _erased.setSize( 0 ); -} - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -AssocVector< _Key, _Mapped, _Cmp, _Allocator > & -AssocVector< _Key, _Mapped, _Cmp, _Allocator >::operator=( AssocVector const & other ) -{ - AssocVector temp( other ); - temp.swap( * this ); - - return * this; -} - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -AssocVector< _Key, _Mapped, _Cmp, _Allocator > & -AssocVector< _Key, _Mapped, _Cmp, _Allocator >::operator=( AssocVector && other ) -{ - AssocVector temp( std::move( other ) ); - - temp.swap( * this ); - - return * this; -} - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -void -AssocVector< _Key, _Mapped, _Cmp, _Allocator >::reserve( std::size_t newStorageCapacity ) -{ - if( _storage.get_allocator().max_size() < newStorageCapacity ){ - throw std::length_error( "AssocVector< _K, _M, _C, _A >::reserve" ); - } - - if( util::less_equal( newStorageCapacity, _storage.capacity() ) ){ - return; - } - - {// _erased - if( _erased.empty() == false ){ - mergeStorageWithErased(); - } - - _erased.reserve( calculateNewErasedCapacity( newStorageCapacity ) ); - - AV_CHECK( _erased.empty() ); - } - - std::size_t const newBufferCapacity - = calculateNewBufferCapacity( newStorageCapacity ); - - std::size_t const newStorageSize = _storage.size() + _buffer.size(); - - { - _Storage newStorage( newStorageCapacity, _storage.get_allocator() ); - _Storage newBuffer( newBufferCapacity, _buffer.get_allocator() ); - - util::move_merge_into_uninitialized( - _storage.begin() - , _storage.end() - , _buffer.begin() - , _buffer.end() - , newStorage.begin() - , value_comp() - ); - - newStorage.swap( _storage ); - newBuffer.swap( _buffer ); - }// call newStorage newBuffer destructors - - _storage.setSize( newStorageSize ); - - AV_POSTCONDITION( _buffer.empty() ); - AV_POSTCONDITION( validate() ); -} - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::iterator -AssocVector< _Key, _Mapped, _Cmp, _Allocator >::begin() -{ - return iterator( this, _storage.begin(), _buffer.begin(), _erased.begin(), 0 ); -} - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::reverse_iterator -AssocVector< _Key, _Mapped, _Cmp, _Allocator >::rbegin() -{ - return reverse_iterator( end() ); -} - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::const_iterator -AssocVector< _Key, _Mapped, _Cmp, _Allocator >::begin()const -{ - return const_iterator( this, _storage.begin(), _buffer.begin(), _erased.begin(), 0 ); -} - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::const_iterator -AssocVector< _Key, _Mapped, _Cmp, _Allocator >::cbegin()const -{ - return begin(); -} - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::const_reverse_iterator -AssocVector< _Key, _Mapped, _Cmp, _Allocator >::rbegin()const -{ - return const_reverse_iterator( end() ); -} - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::const_reverse_iterator -AssocVector< _Key, _Mapped, _Cmp, _Allocator >::crbegin()const -{ - return rbegin(); -} - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::iterator -AssocVector< _Key, _Mapped, _Cmp, _Allocator >::end() -{ - return iterator( this, _storage.end(), _buffer.end(), _erased.end(), 0 ); -} - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::reverse_iterator -AssocVector< _Key, _Mapped, _Cmp, _Allocator >::rend() -{ - return reverse_iterator( begin() ); -} - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::const_iterator -AssocVector< _Key, _Mapped, _Cmp, _Allocator >::end()const -{ - return const_iterator( this, _storage.end(), _buffer.end(), _erased.end(), 0 ); -} - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::const_iterator -AssocVector< _Key, _Mapped, _Cmp, _Allocator >::cend()const -{ - return end(); -} - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::const_reverse_iterator -AssocVector< _Key, _Mapped, _Cmp, _Allocator >::rend()const -{ - return const_reverse_iterator( begin() ); -} - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::const_reverse_iterator -AssocVector< _Key, _Mapped, _Cmp, _Allocator >::crend()const -{ - return rend(); -} - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::_iterator -AssocVector< _Key, _Mapped, _Cmp, _Allocator >::_end() -{ - return _iterator( 0 ); -} - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::_const_iterator -AssocVector< _Key, _Mapped, _Cmp, _Allocator >::_end()const -{ - return _const_iterator( 0 ); -} - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -bool -AssocVector< _Key, _Mapped, _Cmp, _Allocator >::empty()const noexcept -{ - return size() == 0; -} - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -std::size_t -AssocVector< _Key, _Mapped, _Cmp, _Allocator >::size()const noexcept -{ - return _storage.size() + _buffer.size() - _erased.size(); -} - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -std::size_t -AssocVector< _Key, _Mapped, _Cmp, _Allocator >::capacity()const noexcept -{ - return _storage.capacity() + _buffer.capacity(); -} - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -std::size_t -AssocVector< _Key, _Mapped, _Cmp, _Allocator >::max_size()const noexcept -{ - return _storage.get_allocator().max_size(); -} - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -std::pair< typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::iterator, bool > -AssocVector< _Key, _Mapped, _Cmp, _Allocator >::insert( value_type const & value ) -{ - _InsertImplResult const result = insertImpl( value ); - - return std::make_pair( - iterator( - this - , result._inStorage - , result._inBuffer - , result._inErased - , result._current - ) - , result._isInserted - ); -} - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -template< - typename __ValueType -> -std::pair< typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::iterator, bool > -AssocVector< _Key, _Mapped, _Cmp, _Allocator >::insert( __ValueType && value ) -{ - _InsertImplResult const result = insertImpl( std::forward< __ValueType >( value ) ); - - return std::make_pair( - iterator( - this - , result._inStorage - , result._inBuffer - , result._inErased - , result._current - ) - , result._isInserted - ); -} - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::iterator -AssocVector< _Key, _Mapped, _Cmp, _Allocator >::insert( - typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::const_iterator hint - , value_type const & value -) -{ - ( void )( hint ); - - _InsertImplResult const result = insertImpl( value ); - - return iterator( - this - , result._inStorage - , result._inBuffer - , result._inErased - , result._current - ); -} - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -template< - typename __ValueType -> -typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::iterator -AssocVector< _Key, _Mapped, _Cmp, _Allocator >::insert( - const_iterator hint - , __ValueType && value -) -{ - ( void )( hint ); - - _InsertImplResult const result = insertImpl( std::forward< __ValueType >( value ) ); - - return iterator( - this - , result._inStorage - , result._inBuffer - , result._inErased - , result._current - ); -} - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -void -AssocVector< _Key, _Mapped, _Cmp, _Allocator >::insert( - std::initializer_list< value_type > list -) -{ - insert( list.begin(), list.end() ); -} - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -bool -AssocVector< _Key, _Mapped, _Cmp, _Allocator >::_insert( value_type const & value ) -{ - return insertImpl( value )._isInserted; -} - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -template< - typename __ValueType -> -bool -AssocVector< _Key, _Mapped, _Cmp, _Allocator >::_insert( __ValueType && value ) -{ - return insertImpl( std::forward< __ValueType >( value ) )._isInserted; -} - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -template< - typename __ValueType -> -typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::_InsertImplResult -AssocVector< _Key, _Mapped, _Cmp, _Allocator >::insertImpl( __ValueType && value ) -{ - _Key const & k = value.first; - _Mapped const & m = value.second; - - {//push back to storage - if( shouldBePushBack( value ) ) - { - pushBack( std::forward< __ValueType >( value ) ); - - _InsertImplResult result; - - { - AV_CHECK( _storage.empty() == false ); - - result._inStorage = ( _storage.end() - 1 ); - result._current = ( _storage.end() - 1 ); - } - - result._isInserted = true; - result._inBuffer = 0; - result._inErased = _erased.end(); - - AV_POSTCONDITION( result.validate() ); - AV_POSTCONDITION( validate() ); - - return result; - } - } - - typename _Storage::iterator const greaterEqualInStorage - = std::lower_bound( _storage.begin(), _storage.end(), k, value_comp() ); - - bool const notPresentInStorage - = greaterEqualInStorage == _storage.end() - || key_comp()( k, greaterEqualInStorage->first ); - - {//find or insert to buffer - if( notPresentInStorage ) - { - _FindOrInsertToBufferResult const findOrInsertToBufferResult - = findOrInsertToBuffer( std::forward< __ValueType >( value ) ); - - _InsertImplResult result; - result._isInserted = findOrInsertToBufferResult._isInserted; - - if( findOrInsertToBufferResult._isReallocated ) - { - result._inStorage = 0; - result._inErased = _erased.end(); - } - else - { - result._inStorage = greaterEqualInStorage; - result._inErased = 0; - } - - result._inBuffer = findOrInsertToBufferResult._inBuffer; - result._current = findOrInsertToBufferResult._inBuffer; - - AV_POSTCONDITION( result.validate() ); - AV_POSTCONDITION( validate() ); - - return result; - } - } - - {// check if not erased - typename _Erased::iterator const greaterEqualInErased = std::lower_bound( - _erased.begin() - , _erased.end() - , greaterEqualInStorage - , std::less< typename _Storage::const_iterator >() - ); - - bool const itemNotMarkedAsErased - = greaterEqualInErased == _erased.end() - || std::less< typename _Storage::const_iterator >() - ( greaterEqualInStorage, * greaterEqualInErased ); - - if( itemNotMarkedAsErased ) - {// item is in storage and is not marked as erased - _InsertImplResult result; - result._isInserted = false; - result._inStorage = greaterEqualInStorage; - result._inBuffer = 0; - result._inErased = greaterEqualInErased; - result._current = greaterEqualInStorage; - - AV_POSTCONDITION( result.validate() ); - AV_POSTCONDITION( validate() ); - - return result; - } - else - {// item is in storage but is marked as erased - _erased.erase( greaterEqualInErased ); - - greaterEqualInStorage->second = m; - - _InsertImplResult result; - result._isInserted = true; - result._inStorage = greaterEqualInStorage; - result._inBuffer = 0; - - // greaterEqualInErased is after 'Array::erase' but still valid - result._inErased = greaterEqualInErased; - - result._current = greaterEqualInStorage; - - AV_POSTCONDITION( validate() ); - AV_POSTCONDITION( result.validate() ); - - return result; - } - } -} - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -template< - typename _Iterator -> -void -AssocVector< _Key, _Mapped, _Cmp, _Allocator >::insert( _Iterator const begin, _Iterator const end ) -{ - for( _Iterator current = begin ; current != end ; ++ current ){ - insert( * current ); - } -} - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -template< - class... __Args -> -std::pair< typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::iterator, bool > -AssocVector< _Key, _Mapped, _Cmp, _Allocator >::emplace( __Args... args ) -{ - return emplaceImpl( args... ); -} - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -template< - class... __Args -> -std::pair< typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::iterator, bool > -AssocVector< _Key, _Mapped, _Cmp, _Allocator >::emplace_hint( const_iterator hint, __Args... args ) -{ - ( void )( hint ); - - return emplaceImpl( args... ); -} - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -template< - class __Head - , class... __Tail -> -std::pair< typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::iterator, bool > -AssocVector< _Key, _Mapped, _Cmp, _Allocator >::emplaceImpl( __Head && head, __Tail... tail ) -{ - _InsertImplResult const result - = insertImpl( value_type_mutable( key_type( head ), mapped_type( tail... ) ) ); - - return std::make_pair( - iterator( - this - , result._inStorage - , result._inBuffer - , result._inErased - , result._current - ) - , result._isInserted - ); -} - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -template< - typename __ValueType -> -bool -AssocVector< _Key, _Mapped, _Cmp, _Allocator >::shouldBePushBack( __ValueType && value )const -{ - bool pushBackToStorage = false; - - {// should be pushed back - if( _storage.empty() ) - { - if( _buffer.empty() ){ - pushBackToStorage = true; - } - else - { - if( _cmp( _buffer.back().first, value.first ) ){ - pushBackToStorage = true; - } - } - } - else - { - if( _buffer.empty() ) - { - if( _cmp( _storage.back().first, value.first ) ){ - pushBackToStorage = true; - } - } - else - { - if( _cmp( _storage.back().first, value.first ) - && _cmp( _buffer.back().first, value.first ) - ){ - pushBackToStorage = true; - } - } - } - } - - return pushBackToStorage; -} - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -template< - typename __ValueType -> -void -AssocVector< _Key, _Mapped, _Cmp, _Allocator >::pushBack( __ValueType && value ) -{ - if( _storage.size() != _storage.capacity() ){ - _storage.place_back( std::forward< __ValueType >( value ) ); - - AV_POSTCONDITION( validate() ); - - return; - } - - std::size_t const newStorageCapacity = calculateNewStorageCapacity( _storage.capacity() ); - std::size_t const newBufferCapacity = calculateNewBufferCapacity( newStorageCapacity ); - std::size_t const newErasedCapacity = calculateNewErasedCapacity( newStorageCapacity ); - - if( util::less_equal( newStorageCapacity, _storage.capacity() ) ){ - return; - } - - if( _storage.get_allocator().max_size() < newStorageCapacity ){ - throw std::length_error( "AssocVector::reserve" ); - } - - _Storage newStorage( newStorageCapacity, _storage.get_allocator() ); - _Storage newBuffer( newBufferCapacity, _buffer.get_allocator() ); - _Erased newErased( newErasedCapacity, _erased.get_allocator() ); - - {// may throw - iterator current = begin(); - iterator const end = this->end(); - - while( current != end ) - { - // for '++ current' object has still exist and can not be moved before - typename iterator::pointer_mutable const current_raw_ptr = current.getCurrent(); - - ++ current; - - newStorage.place_back( AV_MOVE_IF_NOEXCEPT( * current_raw_ptr ) ); - } - } - - // may throw an exception in __ValueType copy constructor, not exception safe - newStorage.place_back( std::forward< __ValueType >( value ) ); - - newStorage.swap( _storage ); - newBuffer.swap( _buffer ); - newErased.swap( _erased ); - - AV_POSTCONDITION( _buffer.empty() ); - AV_POSTCONDITION( _erased.empty() ); - - AV_POSTCONDITION( validate() ); -} - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::_TryRemoveBackResult -AssocVector< _Key, _Mapped, _Cmp, _Allocator >::tryRemoveStorageBack( - typename _Storage::iterator pos -) -{ - if( pos + 1 != _storage.end() ) - { - _TryRemoveBackResult result; - result._anyItemRemoved = false; - result._erasedItemRemoved = false; - - return result; - } - - _storage.get_allocator().destroy( pos ); - - _storage.setSize( _storage.size() - 1 ); - - if( - _erased.empty() == false - && _erased.back() == pos - ) - { - _erased.setSize( _erased.size() - 1 ); - - _TryRemoveBackResult result; - result._anyItemRemoved = true; - result._erasedItemRemoved = true; - - AV_POSTCONDITION( validate() ); - - return result; - } - - _TryRemoveBackResult result; - result._anyItemRemoved = true; - result._erasedItemRemoved = false; - - AV_POSTCONDITION( validate() ); - - return result; -} - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::_TryEraseFromStorageResult -AssocVector< _Key, _Mapped, _Cmp, _Allocator >::tryEraseFromStorage( - typename _Storage::iterator pos -) -{ - std::pair< typename _Erased::iterator, bool > const insertInSortedResult - = array::insert_in_sorted( - _erased - , typename _Storage::const_iterator( pos ) - , std::less< typename _Storage::const_iterator >() - ); - - if( _erased.full() ) - { - mergeStorageWithErased(); - - _TryEraseFromStorageResult result; - result._inErased = _erased.end(); - result._isErased = true; - result._isMerged = true; - - AV_POSTCONDITION( validate() ); - - return result; - } - else - { - _TryEraseFromStorageResult result; - result._inErased = insertInSortedResult.first; - result._isErased = insertInSortedResult.second; - result._isMerged = false; - - AV_POSTCONDITION( validate() ); - - return result; - } -} - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -bool -AssocVector< _Key, _Mapped, _Cmp, _Allocator >::isErased( - typename AssocVector::_Storage::const_iterator iterator -)const -{ - typename _Erased::const_iterator const foundInErased = array::binary_search( - _erased.begin() - , _erased.end() - , iterator - , std::less< typename _Storage::const_iterator >() - ); - - return foundInErased != _erased.end(); -} - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::_FindImplResult -AssocVector< _Key, _Mapped, _Cmp, _Allocator >::findImpl( _Key const & k ) -{ - typename _Storage::iterator const greaterEqualInStorage - = std::lower_bound( _storage.begin(), _storage.end(), k, value_comp() ); - - bool const presentInStorage - = greaterEqualInStorage != _storage.end() - && key_comp()( k, greaterEqualInStorage->first ) == false; - - {// item is in storage, check in erased - if( presentInStorage ) - { - typename _Erased::iterator greaterEqualInErased = std::lower_bound( - _erased.begin() - , _erased.end() - , greaterEqualInStorage - , std::less< typename _Storage::const_iterator >() - ); - - bool const itemNotMarkedAsErased - = greaterEqualInErased == _erased.end() - || std::less< typename _Storage::const_iterator >()( - greaterEqualInStorage - , * greaterEqualInErased - ); - - if( itemNotMarkedAsErased ) - { - _FindImplResult result; - result._inStorage = greaterEqualInStorage; - result._inBuffer = 0; - result._inErased = greaterEqualInErased; - result._current = greaterEqualInStorage; - - AV_POSTCONDITION( result.validate() ); - - return result; - } - else - { - _FindImplResult result; - result._inStorage = 0; - result._inBuffer = 0; - result._inErased = 0; - result._current = 0; - - AV_POSTCONDITION( result.validate() ); - - return result; - } - } - } - - {// check in buffer - typename _Storage::iterator const greaterEqualInBuffer - = std::lower_bound( _buffer.begin(), _buffer.end(), k, value_comp() ); - - bool const presentInBuffer - = greaterEqualInBuffer != _buffer.end() - && key_comp()( k, greaterEqualInBuffer->first ) == false; - - if( presentInBuffer ) - { - _FindImplResult result; - result._inStorage = greaterEqualInStorage; - result._inBuffer = greaterEqualInBuffer; - result._inErased = 0; - result._current = greaterEqualInBuffer; - - AV_POSTCONDITION( result.validate() ); - - return result; - } - else - { - _FindImplResult result; - result._inStorage = 0; - result._inBuffer = 0; - result._inErased = 0; - result._current = 0; - - AV_POSTCONDITION( result.validate() ); - - return result; - } - } -} - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::iterator -AssocVector< _Key, _Mapped, _Cmp, _Allocator >::find( _Key const & k ) -{ - _FindImplResult const result = findImpl( k ); - - if( result._current == 0 ){ - return end(); - } - else - { - return iterator( - this - , result._inStorage - , result._inBuffer - , result._inErased - , result._current - ); - } -} + template + inline bool operator<=(const AssocVector& lhs, + const AssocVector& rhs) + { return !(rhs < lhs); } -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::reference -AssocVector< _Key, _Mapped, _Cmp, _Allocator >::at( _Key const & k ) -{ - _FindImplResult const result = findImpl( k ); - - if( result._current == 0 ){ - throw std::out_of_range( "AssocVector::at" ); - } - else - { - return result._current->second; - } -} - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::const_reference -AssocVector< _Key, _Mapped, _Cmp, _Allocator >::at( _Key const & k )const -{ - typedef AssocVector< _Key, _Mapped, _Cmp, _Allocator > * NonConstThis; - - return const_cast< NonConstThis >( this )->at( k ); -} - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::const_iterator -AssocVector< _Key, _Mapped, _Cmp, _Allocator >::find( _Key const & k )const -{ - typedef AssocVector< _Key, _Mapped, _Cmp, _Allocator > * NonConstThis; - - return const_cast< NonConstThis >( this )->find( k ); -} - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::iterator -AssocVector< _Key, _Mapped, _Cmp, _Allocator >::lower_bound( _Key const & k ) -{ - typename _Storage::iterator const greaterEqualInStorage - = std::lower_bound( _storage.begin(), _storage.end(), k, value_comp() ); - - typename _Storage::iterator const greaterEqualInBuffer - = std::lower_bound( _buffer.begin(), _buffer.end(), k, value_comp() ); - - return iterator( this, greaterEqualInStorage, greaterEqualInBuffer, 0, 0 ); -} - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::const_iterator -AssocVector< _Key, _Mapped, _Cmp, _Allocator >::lower_bound( _Key const & k )const -{ - typedef AssocVector< _Key, _Mapped, _Cmp, _Allocator > * NonConstThis; - - return const_cast< NonConstThis >( this )->lower_bound( k ); -} - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::iterator -AssocVector< _Key, _Mapped, _Cmp, _Allocator >::upper_bound( _Key const & k ) -{ - typename _Storage::iterator const greaterInStorage - = std::upper_bound( _storage.begin(), _storage.end(), k, value_comp() ); - - typename _Storage::iterator const greaterInBuffer - = std::upper_bound( _buffer.begin(), _buffer.end(), k, value_comp() ); - - return iterator( this, greaterInStorage, greaterInBuffer, 0, 0 ); -} - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::const_iterator -AssocVector< _Key, _Mapped, _Cmp, _Allocator >::upper_bound( _Key const & k )const -{ - typedef AssocVector< _Key, _Mapped, _Cmp, _Allocator > * NonConstThis; - - return const_cast< NonConstThis >( this )->upper_bound( k ); -} - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -std::pair< - typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::iterator - , typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::iterator -> -AssocVector< _Key, _Mapped, _Cmp, _Allocator >::equal_range( _Key const & k ) -{ - typename _Storage::iterator const greaterEqualInStorage - = std::lower_bound( _storage.begin(), _storage.end(), k, value_comp() ); - - bool const notPresentInStorage - = greaterEqualInStorage == _storage.end() - || key_comp()( k, greaterEqualInStorage->first ); - - typename _Storage::iterator const greaterEqualInBuffer - = std::lower_bound( _buffer.begin(), _buffer.end(), k, value_comp() ); - - bool const notPresentInBuffer - = greaterEqualInBuffer == _buffer.end() - || key_comp()( k, greaterEqualInBuffer->first ); - - if( notPresentInStorage == false ) - { - return std::make_pair( - iterator( this, greaterEqualInStorage, greaterEqualInBuffer, 0, 0 ) - , iterator( this, greaterEqualInStorage + 1, greaterEqualInBuffer, 0, 0 ) - ); - } - - if( notPresentInBuffer == false ) - { - return std::make_pair( - iterator( this, greaterEqualInStorage, greaterEqualInBuffer, 0, 0 ) - , iterator( this, greaterEqualInStorage, greaterEqualInBuffer + 1, 0, 0 ) - ); - } - - return std::make_pair( - iterator( this, greaterEqualInStorage, greaterEqualInBuffer, 0, 0 ) - , iterator( this, greaterEqualInStorage, greaterEqualInBuffer, 0, 0 ) - ); -} - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -std::pair< - typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::const_iterator - , typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::const_iterator -> -AssocVector< _Key, _Mapped, _Cmp, _Allocator >::equal_range( _Key const & k )const -{ - typedef AssocVector< _Key, _Mapped, _Cmp, _Allocator > * NonConstThis; - - return const_cast< NonConstThis >( this )->equal_range( k ); -} - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::_iterator -AssocVector< _Key, _Mapped, _Cmp, _Allocator >::_find( _Key const & k ) -{ - return _iterator( findImpl( k )._current ); -} - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::_const_iterator -AssocVector< _Key, _Mapped, _Cmp, _Allocator >::_find( _Key const & k )const -{ - typedef AssocVector< _Key, _Mapped, _Cmp, _Allocator > * NonConstThis; - - return const_cast< NonConstThis >( this )->_find( k ); -} - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -bool -AssocVector< _Key, _Mapped, _Cmp, _Allocator >::validateStorage()const -{ - if( _storage.size() > _storage.capacity() ) - { - AV_ERROR(); - - return false; - } - - if( std::is_sorted( _storage.begin(), _storage.end(), value_comp() ) == false ) - { - AV_ERROR(); - - return false; - } - - return true; -} - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -bool -AssocVector< _Key, _Mapped, _Cmp, _Allocator >::validateBuffer()const -{ - if( _buffer.size() > _buffer.capacity() ) - { - AV_ERROR(); - - return false; - } - - if( _buffer.empty() ){ - return true; - } - - if( std::is_sorted( _buffer.begin(), _buffer.end(), value_comp() ) == false ) - { - AV_ERROR(); - - return false; - } - - if( - util::has_intersection( - _buffer.begin() - , _buffer.end() - , _storage.begin() - , _storage.end() - , value_comp() - ) - ) - { - AV_ERROR(); - - return false; - } - - return true; -} - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -bool -AssocVector< _Key, _Mapped, _Cmp, _Allocator >::validateErased()const -{ - if( _erased.size() > _erased.capacity() ) - { - AV_ERROR(); - - return false; - } - - if( _erased.empty() ){ - return true; - } - - if( - std::is_sorted( - _erased.begin() - , _erased.end() - , std::less< typename _Storage::const_iterator >() - ) == false - ) - { - AV_ERROR(); - - return false; - } - - AV_CHECK( _erased.front() >= _storage.begin() ); - AV_CHECK( _erased.back() < _storage.end() ); - - return true; -} - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -bool -AssocVector< _Key, _Mapped, _Cmp, _Allocator >::validate()const -{ - if( calculateNewBufferCapacity( _storage.capacity() ) != _buffer.capacity() ){ - return false; - } - - if( calculateNewErasedCapacity( _storage.capacity() ) != _erased.capacity() ){ - return false; - } - - return validateStorage() && validateBuffer() && validateErased(); -} - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -void -AssocVector< _Key, _Mapped, _Cmp, _Allocator >::_merge() -{ - if( size() > _storage.capacity() ) - { - reserve( calculateNewStorageCapacity( _storage.capacity() ) ); - - return; - } - - if( _erased.empty() == false ){ - mergeStorageWithErased(); - } - - mergeStorageWithBuffer(); -} - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::reference -AssocVector< _Key, _Mapped, _Cmp, _Allocator >::operator[]( key_type const & k ) -{ - return insert( value_type( k, mapped_type() ) ).first->second; -} - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::reference -AssocVector< _Key, _Mapped, _Cmp, _Allocator >::operator[]( key_type && k ) -{ - return insert( value_type( std::move( k ), mapped_type() ) ).first->second; -} - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -std::size_t -AssocVector< _Key, _Mapped, _Cmp, _Allocator >::count( key_type const & k )const -{ - return _find( k ) ? 1 : 0; -} - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -std::size_t -AssocVector< _Key, _Mapped, _Cmp, _Allocator >::erase( key_type const & k ) -{ - typename _Storage::iterator const foundInStorage - = array::binary_search( _storage.begin(), _storage.end(), k, value_comp() ); - - {//erase from _buffer - if( foundInStorage == _storage.end() ) - { - typename _Storage::iterator const foundInBuffer - = array::binary_search( _buffer.begin(), _buffer.end(), k, value_comp() ); - - if( foundInBuffer == _buffer.end() ) - { - AV_POSTCONDITION( validate() ); - - return 0; - } - else - { - _buffer.erase( foundInBuffer ); - - AV_POSTCONDITION( validate() ); - - return 1; - } - } - } - - {//erase from back - _TryRemoveBackResult const result = tryRemoveStorageBack( foundInStorage ); - - if( result._anyItemRemoved ) - { - if( result._erasedItemRemoved ) - { - AV_POSTCONDITION( validate() ); - - return 0; - } - else - { - AV_POSTCONDITION( validate() ); - - return 1; - } - } - } - - {//erase from _storage - bool const result = tryEraseFromStorage( foundInStorage )._isErased ? 1 : 0; - - AV_POSTCONDITION( validate() ); - - return result; - } -} - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::iterator -AssocVector< _Key, _Mapped, _Cmp, _Allocator >::erase( iterator pos ) -{ - if( pos == end() ){ - return end(); - } - - // iterator::get converts : pair< T1, T2 > * -> pair< T1 const, T2 > * - // revert real iterator type: pair< T1 const, T2 > * -> pair< T1, T2 > * - value_type_mutable * const posBase = reinterpret_cast< value_type_mutable * >( pos.get() ); - - _Key const key = pos->first; - - {//erase from _buffer - if( util::is_between( _buffer.begin(), posBase, _buffer.end() ) ) - { - _buffer.erase( posBase ); - - typename _Storage::iterator const greaterEqualInStorage - = pos.getCurrentInStorage() - ? pos.getCurrentInStorage() - : std::lower_bound( _storage.begin(), _storage.end(), key, value_comp() ); - - AV_POSTCONDITION( validate() ); - - return iterator( this, greaterEqualInStorage, posBase, 0, 0 ); - } - } - - {//item not present in container - if( util::is_between( _storage.begin(), posBase, _storage.end() ) == false ){ - return end(); - } - } - - {//erase from back - _TryRemoveBackResult const result = tryRemoveStorageBack( posBase ); - - if( result._anyItemRemoved ) - { - typename _Storage::iterator const greaterEqualInBuffer - = pos.getCurrentInBuffer() - ? pos.getCurrentInBuffer() - : std::lower_bound( _buffer.begin(), _buffer.end(), key, value_comp() ); - - if( greaterEqualInBuffer == _buffer.end() ) - { - AV_POSTCONDITION( validate() ); - - return end(); - } - else - { - AV_POSTCONDITION( validate() ); - - return iterator( this, _storage.end(), greaterEqualInBuffer, 0, 0 ); - } - } - } - - {//erase from _storage - _TryEraseFromStorageResult const result = tryEraseFromStorage( posBase ); - - if( result._isErased == false ){ - return end(); - } - - typename _Storage::iterator const greaterEqualInBuffer - = pos.getCurrentInBuffer() - ? pos.getCurrentInBuffer() - : std::lower_bound( _buffer.begin(), _buffer.end(), key, value_comp() ); - - if( result._isMerged == false ) - { - AV_POSTCONDITION( validate() ); - - return iterator( this, posBase + 1, greaterEqualInBuffer, result._inErased + 1, 0 ); - } - - typename _Storage::iterator const greaterEqualInStorage - = std::lower_bound( _storage.begin(), _storage.end(), key, value_comp() ); - - AV_POSTCONDITION( validate() ); - - return iterator( this, greaterEqualInStorage, greaterEqualInBuffer, _erased.end(), 0 ); - } -} - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -bool -AssocVector< _Key, _Mapped, _Cmp, _Allocator >::_erase( iterator pos ) -{ - // iterator::get converts : pair< T1, T2 > * -> pair< T1 const, T2 > * - // revert real iterator type: pair< T1 const, T2 > * -> pair< T1, T2 > * - value_type_mutable * const posBase = reinterpret_cast< value_type_mutable * >( pos.get() ); - - {//erase from _buffer - if( util::is_between( _buffer.begin(), posBase, _buffer.end() ) ) - { - _buffer.erase( posBase ); - - AV_POSTCONDITION( validate() ); - - return true; - } - } - - {//item not present in container - if( util::is_between( _storage.begin(), posBase, _storage.end() ) == false ){ - return false; - } - } - - {//erase from back - _TryRemoveBackResult const result = tryRemoveStorageBack( posBase ); - - if( result._anyItemRemoved ) - { - AV_POSTCONDITION( validate() ); - - return true; - } - } - - {//erase from _storage - bool const result = tryEraseFromStorage( posBase )._isErased; - - AV_POSTCONDITION( validate() ); - - return result; - } -} - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -void -AssocVector< _Key, _Mapped, _Cmp, _Allocator >::swap( - AssocVector< _Key, _Mapped, _Cmp, _Allocator > & other -) noexcept -{ - std::swap( _storage, other._storage ); - std::swap( _buffer, other._buffer ); - std::swap( _erased, other._erased ); - - std::swap( _cmp, other._cmp ); -} - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -void -AssocVector< _Key, _Mapped, _Cmp, _Allocator >::mergeStorageWithBuffer() -{ - AV_PRECONDITION( _erased.empty() ); - - array::move_merge( _storage, _buffer, value_comp() ); - - util::destroy_range( _buffer.begin(), _buffer.end() ); - - _buffer.setSize( 0 ); - - AV_POSTCONDITION( _buffer.empty() ); - AV_POSTCONDITION( validateStorage() ); -} - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -void -AssocVector< _Key, _Mapped, _Cmp, _Allocator >::mergeStorageWithErased() -{ - typename _Storage::iterator const end = _storage.end(); - - array::erase_removed( _storage, _erased ); - - util::destroy_range( _storage.end(), end ); - - _erased.setSize( 0 ); - - AV_POSTCONDITION( _erased.empty() ); - AV_POSTCONDITION( validateStorage() ); -} - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -template< - typename __ValueType -> -typename AssocVector< _Key, _Mapped, _Cmp, _Allocator >::_FindOrInsertToBufferResult -AssocVector< _Key, _Mapped, _Cmp, _Allocator >::findOrInsertToBuffer( __ValueType && value ) -{ - typename _Storage::iterator const greaterEqualInBuffer - = std::lower_bound( _buffer.begin(), _buffer.end(), value.first, value_comp() ); - - if( greaterEqualInBuffer != _buffer.end() ) - { - bool const isEqual = _cmp( value.first, greaterEqualInBuffer->first ) == false; - - if( isEqual ) - { - _FindOrInsertToBufferResult result; - result._inBuffer = greaterEqualInBuffer; - result._isInserted = false; - result._isReallocated = false; - - return result; - } - } - - if( _buffer.full() ) - { - _merge(); - - AV_CHECK( _buffer.empty() ); - - _buffer.insert( - _buffer.begin() - , value_type_mutable( std::forward< __ValueType >( value ) ) - ); - - _FindOrInsertToBufferResult result; - result._inBuffer = _buffer.begin(); - result._isInserted = true; - result._isReallocated = true; - - AV_POSTCONDITION( validate() ); - - return result; - } - else - { - _buffer.insert( - greaterEqualInBuffer - , value_type_mutable( std::forward< __ValueType >( value ) ) - ); - - _FindOrInsertToBufferResult result; - result._inBuffer = greaterEqualInBuffer; - result._isInserted = true; - result._isReallocated = false; - - AV_POSTCONDITION( validate() ); - - return result; - } -} - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -std::size_t AssocVector< _Key, _Mapped, _Cmp, _Allocator >::calculateNewBufferCapacity( - std::size_t storageSize -) -{ - return static_cast< std::size_t >( 1.0 * sqrt( storageSize )); -} - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -std::size_t AssocVector< _Key, _Mapped, _Cmp, _Allocator >::calculateNewErasedCapacity( - std::size_t storageSize -) -{ - return calculateNewBufferCapacity( storageSize ); -} - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -std::size_t AssocVector< _Key, _Mapped, _Cmp, _Allocator >::calculateNewStorageCapacity( - std::size_t storageSize -) -{ - if( storageSize == 0 ){ - return 1; - } - else if( storageSize == 1 ){ - // size=1 size=2 - // capacity=1 capacity=2 - // S:[a] S:[ab] - // B:[b] -> reserve -> B:[ ] - // E:[ ] E:[ ] - - return 4; - } - else{ - return 2 * storageSize; - } -} - -template< - typename _Key - , typename _Mapped - , typename _Cmp - , typename _Allocator -> -void AssocVector< _Key, _Mapped, _Cmp, _Allocator >::dump( int width )const -{ - std::cout << "storage: "; - for( unsigned i = 0 ; i < _storage.size() ; ++ i ) - { - if( i > 0 && width > 0 && ( i % width == 0 ) ){ - std::cout << "\n "; - } - - std::cout << " (" << _storage[i].first << "," << _storage[i].second << ")"; - } - - std::cout << std::endl << "buffer: "; - for( unsigned i = 0 ; i < _buffer.size() ; ++ i ) - { - if( i > 0 && width > 0 && ( i % width == 0 ) ){ - std::cout << "\n "; - } - - std::cout << " (" << _buffer[i].first << "," << _buffer[i].second << ")"; - } - - std::cout << std::endl << "erased: "; - for( unsigned i = 0 ; i < _erased.size() ; ++ i ) - { - if( i > 0 && width > 0 && ( i % width == 0 ) ){ - std::cout << "\n "; - } - - std::cout << " (" << (*_erased[i]).first << "," << (*_erased[i]).second << ")"; - } - std::cout << "." << std::endl; -} + // specialized algorithms: + template + void swap(AssocVector& lhs, AssocVector& rhs) + { lhs.swap(rhs); } -#endif +} // namespace Loki diff --git a/kiwi/maptype.h b/kiwi/maptype.h index cf10ff98..f8977ad0 100644 --- a/kiwi/maptype.h +++ b/kiwi/maptype.h @@ -1,5 +1,5 @@ /*----------------------------------------------------------------------------- -| Copyright (c) 2013-2017, Nucleic Development Team. +| Copyright (c) 2013-2019, Nucleic Development Team. | | Distributed under the terms of the Modified BSD License. | @@ -24,14 +24,14 @@ template< typename V, typename C = std::less, typename A = std::allocator< std::pair > > -class MapType -{ -public: - typedef AssocVector Type; - //typedef std::map Type; -private: - MapType(); -}; +using MapType = Loki::AssocVector; + +// template< +// typename K, +// typename V, +// typename C = std::less, +// typename A = std::allocator< std::pair > > +// using MapType = std::map; } // namespace impl diff --git a/kiwi/row.h b/kiwi/row.h index 33b501df..c1fc5550 100644 --- a/kiwi/row.h +++ b/kiwi/row.h @@ -22,7 +22,7 @@ class Row public: - typedef MapType::Type CellMap; + typedef MapType CellMap; Row() : m_constant( 0.0 ) {} diff --git a/kiwi/solverimpl.h b/kiwi/solverimpl.h index 72214f28..9b78392d 100644 --- a/kiwi/solverimpl.h +++ b/kiwi/solverimpl.h @@ -44,13 +44,13 @@ class SolverImpl double constant; }; - typedef MapType::Type VarMap; + typedef MapType VarMap; - typedef MapType::Type RowMap; + typedef MapType RowMap; - typedef MapType::Type CnMap; + typedef MapType CnMap; - typedef MapType::Type EditMap; + typedef MapType EditMap; struct DualOptimizeGuard { diff --git a/py/solver.cpp b/py/solver.cpp index 561ec7cc..cfbfa6c3 100644 --- a/py/solver.cpp +++ b/py/solver.cpp @@ -192,7 +192,7 @@ Solver_reset( Solver* self ) static PyObject* Solver_dump( Solver* self ) { - PyObjectPtr dump_str( PyUnicode_FromString( self->solver.dumps().c_str() ) ); + cppy::ptr dump_str( PyUnicode_FromString( self->solver.dumps().c_str() ) ); PyObject_Print( dump_str.get(), stdout, 0 ); Py_RETURN_NONE; } diff --git a/py/tests/test_constraint.py b/py/tests/test_constraint.py index 449b9b0b..71b8ed97 100644 --- a/py/tests/test_constraint.py +++ b/py/tests/test_constraint.py @@ -52,7 +52,7 @@ def test_constraint_creation2(): with pytest.raises(TypeError) as excinfo: Constraint(v + 1, 1) - assert "unicode" in excinfo.exconly() + assert "str" in excinfo.exconly() with pytest.raises(ValueError) as excinfo: Constraint(v + 1, '!=') From cc6f02b40caebebc96c7ee4d4d0cd8dbab96b1c9 Mon Sep 17 00:00:00 2001 From: MatthieuDartiailh Date: Mon, 25 Mar 2019 22:35:40 -0400 Subject: [PATCH 06/16] docs: add explanation about performance tricks used in kiwi --- docs/source/basis/solver_internals.rst | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/docs/source/basis/solver_internals.rst b/docs/source/basis/solver_internals.rst index a0e82338..2b26e481 100644 --- a/docs/source/basis/solver_internals.rst +++ b/docs/source/basis/solver_internals.rst @@ -170,3 +170,27 @@ Those classes are used internally in constraints and are created automatically by the library. A |Term| represents a variable/symbol and the coefficient that multiplies it, |Expression| represents a sum of terms and a constant value and is used as the left hand side of a constraint. + + +Performance implementation tricks +--------------------------------- + +Map type +^^^^^^^^ + +Kiwi uses maps to represent the state of the solver and to manipulate it. As a +consequence the map type should be fast, with a particular emphasis on +iteration. The C++ standard library provides unordered_map and map that could +be used in kiwi, but none of those are very friendly to the CPU cache. For +this reason, Kiwi uses the AssocVector class implemented in Loki (slightly +updated to respect c++11 standards). The use of this class provides a 2x +speedups over std::map. + + +Symbol representation +^^^^^^^^^^^^^^^^^^^^^ + +Symbol are used in Kiwi to represent the state of the solver. Since solving the +system requires a large number of manipulation of the symbols the operations +have to compile down to an efficient representation. In Kiwi, symbols compile +down to long long meaning that a vector of them fits in a CPU cache line. From 744e4c2217f9c2dc30fb9a572f7a7fd0fbff668c Mon Sep 17 00:00:00 2001 From: MatthieuDartiailh Date: Wed, 8 May 2019 18:53:40 -0400 Subject: [PATCH 07/16] MANIFEST: fix the MANIFEST file --- MANIFEST.in | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/MANIFEST.in b/MANIFEST.in index 175af968..fa33b204 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,6 +1,17 @@ include MANIFEST.in -include COPYING.txt +include LICENSE include README.rst include releasenotes.rst recursive-include kiwi *.h recursive-include py *.cpp *.h +recursive-include docs/source *.rst +recursive-include docs/source *.png +recursive-include docs/source *.py +recursive-include docs/source *.svg +recursive docs/make.bat +recursive docs/Makefile +recursive-include tests *.py +prune .git +prune dist +prune build +prune docs/build From 8c776474a47ae671a176e0304e67f7ea2d6bd238 Mon Sep 17 00:00:00 2001 From: MatthieuDartiailh Date: Fri, 24 May 2019 17:47:51 -0400 Subject: [PATCH 08/16] py: use heap type allocated types and 2-phases module initialization --- py/constraint.cpp | 151 +++++++++++-------------------- py/expression.cpp | 165 +++++++++++++--------------------- py/kiwisolver.cpp | 222 ++++++++++++++++++++++++++++++++++------------ py/solver.cpp | 160 +++++++++++++++++---------------- py/strength.cpp | 114 ++++++++++-------------- py/symbolics.h | 27 +++--- py/term.cpp | 165 +++++++++++++--------------------- py/types.h | 86 +++++++++++------- py/util.h | 13 ++- py/variable.cpp | 171 ++++++++++++++--------------------- 10 files changed, 622 insertions(+), 652 deletions(-) diff --git a/py/constraint.cpp b/py/constraint.cpp index 6726deff..9fc2918b 100644 --- a/py/constraint.cpp +++ b/py/constraint.cpp @@ -1,5 +1,5 @@ /*----------------------------------------------------------------------------- -| Copyright (c) 2013-2018, Nucleic Development Team. +| Copyright (c) 2013-2019, Nucleic Development Team. | | Distributed under the terms of the Modified BSD License. | @@ -7,14 +7,18 @@ |----------------------------------------------------------------------------*/ #include #include -#include #include #include #include "types.h" #include "util.h" +namespace kiwisolver +{ + +namespace +{ -static PyObject* +PyObject* Constraint_new( PyTypeObject* type, PyObject* args, PyObject* kwargs ) { static const char *kwlist[] = { "expression", "op", "strength", 0 }; @@ -46,14 +50,14 @@ Constraint_new( PyTypeObject* type, PyObject* args, PyObject* kwargs ) } -static void +void Constraint_clear( Constraint* self ) { Py_CLEAR( self->expression ); } -static int +int Constraint_traverse( Constraint* self, visitproc visit, void* arg ) { Py_VISIT( self->expression ); @@ -61,7 +65,7 @@ Constraint_traverse( Constraint* self, visitproc visit, void* arg ) } -static void +void Constraint_dealloc( Constraint* self ) { PyObject_GC_UnTrack( self ); @@ -71,7 +75,7 @@ Constraint_dealloc( Constraint* self ) } -static PyObject* +PyObject* Constraint_repr( Constraint* self ) { std::stringstream stream; @@ -103,14 +107,14 @@ Constraint_repr( Constraint* self ) } -static PyObject* +PyObject* Constraint_expression( Constraint* self ) { return cppy::incref( self->expression ); } -static PyObject* +PyObject* Constraint_op( Constraint* self ) { PyObject* res = 0; @@ -130,14 +134,14 @@ Constraint_op( Constraint* self ) } -static PyObject* +PyObject* Constraint_strength( Constraint* self ) { return PyFloat_FromDouble( self->constraint.strength() ); } -static PyObject* +PyObject* Constraint_or( PyObject* pyoldcn, PyObject* value ) { if( !Constraint::TypeCheck( pyoldcn ) ) @@ -145,7 +149,7 @@ Constraint_or( PyObject* pyoldcn, PyObject* value ) double strength; if( !convert_to_strength( value, strength ) ) return 0; - PyObject* pynewcn = PyType_GenericNew( &Constraint_Type, 0, 0 ); + PyObject* pynewcn = PyType_GenericNew( Constraint::TypeObject, 0, 0 ); if( !pynewcn ) return 0; Constraint* oldcn = reinterpret_cast( pyoldcn ); @@ -168,98 +172,47 @@ Constraint_methods[] = { }; -static PyNumberMethods -Constraint_as_number = { - 0, /* nb_add */ - 0, /* nb_subtract */ - 0, /* nb_multiply */ - 0, /* nb_remainder */ - 0, /* nb_divmod */ - 0, /* nb_power */ - 0, /* nb_negative */ - 0, /* nb_positive */ - 0, /* nb_absolute */ - 0, /* nb_bool */ - 0, /* nb_invert */ - 0, /* nb_lshift */ - 0, /* nb_rshift */ - 0, /* nb_and */ - 0, /* nb_xor */ - (binaryfunc)Constraint_or, /* nb_or */ - 0, /* nb_int */ - 0, /* nb_long */ - 0, /* nb_float */ - 0, /* nb_inplace_add */ - 0, /* nb_inplace_subtract */ - 0, /* nb_inplace_multiply */ - 0, /* nb_inplace_remainder */ - 0, /* nb_inplace_power */ - 0, /* nb_inplace_lshift */ - 0, /* nb_inplace_rshift */ - 0, /* nb_inplace_and */ - 0, /* nb_inplace_xor */ - 0, /* nb_inplace_or */ - (binaryfunc)0, /* nb_floor_divide */ - (binaryfunc)0, /* nb_true_divide */ - 0, /* nb_inplace_floor_divide */ - 0, /* nb_inplace_true_divide */ - (unaryfunc)0, /* nb_index */ - (binaryfunc)0, /* nb_matrix_multiply */ - (binaryfunc)0, /* nb_inplace_matrix_multiply */ +PyType_Slot Constraint_Type_slots[] = { + { Py_tp_dealloc, void_cast( Constraint_dealloc ) }, /* tp_dealloc */ + { Py_tp_traverse, void_cast( Constraint_traverse ) }, /* tp_traverse */ + { Py_tp_clear, void_cast( Constraint_clear ) }, /* tp_clear */ + { Py_tp_repr, void_cast( Constraint_repr ) }, /* tp_repr */ + { Py_tp_methods, void_cast( Constraint_methods ) }, /* tp_methods */ + { Py_tp_new, void_cast( Constraint_new ) }, /* tp_new */ + { Py_tp_alloc, void_cast( PyType_GenericAlloc ) }, /* tp_alloc */ + { Py_tp_free, void_cast( PyObject_GC_Del ) }, /* tp_free */ + { Py_nb_or, void_cast( Constraint_or ) }, /* nb_or */ + { 0, 0 }, }; -PyTypeObject Constraint_Type = { - PyVarObject_HEAD_INIT( &PyType_Type, 0 ) - "kiwisolver.Constraint", /* tp_name */ - sizeof( Constraint ), /* tp_basicsize */ - 0, /* tp_itemsize */ - (destructor)Constraint_dealloc, /* tp_dealloc */ - (printfunc)0, /* tp_print */ - (getattrfunc)0, /* tp_getattr */ - (setattrfunc)0, /* tp_setattr */ - ( PyAsyncMethods* )0, /* tp_as_async */ - (reprfunc)Constraint_repr, /* tp_repr */ - (PyNumberMethods*)&Constraint_as_number,/* tp_as_number */ - (PySequenceMethods*)0, /* tp_as_sequence */ - (PyMappingMethods*)0, /* tp_as_mapping */ - (hashfunc)0, /* tp_hash */ - (ternaryfunc)0, /* tp_call */ - (reprfunc)0, /* tp_str */ - (getattrofunc)0, /* tp_getattro */ - (setattrofunc)0, /* tp_setattro */ - (PyBufferProcs*)0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT|Py_TPFLAGS_HAVE_GC|Py_TPFLAGS_BASETYPE, /* tp_flags */ - 0, /* Documentation string */ - (traverseproc)Constraint_traverse, /* tp_traverse */ - (inquiry)Constraint_clear, /* tp_clear */ - (richcmpfunc)0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - (getiterfunc)0, /* tp_iter */ - (iternextfunc)0, /* tp_iternext */ - (struct PyMethodDef*)Constraint_methods,/* tp_methods */ - (struct PyMemberDef*)0, /* tp_members */ - 0, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - (descrgetfunc)0, /* tp_descr_get */ - (descrsetfunc)0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - (initproc)0, /* tp_init */ - (allocfunc)PyType_GenericAlloc, /* tp_alloc */ - (newfunc)Constraint_new, /* tp_new */ - (freefunc)PyObject_GC_Del, /* tp_free */ - (inquiry)0, /* tp_is_gc */ - 0, /* tp_bases */ - 0, /* tp_mro */ - 0, /* tp_cache */ - 0, /* tp_subclasses */ - 0, /* tp_weaklist */ - (destructor)0 /* tp_del */ +} // namespace + + +// Initialize static variables (otherwise the compiler eliminates them) +PyTypeObject* Constraint::TypeObject = NULL; + + +PyType_Spec Constraint::TypeObject_Spec = { + "kiwisolver.Constraint", /* tp_name */ + sizeof( Constraint ), /* tp_basicsize */ + 0, /* tp_itemsize */ + Py_TPFLAGS_DEFAULT| + Py_TPFLAGS_HAVE_GC| + Py_TPFLAGS_BASETYPE, /* tp_flags */ + Constraint_Type_slots /* slots */ }; -int import_constraint() +bool Constraint::Ready() { - return PyType_Ready( &Constraint_Type ); + // The reference will be handled by the module to which we will add the type + TypeObject = pytype_cast( PyType_FromSpec( &TypeObject_Spec ) ); + if( !TypeObject ) + { + return false; + } + return true; } + +} // namespace kiwisolver diff --git a/py/expression.cpp b/py/expression.cpp index 2c682975..36fc48ad 100644 --- a/py/expression.cpp +++ b/py/expression.cpp @@ -1,19 +1,24 @@ /*----------------------------------------------------------------------------- -| Copyright (c) 2013-2018, Nucleic Development Team. +| Copyright (c) 2013-2019, Nucleic Development Team. | | Distributed under the terms of the Modified BSD License. | | The full license is in the file COPYING.txt, distributed with this software. |----------------------------------------------------------------------------*/ #include -#include #include #include "symbolics.h" #include "types.h" #include "util.h" -static PyObject* +namespace kiwisolver +{ + +namespace +{ + +PyObject* Expression_new( PyTypeObject* type, PyObject* args, PyObject* kwargs ) { static const char *kwlist[] = { "terms", "constant", 0 }; @@ -46,14 +51,14 @@ Expression_new( PyTypeObject* type, PyObject* args, PyObject* kwargs ) } -static void +void Expression_clear( Expression* self ) { Py_CLEAR( self->terms ); } -static int +int Expression_traverse( Expression* self, visitproc visit, void* arg ) { Py_VISIT( self->terms ); @@ -61,7 +66,7 @@ Expression_traverse( Expression* self, visitproc visit, void* arg ) } -static void +void Expression_dealloc( Expression* self ) { PyObject_GC_UnTrack( self ); @@ -70,7 +75,7 @@ Expression_dealloc( Expression* self ) } -static PyObject* +PyObject* Expression_repr( Expression* self ) { std::stringstream stream; @@ -88,21 +93,21 @@ Expression_repr( Expression* self ) } -static PyObject* +PyObject* Expression_terms( Expression* self ) { return cppy::incref( self->terms ); } -static PyObject* +PyObject* Expression_constant( Expression* self ) { return PyFloat_FromDouble( self->constant ); } -static PyObject* +PyObject* Expression_value( Expression* self ) { double result = self->constant; @@ -118,42 +123,42 @@ Expression_value( Expression* self ) } -static PyObject* +PyObject* Expression_add( PyObject* first, PyObject* second ) { return BinaryInvoke()( first, second ); } -static PyObject* +PyObject* Expression_sub( PyObject* first, PyObject* second ) { return BinaryInvoke()( first, second ); } -static PyObject* +PyObject* Expression_mul( PyObject* first, PyObject* second ) { return BinaryInvoke()( first, second ); } -static PyObject* +PyObject* Expression_div( PyObject* first, PyObject* second ) { return BinaryInvoke()( first, second ); } -static PyObject* +PyObject* Expression_neg( PyObject* value ) { return UnaryInvoke()( value ); } -static PyObject* +PyObject* Expression_richcmp( PyObject* first, PyObject* second, int op ) { switch( op ) @@ -191,98 +196,52 @@ Expression_methods[] = { }; -static PyNumberMethods -Expression_as_number = { - (binaryfunc)Expression_add, /* nb_add */ - (binaryfunc)Expression_sub, /* nb_subtract */ - (binaryfunc)Expression_mul, /* nb_multiply */ - 0, /* nb_remainder */ - 0, /* nb_divmod */ - 0, /* nb_power */ - (unaryfunc)Expression_neg, /* nb_negative */ - 0, /* nb_positive */ - 0, /* nb_absolute */ - 0, /* nb_bool */ - 0, /* nb_invert */ - 0, /* nb_lshift */ - 0, /* nb_rshift */ - 0, /* nb_and */ - 0, /* nb_xor */ - (binaryfunc)0, /* nb_or */ - 0, /* nb_int */ - (void *)0, /* nb_reserved */ - 0, /* nb_float */ - 0, /* nb_inplace_add */ - 0, /* nb_inplace_subtract */ - 0, /* nb_inplace_multiply */ - 0, /* nb_inplace_remainder */ - 0, /* nb_inplace_power */ - 0, /* nb_inplace_lshift */ - 0, /* nb_inplace_rshift */ - 0, /* nb_inplace_and */ - 0, /* nb_inplace_xor */ - 0, /* nb_inplace_or */ - (binaryfunc)0, /* nb_floor_divide */ - (binaryfunc)Expression_div, /* nb_true_divide */ - 0, /* nb_inplace_floor_divide */ - 0, /* nb_inplace_true_divide */ - (unaryfunc)0, /* nb_index */ - (binaryfunc)0, /* nb_matrix_multiply */ - (binaryfunc)0, /* nb_inplace_matrix_multiply */ +PyType_Slot Expression_Type_slots[] = { + { Py_tp_dealloc, void_cast( Expression_dealloc ) }, /* tp_dealloc */ + { Py_tp_traverse, void_cast( Expression_traverse ) }, /* tp_traverse */ + { Py_tp_clear, void_cast( Expression_clear ) }, /* tp_clear */ + { Py_tp_repr, void_cast( Expression_repr ) }, /* tp_repr */ + { Py_tp_richcompare, void_cast( Expression_richcmp ) }, /* tp_richcompare */ + { Py_tp_methods, void_cast( Expression_methods ) }, /* tp_methods */ + { Py_tp_new, void_cast( Expression_new ) }, /* tp_new */ + { Py_tp_alloc, void_cast( PyType_GenericAlloc ) }, /* tp_alloc */ + { Py_tp_free, void_cast( PyObject_GC_Del ) }, /* tp_free */ + { Py_nb_add, void_cast( Expression_add ) }, /* nb_add */ + { Py_nb_subtract, void_cast( Expression_sub ) }, /* nb_sub */ + { Py_nb_multiply, void_cast( Expression_mul ) }, /* nb_mul */ + { Py_nb_negative, void_cast( Expression_neg ) }, /* nb_neg */ + { Py_nb_true_divide, void_cast( Expression_div ) }, /* nb_div */ + { 0, 0 }, }; -PyTypeObject Expression_Type = { - PyVarObject_HEAD_INIT( &PyType_Type, 0 ) - "kiwisolver.Expression", /* tp_name */ - sizeof( Expression ), /* tp_basicsize */ - 0, /* tp_itemsize */ - (destructor)Expression_dealloc, /* tp_dealloc */ - (printfunc)0, /* tp_print */ - (getattrfunc)0, /* tp_getattr */ - (setattrfunc)0, /* tp_setattr */ - ( PyAsyncMethods* )0, /* tp_as_async */ - (reprfunc)Expression_repr, /* tp_repr */ - (PyNumberMethods*)&Expression_as_number,/* tp_as_number */ - (PySequenceMethods*)0, /* tp_as_sequence */ - (PyMappingMethods*)0, /* tp_as_mapping */ - (hashfunc)0, /* tp_hash */ - (ternaryfunc)0, /* tp_call */ - (reprfunc)0, /* tp_str */ - (getattrofunc)0, /* tp_getattro */ - (setattrofunc)0, /* tp_setattro */ - (PyBufferProcs*)0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT|Py_TPFLAGS_HAVE_GC|Py_TPFLAGS_BASETYPE, - 0, /* Documentation string */ - (traverseproc)Expression_traverse, /* tp_traverse */ - (inquiry)Expression_clear, /* tp_clear */ - (richcmpfunc)Expression_richcmp, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - (getiterfunc)0, /* tp_iter */ - (iternextfunc)0, /* tp_iternext */ - (struct PyMethodDef*)Expression_methods,/* tp_methods */ - (struct PyMemberDef*)0, /* tp_members */ - 0, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - (descrgetfunc)0, /* tp_descr_get */ - (descrsetfunc)0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - (initproc)0, /* tp_init */ - (allocfunc)PyType_GenericAlloc, /* tp_alloc */ - (newfunc)Expression_new, /* tp_new */ - (freefunc)PyObject_GC_Del, /* tp_free */ - (inquiry)0, /* tp_is_gc */ - 0, /* tp_bases */ - 0, /* tp_mro */ - 0, /* tp_cache */ - 0, /* tp_subclasses */ - 0, /* tp_weaklist */ - (destructor)0 /* tp_del */ +} // namespace + + +// Initialize static variables (otherwise the compiler eliminates them) +PyTypeObject* Expression::TypeObject = NULL; + + +PyType_Spec Expression::TypeObject_Spec = { + "kiwisolver.Expression", /* tp_name */ + sizeof( Expression ), /* tp_basicsize */ + 0, /* tp_itemsize */ + Py_TPFLAGS_DEFAULT| + Py_TPFLAGS_HAVE_GC| + Py_TPFLAGS_BASETYPE, /* tp_flags */ + Expression_Type_slots /* slots */ }; -int import_expression() +bool Expression::Ready() { - return PyType_Ready( &Expression_Type ); + // The reference will be handled by the module to which we will add the type + TypeObject = pytype_cast( PyType_FromSpec( &TypeObject_Spec ) ); + if( !TypeObject ) + { + return false; + } + return true; } + +} // namesapce kiwisolver diff --git a/py/kiwisolver.cpp b/py/kiwisolver.cpp index 91699d51..c792f968 100644 --- a/py/kiwisolver.cpp +++ b/py/kiwisolver.cpp @@ -1,77 +1,187 @@ /*----------------------------------------------------------------------------- -| Copyright (c) 2013-2018, Nucleic Development Team. +| Copyright (c) 2013-2019, Nucleic Development Team. | | Distributed under the terms of the Modified BSD License. | | The full license is in the file COPYING.txt, distributed with this software. |----------------------------------------------------------------------------*/ -#include #include #include #include "types.h" #define PY_KIWI_VERSION "1.2.0.dev" -struct module_state { - PyObject *error; -}; +namespace +{ + + +bool ready_types() +{ + using namespace kiwisolver; + if( !Variable::Ready() ) + { + return false; + } + if( !Term::Ready() ) + { + return false; + } + if( !Expression::Ready() ) + { + return false; + } + if( !Constraint::Ready() ) + { + return false; + } + if( !strength::Ready() ) + { + return false; + } + if( !Solver::Ready() ) + { + return false; + } + return true; +} + +bool add_objects( PyObject* mod ) +{ + using namespace kiwisolver; + + cppy::ptr kiwiversion( PyUnicode_FromString( KIWI_VERSION ) ); + if( !kiwiversion ) + { + return false; + } + cppy::ptr pyversion( PyUnicode_FromString( PY_KIWI_VERSION ) ); + if( !pyversion ) + { + return false; + } + cppy::ptr pystrength( PyType_GenericNew( strength::TypeObject, 0, 0 ) ); + if( !pystrength ) + { + return false; + } + + if( PyModule_AddObject( mod, "__version__", pyversion.get() ) < 0 ) + { + return false; + } + pyversion.release(); + + if( PyModule_AddObject( mod, "__kiwi_version__", kiwiversion.get() ) < 0 ) + { + return false; + } + kiwiversion.release(); + + if( PyModule_AddObject( mod, "strength", pystrength.get() ) < 0 ) + { + return false; + } + pystrength.release(); + + // Variable + cppy::ptr var( pyobject_cast( Variable::TypeObject ) ); + if( PyModule_AddObject( mod, "Variable", var.get() ) < 0 ) + { + return false; + } + var.release(); + + // Term + cppy::ptr term( pyobject_cast( Term::TypeObject ) ); + if( PyModule_AddObject( mod, "Term", term.get() ) < 0 ) + { + return false; + } + term.release(); + + // Expression + cppy::ptr expr( pyobject_cast( Expression::TypeObject ) ); + if( PyModule_AddObject( mod, "Expression", expr.get() ) < 0 ) + { + return false; + } + expr.release(); + + // Constraint + cppy::ptr cons( pyobject_cast( Constraint::TypeObject ) ); + if( PyModule_AddObject( mod, "Constraint", cons.get() ) < 0 ) + { + return false; + } + cons.release(); + + cppy::ptr solver( pyobject_cast( Solver::TypeObject ) ); + if( PyModule_AddObject( mod, "Solver", solver.get() ) < 0 ) + { + return false; + } + solver.release(); -static PyMethodDef + PyModule_AddObject( mod, "DuplicateConstraint", DuplicateConstraint ); + PyModule_AddObject( mod, "UnsatisfiableConstraint", UnsatisfiableConstraint ); + PyModule_AddObject( mod, "UnknownConstraint", UnknownConstraint ); + PyModule_AddObject( mod, "DuplicateEditVariable", DuplicateEditVariable ); + PyModule_AddObject( mod, "UnknownEditVariable", UnknownEditVariable ); + PyModule_AddObject( mod, "BadRequiredStrength", BadRequiredStrength ); + + return true; +} + + +int +catom_modexec( PyObject *mod ) +{ + if( !ready_types() ) + { + return -1; + } + if( !kiwisolver::init_exceptions() ) + { + return -1; + } + if( !add_objects( mod ) ) + { + return -1; + } + + + return 0; +} + + +PyMethodDef kiwisolver_methods[] = { { 0 } // Sentinel }; -static struct PyModuleDef kiwisolver_moduledef = { - PyModuleDef_HEAD_INIT, - "kiwisolver", - NULL, - sizeof( struct module_state ), - kiwisolver_methods, - NULL + +PyModuleDef_Slot kiwisolver_slots[] = { + {Py_mod_exec, reinterpret_cast( catom_modexec ) }, + {0, NULL} }; -PyMODINIT_FUNC -PyInit_kiwisolver( void ) + +struct PyModuleDef moduledef = { + PyModuleDef_HEAD_INIT, + "kiwisolver", + "kiwisolver extension module", + 0, + kiwisolver_methods, + kiwisolver_slots, + NULL, + NULL, + NULL +}; + +} // namespace + + +PyMODINIT_FUNC PyInit_kiwisolver( void ) { - PyObject *mod = PyModule_Create( &kiwisolver_moduledef ); - if( !mod ) - return NULL; - if( import_variable() < 0 ) - return NULL; - if( import_term() < 0 ) - return NULL; - if( import_expression() < 0 ) - return NULL; - if( import_constraint() < 0 ) - return NULL; - if( import_solver() < 0 ) - return NULL; - if( import_strength() < 0 ) - return NULL; - PyObject* kiwiversion = PyUnicode_FromString( KIWI_VERSION ); - if( !kiwiversion ) - return NULL; - PyObject* pyversion = PyUnicode_FromString( PY_KIWI_VERSION ); - if( !pyversion ) - return NULL; - PyObject* pystrength = PyType_GenericNew( &strength_Type, 0, 0 ); - if( !pystrength ) - return NULL; - - PyModule_AddObject( mod, "__version__", pyversion ); - PyModule_AddObject( mod, "__kiwi_version__", kiwiversion ); - PyModule_AddObject( mod, "strength", pystrength ); - PyModule_AddObject( mod, "Variable", cppy::incref( pyobject_cast( &Variable_Type ) ) ); - PyModule_AddObject( mod, "Term", cppy::incref( pyobject_cast( &Term_Type ) ) ); - PyModule_AddObject( mod, "Expression", cppy::incref( pyobject_cast( &Expression_Type ) ) ); - PyModule_AddObject( mod, "Constraint", cppy::incref( pyobject_cast( &Constraint_Type ) ) ); - PyModule_AddObject( mod, "Solver", cppy::incref( pyobject_cast( &Solver_Type ) ) ); - PyModule_AddObject( mod, "DuplicateConstraint", cppy::incref( DuplicateConstraint ) ); - PyModule_AddObject( mod, "UnsatisfiableConstraint", cppy::incref( UnsatisfiableConstraint ) ); - PyModule_AddObject( mod, "UnknownConstraint", cppy::incref( UnknownConstraint ) ); - PyModule_AddObject( mod, "DuplicateEditVariable", cppy::incref( DuplicateEditVariable ) ); - PyModule_AddObject( mod, "UnknownEditVariable", cppy::incref( UnknownEditVariable ) ); - PyModule_AddObject( mod, "BadRequiredStrength", cppy::incref( BadRequiredStrength ) ); - - return mod; + return PyModuleDef_Init( &moduledef ); } diff --git a/py/solver.cpp b/py/solver.cpp index cfbfa6c3..c91bcd29 100644 --- a/py/solver.cpp +++ b/py/solver.cpp @@ -1,18 +1,23 @@ /*----------------------------------------------------------------------------- -| Copyright (c) 2013-2018, Nucleic Development Team. +| Copyright (c) 2013-2019, Nucleic Development Team. | | Distributed under the terms of the Modified BSD License. | | The full license is in the file COPYING.txt, distributed with this software. |----------------------------------------------------------------------------*/ -#include #include #include #include "types.h" #include "util.h" -static PyObject* +namespace kiwisolver +{ + +namespace +{ + +PyObject* Solver_new( PyTypeObject* type, PyObject* args, PyObject* kwargs ) { if( PyTuple_GET_SIZE( args ) != 0 || ( kwargs && PyDict_Size( kwargs ) != 0 ) ) @@ -26,7 +31,7 @@ Solver_new( PyTypeObject* type, PyObject* args, PyObject* kwargs ) } -static void +void Solver_dealloc( Solver* self ) { self->solver.~Solver(); @@ -34,7 +39,7 @@ Solver_dealloc( Solver* self ) } -static PyObject* +PyObject* Solver_addConstraint( Solver* self, PyObject* other ) { if( !Constraint::TypeCheck( other ) ) @@ -58,7 +63,7 @@ Solver_addConstraint( Solver* self, PyObject* other ) } -static PyObject* +PyObject* Solver_removeConstraint( Solver* self, PyObject* other ) { if( !Constraint::TypeCheck( other ) ) @@ -77,7 +82,7 @@ Solver_removeConstraint( Solver* self, PyObject* other ) } -static PyObject* +PyObject* Solver_hasConstraint( Solver* self, PyObject* other ) { if( !Constraint::TypeCheck( other ) ) @@ -87,7 +92,7 @@ Solver_hasConstraint( Solver* self, PyObject* other ) } -static PyObject* +PyObject* Solver_addEditVariable( Solver* self, PyObject* args ) { PyObject* pyvar; @@ -118,7 +123,7 @@ Solver_addEditVariable( Solver* self, PyObject* args ) } -static PyObject* +PyObject* Solver_removeEditVariable( Solver* self, PyObject* other ) { if( !Variable::TypeCheck( other ) ) @@ -137,7 +142,7 @@ Solver_removeEditVariable( Solver* self, PyObject* other ) } -static PyObject* +PyObject* Solver_hasEditVariable( Solver* self, PyObject* other ) { if( !Variable::TypeCheck( other ) ) @@ -147,7 +152,7 @@ Solver_hasEditVariable( Solver* self, PyObject* other ) } -static PyObject* +PyObject* Solver_suggestValue( Solver* self, PyObject* args ) { PyObject* pyvar; @@ -173,7 +178,7 @@ Solver_suggestValue( Solver* self, PyObject* args ) } -static PyObject* +PyObject* Solver_updateVariables( Solver* self ) { self->solver.updateVariables(); @@ -181,7 +186,7 @@ Solver_updateVariables( Solver* self ) } -static PyObject* +PyObject* Solver_reset( Solver* self ) { self->solver.reset(); @@ -189,7 +194,7 @@ Solver_reset( Solver* self ) } -static PyObject* +PyObject* Solver_dump( Solver* self ) { cppy::ptr dump_str( PyUnicode_FromString( self->solver.dumps().c_str() ) ); @@ -197,7 +202,7 @@ Solver_dump( Solver* self ) Py_RETURN_NONE; } -static PyObject* +PyObject* Solver_dumps( Solver* self ) { return PyUnicode_FromString( self->solver.dumps().c_str() ); @@ -231,62 +236,45 @@ Solver_methods[] = { }; -PyTypeObject Solver_Type = { - PyVarObject_HEAD_INIT( &PyType_Type, 0 ) - "kiwisolver.Solver", /* tp_name */ - sizeof( Solver ), /* tp_basicsize */ - 0, /* tp_itemsize */ - (destructor)Solver_dealloc, /* tp_dealloc */ - (printfunc)0, /* tp_print */ - (getattrfunc)0, /* tp_getattr */ - (setattrfunc)0, /* tp_setattr */ -#if PY_VERSION_HEX >= 0x03050000 - ( PyAsyncMethods* )0, /* tp_as_async */ -#elif PY_VERSION_HEX >= 0x03000000 - ( void* ) 0, /* tp_reserved */ -#else - ( cmpfunc )0, /* tp_compare */ -#endif - (reprfunc)0, /* tp_repr */ - (PyNumberMethods*)0, /* tp_as_number */ - (PySequenceMethods*)0, /* tp_as_sequence */ - (PyMappingMethods*)0, /* tp_as_mapping */ - (hashfunc)0, /* tp_hash */ - (ternaryfunc)0, /* tp_call */ - (reprfunc)0, /* tp_str */ - (getattrofunc)0, /* tp_getattro */ - (setattrofunc)0, /* tp_setattro */ - (PyBufferProcs*)0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, /* tp_flags */ - 0, /* Documentation string */ - (traverseproc)0, /* tp_traverse */ - (inquiry)0, /* tp_clear */ - (richcmpfunc)0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - (getiterfunc)0, /* tp_iter */ - (iternextfunc)0, /* tp_iternext */ - (struct PyMethodDef*)Solver_methods, /* tp_methods */ - (struct PyMemberDef*)0, /* tp_members */ - 0, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - (descrgetfunc)0, /* tp_descr_get */ - (descrsetfunc)0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - (initproc)0, /* tp_init */ - (allocfunc)PyType_GenericAlloc, /* tp_alloc */ - (newfunc)Solver_new, /* tp_new */ - (freefunc)PyObject_Del, /* tp_free */ - (inquiry)0, /* tp_is_gc */ - 0, /* tp_bases */ - 0, /* tp_mro */ - 0, /* tp_cache */ - 0, /* tp_subclasses */ - 0, /* tp_weaklist */ - (destructor)0 /* tp_del */ +PyType_Slot Solver_Type_slots[] = { + { Py_tp_dealloc, void_cast( Solver_dealloc ) }, /* tp_dealloc */ + { Py_tp_methods, void_cast( Solver_methods ) }, /* tp_methods */ + { Py_tp_new, void_cast( Solver_new ) }, /* tp_new */ + { Py_tp_alloc, void_cast( PyType_GenericAlloc ) }, /* tp_alloc */ + { Py_tp_free, void_cast( PyObject_Del ) }, /* tp_free */ + { 0, 0 }, +}; + + +} // namespace + + +// Initialize static variables (otherwise the compiler eliminates them) +PyTypeObject* Solver::TypeObject = NULL; + + +PyType_Spec Solver::TypeObject_Spec = { + "kiwisolver.Solver", /* tp_name */ + sizeof( Solver ), /* tp_basicsize */ + 0, /* tp_itemsize */ + Py_TPFLAGS_DEFAULT| + Py_TPFLAGS_BASETYPE, /* tp_flags */ + Solver_Type_slots /* slots */ }; +bool Solver::Ready() +{ + // The reference will be handled by the module to which we will add the type + TypeObject = pytype_cast( PyType_FromSpec( &TypeObject_Spec ) ); + if( !TypeObject ) + { + return false; + } + return true; +} + + PyObject* DuplicateConstraint; PyObject* UnsatisfiableConstraint; @@ -300,31 +288,51 @@ PyObject* UnknownEditVariable; PyObject* BadRequiredStrength; -int import_solver() +bool init_exceptions() { DuplicateConstraint = PyErr_NewException( const_cast( "kiwisolver.DuplicateConstraint" ), 0, 0 ); if( !DuplicateConstraint ) - return -1; + { + return false; + } + UnsatisfiableConstraint = PyErr_NewException( const_cast( "kiwisolver.UnsatisfiableConstraint" ), 0, 0 ); if( !UnsatisfiableConstraint ) - return -1; + { + return false; + } + UnknownConstraint = PyErr_NewException( const_cast( "kiwisolver.UnknownConstraint" ), 0, 0 ); if( !UnknownConstraint ) - return -1; + { + return false; + } + DuplicateEditVariable = PyErr_NewException( const_cast( "kiwisolver.DuplicateEditVariable" ), 0, 0 ); if( !DuplicateEditVariable ) - return -1; + { + return false; + } + UnknownEditVariable = PyErr_NewException( const_cast( "kiwisolver.UnknownEditVariable" ), 0, 0 ); if( !UnknownEditVariable ) - return -1; + { + return false; + } + BadRequiredStrength = PyErr_NewException( const_cast( "kiwisolver.BadRequiredStrength" ), 0, 0 ); if( !BadRequiredStrength ) - return -1; - return PyType_Ready( &Solver_Type ); + { + return false; + } + + return true; } + +} // namespace diff --git a/py/strength.cpp b/py/strength.cpp index 0160530d..e4a1a4f6 100644 --- a/py/strength.cpp +++ b/py/strength.cpp @@ -1,11 +1,10 @@ /*----------------------------------------------------------------------------- -| Copyright (c) 2013-2017, Nucleic Development Team. +| Copyright (c) 2013-2019, Nucleic Development Team. | | Distributed under the terms of the Modified BSD License. | | The full license is in the file COPYING.txt, distributed with this software. |----------------------------------------------------------------------------*/ -#include #include #include #include "util.h" @@ -19,48 +18,51 @@ #pragma GCC diagnostic ignored "-Wwrite-strings" #endif -struct strength + +namespace kiwisolver +{ + + +namespace { - PyObject_HEAD; -}; -static void +void strength_dealloc( PyObject* self ) { Py_TYPE( self )->tp_free( self ); } -static PyObject* +PyObject* strength_weak( strength* self ) { return PyFloat_FromDouble( kiwi::strength::weak ); } -static PyObject* +PyObject* strength_medium( strength* self ) { return PyFloat_FromDouble( kiwi::strength::medium ); } -static PyObject* +PyObject* strength_strong( strength* self ) { return PyFloat_FromDouble( kiwi::strength::strong ); } -static PyObject* +PyObject* strength_required( strength* self ) { return PyFloat_FromDouble( kiwi::strength::required ); } -static PyObject* +PyObject* strength_create( strength* self, PyObject* args ) { PyObject* pya; @@ -105,63 +107,43 @@ strength_methods[] = { }; -PyTypeObject strength_Type = { - PyVarObject_HEAD_INIT( &PyType_Type, 0 ) - "kiwisolver.strength", /* tp_name */ - sizeof( strength ), /* tp_basicsize */ - 0, /* tp_itemsize */ - (destructor)strength_dealloc, /* tp_dealloc */ - (printfunc)0, /* tp_print */ - (getattrfunc)0, /* tp_getattr */ - (setattrfunc)0, /* tp_setattr */ -#if PY_VERSION_HEX >= 0x03050000 - ( PyAsyncMethods* )0, /* tp_as_async */ -#elif PY_VERSION_HEX >= 0x03000000 - ( void* ) 0, /* tp_reserved */ -#else - ( cmpfunc )0, /* tp_compare */ -#endif - (reprfunc)0, /* tp_repr */ - (PyNumberMethods*)0, /* tp_as_number */ - (PySequenceMethods*)0, /* tp_as_sequence */ - (PyMappingMethods*)0, /* tp_as_mapping */ - (hashfunc)0, /* tp_hash */ - (ternaryfunc)0, /* tp_call */ - (reprfunc)0, /* tp_str */ - (getattrofunc)0, /* tp_getattro */ - (setattrofunc)0, /* tp_setattro */ - (PyBufferProcs*)0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT, /* tp_flags */ - 0, /* Documentation string */ - (traverseproc)0, /* tp_traverse */ - (inquiry)0, /* tp_clear */ - (richcmpfunc)0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - (getiterfunc)0, /* tp_iter */ - (iternextfunc)0, /* tp_iternext */ - (struct PyMethodDef*)strength_methods, /* tp_methods */ - (struct PyMemberDef*)0, /* tp_members */ - strength_getset, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - (descrgetfunc)0, /* tp_descr_get */ - (descrsetfunc)0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - (initproc)0, /* tp_init */ - (allocfunc)PyType_GenericAlloc, /* tp_alloc */ - (newfunc)0, /* tp_new */ - (freefunc)PyObject_Del, /* tp_free */ - (inquiry)0, /* tp_is_gc */ - 0, /* tp_bases */ - 0, /* tp_mro */ - 0, /* tp_cache */ - 0, /* tp_subclasses */ - 0, /* tp_weaklist */ - (destructor)0 /* tp_del */ + +PyType_Slot strength_Type_slots[] = { + { Py_tp_dealloc, void_cast( strength_dealloc ) }, /* tp_dealloc */ + { Py_tp_getset, void_cast( strength_getset ) }, /* tp_getset */ + { Py_tp_methods, void_cast( strength_methods ) }, /* tp_methods */ + { Py_tp_alloc, void_cast( PyType_GenericAlloc ) }, /* tp_alloc */ + { Py_tp_free, void_cast( PyObject_Del ) }, /* tp_free */ + { 0, 0 }, +}; + + +} // namespace + + +// Initialize static variables (otherwise the compiler eliminates them) +PyTypeObject* strength::TypeObject = NULL; + + +PyType_Spec strength::TypeObject_Spec = { + "kiwisolver.strength", /* tp_name */ + sizeof( strength ), /* tp_basicsize */ + 0, /* tp_itemsize */ + Py_TPFLAGS_DEFAULT, /* tp_flags */ + strength_Type_slots /* slots */ }; -int import_strength() +bool strength::Ready() { - return PyType_Ready( &strength_Type ); + // The reference will be handled by the module to which we will add the type + TypeObject = pytype_cast( PyType_FromSpec( &TypeObject_Spec ) ); + if( !TypeObject ) + { + return false; + } + return true; } + + +} // namespace diff --git a/py/symbolics.h b/py/symbolics.h index eb81c511..92c3337b 100644 --- a/py/symbolics.h +++ b/py/symbolics.h @@ -1,17 +1,19 @@ /*----------------------------------------------------------------------------- -| Copyright (c) 2013-2018, Nucleic Development Team. +| Copyright (c) 2013-2019, Nucleic Development Team. | | Distributed under the terms of the Modified BSD License. | | The full license is in the file COPYING.txt, distributed with this software. |----------------------------------------------------------------------------*/ #pragma once -#include #include #include "types.h" #include "util.h" +namespace kiwisolver +{ + template struct UnaryInvoke { @@ -86,7 +88,7 @@ struct BinaryMul template<> inline PyObject* BinaryMul::operator()( Variable* first, double second ) { - PyObject* pyterm = PyType_GenericNew( &Term_Type, 0, 0 ); + PyObject* pyterm = PyType_GenericNew( Term::TypeObject, 0, 0 ); if( !pyterm ) return 0; Term* term = reinterpret_cast( pyterm ); @@ -99,7 +101,7 @@ PyObject* BinaryMul::operator()( Variable* first, double second ) template<> inline PyObject* BinaryMul::operator()( Term* first, double second ) { - PyObject* pyterm = PyType_GenericNew( &Term_Type, 0, 0 ); + PyObject* pyterm = PyType_GenericNew( Term::TypeObject, 0, 0 ); if( !pyterm ) return 0; Term* term = reinterpret_cast( pyterm ); @@ -112,7 +114,7 @@ PyObject* BinaryMul::operator()( Term* first, double second ) template<> inline PyObject* BinaryMul::operator()( Expression* first, double second ) { - cppy::ptr pyexpr( PyType_GenericNew( &Expression_Type, 0, 0 ) ); + cppy::ptr pyexpr( PyType_GenericNew( Expression::TypeObject, 0, 0 ) ); if( !pyexpr ) return 0; Expression* expr = reinterpret_cast( pyexpr.get() ); @@ -247,7 +249,7 @@ struct BinaryAdd template<> inline PyObject* BinaryAdd::operator()( Expression* first, Expression* second ) { - cppy::ptr pyexpr( PyType_GenericNew( &Expression_Type, 0, 0 ) ); + cppy::ptr pyexpr( PyType_GenericNew( Expression::TypeObject, 0, 0 ) ); if( !pyexpr ) return 0; Expression* expr = reinterpret_cast( pyexpr.get() ); @@ -262,7 +264,7 @@ PyObject* BinaryAdd::operator()( Expression* first, Expression* second ) template<> inline PyObject* BinaryAdd::operator()( Expression* first, Term* second ) { - cppy::ptr pyexpr( PyType_GenericNew( &Expression_Type, 0, 0 ) ); + cppy::ptr pyexpr( PyType_GenericNew( Expression::TypeObject, 0, 0 ) ); if( !pyexpr ) return 0; PyObject* terms = PyTuple_New( PyTuple_GET_SIZE( first->terms ) + 1 ); @@ -295,7 +297,7 @@ PyObject* BinaryAdd::operator()( Expression* first, Variable* second ) template<> inline PyObject* BinaryAdd::operator()( Expression* first, double second ) { - cppy::ptr pyexpr( PyType_GenericNew( &Expression_Type, 0, 0 ) ); + cppy::ptr pyexpr( PyType_GenericNew( Expression::TypeObject, 0, 0 ) ); if( !pyexpr ) return 0; Expression* expr = reinterpret_cast( pyexpr.get() ); @@ -308,7 +310,7 @@ PyObject* BinaryAdd::operator()( Expression* first, double second ) template<> inline PyObject* BinaryAdd::operator()( Term* first, double second ) { - cppy::ptr pyexpr( PyType_GenericNew( &Expression_Type, 0, 0 ) ); + cppy::ptr pyexpr( PyType_GenericNew( Expression::TypeObject, 0, 0 ) ); if( !pyexpr ) return 0; Expression* expr = reinterpret_cast( pyexpr.get() ); @@ -330,7 +332,7 @@ PyObject* BinaryAdd::operator()( Term* first, Expression* second ) template<> inline PyObject* BinaryAdd::operator()( Term* first, Term* second ) { - cppy::ptr pyexpr( PyType_GenericNew( &Expression_Type, 0, 0 ) ); + cppy::ptr pyexpr( PyType_GenericNew( Expression::TypeObject, 0, 0 ) ); if( !pyexpr ) return 0; Expression* expr = reinterpret_cast( pyexpr.get() ); @@ -570,7 +572,7 @@ PyObject* makecn( T first, U second, kiwi::RelationalOperator op ) cppy::ptr pyexpr( BinarySub()( first, second ) ); if( !pyexpr ) return 0; - cppy::ptr pycn( PyType_GenericNew( &Constraint_Type, 0, 0 ) ); + cppy::ptr pycn( PyType_GenericNew( Constraint::TypeObject, 0, 0 ) ); if( !pycn ) return 0; Constraint* cn = reinterpret_cast( pycn.get() ); @@ -611,3 +613,6 @@ struct CmpGE return makecn( first, second, kiwi::OP_GE ); } }; + + +} // namespace kiwisolver diff --git a/py/term.cpp b/py/term.cpp index e9ec02b0..6d97647d 100644 --- a/py/term.cpp +++ b/py/term.cpp @@ -1,19 +1,26 @@ /*----------------------------------------------------------------------------- -| Copyright (c) 2013-2018, Nucleic Development Team. +| Copyright (c) 2013-2019, Nucleic Development Team. | | Distributed under the terms of the Modified BSD License. | | The full license is in the file COPYING.txt, distributed with this software. |----------------------------------------------------------------------------*/ #include -#include #include #include "symbolics.h" #include "types.h" #include "util.h" -static PyObject* +namespace kiwisolver +{ + + +namespace +{ + + +PyObject* Term_new( PyTypeObject* type, PyObject* args, PyObject* kwargs ) { static const char *kwlist[] = { "variable", "coefficient", 0 }; @@ -38,14 +45,14 @@ Term_new( PyTypeObject* type, PyObject* args, PyObject* kwargs ) } -static void +void Term_clear( Term* self ) { Py_CLEAR( self->variable ); } -static int +int Term_traverse( Term* self, visitproc visit, void* arg ) { Py_VISIT( self->variable ); @@ -53,7 +60,7 @@ Term_traverse( Term* self, visitproc visit, void* arg ) } -static void +void Term_dealloc( Term* self ) { PyObject_GC_UnTrack( self ); @@ -62,7 +69,7 @@ Term_dealloc( Term* self ) } -static PyObject* +PyObject* Term_repr( Term* self ) { std::stringstream stream; @@ -79,14 +86,14 @@ Term_variable( Term* self ) } -static PyObject* +PyObject* Term_coefficient( Term* self ) { return PyFloat_FromDouble( self->coefficient ); } -static PyObject* +PyObject* Term_value( Term* self ) { Variable* pyvar = reinterpret_cast( self->variable ); @@ -94,42 +101,42 @@ Term_value( Term* self ) } -static PyObject* +PyObject* Term_add( PyObject* first, PyObject* second ) { return BinaryInvoke()( first, second ); } -static PyObject* +PyObject* Term_sub( PyObject* first, PyObject* second ) { return BinaryInvoke()( first, second ); } -static PyObject* +PyObject* Term_mul( PyObject* first, PyObject* second ) { return BinaryInvoke()( first, second ); } -static PyObject* +PyObject* Term_div( PyObject* first, PyObject* second ) { return BinaryInvoke()( first, second ); } -static PyObject* +PyObject* Term_neg( PyObject* value ) { return UnaryInvoke()( value ); } -static PyObject* +PyObject* Term_richcmp( PyObject* first, PyObject* second, int op ) { switch( op ) @@ -167,98 +174,52 @@ Term_methods[] = { }; -static PyNumberMethods -Term_as_number = { - (binaryfunc)Term_add, /* nb_add */ - (binaryfunc)Term_sub, /* nb_subtract */ - (binaryfunc)Term_mul, /* nb_multiply */ - 0, /* nb_remainder */ - 0, /* nb_divmod */ - 0, /* nb_power */ - (unaryfunc)Term_neg, /* nb_negative */ - 0, /* nb_positive */ - 0, /* nb_absolute */ - 0, /* nb_bool */ - 0, /* nb_invert */ - 0, /* nb_lshift */ - 0, /* nb_rshift */ - 0, /* nb_and */ - 0, /* nb_xor */ - (binaryfunc)0, /* nb_or */ - 0, /* nb_int */ - 0, /* nb_long */ - 0, /* nb_float */ - 0, /* nb_inplace_add */ - 0, /* nb_inplace_subtract */ - 0, /* nb_inplace_multiply */ - 0, /* nb_inplace_remainder */ - 0, /* nb_inplace_power */ - 0, /* nb_inplace_lshift */ - 0, /* nb_inplace_rshift */ - 0, /* nb_inplace_and */ - 0, /* nb_inplace_xor */ - 0, /* nb_inplace_or */ - (binaryfunc)0, /* nb_floor_divide */ - (binaryfunc)Term_div, /* nb_true_divide */ - 0, /* nb_inplace_floor_divide */ - 0, /* nb_inplace_true_divide */ - (unaryfunc)0, /* nb_index */ - (binaryfunc)0, /* nb_matrix_multiply */ - (binaryfunc)0, /* nb_inplace_matrix_multiply */ +PyType_Slot Term_Type_slots[] = { + { Py_tp_dealloc, void_cast( Term_dealloc ) }, /* tp_dealloc */ + { Py_tp_traverse, void_cast( Term_traverse ) }, /* tp_traverse */ + { Py_tp_clear, void_cast( Term_clear ) }, /* tp_clear */ + { Py_tp_repr, void_cast( Term_repr ) }, /* tp_repr */ + { Py_tp_richcompare, void_cast( Term_richcmp ) }, /* tp_richcompare */ + { Py_tp_methods, void_cast( Term_methods ) }, /* tp_methods */ + { Py_tp_new, void_cast( Term_new ) }, /* tp_new */ + { Py_tp_alloc, void_cast( PyType_GenericAlloc ) }, /* tp_alloc */ + { Py_tp_free, void_cast( PyObject_GC_Del ) }, /* tp_free */ + { Py_nb_add, void_cast( Term_add ) }, /* nb_add */ + { Py_nb_subtract, void_cast( Term_sub ) }, /* nb_subatract */ + { Py_nb_multiply, void_cast( Term_mul ) }, /* nb_multiply */ + { Py_nb_negative, void_cast( Term_neg ) }, /* nb_negative */ + { Py_nb_true_divide, void_cast( Term_div ) }, /* nb_true_divide */ + { 0, 0 }, }; -PyTypeObject Term_Type = { - PyVarObject_HEAD_INIT( &PyType_Type, 0 ) - "kiwisolver.Term", /* tp_name */ - sizeof( Term ), /* tp_basicsize */ - 0, /* tp_itemsize */ - (destructor)Term_dealloc, /* tp_dealloc */ - (printfunc)0, /* tp_print */ - (getattrfunc)0, /* tp_getattr */ - (setattrfunc)0, /* tp_setattr */ - ( PyAsyncMethods* )0, /* tp_as_async */ - (reprfunc)Term_repr, /* tp_repr */ - (PyNumberMethods*)&Term_as_number, /* tp_as_number */ - (PySequenceMethods*)0, /* tp_as_sequence */ - (PyMappingMethods*)0, /* tp_as_mapping */ - (hashfunc)0, /* tp_hash */ - (ternaryfunc)0, /* tp_call */ - (reprfunc)0, /* tp_str */ - (getattrofunc)0, /* tp_getattro */ - (setattrofunc)0, /* tp_setattro */ - (PyBufferProcs*)0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT|Py_TPFLAGS_HAVE_GC|Py_TPFLAGS_BASETYPE, /* tp_flags */ - 0, /* Documentation string */ - (traverseproc)Term_traverse, /* tp_traverse */ - (inquiry)Term_clear, /* tp_clear */ - (richcmpfunc)Term_richcmp, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - (getiterfunc)0, /* tp_iter */ - (iternextfunc)0, /* tp_iternext */ - (struct PyMethodDef*)Term_methods, /* tp_methods */ - (struct PyMemberDef*)0, /* tp_members */ - 0, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - (descrgetfunc)0, /* tp_descr_get */ - (descrsetfunc)0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - (initproc)0, /* tp_init */ - (allocfunc)PyType_GenericAlloc, /* tp_alloc */ - (newfunc)Term_new, /* tp_new */ - (freefunc)PyObject_GC_Del, /* tp_free */ - (inquiry)0, /* tp_is_gc */ - 0, /* tp_bases */ - 0, /* tp_mro */ - 0, /* tp_cache */ - 0, /* tp_subclasses */ - 0, /* tp_weaklist */ - (destructor)0 /* tp_del */ +} // namespace + + +// Initialize static variables (otherwise the compiler eliminates them) +PyTypeObject* Term::TypeObject = NULL; + + +PyType_Spec Term::TypeObject_Spec = { + "kiwisolver.Term", /* tp_name */ + sizeof( Term ), /* tp_basicsize */ + 0, /* tp_itemsize */ + Py_TPFLAGS_DEFAULT| + Py_TPFLAGS_HAVE_GC| + Py_TPFLAGS_BASETYPE, /* tp_flags */ + Term_Type_slots /* slots */ }; -int import_term() +bool Term::Ready() { - return PyType_Ready( &Term_Type ); + // The reference will be handled by the module to which we will add the type + TypeObject = pytype_cast( PyType_FromSpec( &TypeObject_Spec ) ); + if( !TypeObject ) + { + return false; + } + return true; } + +} // namespace kiwisolver diff --git a/py/types.h b/py/types.h index 628efafb..a26fe09a 100644 --- a/py/types.h +++ b/py/types.h @@ -1,5 +1,5 @@ /*----------------------------------------------------------------------------- -| Copyright (c) 2013-2017, Nucleic Development Team. +| Copyright (c) 2013-2019, Nucleic Development Team. | | Distributed under the terms of the Modified BSD License. | @@ -10,30 +10,8 @@ #include -int import_variable(); - -int import_term(); - -int import_expression(); - -int import_constraint(); - -int import_solver(); - -int import_strength(); - - -extern PyTypeObject Variable_Type; - -extern PyTypeObject Term_Type; - -extern PyTypeObject Expression_Type; - -extern PyTypeObject Constraint_Type; - -extern PyTypeObject Solver_Type; - -extern PyTypeObject strength_Type; +namespace kiwisolver +{ extern PyObject* DuplicateConstraint; @@ -48,15 +26,33 @@ extern PyObject* UnknownEditVariable; extern PyObject* BadRequiredStrength; +struct strength +{ + PyObject_HEAD; + + static PyType_Spec TypeObject_Spec; + + static PyTypeObject* TypeObject; + + static bool Ready(); +}; + + struct Variable { PyObject_HEAD PyObject* context; kiwi::Variable variable; + static PyType_Spec TypeObject_Spec; + + static PyTypeObject* TypeObject; + + static bool Ready(); + static bool TypeCheck( PyObject* obj ) { - return PyObject_TypeCheck( obj, &Variable_Type ) != 0; + return PyObject_TypeCheck( obj, TypeObject ) != 0; } }; @@ -67,9 +63,15 @@ struct Term PyObject* variable; double coefficient; + static PyType_Spec TypeObject_Spec; + + static PyTypeObject* TypeObject; + + static bool Ready(); + static bool TypeCheck( PyObject* obj ) { - return PyObject_TypeCheck( obj, &Term_Type ) != 0; + return PyObject_TypeCheck( obj, TypeObject ) != 0; } }; @@ -80,9 +82,15 @@ struct Expression PyObject* terms; double constant; + static PyType_Spec TypeObject_Spec; + + static PyTypeObject* TypeObject; + + static bool Ready(); + static bool TypeCheck( PyObject* obj ) { - return PyObject_TypeCheck( obj, &Expression_Type ) != 0; + return PyObject_TypeCheck( obj, TypeObject ) != 0; } }; @@ -93,9 +101,15 @@ struct Constraint PyObject* expression; kiwi::Constraint constraint; + static PyType_Spec TypeObject_Spec; + + static PyTypeObject* TypeObject; + + static bool Ready(); + static bool TypeCheck( PyObject* obj ) { - return PyObject_TypeCheck( obj, &Constraint_Type ) != 0; + return PyObject_TypeCheck( obj, TypeObject ) != 0; } }; @@ -105,8 +119,20 @@ struct Solver PyObject_HEAD kiwi::Solver solver; + static PyType_Spec TypeObject_Spec; + + static PyTypeObject* TypeObject; + + static bool Ready(); + static bool TypeCheck( PyObject* obj ) { - return PyObject_TypeCheck( obj, &Solver_Type ) != 0; + return PyObject_TypeCheck( obj, TypeObject ) != 0; } }; + + +bool init_exceptions(); + + +} // namespace kiwisolver diff --git a/py/util.h b/py/util.h index 683d6db6..2796ad91 100644 --- a/py/util.h +++ b/py/util.h @@ -8,12 +8,14 @@ #pragma once #include #include -#include #include #include #include "types.h" +namespace kiwisolver +{ + inline bool convert_to_double( PyObject* obj, double& out ) { @@ -68,7 +70,7 @@ convert_to_strength( PyObject* value, double& out ) ); return false; } - return true; + return true; } if( !convert_to_double( value, out ) ) return false; @@ -121,7 +123,7 @@ make_terms( const std::map& coeffs ) iter_t end = coeffs.end(); for( ; it != end; ++it, ++i ) { - PyObject* pyterm = PyType_GenericNew( &Term_Type, 0, 0 ); + PyObject* pyterm = PyType_GenericNew( Term::TypeObject, 0, 0 ); if( !pyterm ) return 0; Term* term = reinterpret_cast( pyterm ); @@ -148,7 +150,7 @@ reduce_expression( PyObject* pyexpr ) // pyexpr must be an Expression cppy::ptr terms( make_terms( coeffs ) ); if( !terms ) return 0; - PyObject* pynewexpr = PyType_GenericNew( &Expression_Type, 0, 0 ); + PyObject* pynewexpr = PyType_GenericNew( Expression::TypeObject, 0, 0 ); if( !pynewexpr ) return 0; Expression* newexpr = reinterpret_cast( pynewexpr ); @@ -196,3 +198,6 @@ pyop_str( int op ) return ""; } } + + +} // namespace kiwisolver diff --git a/py/variable.cpp b/py/variable.cpp index 855729d6..106cf3bf 100644 --- a/py/variable.cpp +++ b/py/variable.cpp @@ -1,11 +1,10 @@ /*----------------------------------------------------------------------------- -| Copyright (c) 2013-2018, Nucleic Development Team. +| Copyright (c) 2013-2019, Nucleic Development Team. | | Distributed under the terms of the Modified BSD License. | | The full license is in the file COPYING.txt, distributed with this software. |----------------------------------------------------------------------------*/ -#include #include #include #include "symbolics.h" @@ -13,7 +12,15 @@ #include "util.h" -static PyObject* +namespace kiwisolver +{ + + +namespace +{ + + +PyObject* Variable_new( PyTypeObject* type, PyObject* args, PyObject* kwargs ) { static const char *kwlist[] = { "name", "context", 0 }; @@ -50,14 +57,14 @@ Variable_new( PyTypeObject* type, PyObject* args, PyObject* kwargs ) } -static void +void Variable_clear( Variable* self ) { Py_CLEAR( self->context ); } -static int +int Variable_traverse( Variable* self, visitproc visit, void* arg ) { Py_VISIT( self->context ); @@ -65,7 +72,7 @@ Variable_traverse( Variable* self, visitproc visit, void* arg ) } -static void +void Variable_dealloc( Variable* self ) { PyObject_GC_UnTrack( self ); @@ -75,21 +82,21 @@ Variable_dealloc( Variable* self ) } -static PyObject* +PyObject* Variable_repr( Variable* self ) { return PyUnicode_FromString( self->variable.name().c_str() ); } -static PyObject* +PyObject* Variable_name( Variable* self ) { return PyUnicode_FromString( self->variable.name().c_str() ); } -static PyObject* +PyObject* Variable_setName( Variable* self, PyObject* pystr ) { if( !PyUnicode_Check( pystr ) ) @@ -102,7 +109,7 @@ Variable_setName( Variable* self, PyObject* pystr ) } -static PyObject* +PyObject* Variable_context( Variable* self ) { if( self->context ) @@ -111,7 +118,7 @@ Variable_context( Variable* self ) } -static PyObject* +PyObject* Variable_setContext( Variable* self, PyObject* value ) { if( value != self->context ) @@ -124,49 +131,49 @@ Variable_setContext( Variable* self, PyObject* value ) } -static PyObject* +PyObject* Variable_value( Variable* self ) { return PyFloat_FromDouble( self->variable.value() ); } -static PyObject* +PyObject* Variable_add( PyObject* first, PyObject* second ) { return BinaryInvoke()( first, second ); } -static PyObject* +PyObject* Variable_sub( PyObject* first, PyObject* second ) { return BinaryInvoke()( first, second ); } -static PyObject* +PyObject* Variable_mul( PyObject* first, PyObject* second ) { return BinaryInvoke()( first, second ); } -static PyObject* +PyObject* Variable_div( PyObject* first, PyObject* second ) { return BinaryInvoke()( first, second ); } -static PyObject* +PyObject* Variable_neg( PyObject* value ) { return UnaryInvoke()( value ); } -static PyObject* +PyObject* Variable_richcmp( PyObject* first, PyObject* second, int op ) { switch( op ) @@ -208,98 +215,52 @@ Variable_methods[] = { }; -static PyNumberMethods -Variable_as_number = { - (binaryfunc)Variable_add, /* nb_add */ - (binaryfunc)Variable_sub, /* nb_subtract */ - (binaryfunc)Variable_mul, /* nb_multiply */ - 0, /* nb_remainder */ - 0, /* nb_divmod */ - 0, /* nb_power */ - (unaryfunc)Variable_neg, /* nb_negative */ - 0, /* nb_positive */ - 0, /* nb_absolute */ - 0, /* nb_bool */ - 0, /* nb_invert */ - 0, /* nb_lshift */ - 0, /* nb_rshift */ - 0, /* nb_and */ - 0, /* nb_xor */ - (binaryfunc)0, /* nb_or */ - 0, /* nb_int */ - 0, /* nb_long */ - 0, /* nb_float */ - 0, /* nb_inplace_add */ - 0, /* nb_inplace_subtract */ - 0, /* nb_inplace_multiply */ - 0, /* nb_inplace_remainder */ - 0, /* nb_inplace_power */ - 0, /* nb_inplace_lshift */ - 0, /* nb_inplace_rshift */ - 0, /* nb_inplace_and */ - 0, /* nb_inplace_xor */ - 0, /* nb_inplace_or */ - (binaryfunc)0, /* nb_floor_divide */ - (binaryfunc)Variable_div, /* nb_true_divide */ - 0, /* nb_inplace_floor_divide */ - 0, /* nb_inplace_true_divide */ - (unaryfunc)0, /* nb_index */ - (binaryfunc)0, /* nb_matrix_multiply */ - (binaryfunc)0, /* nb_inplace_matrix_multiply */ +PyType_Slot Variable_Type_slots[] = { + { Py_tp_dealloc, void_cast( Variable_dealloc ) }, /* tp_dealloc */ + { Py_tp_traverse, void_cast( Variable_traverse ) }, /* tp_traverse */ + { Py_tp_clear, void_cast( Variable_clear ) }, /* tp_clear */ + { Py_tp_repr, void_cast( Variable_repr ) }, /* tp_repr */ + { Py_tp_richcompare, void_cast( Variable_richcmp ) }, /* tp_richcompare */ + { Py_tp_methods, void_cast( Variable_methods ) }, /* tp_methods */ + { Py_tp_new, void_cast( Variable_new ) }, /* tp_new */ + { Py_tp_alloc, void_cast( PyType_GenericAlloc ) }, /* tp_alloc */ + { Py_tp_free, void_cast( PyObject_GC_Del ) }, /* tp_free */ + { Py_nb_add, void_cast( Variable_add ) }, /* nb_add */ + { Py_nb_subtract, void_cast( Variable_sub ) }, /* nb_subtract */ + { Py_nb_multiply, void_cast( Variable_mul ) }, /* nb_multiply */ + { Py_nb_negative, void_cast( Variable_neg ) }, /* nb_negative */ + { Py_nb_true_divide, void_cast( Variable_div ) }, /* nb_true_divide */ + { 0, 0 }, }; -PyTypeObject Variable_Type = { - PyVarObject_HEAD_INIT( &PyType_Type, 0 ) - "kiwisolver.Variable", /* tp_name */ - sizeof( Variable ), /* tp_basicsize */ - 0, /* tp_itemsize */ - (destructor)Variable_dealloc, /* tp_dealloc */ - (printfunc)0, /* tp_print */ - (getattrfunc)0, /* tp_getattr */ - (setattrfunc)0, /* tp_setattr */ - ( PyAsyncMethods* )0, /* tp_as_async */ - (reprfunc)Variable_repr, /* tp_repr */ - (PyNumberMethods*)&Variable_as_number, /* tp_as_number */ - (PySequenceMethods*)0, /* tp_as_sequence */ - (PyMappingMethods*)0, /* tp_as_mapping */ - (hashfunc)0, /* tp_hash */ - (ternaryfunc)0, /* tp_call */ - (reprfunc)0, /* tp_str */ - (getattrofunc)0, /* tp_getattro */ - (setattrofunc)0, /* tp_setattro */ - (PyBufferProcs*)0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT|Py_TPFLAGS_HAVE_GC|Py_TPFLAGS_BASETYPE, /* tp_flags */ - 0, /* Documentation string */ - (traverseproc)Variable_traverse, /* tp_traverse */ - (inquiry)Variable_clear, /* tp_clear */ - (richcmpfunc)Variable_richcmp, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - (getiterfunc)0, /* tp_iter */ - (iternextfunc)0, /* tp_iternext */ - (struct PyMethodDef*)Variable_methods, /* tp_methods */ - (struct PyMemberDef*)0, /* tp_members */ - 0, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - (descrgetfunc)0, /* tp_descr_get */ - (descrsetfunc)0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - (initproc)0, /* tp_init */ - (allocfunc)PyType_GenericAlloc, /* tp_alloc */ - (newfunc)Variable_new, /* tp_new */ - (freefunc)PyObject_GC_Del, /* tp_free */ - (inquiry)0, /* tp_is_gc */ - 0, /* tp_bases */ - 0, /* tp_mro */ - 0, /* tp_cache */ - 0, /* tp_subclasses */ - 0, /* tp_weaklist */ - (destructor)0 /* tp_del */ +} // namespace + + +// Initialize static variables (otherwise the compiler eliminates them) +PyTypeObject* Variable::TypeObject = NULL; + + +PyType_Spec Variable::TypeObject_Spec = { + "kiwisolver.Variable", /* tp_name */ + sizeof( Variable ), /* tp_basicsize */ + 0, /* tp_itemsize */ + Py_TPFLAGS_DEFAULT| + Py_TPFLAGS_HAVE_GC| + Py_TPFLAGS_BASETYPE, /* tp_flags */ + Variable_Type_slots /* slots */ }; -int import_variable() +bool Variable::Ready() { - return PyType_Ready( &Variable_Type ); + // The reference will be handled by the module to which we will add the type + TypeObject = pytype_cast( PyType_FromSpec( &TypeObject_Spec ) ); + if( !TypeObject ) + { + return false; + } + return true; } + +} // namespace kiwisolver From 77b5aa15382fde7c586eff2c7c7dd89dce2b4e37 Mon Sep 17 00:00:00 2001 From: MatthieuDartiailh Date: Thu, 30 May 2019 22:41:31 -0400 Subject: [PATCH 09/16] setup: use libc++ on OSX --- .travis.yml | 1 - setup.py | 4 ++++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 24068f98..792d520f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -28,7 +28,6 @@ env: - CPPFLAGS=--coverage before_install: - pip install --upgrade pip - - pip install pytest-cov - pip install https://github.com/nucleic/cppy/tarball/nucleic-migration install: - python setup.py develop diff --git a/setup.py b/setup.py index 368a44bf..76da4ebd 100644 --- a/setup.py +++ b/setup.py @@ -5,6 +5,7 @@ # # The full license is in the file COPYING.txt, distributed with this software. #------------------------------------------------------------------------------ +import sys from setuptools import setup, Extension from setuptools.command.build_ext import build_ext @@ -49,6 +50,9 @@ def build_extensions(self): for ext in self.extensions: ext.include_dirs.insert(0, cppy.get_include()) ext.extra_compile_args = opts + if sys.platform == 'darwin': + ext.extra_compile_args += ['-stdlib=libc++'] + ext.extra_link_args += ['-stdlib=libc++'] build_ext.build_extensions(self) From fe49963b81caa35392c3908ab4c2deeba4af29bb Mon Sep 17 00:00:00 2001 From: MatthieuDartiailh Date: Wed, 17 Jul 2019 19:41:44 -0400 Subject: [PATCH 10/16] py: clean up the use of the static keyword --- py/constraint.cpp | 2 +- py/expression.cpp | 2 +- py/kiwisolver.cpp | 2 +- py/solver.cpp | 2 +- py/strength.cpp | 2 +- py/term.cpp | 4 ++-- py/variable.cpp | 2 +- 7 files changed, 8 insertions(+), 8 deletions(-) diff --git a/py/constraint.cpp b/py/constraint.cpp index 9fc2918b..08b8376a 100644 --- a/py/constraint.cpp +++ b/py/constraint.cpp @@ -172,7 +172,7 @@ Constraint_methods[] = { }; -PyType_Slot Constraint_Type_slots[] = { +static PyType_Slot Constraint_Type_slots[] = { { Py_tp_dealloc, void_cast( Constraint_dealloc ) }, /* tp_dealloc */ { Py_tp_traverse, void_cast( Constraint_traverse ) }, /* tp_traverse */ { Py_tp_clear, void_cast( Constraint_clear ) }, /* tp_clear */ diff --git a/py/expression.cpp b/py/expression.cpp index 36fc48ad..5e3fcb24 100644 --- a/py/expression.cpp +++ b/py/expression.cpp @@ -196,7 +196,7 @@ Expression_methods[] = { }; -PyType_Slot Expression_Type_slots[] = { +static PyType_Slot Expression_Type_slots[] = { { Py_tp_dealloc, void_cast( Expression_dealloc ) }, /* tp_dealloc */ { Py_tp_traverse, void_cast( Expression_traverse ) }, /* tp_traverse */ { Py_tp_clear, void_cast( Expression_clear ) }, /* tp_clear */ diff --git a/py/kiwisolver.cpp b/py/kiwisolver.cpp index c792f968..4d4f5023 100644 --- a/py/kiwisolver.cpp +++ b/py/kiwisolver.cpp @@ -154,7 +154,7 @@ catom_modexec( PyObject *mod ) } -PyMethodDef +static PyMethodDef kiwisolver_methods[] = { { 0 } // Sentinel }; diff --git a/py/solver.cpp b/py/solver.cpp index c91bcd29..144aff7b 100644 --- a/py/solver.cpp +++ b/py/solver.cpp @@ -236,7 +236,7 @@ Solver_methods[] = { }; -PyType_Slot Solver_Type_slots[] = { +static PyType_Slot Solver_Type_slots[] = { { Py_tp_dealloc, void_cast( Solver_dealloc ) }, /* tp_dealloc */ { Py_tp_methods, void_cast( Solver_methods ) }, /* tp_methods */ { Py_tp_new, void_cast( Solver_new ) }, /* tp_new */ diff --git a/py/strength.cpp b/py/strength.cpp index e4a1a4f6..85e4ef8f 100644 --- a/py/strength.cpp +++ b/py/strength.cpp @@ -108,7 +108,7 @@ strength_methods[] = { -PyType_Slot strength_Type_slots[] = { +static PyType_Slot strength_Type_slots[] = { { Py_tp_dealloc, void_cast( strength_dealloc ) }, /* tp_dealloc */ { Py_tp_getset, void_cast( strength_getset ) }, /* tp_getset */ { Py_tp_methods, void_cast( strength_methods ) }, /* tp_methods */ diff --git a/py/term.cpp b/py/term.cpp index 6d97647d..6d7a3df0 100644 --- a/py/term.cpp +++ b/py/term.cpp @@ -79,7 +79,7 @@ Term_repr( Term* self ) } -static PyObject* +PyObject* Term_variable( Term* self ) { return cppy::incref( self->variable ); @@ -174,7 +174,7 @@ Term_methods[] = { }; -PyType_Slot Term_Type_slots[] = { +static PyType_Slot Term_Type_slots[] = { { Py_tp_dealloc, void_cast( Term_dealloc ) }, /* tp_dealloc */ { Py_tp_traverse, void_cast( Term_traverse ) }, /* tp_traverse */ { Py_tp_clear, void_cast( Term_clear ) }, /* tp_clear */ diff --git a/py/variable.cpp b/py/variable.cpp index 106cf3bf..24b568a3 100644 --- a/py/variable.cpp +++ b/py/variable.cpp @@ -215,7 +215,7 @@ Variable_methods[] = { }; -PyType_Slot Variable_Type_slots[] = { +static PyType_Slot Variable_Type_slots[] = { { Py_tp_dealloc, void_cast( Variable_dealloc ) }, /* tp_dealloc */ { Py_tp_traverse, void_cast( Variable_traverse ) }, /* tp_traverse */ { Py_tp_clear, void_cast( Variable_clear ) }, /* tp_clear */ From f5d9a7b3d995d9e7a8c3e3057a6db5d5df2461f8 Mon Sep 17 00:00:00 2001 From: MatthieuDartiailh Date: Wed, 17 Jul 2019 19:46:22 -0400 Subject: [PATCH 11/16] setup.py: remove setuptools from install_requires --- setup.py | 1 - 1 file changed, 1 deletion(-) diff --git a/setup.py b/setup.py index 76da4ebd..96ee54c6 100644 --- a/setup.py +++ b/setup.py @@ -75,7 +75,6 @@ def build_extensions(self): 'Programming Language :: Python :: Implementation :: CPython', ], python_requires='>=3.5', - install_requires=['setuptools'], setup_requires=['cppy'], ext_modules=ext_modules, cmdclass={'build_ext': BuildExt}, From d99cb467c93664633ee156e25c846ec99663d8c1 Mon Sep 17 00:00:00 2001 From: MatthieuDartiailh Date: Wed, 17 Jul 2019 19:46:38 -0400 Subject: [PATCH 12/16] README: mention end of Python 2 support --- README.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.rst b/README.rst index 4faa3c32..98dabebe 100644 --- a/README.rst +++ b/README.rst @@ -17,3 +17,6 @@ from 10x to 500x faster than the original Cassowary solver with typical use cases gaining a 40x improvement. Memory savings are consistently > 5x. In addition to the C++ solver, Kiwi ships with hand-rolled Python bindings. + +The version 1.1.0 of the Python bindings will be the last one to support +Python 2, moving forward support will be limited to Python 3.5+. From 855c7b6acbcb275962fb781097e47414b041af66 Mon Sep 17 00:00:00 2001 From: MatthieuDartiailh Date: Fri, 29 Nov 2019 18:00:52 -0500 Subject: [PATCH 13/16] docs: add developer notes --- docs/source/developer_notes/index.rst | 30 +++++++++++++++++++++++++++ docs/source/index.rst | 1 + 2 files changed, 31 insertions(+) create mode 100644 docs/source/developer_notes/index.rst diff --git a/docs/source/developer_notes/index.rst b/docs/source/developer_notes/index.rst new file mode 100644 index 00000000..ab51db13 --- /dev/null +++ b/docs/source/developer_notes/index.rst @@ -0,0 +1,30 @@ +.. _developer: + +Developer notes +================ + +These notes are meant to help developers and contributors with regards to some +details of the implementation and coding style of the project. + +C++ codebase +------------ + +The C++ codebase currently targets C++11 compliance. It is header-only since +one of the focus of the library is speed. + + +Python bindings +--------------- + +Python bindings targets Python 3.6 and above. The bindings are hand-written and +relies on cppy (https://github.com/nucleic/cppy). Kiwisolver tries to use a +reasonably modern C API and to support sub-interpreter, this has a couple of +consequences: + +- static variables use is limited to cases that cannot lead to state leakage + between multiple sub-interpreters +- all the non exported symbol are enclosed in anonymous namespaces +- kiwisolver does not use static types and only dynamical types (note that the + type slots and related structures are stored in a static variable) +- modules use the multi-phases initialization mechanism as defined in + PEP 489 -- Multi-phase extension module initialization diff --git a/docs/source/index.rst b/docs/source/index.rst index ea94e8b6..547bd893 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -21,6 +21,7 @@ In addition to the C++ solver, Kiwi ships with hand-rolled Python bindings. Getting started Use cases + Developer notes API Documentation Indices and tables From 130fb4bb47d2926cb8de8c6eb5d0188e737e968e Mon Sep 17 00:00:00 2001 From: MatthieuDartiailh Date: Fri, 29 Nov 2019 18:01:03 -0500 Subject: [PATCH 14/16] travis: test on 3.8 --- .travis.yml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 792d520f..b556e65a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -16,13 +16,12 @@ branches: jobs: include: - - name: "run test suite with python 3.5" - python: 3.5 - dist: trusty - name: "run test suite with python 3.6" python: 3.6 - name: "run test suite with python 3.7" python: 3.7 + - name: "run test suite with python 3.8" + python: 3.8 env: - CPPFLAGS=--coverage From 15b7bf0de7e9d29b574d491b153c169155506394 Mon Sep 17 00:00:00 2001 From: MatthieuDartiailh Date: Mon, 2 Dec 2019 22:20:53 -0500 Subject: [PATCH 15/16] update release notes and solver version --- kiwi/AssocVector.h | 2 +- kiwi/version.h | 8 ++++---- releasenotes.rst | 7 ++++++- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/kiwi/AssocVector.h b/kiwi/AssocVector.h index e4c5903c..2a509241 100644 --- a/kiwi/AssocVector.h +++ b/kiwi/AssocVector.h @@ -12,7 +12,7 @@ // suitability of this software for any purpose. It is provided "as is" // without express or implied warranty. //////////////////////////////////////////////////////////////////////////////// -// Updated 2019 by Matthieu Dartiialh for C++11 compliancy +// Updated 2019 by Matthieu Dartiailh for C++11 compliancy //////////////////////////////////////////////////////////////////////////////// #pragma once diff --git a/kiwi/version.h b/kiwi/version.h index 197f3ce1..8d83c114 100644 --- a/kiwi/version.h +++ b/kiwi/version.h @@ -1,5 +1,5 @@ /*----------------------------------------------------------------------------- -| Copyright (c) 2013-2017, Nucleic Development Team. +| Copyright (c) 2013-2019, Nucleic Development Team. | | Distributed under the terms of the Modified BSD License. | @@ -8,7 +8,7 @@ #pragma once #define KIWI_MAJOR_VERSION 1 -#define KIWI_MINOR_VERSION 0 -#define KIWI_MICRO_VERSION 1 +#define KIWI_MINOR_VERSION 2 +#define KIWI_MICRO_VERSION 0 #define KIWI_VERSION_HEX 0x010001 -#define KIWI_VERSION "1.1.0" +#define KIWI_VERSION "1.2.0" diff --git a/releasenotes.rst b/releasenotes.rst index 3c4be3c1..f21f7f4d 100644 --- a/releasenotes.rst +++ b/releasenotes.rst @@ -1,13 +1,18 @@ Kiwi Release Notes ================== +Wrappers 1.2.0 | Solver 1.2.0 | unreleased +------------------------------------------ +- make the the c++ part of the code c++11 compliant PR #55 +- use cppy for Python/C bindings PR #55 + Wrappers 1.1.0 | Solver 1.1.0 | 04/24/2019 ------------------------------------------ - prevent attempting a dual optimize on a dummy row PR #56 closes #15 - add ``dump`` and ``dumps`` methods to inspect the internal state of the solver PR #56 - test on Python 3.7 PR #51 -- improvemnts to setup.py and tests PR #46 #50 +- improvements to setup.py and tests PR #46 #50 Wrappers 1.0.1 | Solver 1.0.0 | 10/24/2017 ------------------------------------------ From d47574912c657763b42b2f9c1a99b0e17c7fac76 Mon Sep 17 00:00:00 2001 From: MatthieuDartiailh Date: Wed, 11 Dec 2019 18:31:04 -0500 Subject: [PATCH 16/16] travis: run cppy master --- .travis.yml | 2 +- docs/source/developer_notes/index.rst | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index b556e65a..66d8c0f3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -27,7 +27,7 @@ env: - CPPFLAGS=--coverage before_install: - pip install --upgrade pip - - pip install https://github.com/nucleic/cppy/tarball/nucleic-migration + - pip install https://github.com/nucleic/cppy/tarball/master install: - python setup.py develop script: diff --git a/docs/source/developer_notes/index.rst b/docs/source/developer_notes/index.rst index ab51db13..e532deac 100644 --- a/docs/source/developer_notes/index.rst +++ b/docs/source/developer_notes/index.rst @@ -21,10 +21,9 @@ relies on cppy (https://github.com/nucleic/cppy). Kiwisolver tries to use a reasonably modern C API and to support sub-interpreter, this has a couple of consequences: -- static variables use is limited to cases that cannot lead to state leakage - between multiple sub-interpreters - all the non exported symbol are enclosed in anonymous namespaces - kiwisolver does not use static types and only dynamical types (note that the type slots and related structures are stored in a static variable) - modules use the multi-phases initialization mechanism as defined in PEP 489 -- Multi-phase extension module initialization +- static variables use is limited to type slots, method def