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