<commit_before>/************************************************************************/
/*                                                                      */
/*       Copyright 2009 by Ullrich Koethe and Hans Meine                */
/*                                                                      */
/*    This file is part of the VIGRA computer vision library.           */
/*    The VIGRA Website is                                              */
/*        http://hci.iwr.uni-heidelberg.de/vigra/                       */
/*    Please direct questions, bug reports, and contributions to        */
/*        ullrich.koethe@iwr.uni-heidelberg.de    or                    */
/*        vigra@informatik.uni-hamburg.de                               */
/*                                                                      */
/*    Permission is hereby granted, free of charge, to any person       */
/*    obtaining a copy of this software and associated documentation    */
/*    files (the "Software"), to deal in the Software without           */
/*    restriction, including without limitation the rights to use,      */
/*    copy, modify, merge, publish, distribute, sublicense, and/or      */
/*    sell copies of the Software, and to permit persons to whom the    */
/*    Software is furnished to do so, subject to the following          */
/*    conditions:                                                       */
/*                                                                      */
/*    The above copyright notice and this permission notice shall be    */
/*    included in all copies or substantial portions of the             */
/*    Software.                                                         */
/*                                                                      */
/*    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND    */
/*    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES   */
/*    OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND          */
/*    NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT       */
/*    HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,      */
/*    WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING      */
/*    FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR     */
/*    OTHER DEALINGS IN THE SOFTWARE.                                   */
/*                                                                      */
/************************************************************************/

#define PY_ARRAY_UNIQUE_SYMBOL vigranumpycore_PyArray_API
#define NO_IMPORT_ARRAY

#include <Python.h>
#include <vigra/matrix.hxx>
#include <vigra/numpy_array.hxx>
#include <vigra/numpy_array_converters.hxx>
#include <boost/python.hpp>
#include <boost/python/to_python_converter.hpp>

namespace python = boost::python;

namespace vigra {

#define VIGRA_NUMPY_TYPECHECKER(type) \
    if(python::object((python::detail::new_reference)PyArray_TypeObjectFromType(type)).ptr() == obj) \
        return obj;

#define VIGRA_NUMPY_TYPECONVERTER(type) \
    if(python::object((python::detail::new_reference)PyArray_TypeObjectFromType(type)).ptr() == obj) \
        typeID = type;

struct NumpyTypenumConverter
{
    NumpyTypenumConverter()
    {
        python::converter::registry::insert(&convertible, &construct,
                                            python::type_id<NPY_TYPES>());
        python::to_python_converter<NPY_TYPES, NumpyTypenumConverter>();
    }

    static void* convertible(PyObject* obj)
    {
        // FIXME: there should be a more elegant way to program this...
        if(obj == 0)
            return 0;
        if(obj->ob_type == &PyArrayDescr_Type)
            return obj;
        if(!PyType_Check(obj))
            return 0;
        VIGRA_NUMPY_TYPECHECKER(NPY_BOOL)
        VIGRA_NUMPY_TYPECHECKER(NPY_INT8)
        VIGRA_NUMPY_TYPECHECKER(NPY_UINT8)
        VIGRA_NUMPY_TYPECHECKER(NPY_INT16)
        VIGRA_NUMPY_TYPECHECKER(NPY_UINT16)
        VIGRA_NUMPY_TYPECHECKER(NPY_INT32)
        VIGRA_NUMPY_TYPECHECKER(NPY_UINT32)
        VIGRA_NUMPY_TYPECHECKER(NPY_INT)
        VIGRA_NUMPY_TYPECHECKER(NPY_UINT)
        VIGRA_NUMPY_TYPECHECKER(NPY_INT64)
        VIGRA_NUMPY_TYPECHECKER(NPY_UINT64)
        VIGRA_NUMPY_TYPECHECKER(NPY_FLOAT32)
        VIGRA_NUMPY_TYPECHECKER(NPY_FLOAT64)
        VIGRA_NUMPY_TYPECHECKER(NPY_LONGDOUBLE)
        VIGRA_NUMPY_TYPECHECKER(NPY_CFLOAT)
        VIGRA_NUMPY_TYPECHECKER(NPY_CDOUBLE)
        VIGRA_NUMPY_TYPECHECKER(NPY_CLONGDOUBLE)
        return 0;
    }

    // from Python
    static void construct(PyObject* obj,
        python::converter::rvalue_from_python_stage1_data* data)
    {
        void* const storage =
            ((python::converter::rvalue_from_python_storage<NumpyAnyArray>* ) data)->storage.bytes;

        // FIXME: there should be a more elegant way to program this...
        int typeID = -1;
        if(obj->ob_type == &PyArrayDescr_Type)
            typeID = (NPY_TYPES)((PyArray_Descr*)obj)->type_num;
        VIGRA_NUMPY_TYPECONVERTER(NPY_BOOL)
        VIGRA_NUMPY_TYPECONVERTER(NPY_INT8)
        VIGRA_NUMPY_TYPECONVERTER(NPY_UINT8)
        VIGRA_NUMPY_TYPECONVERTER(NPY_INT16)
        VIGRA_NUMPY_TYPECONVERTER(NPY_UINT16)
        VIGRA_NUMPY_TYPECONVERTER(NPY_INT32)
        VIGRA_NUMPY_TYPECONVERTER(NPY_UINT32)
        VIGRA_NUMPY_TYPECONVERTER(NPY_INT)
        VIGRA_NUMPY_TYPECONVERTER(NPY_UINT)
        VIGRA_NUMPY_TYPECONVERTER(NPY_INT64)
        VIGRA_NUMPY_TYPECONVERTER(NPY_UINT64)
        VIGRA_NUMPY_TYPECONVERTER(NPY_FLOAT32)
        VIGRA_NUMPY_TYPECONVERTER(NPY_FLOAT64)
        VIGRA_NUMPY_TYPECONVERTER(NPY_LONGDOUBLE)
        VIGRA_NUMPY_TYPECONVERTER(NPY_CFLOAT)
        VIGRA_NUMPY_TYPECONVERTER(NPY_CDOUBLE)
        VIGRA_NUMPY_TYPECONVERTER(NPY_CLONGDOUBLE)

        new (storage) NPY_TYPES((NPY_TYPES)typeID);

        data->convertible = storage;
    }

    // to Python
    static PyObject* convert(NPY_TYPES typeID)
    {
        return PyArray_TypeObjectFromType(typeID);
    }
};

#undef VIGRA_NUMPY_TYPECHECKER
#undef VIGRA_NUMPY_TYPECONVERTER

struct NumpyAnyArrayConverter
{
    NumpyAnyArrayConverter()
    {
        python::converter::registry::insert(&convertible, &construct,
                                            python::type_id<NumpyAnyArray>());
        python::to_python_converter<NumpyAnyArray, NumpyAnyArrayConverter>();
    }

    static void* convertible(PyObject* obj)
    {
        return obj && (obj == Py_None || PyArray_Check(obj))
                 ? obj
                 : 0;
    }

    // from Python
    static void construct(PyObject* obj,
        python::converter::rvalue_from_python_stage1_data* data)
    {
        void* const storage =
            ((python::converter::rvalue_from_python_storage<NumpyAnyArray>* ) data)->storage.bytes;

        if(obj == Py_None)
            obj = 0;

        new (storage) NumpyAnyArray(obj);

        data->convertible = storage;
    }

    static PyObject* convert(NumpyAnyArray const& a)
    {
        return returnNumpyArray(a);
    }
};

namespace detail {

template <int N, class T>
struct MultiArrayShapeConverterTraits
{
    typedef TinyVector<T, N> ShapeType;

    static void construct(void* const storage, PyObject * obj)
    {
        ShapeType * shape = new (storage) ShapeType();
        for(int i=0; i<PySequence_Length(obj); ++i)
            (*shape)[i] = python::extract<T>(PySequence_ITEM(obj, i));
    }
};

template <class T>
struct MultiArrayShapeConverterTraits<0, T>
{
    typedef ArrayVector<T> ShapeType;

    static void construct(void* const storage, PyObject * obj)
    {
        int len = (obj == Py_None)
                           ? 0
                           : PySequence_Length(obj);
        ShapeType * shape = new (storage) ShapeType(len);
        for(int i=0; i<len; ++i)
            (*shape)[i] = python::extract<T>(PySequence_ITEM(obj, i));
    }
};

} // namespace detail

template <int M, class T>
struct MultiArrayShapeConverter
{

    typedef typename detail::MultiArrayShapeConverterTraits<M, T>::ShapeType ShapeType;

    MultiArrayShapeConverter()
    {
        python::converter::registry::insert(&convertible, &construct,
                                            python::type_id<ShapeType>());
        python::to_python_converter<ShapeType, MultiArrayShapeConverter>();
    }

    static void* convertible(PyObject* obj)
    {
        if(obj == 0)
            return 0;
        if(M == 0 && obj == Py_None)
            return obj;
        if(!PySequence_Check(obj) || (M != 0 && PySequence_Length(obj) != M))
            return 0;
        for(int i=0; i<PySequence_Length(obj); ++i)
            if(!PyNumber_Check(PySequence_ITEM(obj, i)))
                return 0;
        return obj;
    }

    // from Python
    static void construct(PyObject* obj,
        python::converter::rvalue_from_python_stage1_data* data)
    {
        void* const storage =
            ((python::converter::rvalue_from_python_storage<ShapeType>* ) data)->storage.bytes;

        detail::MultiArrayShapeConverterTraits<M, T>::construct(storage, obj);
        data->convertible = storage;
    }

    // to Python
    static PyObject* convert(ShapeType const& shape)
    {
        return shapeToPythonTuple(shape).release();
    }
};

python_ptr point2DToPythonTuple(Point2D const & point)
{
    python_ptr tuple(PyTuple_New(2), python_ptr::keep_count);
    pythonToCppException(tuple);
    PyTuple_SET_ITEM((PyTupleObject *)tuple.get(), 0 ,pythonFromData(point.x).release());
    PyTuple_SET_ITEM((PyTupleObject *)tuple.get(), 1 ,pythonFromData(point.y).release());
    return tuple;
}

struct Point2DConverter
{
    Point2DConverter()
    {
        python::converter::registry::insert(&convertible, &construct,
                                            python::type_id<Point2D>());
        python::to_python_converter<Point2D, Point2DConverter>();
    }

    static void* convertible(PyObject* obj)
    {
        if(obj == 0 || !PySequence_Check(obj) || (PySequence_Length(obj) !=2))
            return 0;
        if(!PyNumber_Check(PySequence_Fast_GET_ITEM(obj,0)))
            return 0;
        if(!PyNumber_Check(PySequence_Fast_GET_ITEM(obj,0)))
            return 0;
        return obj;
    }

    //from python
    static void construct(PyObject* obj, python::converter::rvalue_from_python_stage1_data* data)
    {
        void* const storage =
            ((python::converter::rvalue_from_python_storage<Point2D>*) data)->storage.bytes;
        new (storage) Point2D(python::extract<int>(PySequence_Fast_GET_ITEM(obj,0)),
                              python::extract<int>(PySequence_Fast_GET_ITEM(obj,1)));
        data->convertible = storage;
    }

