1*5ba1f45fSchristos /* Copyright (C) 2017-2024 Free Software Foundation, Inc. 28dffb485Schristos 38dffb485Schristos This file is part of GDB. 48dffb485Schristos 58dffb485Schristos This program is free software; you can redistribute it and/or modify 68dffb485Schristos it under the terms of the GNU General Public License as published by 78dffb485Schristos the Free Software Foundation; either version 3 of the License, or 88dffb485Schristos (at your option) any later version. 98dffb485Schristos 108dffb485Schristos This program is distributed in the hope that it will be useful, 118dffb485Schristos but WITHOUT ANY WARRANTY; without even the implied warranty of 128dffb485Schristos MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 138dffb485Schristos GNU General Public License for more details. 148dffb485Schristos 158dffb485Schristos You should have received a copy of the GNU General Public License 168dffb485Schristos along with this program. If not, see <http://www.gnu.org/licenses/>. */ 178dffb485Schristos 188dffb485Schristos #ifndef COMMON_ARRAY_VIEW_H 198dffb485Schristos #define COMMON_ARRAY_VIEW_H 208dffb485Schristos 218dffb485Schristos #include "traits.h" 224b169a6bSchristos #include <algorithm> 238dffb485Schristos #include <type_traits> 24*5ba1f45fSchristos #include "gdbsupport/gdb_assert.h" 258dffb485Schristos 268dffb485Schristos /* An array_view is an abstraction that provides a non-owning view 278dffb485Schristos over a sequence of contiguous objects. 288dffb485Schristos 298dffb485Schristos A way to put it is that array_view is to std::vector (and 308dffb485Schristos std::array and built-in arrays with rank==1) like std::string_view 318dffb485Schristos is to std::string. 328dffb485Schristos 338dffb485Schristos The main intent of array_view is to use it as function input 348dffb485Schristos parameter type, making it possible to pass in any sequence of 358dffb485Schristos contiguous objects, irrespective of whether the objects live on the 368dffb485Schristos stack or heap and what actual container owns them. Implicit 378dffb485Schristos construction from the element type is supported too, making it easy 388dffb485Schristos to call functions that expect an array of elements when you only 398dffb485Schristos have one element (usually on the stack). For example: 408dffb485Schristos 418dffb485Schristos struct A { .... }; 428dffb485Schristos void function (gdb::array_view<A> as); 438dffb485Schristos 448dffb485Schristos std::vector<A> std_vec = ...; 458dffb485Schristos std::array<A, N> std_array = ...; 468dffb485Schristos A array[] = {...}; 478dffb485Schristos A elem; 488dffb485Schristos 498dffb485Schristos function (std_vec); 508dffb485Schristos function (std_array); 518dffb485Schristos function (array); 528dffb485Schristos function (elem); 538dffb485Schristos 548dffb485Schristos Views can be either mutable or const. A const view is simply 558dffb485Schristos created by specifying a const T as array_view template parameter, 568dffb485Schristos in which case operator[] of non-const array_view objects ends up 578dffb485Schristos returning const references. Making the array_view itself const is 588dffb485Schristos analogous to making a pointer itself be const. I.e., disables 598dffb485Schristos re-seating the view/pointer. 608dffb485Schristos 618dffb485Schristos Since array_view objects are small (pointer plus size), and 628dffb485Schristos designed to be trivially copyable, they should generally be passed 638dffb485Schristos around by value. 648dffb485Schristos 658dffb485Schristos You can find unit tests covering the whole API in 668dffb485Schristos unittests/array-view-selftests.c. */ 678dffb485Schristos 688dffb485Schristos namespace gdb { 698dffb485Schristos 708dffb485Schristos template <typename T> 718dffb485Schristos class array_view 728dffb485Schristos { 738dffb485Schristos /* True iff decayed T is the same as decayed U. E.g., we want to 748dffb485Schristos say that 'T&' is the same as 'const T'. */ 758dffb485Schristos template <typename U> 768dffb485Schristos using IsDecayedT = typename std::is_same<typename std::decay<T>::type, 778dffb485Schristos typename std::decay<U>::type>; 788dffb485Schristos 798dffb485Schristos /* True iff decayed T is the same as decayed U, and 'U *' is 808dffb485Schristos implicitly convertible to 'T *'. This is a requirement for 818dffb485Schristos several methods. */ 828dffb485Schristos template <typename U> 838dffb485Schristos using DecayedConvertible = gdb::And<IsDecayedT<U>, 848dffb485Schristos std::is_convertible<U *, T *>>; 858dffb485Schristos 868dffb485Schristos public: 878dffb485Schristos using value_type = T; 888dffb485Schristos using reference = T &; 898dffb485Schristos using const_reference = const T &; 908dffb485Schristos using size_type = size_t; 918dffb485Schristos 928dffb485Schristos /* Default construction creates an empty view. */ 938dffb485Schristos constexpr array_view () noexcept 948dffb485Schristos : m_array (nullptr), m_size (0) 958dffb485Schristos {} 968dffb485Schristos 978dffb485Schristos /* Create an array view over a single object of the type of an 988dffb485Schristos array_view element. The created view as size==1. This is 998dffb485Schristos templated on U to allow constructing a array_view<const T> over a 1008dffb485Schristos (non-const) T. The "convertible" requirement makes sure that you 1018dffb485Schristos can't create an array_view<T> over a const T. */ 1028dffb485Schristos template<typename U, 1038dffb485Schristos typename = Requires<DecayedConvertible<U>>> 1048dffb485Schristos constexpr array_view (U &elem) noexcept 1058dffb485Schristos : m_array (&elem), m_size (1) 1068dffb485Schristos {} 1078dffb485Schristos 1088dffb485Schristos /* Same as above, for rvalue references. */ 1098dffb485Schristos template<typename U, 1108dffb485Schristos typename = Requires<DecayedConvertible<U>>> 1118dffb485Schristos constexpr array_view (U &&elem) noexcept 1128dffb485Schristos : m_array (&elem), m_size (1) 1138dffb485Schristos {} 1148dffb485Schristos 1158dffb485Schristos /* Create an array view from a pointer to an array and an element 1168dffb485Schristos count. */ 1178dffb485Schristos template<typename U, 1188dffb485Schristos typename = Requires<DecayedConvertible<U>>> 1198dffb485Schristos constexpr array_view (U *array, size_t size) noexcept 1208dffb485Schristos : m_array (array), m_size (size) 1218dffb485Schristos {} 1228dffb485Schristos 1238dffb485Schristos /* Create an array view from a range. This is templated on both U 1248dffb485Schristos an V to allow passing in a mix of 'const T *' and 'T *'. */ 1258dffb485Schristos template<typename U, typename V, 1268dffb485Schristos typename = Requires<DecayedConvertible<U>>, 1278dffb485Schristos typename = Requires<DecayedConvertible<V>>> 1288dffb485Schristos constexpr array_view (U *begin, V *end) noexcept 1298dffb485Schristos : m_array (begin), m_size (end - begin) 1308dffb485Schristos {} 1318dffb485Schristos 1328dffb485Schristos /* Create an array view from an array. */ 1338dffb485Schristos template<typename U, size_t Size, 1348dffb485Schristos typename = Requires<DecayedConvertible<U>>> 1358dffb485Schristos constexpr array_view (U (&array)[Size]) noexcept 1368dffb485Schristos : m_array (array), m_size (Size) 1378dffb485Schristos {} 1388dffb485Schristos 1398dffb485Schristos /* Create an array view from a contiguous container. E.g., 1408dffb485Schristos std::vector and std::array. */ 1418dffb485Schristos template<typename Container, 1428dffb485Schristos typename = Requires<gdb::Not<IsDecayedT<Container>>>, 1438dffb485Schristos typename 1444b169a6bSchristos = Requires<DecayedConvertible 1454b169a6bSchristos <typename std::remove_pointer 1464b169a6bSchristos <decltype (std::declval<Container> ().data ()) 1474b169a6bSchristos >::type>>, 1488dffb485Schristos typename 1498dffb485Schristos = Requires<std::is_convertible 1508dffb485Schristos <decltype (std::declval<Container> ().size ()), 1518dffb485Schristos size_type>>> 1528dffb485Schristos constexpr array_view (Container &&c) noexcept 1538dffb485Schristos : m_array (c.data ()), m_size (c.size ()) 1548dffb485Schristos {} 1558dffb485Schristos 156*5ba1f45fSchristos /* Observer methods. */ 157*5ba1f45fSchristos constexpr T *data () noexcept { return m_array; } 1588dffb485Schristos constexpr const T *data () const noexcept { return m_array; } 1598dffb485Schristos 160*5ba1f45fSchristos constexpr T *begin () noexcept { return m_array; } 1618dffb485Schristos constexpr const T *begin () const noexcept { return m_array; } 1628dffb485Schristos 163*5ba1f45fSchristos constexpr T *end () noexcept { return m_array + m_size; } 1648dffb485Schristos constexpr const T *end () const noexcept { return m_array + m_size; } 1658dffb485Schristos 166*5ba1f45fSchristos constexpr reference operator[] (size_t index) noexcept 1674b169a6bSchristos { 1684b169a6bSchristos #if defined(_GLIBCXX_DEBUG) 1694b169a6bSchristos gdb_assert (index < m_size); 1704b169a6bSchristos #endif 1714b169a6bSchristos return m_array[index]; 1724b169a6bSchristos } 1738dffb485Schristos constexpr const_reference operator[] (size_t index) const noexcept 1744b169a6bSchristos { 175*5ba1f45fSchristos #if defined(_GLIBCXX_DEBUG) 1764b169a6bSchristos gdb_assert (index < m_size); 1774b169a6bSchristos #endif 1784b169a6bSchristos return m_array[index]; 1794b169a6bSchristos } 1808dffb485Schristos 1818dffb485Schristos constexpr size_type size () const noexcept { return m_size; } 1828dffb485Schristos constexpr bool empty () const noexcept { return m_size == 0; } 1838dffb485Schristos 1848dffb485Schristos /* Slice an array view. */ 1858dffb485Schristos 1868dffb485Schristos /* Return a new array view over SIZE elements starting at START. */ 187*5ba1f45fSchristos [[nodiscard]] 1888dffb485Schristos constexpr array_view<T> slice (size_type start, size_type size) const noexcept 1894b169a6bSchristos { 190*5ba1f45fSchristos #if defined(_GLIBCXX_DEBUG) 1914b169a6bSchristos gdb_assert (start + size <= m_size); 1924b169a6bSchristos #endif 1934b169a6bSchristos return {m_array + start, size}; 1944b169a6bSchristos } 1958dffb485Schristos 1968dffb485Schristos /* Return a new array view over all the elements after START, 1978dffb485Schristos inclusive. */ 198*5ba1f45fSchristos [[nodiscard]] 1998dffb485Schristos constexpr array_view<T> slice (size_type start) const noexcept 2004b169a6bSchristos { 201*5ba1f45fSchristos #if defined(_GLIBCXX_DEBUG) 2024b169a6bSchristos gdb_assert (start <= m_size); 2034b169a6bSchristos #endif 2044b169a6bSchristos return {m_array + start, size () - start}; 2054b169a6bSchristos } 2068dffb485Schristos 2078dffb485Schristos private: 2088dffb485Schristos T *m_array; 2098dffb485Schristos size_type m_size; 2108dffb485Schristos }; 2118dffb485Schristos 2124b169a6bSchristos /* Copy the contents referenced by the array view SRC to the array view DEST. 2134b169a6bSchristos 2144b169a6bSchristos The two array views must have the same length. */ 2154b169a6bSchristos 2164b169a6bSchristos template <typename U, typename T> 2174b169a6bSchristos void copy (gdb::array_view<U> src, gdb::array_view<T> dest) 2184b169a6bSchristos { 2194b169a6bSchristos gdb_assert (dest.size () == src.size ()); 2204b169a6bSchristos if (dest.data () < src.data ()) 2214b169a6bSchristos std::copy (src.begin (), src.end (), dest.begin ()); 2224b169a6bSchristos else if (dest.data () > src.data ()) 2234b169a6bSchristos std::copy_backward (src.begin (), src.end (), dest.end ()); 2244b169a6bSchristos } 2254b169a6bSchristos 2268dffb485Schristos /* Compare LHS and RHS for (deep) equality. That is, whether LHS and 2278dffb485Schristos RHS have the same sizes, and whether each pair of elements of LHS 2288dffb485Schristos and RHS at the same position compares equal. */ 2298dffb485Schristos 2308dffb485Schristos template <typename T> 2318dffb485Schristos bool 2328dffb485Schristos operator== (const gdb::array_view<T> &lhs, const gdb::array_view<T> &rhs) 2338dffb485Schristos { 2348dffb485Schristos if (lhs.size () != rhs.size ()) 2358dffb485Schristos return false; 2368dffb485Schristos 2378dffb485Schristos for (size_t i = 0; i < lhs.size (); i++) 2388dffb485Schristos if (!(lhs[i] == rhs[i])) 2398dffb485Schristos return false; 2408dffb485Schristos 2418dffb485Schristos return true; 2428dffb485Schristos } 2438dffb485Schristos 2448dffb485Schristos /* Compare two array_views for inequality. */ 2458dffb485Schristos 2468dffb485Schristos template <typename T> 2478dffb485Schristos bool 2488dffb485Schristos operator!= (const gdb::array_view<T> &lhs, const gdb::array_view<T> &rhs) 2498dffb485Schristos { 2508dffb485Schristos return !(lhs == rhs); 2518dffb485Schristos } 2528dffb485Schristos 2538dffb485Schristos /* Create an array view from a pointer to an array and an element 2548dffb485Schristos count. 2558dffb485Schristos 2568dffb485Schristos This is useful as alternative to constructing an array_view using 2578dffb485Schristos brace initialization when the size variable you have handy is of 2588dffb485Schristos signed type, since otherwise without an explicit cast the code 2598dffb485Schristos would be ill-formed. 2608dffb485Schristos 2618dffb485Schristos For example, with: 2628dffb485Schristos 2638dffb485Schristos extern void foo (int, int, gdb::array_view<value *>); 2648dffb485Schristos 2658dffb485Schristos value *args[2]; 2668dffb485Schristos int nargs; 2678dffb485Schristos foo (1, 2, {values, nargs}); 2688dffb485Schristos 2698dffb485Schristos You'd get: 2708dffb485Schristos 2718dffb485Schristos source.c:10: error: narrowing conversion of ‘nargs’ from ‘int’ to 2728dffb485Schristos ‘size_t {aka long unsigned int}’ inside { } [-Werror=narrowing] 2738dffb485Schristos 2748dffb485Schristos You could fix it by writing the somewhat distracting explicit cast: 2758dffb485Schristos 2768dffb485Schristos foo (1, 2, {values, (size_t) nargs}); 2778dffb485Schristos 2788dffb485Schristos Or by instantiating an array_view explicitly: 2798dffb485Schristos 2808dffb485Schristos foo (1, 2, gdb::array_view<value *>(values, nargs)); 2818dffb485Schristos 2828dffb485Schristos Or, better, using make_array_view, which has the advantage of 283*5ba1f45fSchristos inferring the array_view element's type: 2848dffb485Schristos 2858dffb485Schristos foo (1, 2, gdb::make_array_view (values, nargs)); 2868dffb485Schristos */ 2878dffb485Schristos 2888dffb485Schristos template<typename U> 2898dffb485Schristos constexpr inline array_view<U> 2908dffb485Schristos make_array_view (U *array, size_t size) noexcept 2918dffb485Schristos { 2928dffb485Schristos return {array, size}; 2938dffb485Schristos } 2948dffb485Schristos 2958dffb485Schristos } /* namespace gdb */ 2968dffb485Schristos 2978dffb485Schristos #endif 298