diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index a58d6d205..24c282d0f 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -56,6 +56,7 @@ Set(SOURCEPYTHON_UTILITIES_HEADERS core/utilities/wrap_macros.h core/utilities/conversions.h core/utilities/ipythongenerator.h + core/utilities/array.h ) Set(SOURCEPYTHON_UTILITIES_SOURCES diff --git a/src/core/modules/core/core_wrap.cpp b/src/core/modules/core/core_wrap.cpp index a0abf7199..1379b0e0b 100644 --- a/src/core/modules/core/core_wrap.cpp +++ b/src/core/modules/core/core_wrap.cpp @@ -30,6 +30,7 @@ #include "export_main.h" #include "sp_main.h" #include "core.h" +#include "utilities/array.h" //----------------------------------------------------------------------------- @@ -46,6 +47,7 @@ static void export_message_severity(scope); static void export_output_return(scope); static void export_constants(scope); static void export_functions(scope); +static void export_arrays(scope); //----------------------------------------------------------------------------- @@ -58,6 +60,7 @@ DECLARE_SP_MODULE(_core) export_output_return(_core); export_constants(_core); export_functions(_core); + export_arrays(_core); scope().attr("BoostPythonClass") = objects::class_metatype(); } @@ -135,3 +138,65 @@ void export_functions(scope _core) "Return a list of all modules exposed by Source.Python's core.\n\n" ":rtype: list"); } + +//----------------------------------------------------------------------------- +// Expose functions. +//----------------------------------------------------------------------------- +void export_arrays(scope _core) +{ + class_< Array >( "BoolArray" ) + .def(array_indexing_suite< Array >()) + ; + + class_< Array >( "CharArray" ) + .def(array_indexing_suite< Array >()) + ; + + class_< Array >( "UCharArray" ) + .def(array_indexing_suite< Array >()) + ; + + class_< Array >( "UShortArray" ) + .def(array_indexing_suite< Array >()) + ; + + class_< Array >( "ShortArray" ) + .def(array_indexing_suite< Array >()) + ; + + class_< Array >( "IntArray" ) + .def(array_indexing_suite< Array >()) + ; + + class_< Array >( "UIntArray" ) + .def(array_indexing_suite< Array >()) + ; + + class_< Array >( "LongArray" ) + .def(array_indexing_suite< Array >()) + ; + + class_< Array >( "ULongArray" ) + .def(array_indexing_suite< Array >()) + ; + + class_< Array >( "LongLongArray" ) + .def(array_indexing_suite< Array >()) + ; + + class_< Array >( "ULongLongArray" ) + .def(array_indexing_suite< Array >()) + ; + + class_< Array >( "FloatArray" ) + .def(array_indexing_suite< Array >()) + ; + + class_< Array >( "DoubleArray" ) + .def(array_indexing_suite< Array >()) + ; + + //class_< Array >( "StringArray" ) + // .def(array_indexing_suite< Array >()) + //; +} \ No newline at end of file diff --git a/src/core/utilities/array.h b/src/core/utilities/array.h new file mode 100644 index 000000000..8af4ed5e0 --- /dev/null +++ b/src/core/utilities/array.h @@ -0,0 +1,349 @@ +/** +* ============================================================================= +* Source Python +* Copyright (C) 2012-2020 Source Python Development Team. All rights reserved. +* ============================================================================= +* +* This program is free software; you can redistribute it and/or modify it under +* the terms of the GNU General Public License, version 3.0, as published by the +* Free Software Foundation. +* +* This program is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more +* details. +* +* You should have received a copy of the GNU General Public License along with +* this program. If not, see . +* +* As a special exception, the Source Python Team gives you permission +* to link the code of this program (as well as its derivative works) to +* "Half-Life 2," the "Source Engine," and any Game MODs that run on software +* by the Valve Corporation. You must obey the GNU General Public License in +* all respects for all other code used. Additionally, the Source.Python +* Development Team grants this exception to all derivative works. +*/ + +#ifndef _ARRAY_H +#define _ARRAY_H + +// ============================================================================ +// >> INCLUDES +// ============================================================================ +#include "utilities/wrap_macros.h" +#include "modules/memory/memory_utilities.h" + +#include +#include + +#include +#include +#include +#include + + +// ============================================================================ +// >> Array +// ============================================================================ +// Implementation is based on this: +// https://stackoverflow.com/a/27560620 +template +class Array +{ +public: + typedef T* pointer; + typedef T value_type; + typedef std::size_t size_type; + typedef T* iterator; + typedef T const* const_iterator; + typedef std::reverse_iterator reverse_iterator; + typedef std::reverse_iterator const_reverse_iterator; + + Array() + : arr_(NULL), + length_(-1) + {} + + Array(pointer arr) + : arr_(arr), + length_(-1) + {} + + Array(void* arr) + : arr_(reinterpret_cast(arr)), + length_(-1) + {} + + Array(void* arr, size_type length) + : arr_(reinterpret_cast(arr)), + length_(length) + {} + + Array(pointer arr, size_type length) + : arr_(arr), + length_(length) + {} + + Array( pointer first, pointer last ) + : arr_(first), + length_(static_cast(std::distance(first, last))) + {} + + T& operator[](size_type index) + { + return arr_[index]; + } + + iterator begin() + { + return arr_; + } + + const_iterator cbegin() const + { + return arr_; + } + + iterator end() + { + return begin() + size(); + } + + const_iterator cend() const + { + return cbegin() + size(); + } + + reverse_iterator rbegin() + { + return reverse_iterator( end() ); + } + + const_reverse_iterator rbegin() const + { + return const_reverse_iterator( cend() ); + } + + const_reverse_iterator crbegin() const + { + return const_reverse_iterator( cend() ); + } + + reverse_iterator rend() + { + return reverse_iterator( begin() ); + } + + const_reverse_iterator rend() const + { + return const_reverse_iterator( cbegin() ); + } + + const_reverse_iterator crend() const + { + return const_reverse_iterator( cbegin() ); + } + + size_type size() const + { + validate_length(); + return length_; + } + + void check_index(size_type index) const + { + size_type length = size(); + if (!(0 <= index < length)) + BOOST_RAISE_EXCEPTION(PyExc_IndexError, "Index (%d) out of range (%d)", index, length) + } + + void validate_length() const + { + if (!has_length()) + BOOST_RAISE_EXCEPTION(PyExc_TypeError, "Operation requires length of the array.") + } + + bool has_length() const + { + return length_ != -1; + } + + static void set_length(Array& arr, size_type size) + { + arr.length_ = size; + } + +protected: + iterator arr_; + size_type length_; +}; + + +// ============================================================================ +// >> array_indexing_suite +// ============================================================================ +// Forward declaration +template< + typename Array, + bool NoProxy, + typename DerivedPolicies> +class array_indexing_suite; + + +namespace detail { + +template +struct final_array_derived_policies +: array_indexing_suite> +{}; + +} /* namespace detail */ + + +template< + typename Array, + bool NoProxy = std::is_arithmetic::value, + typename DerivedPolicies = ::detail::final_array_derived_policies +> +class array_indexing_suite + : public boost::python::indexing_suite +{ +public: + typedef typename Array::value_type data_type; + typedef typename Array::value_type key_type; + typedef typename Array::size_type index_type; + typedef typename Array::size_type size_type; + + template + static void extension_def(Class& cl) + { + cl + .def(init()) + .def(init()) + .def("set_length", Array::set_length) + + .def(GET_PTR_NAME, &get_ptr, manage_new_object_policy()) + .def(GET_OBJ_NAME, &make_obj, manage_new_object_policy()).staticmethod(GET_OBJ_NAME) + .add_property(GET_SIZE_NAME, &get_size) + ; + + extern dict g_oExposedClasses; + g_oExposedClasses[cl.attr("__name__")] = cl; + } + + static CPointer* get_ptr(Array& arr) + { + return new CPointer((unsigned long) arr.begin()); + } + + static Array* make_obj(CPointer* pPtr) + { + return new Array((void *) pPtr->m_ulAddr); + } + + static size_type get_size(Array& arr) + { + return arr.size() * sizeof(data_type); + } + + static data_type & get_item(Array & arr, index_type i) { + return arr[i]; + } + + static void set_item(Array & arr, index_type i, data_type const & v) { + arr[i] = v; + } + + static void delete_item(Array & arr, index_type i) { + BOOST_RAISE_EXCEPTION(PyExc_NotImplementedError, "delete_item") + } + + static size_type size(Array & arr) { + return arr.size(); + } + + static bool contains(Array & arr, key_type const & key) { + return std::find(arr.cbegin(), arr.cend(), key) != arr.cend(); + } + + static index_type get_min_index(Array & arr) { + return 0; + } + + static index_type get_max_index(Array & arr) { + return arr.size(); + } + + static bool compare_index(Array & , index_type a, index_type b) { + return a < b; + } + + static index_type convert_index(Array & arr, PyObject * i_) { + boost::python::extract < long > i(i_); + if (i.check()) { + long index = i(); + + if (index < 0) { + index += static_cast < decltype(index) > (DerivedPolicies::size(arr)); + } + + // This allows wrapping raw pointers and treat them like an array. + // The programmer has to take care of not exceeding the array size. + if (arr.has_length()) + arr.validate_length(); + + return index; + } + + BOOST_RAISE_EXCEPTION(PyExc_TypeError, "Invalid index type"); + + return index_type(); + } + + static boost::python::object get_slice(Array & arr, index_type from, index_type to) { + if (from > to) { + return boost::python::object(Array()); + } + return boost::python::object(Array(arr.begin() + from, arr.begin() + to)); + } + + static void set_slice(Array & arr, index_type from, index_type to, data_type + const & v) { + if (from > to) { + return; + + } else if (to > arr.size()) { + BOOST_RAISE_EXCEPTION(PyExc_IndexError, "Index out of range"); + + } else { + std::fill(arr.begin() + from, arr.begin() + to, v); + + } + } + + template < typename Iter > + static void set_slice(Array & arr, index_type from, index_type to, Iter first, Iter last) { + auto num_items = std::distance(first, last); + + if ((from + num_items) > arr.size()) { + BOOST_RAISE_EXCEPTION(PyExc_IndexError, "Index out of range"); + } + + if (from > to) { + std::copy(first, last, arr.begin() + from); + + } else { + if (static_cast < decltype(num_items) > (to - from) != num_items) { + BOOST_RAISE_EXCEPTION(PyExc_TypeError, "Array length is immutable"); + } + + std::copy(first, last, arr.begin() + from); + } + } + + static void delete_slice(Array & arr, index_type from , index_type to) { + BOOST_RAISE_EXCEPTION(PyExc_NotImplementedError, "delete_slice") + } +}; + +#endif // _ARRAY_H \ No newline at end of file