    //to python
    static PyObject* convert(Point2D const& p)
    {
        return point2DToPythonTuple(p).release();
    }

};

void registerNumpyPoint2DConverter()
{
    Point2DConverter();
}

template <class T>
void registerNumpyShapeConvertersOneType()
{
    MultiArrayShapeConverter<0, T>();
    MultiArrayShapeConverter<1, T>();
    MultiArrayShapeConverter<2, T>();
    MultiArrayShapeConverter<3, T>();
    MultiArrayShapeConverter<4, T>();
    MultiArrayShapeConverter<5, T>();
    MultiArrayShapeConverter<6, T>();
    MultiArrayShapeConverter<7, T>();
}

void registerNumpyShapeConvertersAllTypes()
{
    registerNumpyShapeConvertersOneType<MultiArrayIndex>();
    registerNumpyShapeConvertersOneType<float>();
    registerNumpyShapeConvertersOneType<double>();
    if(typeid(npy_intp) != typeid(MultiArrayIndex))
        MultiArrayShapeConverter<0, npy_intp>();
}

#if 0 // FIXME: reimplement to replace the Python versions for consistence?
PyObject *
constructNumpyArrayFromShape(python::object type, ArrayVector<npy_intp> const & shape,
                       unsigned int spatialDimensions, unsigned int channels,
                       NPY_TYPES typeCode, std::string order, bool init)
{
    PyObject * obj = type.ptr();
    vigra_precondition(obj && PyType_Check(obj) && PyType_IsSubtype((PyTypeObject *)obj, &PyArray_Type),
           "constructNumpyArray(type, ...): first argument was not an array type.");
    return detail::constructNumpyArrayImpl((PyTypeObject *)obj, shape, spatialDimensions, channels, typeCode, order, init).release();
}

PyObject *
constructNumpyArrayFromArray(python::object type, NumpyAnyArray array,
                       unsigned int spatialDimensions, unsigned int channels,
                       NPY_TYPES typeCode, std::string order, bool init)
{
    PyObject * obj = type.ptr();
    vigra_precondition(obj && PyType_Check(obj) && PyType_IsSubtype((PyTypeObject *)obj, &PyArray_Type),
           "constructNumpyArray(type, ...): first argument was not an array type.");
    PyObject * res = detail::constructNumpyArrayImpl((PyTypeObject *)obj, array.shape(), spatialDimensions, channels,
                                                     typeCode, order, false, array.strideOrdering()).release();
    if(init)
    {
        NumpyAnyArray lhs(res);
        lhs = array;
    }
    return res;
}
#endif

PyObject *
constructArrayFromAxistags(python::object type, ArrayVector<npy_intp> const & shape,
                           NPY_TYPES typeCode, AxisTags const & axistags, bool init)
{
    PyAxisTags pyaxistags(python_ptr(python::object(axistags).ptr()));

    ArrayVector<npy_intp> norm_shape(shape);
    if(pyaxistags.size() > 0)
    {
        ArrayVector<npy_intp> permutation(pyaxistags.permutationToNormalOrder());
        applyPermutation(permutation.begin(), permutation.end(), shape.begin(), norm_shape.begin());
    }

    TaggedShape tagged_shape(norm_shape, pyaxistags);
    // FIXME: check that type is an array class?
    return constructArray(tagged_shape, typeCode, init, python_ptr(type.ptr()));
}

template <class T>
struct MatrixConverter
{
    typedef linalg::Matrix<T> ArrayType;

    MatrixConverter();

    // to Python
    static PyObject* convert(ArrayType const& a)
    {
        return returnNumpyArray(NumpyArray<2, T>(a));
    }
};

template <class T>
MatrixConverter<T>::MatrixConverter()
{
    using namespace boost::python;

    converter::registration const * reg = converter::registry::query(type_id<ArrayType>());

    // register the to_python_converter only once
    if(!reg || !reg->rvalue_chain)
    {
        to_python_converter<ArrayType, MatrixConverter>();
    }
}

void registerNumpyArrayConverters()
{
    NumpyTypenumConverter();
    registerNumpyShapeConvertersAllTypes();
    registerNumpyPoint2DConverter();
    NumpyAnyArrayConverter();
    MatrixConverter<float>();
    MatrixConverter<double>();

    python::docstring_options doc_options(true, true, false);

    doc_options.disable_all();
    python::def("constructArrayFromAxistags", &constructArrayFromAxistags);
    // python::def("constructNumpyArray", &constructNumpyArrayFromArray);
}

} // namespace vigra

<commit_msg>export TinyVector with short type, too<commit_after>/************************************************************************/
/*                                                                      */
/*       Copyright 2009 by Ullrich Koethe and Hans Meine                */
/*                                                                      */
/*    This file is part of the VIGRA computer vision library.           */
/*    The VIGRA Website is                                              */
/*        http://hci.iwr.uni-heidelberg.de/vigra/                       */
/*    Please direct questions, bug reports, and contributions to        */
/*        ullrich.koethe@iwr.uni-heidelberg.de    or                    */
/*        vigra@informatik.uni-hamburg.de                               */
/*                                                                      */
/*    Permission is hereby granted, free of charge, to any person       */
/*    obtaining a copy of this software and associated documentation    */
/*    files (the "Software"), to deal in the Software without           */
/*    restriction, including without limitation the rights to use,      */
/*    copy, modify, merge, publish, distribute, sublicense, and/or      */
/*    sell copies of the Software, and to permit persons to whom the    */
/*    Software is furnished to do so, subject to the following          */
/*    conditions:                                                       */
/*                                                                      */
/*    The above copyright notice and this permission notice shall be    */
/*    included in all copies or substantial portions of the             */
/*    Software.                                                         */
/*                                                                      */
/*    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND    */
/*    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES   */
/*    OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND          */
/*    NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT       */
/*    HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,      */
/*    WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING      */
/*    FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR     */
/*    OTHER DEALINGS IN THE SOFTWARE.                                   */
/*                                                                      */
/************************************************************************/

#define PY_ARRAY_UNIQUE_SYMBOL vigranumpycore_PyArray_API
#define NO_IMPORT_ARRAY

#include <Python.h>
#include <vigra/matrix.hxx>
#include <vigra/numpy_array.hxx>
#include <vigra/numpy_array_converters.hxx>
#include <boost/python.hpp>
#include <boost/python/to_python_converter.hpp>

namespace python = boost::python;

namespace vigra {

#define VIGRA_NUMPY_TYPECHECKER(type) \
    if(python::object((python::detail::new_reference)PyArray_TypeObjectFromType(type)).ptr() == obj) \
        return obj;

#define VIGRA_NUMPY_TYPECONVERTER(type) \
    if(python::object((python::detail::new_reference)PyArray_TypeObjectFromType(type)).ptr() == obj) \
        typeID = type;

struct NumpyTypenumConverter
{
    NumpyTypenumConverter()
    {
        python::converter::registry::insert(&convertible, &construct,
                                            python::type_id<NPY_TYPES>());
        python::to_python_converter<NPY_TYPES, NumpyTypenumConverter>();
    }

    static void* convertible(PyObject* obj)
    {
        // FIXME: there should be a more elegant way to program this...
        if(obj == 0)
            return 0;
        if(obj->ob_type == &PyArrayDescr_Type)
            return obj;
        if(!PyType_Check(obj))
            return 0;
        VIGRA_NUMPY_TYPECHECKER(NPY_BOOL)
        VIGRA_NUMPY_TYPECHECKER(NPY_INT8)
        VIGRA_NUMPY_TYPECHECKER(NPY_UINT8)
        VIGRA_NUMPY_TYPECHECKER(NPY_INT16)
        VIGRA_NUMPY_TYPECHECKER(NPY_UINT16)
        VIGRA_NUMPY_TYPECHECKER(NPY_INT32)
        VIGRA_NUMPY_TYPECHECKER(NPY_UINT32)
        VIGRA_NUMPY_TYPECHECKER(NPY_INT)
        VIGRA_NUMPY_TYPECHECKER(NPY_UINT)
        VIGRA_NUMPY_TYPECHECKER(NPY_INT64)
        VIGRA_NUMPY_TYPECHECKER(NPY_UINT64)
        VIGRA_NUMPY_TYPECHECKER(NPY_FLOAT32)
        VIGRA_NUMPY_TYPECHECKER(NPY_FLOAT64)
        VIGRA_NUMPY_TYPECHECKER(NPY_LONGDOUBLE)
        VIGRA_NUMPY_TYPECHECKER(NPY_CFLOAT)
        VIGRA_NUMPY_TYPECHECKER(NPY_CDOUBLE)
        VIGRA_NUMPY_TYPECHECKER(NPY_CLONGDOUBLE)
        return 0;
    }

    // from Python
    static void construct(PyObject* obj,
        python::converter::rvalue_from_python_stage1_data* data)
    {
        void* const storage =
            ((python::converter::rvalue_from_python_storage<NumpyAnyArray>* ) data)->storage.bytes;

        // FIXME: there should be a more elegant way to program this...
        int typeID = -1;
        if(obj->ob_type == &PyArrayDescr_Type)
            typeID = (NPY_TYPES)((PyArray_Descr*)obj)->type_num;
        VIGRA_NUMPY_TYPECONVERTER(NPY_BOOL)
        VIGRA_NUMPY_TYPECONVERTER(NPY_INT8)
        VIGRA_NUMPY_TYPECONVERTER(NPY_UINT8)
        VIGRA_NUMPY_TYPECONVERTER(NPY_INT16)
        VIGRA_NUMPY_TYPECONVERTER(NPY_UINT16)
        VIGRA_NUMPY_TYPECONVERTER(NPY_INT32)
        VIGRA_NUMPY_TYPECONVERTER(NPY_UINT32)
        VIGRA_NUMPY_TYPECONVERTER(NPY_INT)
        VIGRA_NUMPY_TYPECONVERTER(NPY_UINT)
        VIGRA_NUMPY_TYPECONVERTER(NPY_INT64)
        VIGRA_NUMPY_TYPECONVERTER(NPY_UINT64)
        VIGRA_NUMPY_TYPECONVERTER(NPY_FLOAT32)
        VIGRA_NUMPY_TYPECONVERTER(NPY_FLOAT64)
        VIGRA_NUMPY_TYPECONVERTER(NPY_LONGDOUBLE)
        VIGRA_NUMPY_TYPECONVERTER(NPY_CFLOAT)
        VIGRA_NUMPY_TYPECONVERTER(NPY_CDOUBLE)
        VIGRA_NUMPY_TYPECONVERTER(NPY_CLONGDOUBLE)

        new (storage) NPY_TYPES((NPY_TYPES)typeID);

        data->convertible = storage;
    }

    // to Python
    static PyObject* convert(NPY_TYPES typeID)
    {
        return PyArray_TypeObjectFromType(typeID);
    }
};

#undef VIGRA_NUMPY_TYPECHECKER
#undef VIGRA_NUMPY_TYPECONVERTER

struct NumpyAnyArrayConverter
{
    NumpyAnyArrayConverter()
    {
        python::converter::registry::insert(&convertible, &construct,
                                            python::type_id<NumpyAnyArray>());
        python::to_python_converter<NumpyAnyArray, NumpyAnyArrayConverter>();
    }

    static void* convertible(PyObject* obj)
    {
        return obj && (obj == Py_None || PyArray_Check(obj))
                 ? obj
                 : 0;
    }

    // from Python
    static void construct(PyObject* obj,
        python::converter::rvalue_from_python_stage1_data* data)
    {
        void* const storage =
            ((python::converter::rvalue_from_python_storage<NumpyAnyArray>* ) data)->storage.bytes;

        if(obj == Py_None)
            obj = 0;

        new (storage) NumpyAnyArray(obj);

        data->convertible = storage;
    }

    static PyObject* convert(NumpyAnyArray const& a)
    {
        return returnNumpyArray(a);
    }
};

namespace detail {

template <int N, class T>
struct MultiArrayShapeConverterTraits
{
    typedef TinyVector<T, N> ShapeType;

    static void construct(void* const storage, PyObject * obj)
    {
        ShapeType * shape = new (storage) ShapeType();
        for(int i=0; i<PySequence_Length(obj); ++i)
            (*shape)[i] = python::extract<T>(PySequence_ITEM(obj, i));
    }
};

template <class T>
struct MultiArrayShapeConverterTraits<0, T>
{
    typedef ArrayVector<T> ShapeType;

    static void construct(void* const storage, PyObject * obj)
    {
        int len = (obj == Py_None)
                           ? 0
                           : PySequence_Length(obj);
        ShapeType * shape = new (storage) ShapeType(len);
        for(int i=0; i<len; ++i)
            (*shape)[i] = python::extract<T>(PySequence_ITEM(obj, i));
    }
};

} // namespace detail

template <int M, class T>
struct MultiArrayShapeConverter
{

