xref: /netbsd-src/external/gpl3/gdb/dist/gdbsupport/array-view.h (revision 5ba1f45f2a09259cc846f20c7c5501604d633c90)
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