1*6881a400Schristos /* Copyright (C) 2017-2023 Free Software Foundation, Inc. 27d62b00eSchristos 37d62b00eSchristos This file is part of GDB. 47d62b00eSchristos 57d62b00eSchristos This program is free software; you can redistribute it and/or modify 67d62b00eSchristos it under the terms of the GNU General Public License as published by 77d62b00eSchristos the Free Software Foundation; either version 3 of the License, or 87d62b00eSchristos (at your option) any later version. 97d62b00eSchristos 107d62b00eSchristos This program is distributed in the hope that it will be useful, 117d62b00eSchristos but WITHOUT ANY WARRANTY; without even the implied warranty of 127d62b00eSchristos MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 137d62b00eSchristos GNU General Public License for more details. 147d62b00eSchristos 157d62b00eSchristos You should have received a copy of the GNU General Public License 167d62b00eSchristos along with this program. If not, see <http://www.gnu.org/licenses/>. */ 177d62b00eSchristos 187d62b00eSchristos #ifndef COMMON_ARRAY_VIEW_H 197d62b00eSchristos #define COMMON_ARRAY_VIEW_H 207d62b00eSchristos 217d62b00eSchristos #include "traits.h" 22*6881a400Schristos #include <algorithm> 237d62b00eSchristos #include <type_traits> 247d62b00eSchristos 257d62b00eSchristos /* An array_view is an abstraction that provides a non-owning view 267d62b00eSchristos over a sequence of contiguous objects. 277d62b00eSchristos 287d62b00eSchristos A way to put it is that array_view is to std::vector (and 297d62b00eSchristos std::array and built-in arrays with rank==1) like std::string_view 307d62b00eSchristos is to std::string. 317d62b00eSchristos 327d62b00eSchristos The main intent of array_view is to use it as function input 337d62b00eSchristos parameter type, making it possible to pass in any sequence of 347d62b00eSchristos contiguous objects, irrespective of whether the objects live on the 357d62b00eSchristos stack or heap and what actual container owns them. Implicit 367d62b00eSchristos construction from the element type is supported too, making it easy 377d62b00eSchristos to call functions that expect an array of elements when you only 387d62b00eSchristos have one element (usually on the stack). For example: 397d62b00eSchristos 407d62b00eSchristos struct A { .... }; 417d62b00eSchristos void function (gdb::array_view<A> as); 427d62b00eSchristos 437d62b00eSchristos std::vector<A> std_vec = ...; 447d62b00eSchristos std::array<A, N> std_array = ...; 457d62b00eSchristos A array[] = {...}; 467d62b00eSchristos A elem; 477d62b00eSchristos 487d62b00eSchristos function (std_vec); 497d62b00eSchristos function (std_array); 507d62b00eSchristos function (array); 517d62b00eSchristos function (elem); 527d62b00eSchristos 537d62b00eSchristos Views can be either mutable or const. A const view is simply 547d62b00eSchristos created by specifying a const T as array_view template parameter, 557d62b00eSchristos in which case operator[] of non-const array_view objects ends up 567d62b00eSchristos returning const references. Making the array_view itself const is 577d62b00eSchristos analogous to making a pointer itself be const. I.e., disables 587d62b00eSchristos re-seating the view/pointer. 597d62b00eSchristos 607d62b00eSchristos Since array_view objects are small (pointer plus size), and 617d62b00eSchristos designed to be trivially copyable, they should generally be passed 627d62b00eSchristos around by value. 637d62b00eSchristos 647d62b00eSchristos You can find unit tests covering the whole API in 657d62b00eSchristos unittests/array-view-selftests.c. */ 667d62b00eSchristos 677d62b00eSchristos namespace gdb { 687d62b00eSchristos 697d62b00eSchristos template <typename T> 707d62b00eSchristos class array_view 717d62b00eSchristos { 727d62b00eSchristos /* True iff decayed T is the same as decayed U. E.g., we want to 737d62b00eSchristos say that 'T&' is the same as 'const T'. */ 747d62b00eSchristos template <typename U> 757d62b00eSchristos using IsDecayedT = typename std::is_same<typename std::decay<T>::type, 767d62b00eSchristos typename std::decay<U>::type>; 777d62b00eSchristos 787d62b00eSchristos /* True iff decayed T is the same as decayed U, and 'U *' is 797d62b00eSchristos implicitly convertible to 'T *'. This is a requirement for 807d62b00eSchristos several methods. */ 817d62b00eSchristos template <typename U> 827d62b00eSchristos using DecayedConvertible = gdb::And<IsDecayedT<U>, 837d62b00eSchristos std::is_convertible<U *, T *>>; 847d62b00eSchristos 857d62b00eSchristos public: 867d62b00eSchristos using value_type = T; 877d62b00eSchristos using reference = T &; 887d62b00eSchristos using const_reference = const T &; 897d62b00eSchristos using size_type = size_t; 907d62b00eSchristos 917d62b00eSchristos /* Default construction creates an empty view. */ 927d62b00eSchristos constexpr array_view () noexcept 937d62b00eSchristos : m_array (nullptr), m_size (0) 947d62b00eSchristos {} 957d62b00eSchristos 967d62b00eSchristos /* Create an array view over a single object of the type of an 977d62b00eSchristos array_view element. The created view as size==1. This is 987d62b00eSchristos templated on U to allow constructing a array_view<const T> over a 997d62b00eSchristos (non-const) T. The "convertible" requirement makes sure that you 1007d62b00eSchristos can't create an array_view<T> over a const T. */ 1017d62b00eSchristos template<typename U, 1027d62b00eSchristos typename = Requires<DecayedConvertible<U>>> 1037d62b00eSchristos constexpr array_view (U &elem) noexcept 1047d62b00eSchristos : m_array (&elem), m_size (1) 1057d62b00eSchristos {} 1067d62b00eSchristos 1077d62b00eSchristos /* Same as above, for rvalue references. */ 1087d62b00eSchristos template<typename U, 1097d62b00eSchristos typename = Requires<DecayedConvertible<U>>> 1107d62b00eSchristos constexpr array_view (U &&elem) noexcept 1117d62b00eSchristos : m_array (&elem), m_size (1) 1127d62b00eSchristos {} 1137d62b00eSchristos 1147d62b00eSchristos /* Create an array view from a pointer to an array and an element 1157d62b00eSchristos count. */ 1167d62b00eSchristos template<typename U, 1177d62b00eSchristos typename = Requires<DecayedConvertible<U>>> 1187d62b00eSchristos constexpr array_view (U *array, size_t size) noexcept 1197d62b00eSchristos : m_array (array), m_size (size) 1207d62b00eSchristos {} 1217d62b00eSchristos 1227d62b00eSchristos /* Create an array view from a range. This is templated on both U 1237d62b00eSchristos an V to allow passing in a mix of 'const T *' and 'T *'. */ 1247d62b00eSchristos template<typename U, typename V, 1257d62b00eSchristos typename = Requires<DecayedConvertible<U>>, 1267d62b00eSchristos typename = Requires<DecayedConvertible<V>>> 1277d62b00eSchristos constexpr array_view (U *begin, V *end) noexcept 1287d62b00eSchristos : m_array (begin), m_size (end - begin) 1297d62b00eSchristos {} 1307d62b00eSchristos 1317d62b00eSchristos /* Create an array view from an array. */ 1327d62b00eSchristos template<typename U, size_t Size, 1337d62b00eSchristos typename = Requires<DecayedConvertible<U>>> 1347d62b00eSchristos constexpr array_view (U (&array)[Size]) noexcept 1357d62b00eSchristos : m_array (array), m_size (Size) 1367d62b00eSchristos {} 1377d62b00eSchristos 1387d62b00eSchristos /* Create an array view from a contiguous container. E.g., 1397d62b00eSchristos std::vector and std::array. */ 1407d62b00eSchristos template<typename Container, 1417d62b00eSchristos typename = Requires<gdb::Not<IsDecayedT<Container>>>, 1427d62b00eSchristos typename 143*6881a400Schristos = Requires<DecayedConvertible 144*6881a400Schristos <typename std::remove_pointer 145*6881a400Schristos <decltype (std::declval<Container> ().data ()) 146*6881a400Schristos >::type>>, 1477d62b00eSchristos typename 1487d62b00eSchristos = Requires<std::is_convertible 1497d62b00eSchristos <decltype (std::declval<Container> ().size ()), 1507d62b00eSchristos size_type>>> 1517d62b00eSchristos constexpr array_view (Container &&c) noexcept 1527d62b00eSchristos : m_array (c.data ()), m_size (c.size ()) 1537d62b00eSchristos {} 1547d62b00eSchristos 1557d62b00eSchristos /* Observer methods. Some of these can't be constexpr until we 1567d62b00eSchristos require C++14. */ 1577d62b00eSchristos /*constexpr14*/ T *data () noexcept { return m_array; } 1587d62b00eSchristos constexpr const T *data () const noexcept { return m_array; } 1597d62b00eSchristos 1607d62b00eSchristos /*constexpr14*/ T *begin () noexcept { return m_array; } 1617d62b00eSchristos constexpr const T *begin () const noexcept { return m_array; } 1627d62b00eSchristos 1637d62b00eSchristos /*constexpr14*/ T *end () noexcept { return m_array + m_size; } 1647d62b00eSchristos constexpr const T *end () const noexcept { return m_array + m_size; } 1657d62b00eSchristos 1667d62b00eSchristos /*constexpr14*/ reference operator[] (size_t index) noexcept 167*6881a400Schristos { 168*6881a400Schristos #if defined(_GLIBCXX_DEBUG) 169*6881a400Schristos gdb_assert (index < m_size); 170*6881a400Schristos #endif 171*6881a400Schristos return m_array[index]; 172*6881a400Schristos } 1737d62b00eSchristos constexpr const_reference operator[] (size_t index) const noexcept 174*6881a400Schristos { 175*6881a400Schristos #if defined(_GLIBCXX_DEBUG) && __cplusplus >= 201402L 176*6881a400Schristos gdb_assert (index < m_size); 177*6881a400Schristos #endif 178*6881a400Schristos return m_array[index]; 179*6881a400Schristos } 1807d62b00eSchristos 1817d62b00eSchristos constexpr size_type size () const noexcept { return m_size; } 1827d62b00eSchristos constexpr bool empty () const noexcept { return m_size == 0; } 1837d62b00eSchristos 1847d62b00eSchristos /* Slice an array view. */ 1857d62b00eSchristos 1867d62b00eSchristos /* Return a new array view over SIZE elements starting at START. */ 1877d62b00eSchristos constexpr array_view<T> slice (size_type start, size_type size) const noexcept 188*6881a400Schristos { 189*6881a400Schristos #if defined(_GLIBCXX_DEBUG) && __cplusplus >= 201402L 190*6881a400Schristos gdb_assert (start + size <= m_size); 191*6881a400Schristos #endif 192*6881a400Schristos return {m_array + start, size}; 193*6881a400Schristos } 1947d62b00eSchristos 1957d62b00eSchristos /* Return a new array view over all the elements after START, 1967d62b00eSchristos inclusive. */ 1977d62b00eSchristos constexpr array_view<T> slice (size_type start) const noexcept 198*6881a400Schristos { 199*6881a400Schristos #if defined(_GLIBCXX_DEBUG) && __cplusplus >= 201402L 200*6881a400Schristos gdb_assert (start <= m_size); 201*6881a400Schristos #endif 202*6881a400Schristos return {m_array + start, size () - start}; 203*6881a400Schristos } 2047d62b00eSchristos 2057d62b00eSchristos private: 2067d62b00eSchristos T *m_array; 2077d62b00eSchristos size_type m_size; 2087d62b00eSchristos }; 2097d62b00eSchristos 210*6881a400Schristos /* Copy the contents referenced by the array view SRC to the array view DEST. 211*6881a400Schristos 212*6881a400Schristos The two array views must have the same length. */ 213*6881a400Schristos 214*6881a400Schristos template <typename U, typename T> 215*6881a400Schristos void copy (gdb::array_view<U> src, gdb::array_view<T> dest) 216*6881a400Schristos { 217*6881a400Schristos gdb_assert (dest.size () == src.size ()); 218*6881a400Schristos if (dest.data () < src.data ()) 219*6881a400Schristos std::copy (src.begin (), src.end (), dest.begin ()); 220*6881a400Schristos else if (dest.data () > src.data ()) 221*6881a400Schristos std::copy_backward (src.begin (), src.end (), dest.end ()); 222*6881a400Schristos } 223*6881a400Schristos 2247d62b00eSchristos /* Compare LHS and RHS for (deep) equality. That is, whether LHS and 2257d62b00eSchristos RHS have the same sizes, and whether each pair of elements of LHS 2267d62b00eSchristos and RHS at the same position compares equal. */ 2277d62b00eSchristos 2287d62b00eSchristos template <typename T> 2297d62b00eSchristos bool 2307d62b00eSchristos operator== (const gdb::array_view<T> &lhs, const gdb::array_view<T> &rhs) 2317d62b00eSchristos { 2327d62b00eSchristos if (lhs.size () != rhs.size ()) 2337d62b00eSchristos return false; 2347d62b00eSchristos 2357d62b00eSchristos for (size_t i = 0; i < lhs.size (); i++) 2367d62b00eSchristos if (!(lhs[i] == rhs[i])) 2377d62b00eSchristos return false; 2387d62b00eSchristos 2397d62b00eSchristos return true; 2407d62b00eSchristos } 2417d62b00eSchristos 2427d62b00eSchristos /* Compare two array_views for inequality. */ 2437d62b00eSchristos 2447d62b00eSchristos template <typename T> 2457d62b00eSchristos bool 2467d62b00eSchristos operator!= (const gdb::array_view<T> &lhs, const gdb::array_view<T> &rhs) 2477d62b00eSchristos { 2487d62b00eSchristos return !(lhs == rhs); 2497d62b00eSchristos } 2507d62b00eSchristos 2517d62b00eSchristos /* Create an array view from a pointer to an array and an element 2527d62b00eSchristos count. 2537d62b00eSchristos 2547d62b00eSchristos This is useful as alternative to constructing an array_view using 2557d62b00eSchristos brace initialization when the size variable you have handy is of 2567d62b00eSchristos signed type, since otherwise without an explicit cast the code 2577d62b00eSchristos would be ill-formed. 2587d62b00eSchristos 2597d62b00eSchristos For example, with: 2607d62b00eSchristos 2617d62b00eSchristos extern void foo (int, int, gdb::array_view<value *>); 2627d62b00eSchristos 2637d62b00eSchristos value *args[2]; 2647d62b00eSchristos int nargs; 2657d62b00eSchristos foo (1, 2, {values, nargs}); 2667d62b00eSchristos 2677d62b00eSchristos You'd get: 2687d62b00eSchristos 2697d62b00eSchristos source.c:10: error: narrowing conversion of ‘nargs’ from ‘int’ to 2707d62b00eSchristos ‘size_t {aka long unsigned int}’ inside { } [-Werror=narrowing] 2717d62b00eSchristos 2727d62b00eSchristos You could fix it by writing the somewhat distracting explicit cast: 2737d62b00eSchristos 2747d62b00eSchristos foo (1, 2, {values, (size_t) nargs}); 2757d62b00eSchristos 2767d62b00eSchristos Or by instantiating an array_view explicitly: 2777d62b00eSchristos 2787d62b00eSchristos foo (1, 2, gdb::array_view<value *>(values, nargs)); 2797d62b00eSchristos 2807d62b00eSchristos Or, better, using make_array_view, which has the advantage of 2817d62b00eSchristos inferring the arrav_view element's type: 2827d62b00eSchristos 2837d62b00eSchristos foo (1, 2, gdb::make_array_view (values, nargs)); 2847d62b00eSchristos */ 2857d62b00eSchristos 2867d62b00eSchristos template<typename U> 2877d62b00eSchristos constexpr inline array_view<U> 2887d62b00eSchristos make_array_view (U *array, size_t size) noexcept 2897d62b00eSchristos { 2907d62b00eSchristos return {array, size}; 2917d62b00eSchristos } 2927d62b00eSchristos 2937d62b00eSchristos } /* namespace gdb */ 2947d62b00eSchristos 2957d62b00eSchristos #endif 296