    typedef typename detail::MultiArrayShapeConverterTraits<M, T>::ShapeType ShapeType;

    MultiArrayShapeConverter()
    {
        python::converter::registry::insert(&convertible, &construct,
                                            python::type_id<ShapeType>());
        python::to_python_converter<ShapeType, MultiArrayShapeConverter>();
    }

    static void* convertible(PyObject* obj)
    {
        if(obj == 0)
            return 0;
        if(M == 0 && obj == Py_None)
            return obj;
        if(!PySequence_Check(obj) || (M != 0 && PySequence_Length(obj) != M))
            return 0;
        for(int i=0; i<PySequence_Length(obj); ++i)
            if(!PyNumber_Check(PySequence_ITEM(obj, i)))
                return 0;
        return obj;
    }

    // from Python
    static void construct(PyObject* obj,
        python::converter::rvalue_from_python_stage1_data* data)
    {
        void* const storage =
            ((python::converter::rvalue_from_python_storage<ShapeType>* ) data)->storage.bytes;

        detail::MultiArrayShapeConverterTraits<M, T>::construct(storage, obj);
        data->convertible = storage;
    }

    // to Python
    static PyObject* convert(ShapeType const& shape)
    {
        return shapeToPythonTuple(shape).release();
    }
};

python_ptr point2DToPythonTuple(Point2D const & point)
{
    python_ptr tuple(PyTuple_New(2), python_ptr::keep_count);
    pythonToCppException(tuple);
    PyTuple_SET_ITEM((PyTupleObject *)tuple.get(), 0 ,pythonFromData(point.x).release());
    PyTuple_SET_ITEM((PyTupleObject *)tuple.get(), 1 ,pythonFromData(point.y).release());
    return tuple;
}

struct Point2DConverter
{
    Point2DConverter()
    {
        python::converter::registry::insert(&convertible, &construct,
                                            python::type_id<Point2D>());
        python::to_python_converter<Point2D, Point2DConverter>();
    }

    static void* convertible(PyObject* obj)
    {
        if(obj == 0 || !PySequence_Check(obj) || (PySequence_Length(obj) !=2))
            return 0;
        if(!PyNumber_Check(PySequence_Fast_GET_ITEM(obj,0)))
            return 0;
        if(!PyNumber_Check(PySequence_Fast_GET_ITEM(obj,0)))
            return 0;
        return obj;
    }

    //from python
    static void construct(PyObject* obj, python::converter::rvalue_from_python_stage1_data* data)
    {
        void* const storage =
            ((python::converter::rvalue_from_python_storage<Point2D>*) data)->storage.bytes;
        new (storage) Point2D(python::extract<int>(PySequence_Fast_GET_ITEM(obj,0)),
                              python::extract<int>(PySequence_Fast_GET_ITEM(obj,1)));
        data->convertible = storage;
    }

    //to python
    static PyObject* convert(Point2D const& p)
    {
        return point2DToPythonTuple(p).release();
    }

};

void registerNumpyPoint2DConverter()
{
    Point2DConverter();
}

template <class T>
void registerNumpyShapeConvertersOneType()
{
    MultiArrayShapeConverter<0, T>();
    MultiArrayShapeConverter<1, T>();
    MultiArrayShapeConverter<2, T>();
    MultiArrayShapeConverter<3, T>();
    MultiArrayShapeConverter<4, T>();
    MultiArrayShapeConverter<5, T>();
    MultiArrayShapeConverter<6, T>();
    MultiArrayShapeConverter<7, T>();
}

void registerNumpyShapeConvertersAllTypes()
{
    registerNumpyShapeConvertersOneType<MultiArrayIndex>();
    registerNumpyShapeConvertersOneType<float>();
    registerNumpyShapeConvertersOneType<double>();
    registerNumpyShapeConvertersOneType<short>();
    if(typeid(npy_intp) != typeid(MultiArrayIndex))
        MultiArrayShapeConverter<0, npy_intp>();
}

#if 0 // FIXME: reimplement to replace the Python versions for consistence?
PyObject *
constructNumpyArrayFromShape(python::object type, ArrayVector<npy_intp> const & shape,
                       unsigned int spatialDimensions, unsigned int channels,
                       NPY_TYPES typeCode, std::string order, bool init)
{
    PyObject * obj = type.ptr();
    vigra_precondition(obj && PyType_Check(obj) && PyType_IsSubtype((PyTypeObject *)obj, &PyArray_Type),
           "constructNumpyArray(type, ...): first argument was not an array type.");
    return detail::constructNumpyArrayImpl((PyTypeObject *)obj, shape, spatialDimensions, channels, typeCode, order, init).release();
}

PyObject *
constructNumpyArrayFromArray(python::object type, NumpyAnyArray array,
                       unsigned int spatialDimensions, unsigned int channels,
                       NPY_TYPES typeCode, std::string order, bool init)
{
    PyObject * obj = type.ptr();
    vigra_precondition(obj && PyType_Check(obj) && PyType_IsSubtype((PyTypeObject *)obj, &PyArray_Type),
           "constructNumpyArray(type, ...): first argument was not an array type.");
    PyObject * res = detail::constructNumpyArrayImpl((PyTypeObject *)obj, array.shape(), spatialDimensions, channels,
                                                     typeCode, order, false, array.strideOrdering()).release();
    if(init)
    {
        NumpyAnyArray lhs(res);
        lhs = array;
    }
    return res;
}
#endif

PyObject *
constructArrayFromAxistags(python::object type, ArrayVector<npy_intp> const & shape,
                           NPY_TYPES typeCode, AxisTags const & axistags, bool init)
{
    PyAxisTags pyaxistags(python_ptr(python::object(axistags).ptr()));

    ArrayVector<npy_intp> norm_shape(shape);
    if(pyaxistags.size() > 0)
    {
        ArrayVector<npy_intp> permutation(pyaxistags.permutationToNormalOrder());
        applyPermutation(permutation.begin(), permutation.end(), shape.begin(), norm_shape.begin());
    }

    TaggedShape tagged_shape(norm_shape, pyaxistags);
    // FIXME: check that type is an array class?
    return constructArray(tagged_shape, typeCode, init, python_ptr(type.ptr()));
}

template <class T>
struct MatrixConverter
{
    typedef linalg::Matrix<T> ArrayType;

    MatrixConverter();

    // to Python
    static PyObject* convert(ArrayType const& a)
    {
        return returnNumpyArray(NumpyArray<2, T>(a));
    }
};

template <class T>
MatrixConverter<T>::MatrixConverter()
{
    using namespace boost::python;

    converter::registration const * reg = converter::registry::query(type_id<ArrayType>());

    // register the to_python_converter only once
    if(!reg || !reg->rvalue_chain)
    {
        to_python_converter<ArrayType, MatrixConverter>();
    }
}

void registerNumpyArrayConverters()
{
    NumpyTypenumConverter();
    registerNumpyShapeConvertersAllTypes();
    registerNumpyPoint2DConverter();
    NumpyAnyArrayConverter();
    MatrixConverter<float>();
    MatrixConverter<double>();

    python::docstring_options doc_options(true, true, false);

    doc_options.disable_all();
    python::def("constructArrayFromAxistags", &constructArrayFromAxistags);
    // python::def("constructNumpyArray", &constructNumpyArrayFromArray);
}

} // namespace vigra

<|endoftext|>


<commit_before>#include <cstring>
#include <cassert>
#include <string>
#include <atomic>
#include <memory>
#include <set>
#include <boost/asio.hpp>
#include <mutex>

#include "I2PService.h"
#include "Destination.h"
#include "HTTPProxy.h"
#include "util.h"
#include "Identity.h"
#include "Streaming.h"
#include "Destination.h"
#include "ClientContext.h"
#include "I2PEndian.h"
#include "I2PTunnel.h"
#include "Config.h"
#include "HTTP.h"

namespace i2p {
namespace proxy {
	bool str_rmatch(std::string & str, const char *suffix) {
		auto pos = str.rfind (suffix);
		if (pos == std::string::npos)
			return false; /* not found */
		if (str.length() == (pos + std::strlen(suffix)))
			return true; /* match */
		return false;
	}

	static const size_t http_buffer_size = 8192;
	class HTTPReqHandler: public i2p::client::I2PServiceHandler, public std::enable_shared_from_this<HTTPReqHandler>
	{
		private:
			enum state
			{
				GET_METHOD,
				GET_HOSTNAME,
				GET_HTTPV,
				GET_HTTPVNL, //TODO: fallback to finding HOst: header if needed
				DONE
			};

			void EnterState(state nstate);
			bool HandleData(uint8_t *http_buff, std::size_t len);
			void HandleSockRecv(const boost::system::error_code & ecode, std::size_t bytes_transfered);
			void Terminate();
			void AsyncSockRead();
			void HTTPRequestFailed(const char *message);
			void RedirectToJumpService(std::string & host);
			bool ValidateHTTPRequest();
			bool ExtractAddressHelper(i2p::http::URL & url, std::string & b64);
			bool CreateHTTPRequest(uint8_t *http_buff, std::size_t len);
			void SentHTTPFailed(const boost::system::error_code & ecode);
			void HandleStreamRequestComplete (std::shared_ptr<i2p::stream::Stream> stream);

			uint8_t m_http_buff[http_buffer_size];
			std::shared_ptr<boost::asio::ip::tcp::socket> m_sock;
			std::string m_request; //Data left to be sent
			std::string m_url; //URL
			std::string m_method; //Method
			std::string m_version; //HTTP version
			std::string m_address; //Address
			std::string m_path; //Path
			int m_port; //Port
			state m_state;//Parsing state

		public:

			HTTPReqHandler(HTTPProxy * parent, std::shared_ptr<boost::asio::ip::tcp::socket> sock) :
				I2PServiceHandler(parent), m_sock(sock)
				{ EnterState(GET_METHOD); }
			~HTTPReqHandler() { Terminate(); }
			void Handle () { AsyncSockRead(); }
	};

	void HTTPReqHandler::AsyncSockRead()
	{
		LogPrint(eLogDebug, "HTTPProxy: async sock read");
		if (!m_sock) {
			LogPrint(eLogError, "HTTPProxy: no socket for read");
			return;
		}
		m_sock->async_receive(boost::asio::buffer(m_http_buff, http_buffer_size),
					std::bind(&HTTPReqHandler::HandleSockRecv, shared_from_this(),
							std::placeholders::_1, std::placeholders::_2));
	}

	void HTTPReqHandler::Terminate() {
		if (Kill()) return;
		if (m_sock)
		{
			LogPrint(eLogDebug, "HTTPProxy: close sock");
			m_sock->close();
			m_sock = nullptr;
		}
		Done(shared_from_this());
	}

	/* All hope is lost beyond this point */
	//TODO: handle this apropriately
	void HTTPReqHandler::HTTPRequestFailed(const char *message)
	{
		i2p::http::HTTPRes res;
		res.code = 500;
		res.add_header("Content-Type", "text/plain");
		res.add_header("Connection", "close");
		res.body = message;
		res.body += "\r\n";
		std::string response = res.to_string();
		boost::asio::async_write(*m_sock, boost::asio::buffer(response),
					 std::bind(&HTTPReqHandler::SentHTTPFailed, shared_from_this(), std::placeholders::_1));
	}

	void HTTPReqHandler::RedirectToJumpService(std::string & host)
	{
		i2p::http::HTTPRes res;
		i2p::http::URL url;

		/* TODO: don't redirect to webconsole, it's not always work, handle jumpservices here */
		i2p::config::GetOption("http.address", url.host);
		i2p::config::GetOption("http.port",    url.port);
		url.schema = "http";
		url.path  = "/";
		url.query = "page=jumpservices&address=";
		url.query += host;

		res.code = 302; /* redirect */
		res.add_header("Location", url.to_string().c_str());
		res.add_header("Connection", "close");

		std::string response = res.to_string();
		boost::asio::async_write(*m_sock, boost::asio::buffer(response),
					 std::bind(&HTTPReqHandler::SentHTTPFailed, shared_from_this(), std::placeholders::_1));
	}

	void HTTPReqHandler::EnterState(HTTPReqHandler::state nstate)
	{
		m_state = nstate;
	}

	bool HTTPReqHandler::ValidateHTTPRequest()
	{
		if ( m_version != "HTTP/1.0" && m_version != "HTTP/1.1" )
		{
			LogPrint(eLogError, "HTTPProxy: unsupported version: ", m_version);
			HTTPRequestFailed("unsupported HTTP version");
			return false;
		}
		return true;
	}

	bool HTTPReqHandler::ExtractAddressHelper(i2p::http::URL & url, std::string & b64)
	{
		const char *param = "i2paddresshelper=";
		std::size_t pos = url.query.find(param);
		std::size_t len = std::strlen(param);
		std::map<std::string, std::string> params;

		if (pos == std::string::npos)
			return false; /* not found */
		if (!url.parse_query(params))
			return false;

		std::string value = params["i2paddresshelper"];
		len += value.length();
		b64 = i2p::http::UrlDecode(value);
		url.query.replace(pos, len, "");
		return true;
	}

	bool HTTPReqHandler::CreateHTTPRequest(uint8_t *http_buff, std::size_t len)
	{
		std::string b64;
		i2p::http::URL url;
		url.parse(m_url);
		m_address = url.host; /* < compatibility */
		m_port    = url.port; /* < compatibility */
		if (!m_port) {
			m_port = (url.schema == "https") ? 443 : 80;
		}
		url.schema = "";
		url.host   = "";
		m_path    = url.to_string(); /* < compatibility */
		if (!ValidateHTTPRequest()) return false;

		/* TODO: notify user */
		if (ExtractAddressHelper(url, b64)) {
			i2p::client::context.GetAddressBook ().InsertAddress (url.host, b64);
			LogPrint (eLogInfo, "HTTPProxy: added b64 from addresshelper for ", url.host, " to address book");
		}

		i2p::data::IdentHash identHash;
		if (str_rmatch(m_address, ".i2p"))
		{
			if (!i2p::client::context.GetAddressBook ().GetIdentHash (m_address, identHash)){
				RedirectToJumpService(m_address);
				return false;
			}
		}

		m_request = m_method;
		m_request.push_back(' ');
		m_request += m_path;
		m_request.push_back(' ');
		m_request += m_version;
		m_request.push_back('\r');
		m_request.push_back('\n');
		m_request.append("Connection: close\r\n");
		// TODO: temporary shortcut. Must be implemented properly
		uint8_t * eol = nullptr;
		bool isEndOfHeader = false;
		while (!isEndOfHeader && len && (eol = (uint8_t *)memchr (http_buff, '\r', len)))
		{
			if (eol)
			{
				*eol = 0; eol++;
				if (strncmp ((const char *)http_buff, "Referer", 7) && strncmp ((const char *)http_buff, "Connection", 10)) // strip out referer and connection
				{
					if (!strncmp ((const char *)http_buff, "User-Agent", 10)) // replace UserAgent
						m_request.append("User-Agent: MYOB/6.66 (AN/ON)");
					else
						m_request.append ((const char *)http_buff);
					m_request.append ("\r\n");
				}
				isEndOfHeader = !http_buff[0];
				auto l = eol - http_buff;
				http_buff = eol;
				len -= l;
				if (len > 0) // \r
				{
					http_buff++;
					len--;
				}
			}
		}
		m_request.append(reinterpret_cast<const char *>(http_buff),len);
		return true;
	}

	bool HTTPReqHandler::HandleData(uint8_t *http_buff, std::size_t len)
	{
		while (len > 0)
		{
			//TODO: fallback to finding HOst: header if needed
			switch (m_state)
			{
				case GET_METHOD:
					switch (*http_buff)
					{
						case ' ': EnterState(GET_HOSTNAME); break;
						default: m_method.push_back(*http_buff); break;
					}
				break;
				case GET_HOSTNAME:
					switch (*http_buff)
					{
						case ' ': EnterState(GET_HTTPV); break;
						default: m_url.push_back(*http_buff); break;
					}
				break;
				case GET_HTTPV:
					switch (*http_buff)
					{
						case '\r': EnterState(GET_HTTPVNL); break;
						default: m_version.push_back(*http_buff); break;
					}
				break;
				case GET_HTTPVNL:
					switch (*http_buff)
					{
						case '\n': EnterState(DONE); break;
						default:
							LogPrint(eLogError, "HTTPProxy: rejected invalid request ending with: ", ((int)*http_buff));
							HTTPRequestFailed("rejected invalid request");
							return false;
					}
				break;
				default:
					LogPrint(eLogError, "HTTPProxy: invalid state: ", m_state);
					HTTPRequestFailed("invalid parser state");
					return false;
			}
			http_buff++;
			len--;
			if (m_state == DONE)
				return CreateHTTPRequest(http_buff,len);
		}
		return true;
	}

	void HTTPReqHandler::HandleSockRecv(const boost::system::error_code & ecode, std::size_t len)
	{
		LogPrint(eLogDebug, "HTTPProxy: sock recv: ", len, " bytes");
		if(ecode)
		{
			LogPrint(eLogWarning, "HTTPProxy: sock recv got error: ", ecode);
			Terminate();
			return;
		}

		if (HandleData(m_http_buff, len))
		{
			if (m_state == DONE)
			{
				LogPrint(eLogDebug, "HTTPProxy: requested: ", m_url);
				GetOwner()->CreateStream (std::bind (&HTTPReqHandler::HandleStreamRequestComplete,
						shared_from_this(), std::placeholders::_1), m_address, m_port);
			}
			else
				AsyncSockRead();
		}
	}

	void HTTPReqHandler::SentHTTPFailed(const boost::system::error_code & ecode)
	{
		if (ecode)
			LogPrint (eLogError, "HTTPProxy: Closing socket after sending failure because: ", ecode.message ());
		Terminate();
	}

	void HTTPReqHandler::HandleStreamRequestComplete (std::shared_ptr<i2p::stream::Stream> stream)
	{
		if (!stream) {
			LogPrint (eLogError, "HTTPProxy: error when creating the stream, check the previous warnings for more info");
			HTTPRequestFailed("error when creating the stream, check logs");
			return;
		}
		if (Kill())
			return;
		LogPrint (eLogDebug, "HTTPProxy: New I2PTunnel connection");
		auto connection = std::make_shared<i2p::client::I2PTunnelConnection>(GetOwner(), m_sock, stream);
		GetOwner()->AddHandler (connection);
		connection->I2PConnect (reinterpret_cast<const uint8_t*>(m_request.data()), m_request.size());
		Done (shared_from_this());
	}

	HTTPProxy::HTTPProxy(const std::string& address, int port, std::shared_ptr<i2p::client::ClientDestination> localDestination):
		TCPIPAcceptor(address, port, localDestination ? localDestination : i2p::client::context.GetSharedLocalDestination ())
	{
	}

	std::shared_ptr<i2p::client::I2PServiceHandler> HTTPProxy::CreateHandler(std::shared_ptr<boost::asio::ip::tcp::socket> socket)
	{
		return std::make_shared<HTTPReqHandler> (this, socket);
	}
} // http
} // i2p
<commit_msg>* HTTPProxy.cpp : rename variable<commit_after>#include <cstring>
#include <cassert>
#include <string>
#include <atomic>
#include <memory>
#include <set>
#include <boost/asio.hpp>
#include <mutex>

#include "I2PService.h"
#include "Destination.h"
#include "HTTPProxy.h"
#include "util.h"
#include "Identity.h"
#include "Streaming.h"
#include "Destination.h"
#include "ClientContext.h"
#include "I2PEndian.h"
#include "I2PTunnel.h"
#include "Config.h"
#include "HTTP.h"

namespace i2p {
namespace proxy {
	bool str_rmatch(std::string & str, const char *suffix) {
		auto pos = str.rfind (suffix);
		if (pos == std::string::npos)
			return false; /* not found */
		if (str.length() == (pos + std::strlen(suffix)))
			return true; /* match */
		return false;
	}

	class HTTPReqHandler: public i2p::client::I2PServiceHandler, public std::enable_shared_from_this<HTTPReqHandler>
	{
		private:
			enum state
			{
				GET_METHOD,
				GET_HOSTNAME,
				GET_HTTPV,
				GET_HTTPVNL, //TODO: fallback to finding HOst: header if needed
				DONE
			};

			void EnterState(state nstate);
			bool HandleData(uint8_t *http_buff, std::size_t len);
			void HandleSockRecv(const boost::system::error_code & ecode, std::size_t bytes_transfered);
			void Terminate();
			void AsyncSockRead();
			void HTTPRequestFailed(const char *message);
			void RedirectToJumpService(std::string & host);
			bool ValidateHTTPRequest();
			bool ExtractAddressHelper(i2p::http::URL & url, std::string & b64);
			bool CreateHTTPRequest(uint8_t *http_buff, std::size_t len);
			void SentHTTPFailed(const boost::system::error_code & ecode);
			void HandleStreamRequestComplete (std::shared_ptr<i2p::stream::Stream> stream);

			uint8_t m_recv_buf[8192];
			std::string m_request; //Data left to be sent. TODO: rename to m_send_buf
			std::shared_ptr<boost::asio::ip::tcp::socket> m_sock;
			std::string m_url; //URL
			std::string m_method; //Method
			std::string m_version; //HTTP version
			std::string m_address; //Address
			std::string m_path; //Path
			int m_port; //Port
			state m_state;//Parsing state

		public:

			HTTPReqHandler(HTTPProxy * parent, std::shared_ptr<boost::asio::ip::tcp::socket> sock) :
				I2PServiceHandler(parent), m_sock(sock)
				{ EnterState(GET_METHOD); }
			~HTTPReqHandler() { Terminate(); }
			void Handle () { AsyncSockRead(); }
	};

	void HTTPReqHandler::AsyncSockRead()
	{
		LogPrint(eLogDebug, "HTTPProxy: async sock read");
		if (!m_sock) {
			LogPrint(eLogError, "HTTPProxy: no socket for read");
			return;
		}
		m_sock->async_receive(boost::asio::buffer(m_recv_buf, sizeof(m_recv_buf)),
					std::bind(&HTTPReqHandler::HandleSockRecv, shared_from_this(),
							std::placeholders::_1, std::placeholders::_2));
	}

	void HTTPReqHandler::Terminate() {
		if (Kill()) return;
		if (m_sock)
		{
			LogPrint(eLogDebug, "HTTPProxy: close sock");
			m_sock->close();
			m_sock = nullptr;
		}
		Done(shared_from_this());
	}

	/* All hope is lost beyond this point */
	//TODO: handle this apropriately
	void HTTPReqHandler::HTTPRequestFailed(const char *message)
	{
		i2p::http::HTTPRes res;
		res.code = 500;
		res.add_header("Content-Type", "text/plain");
		res.add_header("Connection", "close");
		res.body = message;
		res.body += "\r\n";
		std::string response = res.to_string();
		boost::asio::async_write(*m_sock, boost::asio::buffer(response),
					 std::bind(&HTTPReqHandler::SentHTTPFailed, shared_from_this(), std::placeholders::_1));
	}

	void HTTPReqHandler::RedirectToJumpService(std::string & host)
	{
		i2p::http::HTTPRes res;
		i2p::http::URL url;

		/* TODO: don't redirect to webconsole, it's not always work, handle jumpservices here */
		i2p::config::GetOption("http.address", url.host);
		i2p::config::GetOption("http.port",    url.port);
		url.schema = "http";
		url.path  = "/";
		url.query = "page=jumpservices&address=";
		url.query += host;

		res.code = 302; /* redirect */
		res.add_header("Location", url.to_string().c_str());
		res.add_header("Connection", "close");

		std::string response = res.to_string();
		boost::asio::async_write(*m_sock, boost::asio::buffer(response),
					 std::bind(&HTTPReqHandler::SentHTTPFailed, shared_from_this(), std::placeholders::_1));
	}

	void HTTPReqHandler::EnterState(HTTPReqHandler::state nstate)
	{
		m_state = nstate;
	}

	bool HTTPReqHandler::ValidateHTTPRequest()
	{
		if ( m_version != "HTTP/1.0" && m_version != "HTTP/1.1" )
		{
			LogPrint(eLogError, "HTTPProxy: unsupported version: ", m_version);
			HTTPRequestFailed("unsupported HTTP version");
			return false;
		}
		return true;
	}

	bool HTTPReqHandler::ExtractAddressHelper(i2p::http::URL & url, std::string & b64)
	{
		const char *param = "i2paddresshelper=";
		std::size_t pos = url.query.find(param);
		std::size_t len = std::strlen(param);
		std::map<std::string, std::string> params;

		if (pos == std::string::npos)
			return false; /* not found */
		if (!url.parse_query(params))
			return false;

		std::string value = params["i2paddresshelper"];
		len += value.length();
		b64 = i2p::http::UrlDecode(value);
		url.query.replace(pos, len, "");
		return true;
	}

	bool HTTPReqHandler::CreateHTTPRequest(uint8_t *http_buff, std::size_t len)
	{
		std::string b64;
		i2p::http::URL url;
		url.parse(m_url);
		m_address = url.host; /* < compatibility */
		m_port    = url.port; /* < compatibility */
		if (!m_port) {
			m_port = (url.schema == "https") ? 443 : 80;
		}
		url.schema = "";
		url.host   = "";
		m_path    = url.to_string(); /* < compatibility */
		if (!ValidateHTTPRequest()) return false;

		/* TODO: notify user */
		if (ExtractAddressHelper(url, b64)) {
			i2p::client::context.GetAddressBook ().InsertAddress (url.host, b64);
			LogPrint (eLogInfo, "HTTPProxy: added b64 from addresshelper for ", url.host, " to address book");
		}

		i2p::data::IdentHash identHash;
		if (str_rmatch(m_address, ".i2p"))
		{
			if (!i2p::client::context.GetAddressBook ().GetIdentHash (m_address, identHash)){
				RedirectToJumpService(m_address);
				return false;
			}
		}

		m_request = m_method;
		m_request.push_back(' ');
		m_request += m_path;
		m_request.push_back(' ');
		m_request += m_version;
		m_request.push_back('\r');
		m_request.push_back('\n');
		m_request.append("Connection: close\r\n");
		// TODO: temporary shortcut. Must be implemented properly
		uint8_t * eol = nullptr;
		bool isEndOfHeader = false;
		while (!isEndOfHeader && len && (eol = (uint8_t *)memchr (http_buff, '\r', len)))
		{
			if (eol)
			{
				*eol = 0; eol++;
				if (strncmp ((const char *)http_buff, "Referer", 7) && strncmp ((const char *)http_buff, "Connection", 10)) // strip out referer and connection
				{
					if (!strncmp ((const char *)http_buff, "User-Agent", 10)) // replace UserAgent
						m_request.append("User-Agent: MYOB/6.66 (AN/ON)");
					else
						m_request.append ((const char *)http_buff);
					m_request.append ("\r\n");
				}
				isEndOfHeader = !http_buff[0];
				auto l = eol - http_buff;
				http_buff = eol;
				len -= l;
				if (len > 0) // \r
				{
					http_buff++;
					len--;
				}
			}
		}
		m_request.append(reinterpret_cast<const char *>(http_buff),len);
		return true;
	}

	bool HTTPReqHandler::HandleData(uint8_t *http_buff, std::size_t len)
	{
		while (len > 0)
		{
			//TODO: fallback to finding HOst: header if needed
			switch (m_state)
			{
				case GET_METHOD:
					switch (*http_buff)
					{
						case ' ': EnterState(GET_HOSTNAME); break;
						default: m_method.push_back(*http_buff); break;
					}
				break;
				case GET_HOSTNAME:
					switch (*http_buff)
					{
						case ' ': EnterState(GET_HTTPV); break;
						default: m_url.push_back(*http_buff); break;
					}
				break;
				case GET_HTTPV:
					switch (*http_buff)
					{
						case '\r': EnterState(GET_HTTPVNL); break;
						default: m_version.push_back(*http_buff); break;
					}
				break;
				case GET_HTTPVNL:
					switch (*http_buff)
					{
						case '\n': EnterState(DONE); break;
						default:
							LogPrint(eLogError, "HTTPProxy: rejected invalid request ending with: ", ((int)*http_buff));
							HTTPRequestFailed("rejected invalid request");
							return false;
					}
				break;
				default:
					LogPrint(eLogError, "HTTPProxy: invalid state: ", m_state);
					HTTPRequestFailed("invalid parser state");
					return false;
			}
			http_buff++;
			len--;
			if (m_state == DONE)
				return CreateHTTPRequest(http_buff,len);
		}
		return true;
	}

	void HTTPReqHandler::HandleSockRecv(const boost::system::error_code & ecode, std::size_t len)
	{
		LogPrint(eLogDebug, "HTTPProxy: sock recv: ", len, " bytes");
		if(ecode)
		{
			LogPrint(eLogWarning, "HTTPProxy: sock recv got error: ", ecode);
			Terminate();
			return;
		}

		if (HandleData(m_recv_buf, len))
		{
			if (m_state == DONE)
			{
				LogPrint(eLogDebug, "HTTPProxy: requested: ", m_url);
				GetOwner()->CreateStream (std::bind (&HTTPReqHandler::HandleStreamRequestComplete,
						shared_from_this(), std::placeholders::_1), m_address, m_port);
			}
			else
				AsyncSockRead();
		}
	}

	void HTTPReqHandler::SentHTTPFailed(const boost::system::error_code & ecode)
	{
		if (ecode)
			LogPrint (eLogError, "HTTPProxy: Closing socket after sending failure because: ", ecode.message ());
		Terminate();
	}

	void HTTPReqHandler::HandleStreamRequestComplete (std::shared_ptr<i2p::stream::Stream> stream)
	{
		if (!stream) {
			LogPrint (eLogError, "HTTPProxy: error when creating the stream, check the previous warnings for more info");
			HTTPRequestFailed("error when creating the stream, check logs");
			return;
		}
		if (Kill())
			return;
		LogPrint (eLogDebug, "HTTPProxy: New I2PTunnel connection");
		auto connection = std::make_shared<i2p::client::I2PTunnelConnection>(GetOwner(), m_sock, stream);
		GetOwner()->AddHandler (connection);
		connection->I2PConnect (reinterpret_cast<const uint8_t*>(m_request.data()), m_request.size());
		Done (shared_from_this());
	}

	HTTPProxy::HTTPProxy(const std::string& address, int port, std::shared_ptr<i2p::client::ClientDestination> localDestination):
		TCPIPAcceptor(address, port, localDestination ? localDestination : i2p::client::context.GetSharedLocalDestination ())
	{
	}

	std::shared_ptr<i2p::client::I2PServiceHandler> HTTPProxy::CreateHandler(std::shared_ptr<boost::asio::ip::tcp::socket> socket)
	{
		return std::make_shared<HTTPReqHandler> (this, socket);
	}
} // http
} // i2p
<|endoftext|>


<commit_before>#include <boost/bind.hpp>
#include "base64.h"
#include "Log.h"
#include "NetDb.h"
#include "I2PTunnel.h"

namespace i2p
{
namespace stream
{
	I2PTunnelConnection::I2PTunnelConnection (I2PTunnel * owner,
	    boost::asio::ip::tcp::socket * socket, const i2p::data::LeaseSet * leaseSet):
		m_Socket (socket), m_Owner (owner)
	{
		m_Stream = m_Owner->GetLocalDestination ()->CreateNewOutgoingStream (*leaseSet);
		m_Stream->Send (m_Buffer, 0); // connect
		StreamReceive ();
		Receive ();
	}

	I2PTunnelConnection::I2PTunnelConnection (I2PTunnel * owner, Stream * stream,
	    boost::asio::ip::tcp::socket * socket, const boost::asio::ip::tcp::endpoint& target):
		m_Socket (socket), m_Stream (stream), m_Owner (owner)
	{
		if (m_Socket)
			m_Socket->async_connect (target, boost::bind (&I2PTunnelConnection::HandleConnect,
				this, boost::asio::placeholders::error));
	}

	I2PTunnelConnection::~I2PTunnelConnection ()
	{
		if (m_Stream)
		{
			m_Stream->Close ();
			DeleteStream (m_Stream);
			m_Stream = nullptr;
		}
		delete m_Socket;
	}

	void I2PTunnelConnection::Terminate ()
	{
		m_Socket->close ();
		if (m_Owner)
			m_Owner->RemoveConnection (this);
		// TODO: delete
	}

	void I2PTunnelConnection::Receive ()
	{
		m_Socket->async_read_some (boost::asio::buffer(m_Buffer, I2P_TUNNEL_CONNECTION_BUFFER_SIZE),
			boost::bind(&I2PTunnelConnection::HandleReceived, this,
			boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred));
	}

	void I2PTunnelConnection::HandleReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred)
	{
		if (ecode)
        {
			LogPrint ("I2PTunnel read error: ", ecode.message ());
			m_Stream->Close ();
			Terminate ();
		}
		else
		{
			if (m_Stream)
				m_Stream->Send (m_Buffer, bytes_transferred);
			Receive ();
		}
	}

	void I2PTunnelConnection::HandleWrite (const boost::system::error_code& ecode)
	{
		if (ecode)
		{
			LogPrint ("I2PTunnel write error: ", ecode.message ());
			m_Stream->Close ();
			Terminate ();
		}
		else
			StreamReceive ();
	}

	void I2PTunnelConnection::StreamReceive ()
	{
		if (m_Stream)
			m_Stream->AsyncReceive (boost::asio::buffer (m_StreamBuffer, I2P_TUNNEL_CONNECTION_BUFFER_SIZE),
				boost::bind (&I2PTunnelConnection::HandleStreamReceive, this,
					boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred),
				I2P_TUNNEL_CONNECTION_MAX_IDLE);
	}

	void I2PTunnelConnection::HandleStreamReceive (const boost::system::error_code& ecode, std::size_t bytes_transferred)
	{
		if (ecode)
		{
			LogPrint ("I2PTunnel stream read error: ", ecode.message ());
			Terminate ();
		}
		else
		{
			boost::asio::async_write (*m_Socket, boost::asio::buffer (m_StreamBuffer, bytes_transferred),
        		boost::bind (&I2PTunnelConnection::HandleWrite, this, boost::asio::placeholders::error));
		}
	}

	void I2PTunnelConnection::HandleConnect (const boost::system::error_code& ecode)
	{
		if (ecode)
		{
			LogPrint ("I2PTunnel connect error: ", ecode.message ());
			if (m_Stream) m_Stream->Close ();
			DeleteStream (m_Stream);
			m_Stream = nullptr;
		}
		else
		{
			LogPrint ("I2PTunnel connected");
			StreamReceive ();
			Receive ();
		}
	}

	void I2PTunnel::AddConnection (I2PTunnelConnection * conn)
	{
		m_Connections.insert (conn);
	}

	void I2PTunnel::RemoveConnection (I2PTunnelConnection * conn)
	{
		m_Connections.erase (conn);
	}

	void I2PTunnel::ClearConnections ()
	{
		for (auto it: m_Connections)
			delete it;
		m_Connections.clear ();
	}

	I2PClientTunnel::I2PClientTunnel (boost::asio::io_service& service, const std::string& destination,
		int port, StreamingDestination * localDestination):
		I2PTunnel (service, localDestination ? localDestination : GetSharedLocalDestination ()),
		m_Acceptor (service, boost::asio::ip::tcp::endpoint (boost::asio::ip::tcp::v4(), port)),
		m_Destination (destination), m_DestinationIdentHash (nullptr), m_RemoteLeaseSet (nullptr)
	{
	}

	I2PClientTunnel::~I2PClientTunnel ()
	{
		Stop ();
	}

	void I2PClientTunnel::Start ()
	{
		i2p::data::IdentHash identHash;
		if (i2p::data::netdb.GetAddressBook ().GetIdentHash (m_Destination, identHash))
			m_DestinationIdentHash = new i2p::data::IdentHash (identHash);
		if (m_DestinationIdentHash)
		{
			i2p::data::netdb.Subscribe (*m_DestinationIdentHash, GetLocalDestination ()->GetTunnelPool ());
			m_RemoteLeaseSet = i2p::data::netdb.FindLeaseSet (*m_DestinationIdentHash);
		}
		else
			LogPrint ("I2PTunnel unknown destination ", m_Destination);
		m_Acceptor.listen ();
		Accept ();
	}

	void I2PClientTunnel::Stop ()
	{
		m_Acceptor.close();
		ClearConnections ();
		m_DestinationIdentHash = nullptr;
	}

	void I2PClientTunnel::Accept ()
	{
		auto newSocket = new boost::asio::ip::tcp::socket (GetService ());
		m_Acceptor.async_accept (*newSocket, boost::bind (&I2PClientTunnel::HandleAccept, this,
			boost::asio::placeholders::error, newSocket));
	}

	void I2PClientTunnel::HandleAccept (const boost::system::error_code& ecode, boost::asio::ip::tcp::socket * socket)
	{
		if (!ecode)
		{
			if (!m_RemoteLeaseSet)
			{
				// try to get it
				if (m_DestinationIdentHash)
					m_RemoteLeaseSet = i2p::data::netdb.FindLeaseSet (*m_DestinationIdentHash);
				else
				{
					i2p::data::IdentHash identHash;
					if (i2p::data::netdb.GetAddressBook ().GetIdentHash (m_Destination, identHash))
					{
						m_DestinationIdentHash = new i2p::data::IdentHash (identHash);
						i2p::data::netdb.Subscribe (*m_DestinationIdentHash, GetSharedLocalDestination ()->GetTunnelPool ());
					}
				}
			}

			if (m_RemoteLeaseSet) // leaseSet found
			{
				LogPrint ("New I2PTunnel connection");
				auto connection = new I2PTunnelConnection (this, socket, m_RemoteLeaseSet);
				AddConnection (connection);
			}
			else
			{
				LogPrint ("LeaseSet for I2PTunnel destination not found");
				delete socket;
			}
			Accept ();
		}
		else
			delete socket;
	}

	I2PServerTunnel::I2PServerTunnel (boost::asio::io_service& service, const std::string& address, int port,
		StreamingDestination * localDestination): I2PTunnel (service, localDestination),
		m_Endpoint (boost::asio::ip::address::from_string (address), port)
	{
	}

	void I2PServerTunnel::Start ()
	{
		Accept ();
	}

	void I2PServerTunnel::Stop ()
	{
		ClearConnections ();
	}

	void I2PServerTunnel::Accept ()
	{
		auto localDestination = GetLocalDestination ();
		if (localDestination)
			localDestination->SetAcceptor (std::bind (&I2PServerTunnel::HandleAccept, this, std::placeholders::_1));
		else
			LogPrint ("Local destination not set for server tunnel");
	}

	void I2PServerTunnel::HandleAccept (i2p::stream::Stream * stream)
	{
		if (stream)
			new I2PTunnelConnection (this, stream, new boost::asio::ip::tcp::socket (GetService ()), m_Endpoint);
	}
}
}
<commit_msg>use own pool for request of destination of new I2P client tunnel<commit_after>#include <boost/bind.hpp>
#include "base64.h"
#include "Log.h"
#include "NetDb.h"
#include "I2PTunnel.h"

namespace i2p
{
namespace stream
{
	I2PTunnelConnection::I2PTunnelConnection (I2PTunnel * owner,
	    boost::asio::ip::tcp::socket * socket, const i2p::data::LeaseSet * leaseSet):
		m_Socket (socket), m_Owner (owner)
	{
		m_Stream = m_Owner->GetLocalDestination ()->CreateNewOutgoingStream (*leaseSet);
		m_Stream->Send (m_Buffer, 0); // connect
		StreamReceive ();
		Receive ();
	}

	I2PTunnelConnection::I2PTunnelConnection (I2PTunnel * owner, Stream * stream,
	    boost::asio::ip::tcp::socket * socket, const boost::asio::ip::tcp::endpoint& target):
		m_Socket (socket), m_Stream (stream), m_Owner (owner)
	{
		if (m_Socket)
			m_Socket->async_connect (target, boost::bind (&I2PTunnelConnection::HandleConnect,
				this, boost::asio::placeholders::error));
	}

	I2PTunnelConnection::~I2PTunnelConnection ()
	{
		if (m_Stream)
		{
			m_Stream->Close ();
			DeleteStream (m_Stream);
			m_Stream = nullptr;
		}
		delete m_Socket;
	}

	void I2PTunnelConnection::Terminate ()
	{
		m_Socket->close ();
		if (m_Owner)
			m_Owner->RemoveConnection (this);
		// TODO: delete
	}

	void I2PTunnelConnection::Receive ()
	{
		m_Socket->async_read_some (boost::asio::buffer(m_Buffer, I2P_TUNNEL_CONNECTION_BUFFER_SIZE),
			boost::bind(&I2PTunnelConnection::HandleReceived, this,
			boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred));
	}

	void I2PTunnelConnection::HandleReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred)
	{
		if (ecode)
        {
			LogPrint ("I2PTunnel read error: ", ecode.message ());
			m_Stream->Close ();
			Terminate ();
		}
		else
		{
			if (m_Stream)
				m_Stream->Send (m_Buffer, bytes_transferred);
			Receive ();
		}
	}

	void I2PTunnelConnection::HandleWrite (const boost::system::error_code& ecode)
	{
		if (ecode)
		{
			LogPrint ("I2PTunnel write error: ", ecode.message ());
			m_Stream->Close ();
			Terminate ();
		}
		else
			StreamReceive ();
	}

	void I2PTunnelConnection::StreamReceive ()
	{
		if (m_Stream)
			m_Stream->AsyncReceive (boost::asio::buffer (m_StreamBuffer, I2P_TUNNEL_CONNECTION_BUFFER_SIZE),
				boost::bind (&I2PTunnelConnection::HandleStreamReceive, this,
					boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred),
				I2P_TUNNEL_CONNECTION_MAX_IDLE);
	}

	void I2PTunnelConnection::HandleStreamReceive (const boost::system::error_code& ecode, std::size_t bytes_transferred)
	{
		if (ecode)
		{
			LogPrint ("I2PTunnel stream read error: ", ecode.message ());
			Terminate ();
		}
		else
		{
			boost::asio::async_write (*m_Socket, boost::asio::buffer (m_StreamBuffer, bytes_transferred),
        		boost::bind (&I2PTunnelConnection::HandleWrite, this, boost::asio::placeholders::error));
		}
	}

	void I2PTunnelConnection::HandleConnect (const boost::system::error_code& ecode)
	{
		if (ecode)
		{
			LogPrint ("I2PTunnel connect error: ", ecode.message ());
			if (m_Stream) m_Stream->Close ();
			DeleteStream (m_Stream);
			m_Stream = nullptr;
		}
		else
		{
			LogPrint ("I2PTunnel connected");
			StreamReceive ();
			Receive ();
		}
	}

	void I2PTunnel::AddConnection (I2PTunnelConnection * conn)
	{
		m_Connections.insert (conn);
	}

	void I2PTunnel::RemoveConnection (I2PTunnelConnection * conn)
	{
		m_Connections.erase (conn);
	}

	void I2PTunnel::ClearConnections ()
	{
		for (auto it: m_Connections)
			delete it;
		m_Connections.clear ();
	}

	I2PClientTunnel::I2PClientTunnel (boost::asio::io_service& service, const std::string& destination,
		int port, StreamingDestination * localDestination):
		I2PTunnel (service, localDestination ? localDestination : GetSharedLocalDestination ()),
		m_Acceptor (service, boost::asio::ip::tcp::endpoint (boost::asio::ip::tcp::v4(), port)),
		m_Destination (destination), m_DestinationIdentHash (nullptr), m_RemoteLeaseSet (nullptr)
	{
	}

	I2PClientTunnel::~I2PClientTunnel ()
	{
		Stop ();
	}

	void I2PClientTunnel::Start ()
	{
		i2p::data::IdentHash identHash;
		if (i2p::data::netdb.GetAddressBook ().GetIdentHash (m_Destination, identHash))
			m_DestinationIdentHash = new i2p::data::IdentHash (identHash);
		if (m_DestinationIdentHash)
		{
			i2p::data::netdb.Subscribe (*m_DestinationIdentHash, GetLocalDestination ()->GetTunnelPool ());
			m_RemoteLeaseSet = i2p::data::netdb.FindLeaseSet (*m_DestinationIdentHash);
		}
		else
			LogPrint ("I2PTunnel unknown destination ", m_Destination);
		m_Acceptor.listen ();
		Accept ();
	}

	void I2PClientTunnel::Stop ()
	{
		m_Acceptor.close();
		ClearConnections ();
		m_DestinationIdentHash = nullptr;
	}

	void I2PClientTunnel::Accept ()
	{
		auto newSocket = new boost::asio::ip::tcp::socket (GetService ());
		m_Acceptor.async_accept (*newSocket, boost::bind (&I2PClientTunnel::HandleAccept, this,
			boost::asio::placeholders::error, newSocket));
	}

	void I2PClientTunnel::HandleAccept (const boost::system::error_code& ecode, boost::asio::ip::tcp::socket * socket)
	{
		if (!ecode)
		{
			if (!m_RemoteLeaseSet)
			{
				// try to get it
				if (m_DestinationIdentHash)
					m_RemoteLeaseSet = i2p::data::netdb.FindLeaseSet (*m_DestinationIdentHash);
				else
				{
					i2p::data::IdentHash identHash;
					if (i2p::data::netdb.GetAddressBook ().GetIdentHash (m_Destination, identHash))
					{
						m_DestinationIdentHash = new i2p::data::IdentHash (identHash);
						i2p::data::netdb.Subscribe (*m_DestinationIdentHash, GetLocalDestination ()->GetTunnelPool ());
					}
				}
			}

			if (m_RemoteLeaseSet) // leaseSet found
			{
				LogPrint ("New I2PTunnel connection");
				auto connection = new I2PTunnelConnection (this, socket, m_RemoteLeaseSet);
				AddConnection (connection);
			}
			else
			{
				LogPrint ("LeaseSet for I2PTunnel destination not found");
				delete socket;
			}
			Accept ();
		}
		else
			delete socket;
	}

	I2PServerTunnel::I2PServerTunnel (boost::asio::io_service& service, const std::string& address, int port,
		StreamingDestination * localDestination): I2PTunnel (service, localDestination),
		m_Endpoint (boost::asio::ip::address::from_string (address), port)
	{
	}

	void I2PServerTunnel::Start ()
	{
		Accept ();
	}

	void I2PServerTunnel::Stop ()
	{
		ClearConnections ();
	}

	void I2PServerTunnel::Accept ()
	{
		auto localDestination = GetLocalDestination ();
		if (localDestination)
			localDestination->SetAcceptor (std::bind (&I2PServerTunnel::HandleAccept, this, std::placeholders::_1));
		else
			LogPrint ("Local destination not set for server tunnel");
	}

	void I2PServerTunnel::HandleAccept (i2p::stream::Stream * stream)
	{
		if (stream)
			new I2PTunnelConnection (this, stream, new boost::asio::ip::tcp::socket (GetService ()), m_Endpoint);
	}
}
}
<|endoftext|>


<commit_before>/*!
  \copyright (c) RDO-Team, 2011
  \file      rdoprocess.inl
  \author      (dluschan@rk9.bmstu.ru)
  \date      24.07.2011
  \brief
  \indent    4T
*/

// ----------------------------------------------------------------------- INCLUDES
// ----------------------------------------------------------------------- SYNOPSIS
#include "simulator/runtime/rdo.h"
#include "simulator/runtime/rdo_runtime.h"
#include "simulator/runtime/rdoprocess_i.h"
#include "simulator/runtime/rdo_logic.h"
// --------------------------------------------------------------------------------

OPEN_RDO_RUNTIME_NAMESPACE

// --------------------------------------------------------------------------------
// -------------------- RDOPROCBlock
// --------------------------------------------------------------------------------
inline RDOPROCBlock::~RDOPROCBlock()
{}

// --------------------------------------------------------------------------------
// -------------------- RDOPROCTransact
// --------------------------------------------------------------------------------
inline LPRDOPROCResource RDOPROCTransact::getRes()
{
	return m_res;
}

inline void RDOPROCTransact::setRes(CREF(LPRDOPROCResource) pResource)
{
	m_res = pResource;
}

inline REF(LPIPROCBlock) RDOPROCTransact::getBlock()
{
	return m_block;
}

inline void RDOPROCTransact::setBlock(CREF(LPIPROCBlock) block)
{
	m_block = block;
}

// --------------------------------------------------------------------------------
// -------------------- RDOPROCResource
// --------------------------------------------------------------------------------
inline tstring RDOPROCResource::whoAreYou()
{
	return "procRes";
}

// --------------------------------------------------------------------------------
// -------------------- RDOPROCGenerate
// --------------------------------------------------------------------------------
inline RDOPROCGenerate::RDOPROCGenerate(LPIPROCProcess process, CREF(LPRDOCalc) pTime, int maxTransCount)
	: RDOPROCBlock   (process      )
	, timeNext       (0	           )
	, pTimeCalc      (pTime        )
	, m_maxTransCount(maxTransCount)
{
	m_TransCount = 0;
}

// --------------------------------------------------------------------------------
// -------------------- RDOPROCQueue
// --------------------------------------------------------------------------------
inline RDOPROCQueue::RDOPROCQueue(LPIPROCProcess process, parser_for_Queue From_Par)
	: RDOPROCBlockForQueue(process, From_Par)
{}

inline ruint RDOPROCQueue::getDefaultValue()
{
	return 0;
}

inline tstring RDOPROCQueue::getQueueParamName()
{
	return "_";
}

// --------------------------------------------------------------------------------
// -------------------- RDOPROCDepart
// --------------------------------------------------------------------------------
inline RDOPROCDepart::RDOPROCDepart(LPIPROCProcess process, parser_for_Queue From_Par)
	: RDOPROCBlockForQueue(process, From_Par)
{}

inline ruint RDOPROCDepart::getDefaultValue()
{
	return 0;
}

inline tstring RDOPROCDepart::getDepartParamName()
{
	return "_";
}

// --------------------------------------------------------------------------------
// -------------------- RDOPROCBlockForSeize
// --------------------------------------------------------------------------------
inline tstring RDOPROCBlockForSeize::getStateParamName()
{
	return "";
}

inline tstring RDOPROCBlockForSeize::getStateEnumFree()
{
	return "";
}

inline tstring RDOPROCBlockForSeize::getStateEnumBuzy()
{
	return "";
}

// --------------------------------------------------------------------------------
// -------------------- RDOPROCSeize
// --------------------------------------------------------------------------------
inline RDOPROCSeize::RDOPROCSeize(LPIPROCProcess process, std::vector<parser_for_Seize> From_Par)
	: RDOPROCBlockForSeize(process, From_Par)
{
	static ruint g_index = 1;
	index = g_index++;
}

// --------------------------------------------------------------------------------
// -------------------- RDOPROCRelease
// --------------------------------------------------------------------------------
inline RDOPROCRelease::RDOPROCRelease(LPIPROCProcess process, std::vector<parser_for_Seize> From_Par)
	: RDOPROCBlockForSeize(process, From_Par)
{
	static ruint g_index = 1;
	index = g_index++;
}

// --------------------------------------------------------------------------------
// -------------------- RDOPROCAdvance
// --------------------------------------------------------------------------------
inline RDOPROCAdvance::RDOPROCAdvance(LPIPROCProcess process, CREF(LPRDOCalc) _pDelayCalc)
	: RDOPROCBlock(process    )
	, pDelayCalc  (_pDelayCalc)
{}

inline RDOPROCAdvance::LeaveTr::LeaveTr(CREF(LPRDOPROCTransact) _transact, double _timeLeave)
	: transact (_transact )
	, timeLeave(_timeLeave)
{}

// --------------------------------------------------------------------------------
// -------------------- RDOPROCTerminate
// --------------------------------------------------------------------------------
inline RDOPROCTerminate::RDOPROCTerminate(LPIPROCProcess process, ruint _term)
	: RDOPROCBlock(process)
	, term        (_term  )
{}

inline int RDOPROCTerminate::getTerm()
{
	return term;
}

// --------------------------------------------------------------------------------
// -------------------- RDOPROCAssign
// --------------------------------------------------------------------------------
inline RDOPROCAssign::RDOPROCAssign(LPIPROCProcess process, CREF(LPRDOCalc) pValue, int Id_res, int Id_param)
	: RDOPROCBlock(process )
	, pParamValue (pValue  )
	, t_resId     (Id_res  )
	, t_parId     (Id_param)
{}

CLOSE_RDO_RUNTIME_NAMESPACE
<commit_msg> - форматирование<commit_after>/*!
  \copyright (c) RDO-Team, 2011
  \file      rdoprocess.inl
  \author      (dluschan@rk9.bmstu.ru)
  \date      24.07.2011
  \brief
  \indent    4T
*/

// ----------------------------------------------------------------------- INCLUDES
// ----------------------------------------------------------------------- SYNOPSIS
#include "simulator/runtime/rdo.h"
#include "simulator/runtime/rdo_runtime.h"
#include "simulator/runtime/rdoprocess_i.h"
#include "simulator/runtime/rdo_logic.h"
// --------------------------------------------------------------------------------

OPEN_RDO_RUNTIME_NAMESPACE

// --------------------------------------------------------------------------------
// -------------------- RDOPROCBlock
// --------------------------------------------------------------------------------
inline RDOPROCBlock::~RDOPROCBlock()
{}

// --------------------------------------------------------------------------------
// -------------------- RDOPROCTransact
// --------------------------------------------------------------------------------
inline LPRDOPROCResource RDOPROCTransact::getRes()
{
	return m_res;
}

inline void RDOPROCTransact::setRes(CREF(LPRDOPROCResource) pResource)
{
	m_res = pResource;
}

inline REF(LPIPROCBlock) RDOPROCTransact::getBlock()
{
	return m_block;
}

inline void RDOPROCTransact::setBlock(CREF(LPIPROCBlock) block)
{
	m_block = block;
}

// --------------------------------------------------------------------------------
// -------------------- RDOPROCResource
// --------------------------------------------------------------------------------
inline tstring RDOPROCResource::whoAreYou()
{
	return "procRes";
}

// --------------------------------------------------------------------------------
// -------------------- RDOPROCGenerate
// --------------------------------------------------------------------------------
inline RDOPROCGenerate::RDOPROCGenerate(LPIPROCProcess process, CREF(LPRDOCalc) pTime, int maxTransCount)
	: RDOPROCBlock   (process      )
	, timeNext       (0.0          )
	, pTimeCalc      (pTime        )
	, m_maxTransCount(maxTransCount)
{
	m_TransCount = 0;
}

// --------------------------------------------------------------------------------
// -------------------- RDOPROCQueue
// --------------------------------------------------------------------------------
inline RDOPROCQueue::RDOPROCQueue(LPIPROCProcess process, parser_for_Queue From_Par)
	: RDOPROCBlockForQueue(process, From_Par)
{}

inline ruint RDOPROCQueue::getDefaultValue()
{
	return 0;
}

inline tstring RDOPROCQueue::getQueueParamName()
{
	return "_";
}

// --------------------------------------------------------------------------------
// -------------------- RDOPROCDepart
// --------------------------------------------------------------------------------
inline RDOPROCDepart::RDOPROCDepart(LPIPROCProcess process, parser_for_Queue From_Par)
	: RDOPROCBlockForQueue(process, From_Par)
{}

inline ruint RDOPROCDepart::getDefaultValue()
{
	return 0;
}

inline tstring RDOPROCDepart::getDepartParamName()
{
	return "_";
}

// --------------------------------------------------------------------------------
// -------------------- RDOPROCBlockForSeize
// --------------------------------------------------------------------------------
inline tstring RDOPROCBlockForSeize::getStateParamName()
{
	return "";
}

inline tstring RDOPROCBlockForSeize::getStateEnumFree()
{
	return "";
}

inline tstring RDOPROCBlockForSeize::getStateEnumBuzy()
{
	return "";
}

// --------------------------------------------------------------------------------
// -------------------- RDOPROCSeize
// --------------------------------------------------------------------------------
inline RDOPROCSeize::RDOPROCSeize(LPIPROCProcess process, std::vector<parser_for_Seize> From_Par)
	: RDOPROCBlockForSeize(process, From_Par)
{
	static ruint g_index = 1;
	index = g_index++;
}

// --------------------------------------------------------------------------------
// -------------------- RDOPROCRelease
// --------------------------------------------------------------------------------
inline RDOPROCRelease::RDOPROCRelease(LPIPROCProcess process, std::vector<parser_for_Seize> From_Par)
	: RDOPROCBlockForSeize(process, From_Par)
{
	static ruint g_index = 1;
	index = g_index++;
}

// --------------------------------------------------------------------------------
// -------------------- RDOPROCAdvance
// --------------------------------------------------------------------------------
inline RDOPROCAdvance::RDOPROCAdvance(LPIPROCProcess process, CREF(LPRDOCalc) _pDelayCalc)
	: RDOPROCBlock(process    )
	, pDelayCalc  (_pDelayCalc)
{}

inline RDOPROCAdvance::LeaveTr::LeaveTr(CREF(LPRDOPROCTransact) _transact, double _timeLeave)
	: transact (_transact )
	, timeLeave(_timeLeave)
{}

// --------------------------------------------------------------------------------
// -------------------- RDOPROCTerminate
// --------------------------------------------------------------------------------
inline RDOPROCTerminate::RDOPROCTerminate(LPIPROCProcess process, ruint _term)
	: RDOPROCBlock(process)
	, term        (_term  )
{}

inline int RDOPROCTerminate::getTerm()
{
	return term;
}

// --------------------------------------------------------------------------------
// -------------------- RDOPROCAssign
// --------------------------------------------------------------------------------
inline RDOPROCAssign::RDOPROCAssign(LPIPROCProcess process, CREF(LPRDOCalc) pValue, int Id_res, int Id_param)
	: RDOPROCBlock(process )
	, pParamValue (pValue  )
	, t_resId     (Id_res  )
	, t_parId     (Id_param)
{}

CLOSE_RDO_RUNTIME_NAMESPACE
<|endoftext|>


<commit_before>/**************************************************************************
 *
 * Copyright 2011 Zack Rusin
 * All Rights Reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 *
 **************************************************************************/

#include "trace_snappyfile.hpp"

#include <snappy.h>

#include <assert.h>
#include <string.h>
#include <stdint.h>

using namespace Trace;

/*
 * Snappy file format.
 * -------------------
 *
 * Snappy at its core is just a compressoin algorithm so we're
 * creating a new file format which uses snappy compression
 * to hold the trace data.
 *
 * The file is composed of a number of chunks, they are:
 * chunk {
 *     uint32 - specifying the length of the compressed data
 *     compressed data
 * }
 * File can contain any number of such chunks.
 * The default size of an uncompressed chunk is specified in
 * SNAPPY_CHUNK_SIZE.
 *
 * Note:
 * Currently the default size for a a to-be-compressed data is
 * 1mb, meaning that the compressed data will be <= 1mb.
 * The reason it's 1mb is because it seems
 * to offer a pretty good compression/disk io speed ratio
 * but that might change.
 *
 */

SnappyFile::SnappyFile(const std::string &filename,
                              File::Mode mode)
    : File(),
      m_cache(0),
      m_cachePtr(0),
      m_cacheSize(0)
{
    size_t maxCompressedLength =
        snappy::MaxCompressedLength(SNAPPY_CHUNK_SIZE);
    m_compressedCache = new char[maxCompressedLength];
}

SnappyFile::~SnappyFile()
{
    delete [] m_compressedCache;
}

bool SnappyFile::rawOpen(const std::string &filename, File::Mode mode)
{
    std::ios_base::openmode fmode = std::fstream::binary;
    if (mode == File::Write) {
        fmode |= (std::fstream::out | std::fstream::trunc);
        createCache(SNAPPY_CHUNK_SIZE);
    } else if (mode == File::Read) {
        fmode |= std::fstream::in;
    }

    m_stream.open(filename.c_str(), fmode);

    //read in the initial buffer if we're reading
    if (m_stream.is_open() && mode == File::Read) {
        // read the snappy file identifier
        unsigned char byte1, byte2;
        m_stream >> byte1;
        m_stream >> byte2;
        assert(byte1 == SNAPPY_BYTE1 && byte2 == SNAPPY_BYTE2);

        flushCache();
    } else if (m_stream.is_open() && mode == File::Write) {
        // write the snappy file identifier
        m_stream << SNAPPY_BYTE1;
        m_stream << SNAPPY_BYTE2;
    }
    return m_stream.is_open();
}

bool SnappyFile::rawWrite(const void *buffer, size_t length)
{
    if (freeCacheSize() > length) {
        memcpy(m_cachePtr, buffer, length);
        m_cachePtr += length;
    } else if (freeCacheSize() == length) {
        memcpy(m_cachePtr, buffer, length);
        m_cachePtr += length;
        flushCache();
    } else {
        int sizeToWrite = length;

        while (sizeToWrite >= freeCacheSize()) {
            int endSize = freeCacheSize();
            int offset = length - sizeToWrite;
            memcpy(m_cachePtr, (const char*)buffer + offset, endSize);
            sizeToWrite -= endSize;
            m_cachePtr += endSize;
            flushCache();
        }
        if (sizeToWrite) {
            int offset = length - sizeToWrite;
            memcpy(m_cachePtr, (const char*)buffer + offset, sizeToWrite);
            m_cachePtr += sizeToWrite;
        }
    }

    return true;
}

bool SnappyFile::rawRead(void *buffer, size_t length)
{
    if (endOfData()) {
        return false;
    }

    if (freeCacheSize() >= length) {
        memcpy(buffer, m_cachePtr, length);
        m_cachePtr += length;
    } else {
        size_t sizeToRead = length;
        size_t offset = 0;
        while (sizeToRead) {
            size_t chunkSize = std::min(freeCacheSize(), sizeToRead);
            offset = length - sizeToRead;
            memcpy((char*)buffer + offset, m_cachePtr, chunkSize);
            m_cachePtr += chunkSize;
            sizeToRead -= chunkSize;
            if (sizeToRead > 0)
                flushCache();
            if (!m_cacheSize)
                break;
        }
    }

    return true;
}

int SnappyFile::rawGetc()
{
    int c = 0;
    if (!rawRead(&c, 1))
        return -1;
    return c;
}

void SnappyFile::rawClose()
{
    flushCache();
    m_stream.close();
    delete [] m_cache;
    m_cache = NULL;
    m_cachePtr = NULL;
}

void SnappyFile::rawFlush()
{
    flushCache();
    m_stream.flush();
}

void SnappyFile::flushCache()
{
    if (m_mode == File::Write) {
        size_t compressedLength;

        ::snappy::RawCompress(m_cache, SNAPPY_CHUNK_SIZE - freeCacheSize(),
                              m_compressedCache, &compressedLength);

        writeCompressedLength(compressedLength);
        m_stream.write(m_compressedCache, compressedLength);
        m_cachePtr = m_cache;
    } else if (m_mode == File::Read) {
        if (m_stream.eof())
            return;
        //assert(m_cachePtr == m_cache + m_cacheSize);
        size_t compressedLength;
        compressedLength = readCompressedLength();
        m_stream.read((char*)m_compressedCache, compressedLength);
        /*
         * The reason we peek here is because the last read will
         * read all the way until the last character, but that will not
         * trigger m_stream.eof() to be set, so by calling peek
         * we assure that if we in fact have read the entire stream
         * then the m_stream.eof() is always set.
         */
        m_stream.peek();
        ::snappy::GetUncompressedLength(m_compressedCache, compressedLength,
                                        &m_cacheSize);
        if (m_cache)
            delete [] m_cache;
        createCache(m_cacheSize);
        ::snappy::RawUncompress(m_compressedCache, compressedLength,
                                m_cache);
    }
}

void SnappyFile::createCache(size_t size)
{
    m_cache = new char[size];
    m_cachePtr = m_cache;
    m_cacheSize = size;
}

void SnappyFile::writeCompressedLength(size_t length)
{
    uint32_t value = length;
    assert(value == length);
    m_stream.write((const char*)&value, sizeof value);
}

size_t SnappyFile::readCompressedLength()
{
    uint32_t length = 0;
    m_stream.read((char*)&length, sizeof length);
    return length;
}
<commit_msg>Always write snappy chunk lengths in little endian.<commit_after>/**************************************************************************
 *
 * Copyright 2011 Zack Rusin
 * All Rights Reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 *
 **************************************************************************/

#include "trace_snappyfile.hpp"

#include <snappy.h>

#include <assert.h>
#include <string.h>

using namespace Trace;

/*
 * Snappy file format.
 * -------------------
 *
 * Snappy at its core is just a compressoin algorithm so we're
 * creating a new file format which uses snappy compression
 * to hold the trace data.
 *
 * The file is composed of a number of chunks, they are:
 * chunk {
 *     uint32 - specifying the length of the compressed data
 *     compressed data, in little endian
 * }
 * File can contain any number of such chunks.
 * The default size of an uncompressed chunk is specified in
 * SNAPPY_CHUNK_SIZE.
 *
 * Note:
 * Currently the default size for a a to-be-compressed data is
 * 1mb, meaning that the compressed data will be <= 1mb.
 * The reason it's 1mb is because it seems
 * to offer a pretty good compression/disk io speed ratio
 * but that might change.
 *
 */

SnappyFile::SnappyFile(const std::string &filename,
                              File::Mode mode)
    : File(),
      m_cache(0),
      m_cachePtr(0),
      m_cacheSize(0)
{
    size_t maxCompressedLength =
        snappy::MaxCompressedLength(SNAPPY_CHUNK_SIZE);
    m_compressedCache = new char[maxCompressedLength];
}

SnappyFile::~SnappyFile()
{
    delete [] m_compressedCache;
}

bool SnappyFile::rawOpen(const std::string &filename, File::Mode mode)
{
    std::ios_base::openmode fmode = std::fstream::binary;
    if (mode == File::Write) {
        fmode |= (std::fstream::out | std::fstream::trunc);
        createCache(SNAPPY_CHUNK_SIZE);
    } else if (mode == File::Read) {
        fmode |= std::fstream::in;
    }

    m_stream.open(filename.c_str(), fmode);

    //read in the initial buffer if we're reading
    if (m_stream.is_open() && mode == File::Read) {
        // read the snappy file identifier
        unsigned char byte1, byte2;
        m_stream >> byte1;
        m_stream >> byte2;
        assert(byte1 == SNAPPY_BYTE1 && byte2 == SNAPPY_BYTE2);

        flushCache();
    } else if (m_stream.is_open() && mode == File::Write) {
        // write the snappy file identifier
        m_stream << SNAPPY_BYTE1;
        m_stream << SNAPPY_BYTE2;
    }
    return m_stream.is_open();
}

bool SnappyFile::rawWrite(const void *buffer, size_t length)
{
    if (freeCacheSize() > length) {
        memcpy(m_cachePtr, buffer, length);
        m_cachePtr += length;
    } else if (freeCacheSize() == length) {
        memcpy(m_cachePtr, buffer, length);
        m_cachePtr += length;
        flushCache();
    } else {
        int sizeToWrite = length;

        while (sizeToWrite >= freeCacheSize()) {
            int endSize = freeCacheSize();
            int offset = length - sizeToWrite;
            memcpy(m_cachePtr, (const char*)buffer + offset, endSize);
            sizeToWrite -= endSize;
            m_cachePtr += endSize;
            flushCache();
        }
        if (sizeToWrite) {
            int offset = length - sizeToWrite;
            memcpy(m_cachePtr, (const char*)buffer + offset, sizeToWrite);
            m_cachePtr += sizeToWrite;
        }
    }

    return true;
}

bool SnappyFile::rawRead(void *buffer, size_t length)
{
    if (endOfData()) {
        return false;
    }

    if (freeCacheSize() >= length) {
        memcpy(buffer, m_cachePtr, length);
        m_cachePtr += length;
    } else {
        size_t sizeToRead = length;
        size_t offset = 0;
        while (sizeToRead) {
            size_t chunkSize = std::min(freeCacheSize(), sizeToRead);
            offset = length - sizeToRead;
            memcpy((char*)buffer + offset, m_cachePtr, chunkSize);
            m_cachePtr += chunkSize;
            sizeToRead -= chunkSize;
            if (sizeToRead > 0)
                flushCache();
            if (!m_cacheSize)
                break;
        }
    }

    return true;
}

int SnappyFile::rawGetc()
{
    int c = 0;
    if (!rawRead(&c, 1))
        return -1;
    return c;
}

void SnappyFile::rawClose()
{
    flushCache();
    m_stream.close();
    delete [] m_cache;
    m_cache = NULL;
    m_cachePtr = NULL;
}

void SnappyFile::rawFlush()
{
    flushCache();
    m_stream.flush();
}

void SnappyFile::flushCache()
{
    if (m_mode == File::Write) {
        size_t compressedLength;

        ::snappy::RawCompress(m_cache, SNAPPY_CHUNK_SIZE - freeCacheSize(),
                              m_compressedCache, &compressedLength);

        writeCompressedLength(compressedLength);
        m_stream.write(m_compressedCache, compressedLength);
        m_cachePtr = m_cache;
    } else if (m_mode == File::Read) {
        if (m_stream.eof())
            return;
        //assert(m_cachePtr == m_cache + m_cacheSize);
        size_t compressedLength;
        compressedLength = readCompressedLength();
        m_stream.read((char*)m_compressedCache, compressedLength);
        /*
         * The reason we peek here is because the last read will
         * read all the way until the last character, but that will not
         * trigger m_stream.eof() to be set, so by calling peek
         * we assure that if we in fact have read the entire stream
         * then the m_stream.eof() is always set.
         */
        m_stream.peek();
        ::snappy::GetUncompressedLength(m_compressedCache, compressedLength,
                                        &m_cacheSize);
        if (m_cache)
            delete [] m_cache;
        createCache(m_cacheSize);
        ::snappy::RawUncompress(m_compressedCache, compressedLength,
                                m_cache);
    }
}

void SnappyFile::createCache(size_t size)
{
    m_cache = new char[size];
    m_cachePtr = m_cache;
    m_cacheSize = size;
}

void SnappyFile::writeCompressedLength(size_t length)
{
    unsigned char buf[4];
    buf[0] = length & 0xff; length >>= 8;
    buf[1] = length & 0xff; length >>= 8;
    buf[2] = length & 0xff; length >>= 8;
    buf[3] = length & 0xff; length >>= 8;
    assert(length == 0);
    m_stream.write((const char *)buf, sizeof buf);
}

size_t SnappyFile::readCompressedLength()
{
    unsigned char buf[4];
    size_t length;
    m_stream.read((char *)buf, sizeof buf);
    if (m_stream.fail()) {
        length = 0;
    } else {
        length  =  (size_t)buf[0];
        length |= ((size_t)buf[1] <<  8);
        length |= ((size_t)buf[2] << 16);
        length |= ((size_t)buf[3] << 24);
    }
    return length;
}
<|endoftext|>