xref: /aosp_15_r20/external/emboss/runtime/cpp/emboss_array_view.h (revision 99e0aae7469b87d12f0ad23e61142c2d74c1ef70)
1 // Copyright 2019 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 // View classes for arrays and bit arrays.
16 #ifndef EMBOSS_RUNTIME_CPP_EMBOSS_ARRAY_VIEW_H_
17 #define EMBOSS_RUNTIME_CPP_EMBOSS_ARRAY_VIEW_H_
18 
19 #include <cstddef>
20 #include <iterator>
21 #include <tuple>
22 #include <type_traits>
23 
24 #include "runtime/cpp/emboss_arithmetic.h"
25 
26 namespace emboss {
27 
28 // Forward declarations for use by WriteShorthandArrayCommentToTextStream.
29 class TextOutputOptions;
30 namespace support {
31 template <class Array, class Stream>
32 void WriteShorthandAsciiArrayCommentToTextStream(
33     const Array *array, Stream *stream, const TextOutputOptions &options);
34 }
35 namespace prelude {
36 template <class Parameters, class BitViewType>
37 class UIntView;
38 template <class Parameters, class BitViewType>
39 class IntView;
40 }  // namespace prelude
41 
42 namespace support {
43 
44 // Advance direction for ElementViewIterator.
45 enum class ElementViewIteratorDirection { kForward, kReverse };
46 
47 // Iterator adapter for elements in a GenericArrayView.
48 template <class GenericArrayView, ElementViewIteratorDirection kDirection>
49 class ElementViewIterator {
50  public:
51   using iterator_category = ::std::random_access_iterator_tag;
52   using value_type = typename GenericArrayView::ViewType;
53   using difference_type = ::std::ptrdiff_t;
54   using pointer = typename ::std::add_pointer<value_type>::type;
55   using reference = typename ::std::add_lvalue_reference<value_type>::type;
56 
ElementViewIterator(const GenericArrayView array_view,::std::ptrdiff_t index)57   explicit ElementViewIterator(const GenericArrayView array_view,
58                                ::std::ptrdiff_t index)
59       : array_view_(array_view), view_(array_view[index]), index_(index) {}
60 
61   ElementViewIterator() = default;
62 
63   reference operator*() { return view_; }
64 
65   pointer operator->() { return &view_; }
66 
67   ElementViewIterator &operator+=(difference_type d) {
68     index_ += (kDirection == ElementViewIteratorDirection::kForward ? d : -d);
69     view_ = array_view_[index_];
70     return *this;
71   }
72 
73   ElementViewIterator &operator-=(difference_type d) { return *this += (-d); }
74 
75   ElementViewIterator &operator++() {
76     *this += 1;
77     return *this;
78   }
79 
80   ElementViewIterator &operator--() {
81     *this -= 1;
82     return *this;
83   }
84 
85   ElementViewIterator operator++(int) {
86     auto copy = *this;
87     ++(*this);
88     return copy;
89   }
90 
91   ElementViewIterator operator--(int) {
92     auto copy = *this;
93     --(*this);
94     return copy;
95   }
96 
97   ElementViewIterator operator+(difference_type d) const {
98     auto copy = *this;
99     copy += d;
100     return copy;
101   }
102 
103   ElementViewIterator operator-(difference_type d) const {
104     return *this + (-d);
105   }
106 
107   difference_type operator-(const ElementViewIterator &other) const {
108     return kDirection == ElementViewIteratorDirection::kForward
109                ? index_ - other.index_
110                : other.index_ - index_;
111   }
112 
113   bool operator==(const ElementViewIterator &other) const {
114     return array_view_ == other.array_view_ && index_ == other.index_;
115   }
116 
117   bool operator!=(const ElementViewIterator &other) const {
118     return !(*this == other);
119   }
120 
121   bool operator<(const ElementViewIterator &other) const {
122     return kDirection == ElementViewIteratorDirection::kForward
123                ? index_ < other.index_
124                : other.index_ < index_;
125   }
126 
127   bool operator<=(const ElementViewIterator &other) const {
128     return kDirection == ElementViewIteratorDirection::kForward
129                ? index_ <= other.index_
130                : other.index_ <= index_;
131   }
132 
133   bool operator>(const ElementViewIterator &other) const {
134     return !(*this <= other);
135   }
136 
137   bool operator>=(const ElementViewIterator &other) const {
138     return !(*this < other);
139   }
140 
141  private:
142   const GenericArrayView array_view_;
143   typename GenericArrayView::ViewType view_;
144   ::std::ptrdiff_t index_;
145 };
146 
147 // View for an array in a structure.
148 //
149 // ElementView should be the view class for a single array element (e.g.,
150 // UIntView<...> or ArrayView<...>).
151 //
152 // BufferType is the storage type that will be passed into the array.
153 //
154 // kElementSize is the fixed size of a single element, in addressable units.
155 //
156 // kAddressableUnitSize is the size of a single addressable unit.  It should be
157 // either 1 (one bit) or 8 (one byte).
158 //
159 // ElementViewParameterTypes is a list of the types of parameters which must be
160 // passed down to each element of the array.  ElementViewParameterTypes can be
161 // empty.
162 template <class ElementView, class BufferType, ::std::size_t kElementSize,
163           ::std::size_t kAddressableUnitSize,
164           typename... ElementViewParameterTypes>
165 class GenericArrayView final {
166  public:
167   using ViewType = ElementView;
168   using ForwardIterator =
169       ElementViewIterator<GenericArrayView,
170                           ElementViewIteratorDirection::kForward>;
171   using ReverseIterator =
172       ElementViewIterator<GenericArrayView,
173                           ElementViewIteratorDirection::kReverse>;
174 
GenericArrayView()175   GenericArrayView() : buffer_() {}
GenericArrayView(const ElementViewParameterTypes &...parameters,BufferType buffer)176   explicit GenericArrayView(const ElementViewParameterTypes &...parameters,
177                             BufferType buffer)
178       : parameters_{parameters...}, buffer_{buffer} {}
179 
180   ElementView operator[](::std::size_t index) const {
181     return IndexOperatorHelper<sizeof...(ElementViewParameterTypes) ==
182                                0>::ConstructElement(parameters_, buffer_,
183                                                     index);
184   }
185 
begin()186   ForwardIterator begin() const { return ForwardIterator(*this, 0); }
end()187   ForwardIterator end() const { return ForwardIterator(*this, ElementCount()); }
rbegin()188   ReverseIterator rbegin() const {
189     return ReverseIterator(*this, ElementCount() - 1);
190   }
rend()191   ReverseIterator rend() const { return ReverseIterator(*this, -1); }
192 
193   // In order to selectively enable SizeInBytes and SizeInBits, it is
194   // necessary to make them into templates.  Further, it is necessary for
195   // ::std::enable_if to have a dependency on the template parameter, otherwise
196   // SFINAE won't kick in.  Thus, these are templated on an int, and that int
197   // is (spuriously) used as the left argument to `,` in the enable_if
198   // condition.  The explicit cast to void is needed to silence GCC's
199   // -Wunused-value.
200   template <int N = 0>
201   typename ::std::enable_if<((void)N, kAddressableUnitSize == 8),
202                             ::std::size_t>::type
SizeInBytes()203   SizeInBytes() const {
204     return buffer_.SizeInBytes();
205   }
206   template <int N = 0>
207   typename ::std::enable_if<((void)N, kAddressableUnitSize == 1),
208                             ::std::size_t>::type
SizeInBits()209   SizeInBits() const {
210     return buffer_.SizeInBits();
211   }
212 
ElementCount()213   ::std::size_t ElementCount() const { return SizeOfBuffer() / kElementSize; }
Ok()214   bool Ok() const {
215     if (!buffer_.Ok()) return false;
216     if (SizeOfBuffer() % kElementSize != 0) return false;
217     for (::std::size_t i = 0; i < ElementCount(); ++i) {
218       if (!(*this)[i].Ok()) return false;
219     }
220     return true;
221   }
222   template <class OtherElementView, class OtherBufferType>
Equals(const GenericArrayView<OtherElementView,OtherBufferType,kElementSize,kAddressableUnitSize> & other)223   bool Equals(
224       const GenericArrayView<OtherElementView, OtherBufferType, kElementSize,
225                              kAddressableUnitSize> &other) const {
226     if (ElementCount() != other.ElementCount()) return false;
227     for (::std::size_t i = 0; i < ElementCount(); ++i) {
228       if (!(*this)[i].Equals(other[i])) return false;
229     }
230     return true;
231   }
232   template <class OtherElementView, class OtherBufferType>
UncheckedEquals(const GenericArrayView<OtherElementView,OtherBufferType,kElementSize,kAddressableUnitSize> & other)233   bool UncheckedEquals(
234       const GenericArrayView<OtherElementView, OtherBufferType, kElementSize,
235                              kAddressableUnitSize> &other) const {
236     if (ElementCount() != other.ElementCount()) return false;
237     for (::std::size_t i = 0; i < ElementCount(); ++i) {
238       if (!(*this)[i].UncheckedEquals(other[i])) return false;
239     }
240     return true;
241   }
IsComplete()242   bool IsComplete() const { return buffer_.Ok(); }
243 
244   template <class Stream>
UpdateFromTextStream(Stream * stream)245   bool UpdateFromTextStream(Stream *stream) const {
246     return ReadArrayFromTextStream(this, stream);
247   }
248 
249   template <class Stream>
WriteToTextStream(Stream * stream,const TextOutputOptions & options)250   void WriteToTextStream(Stream *stream,
251                          const TextOutputOptions &options) const {
252     WriteArrayToTextStream(this, stream, options);
253   }
254 
IsAggregate()255   static constexpr bool IsAggregate() { return true; }
256 
BackingStorage()257   BufferType BackingStorage() const { return buffer_; }
258 
259   // Forwards to BufferType's ToString(), if any, but only if ElementView is a
260   // 1-byte type.
261   template <typename String>
262   typename ::std::enable_if<kAddressableUnitSize == 8 && kElementSize == 1,
263                             String>::type
ToString()264   ToString() const {
265     EMBOSS_CHECK(Ok());
266     return BackingStorage().template ToString<String>();
267   }
268 
269   bool operator==(const GenericArrayView &other) const {
270     return parameters_ == other.parameters_ && buffer_ == other.buffer_;
271   }
272 
273  private:
274   // This uses the same technique to select the correct definition of
275   // SizeOfBuffer() as in the SizeInBits()/SizeInBytes() selection above.
276   template <int N = 0>
277   typename ::std::enable_if<((void)N, kAddressableUnitSize == 8),
278                             ::std::size_t>::type
SizeOfBuffer()279   SizeOfBuffer() const {
280     return SizeInBytes();
281   }
282   template <int N = 0>
283   typename ::std::enable_if<((void)N, kAddressableUnitSize == 1),
284                             ::std::size_t>::type
SizeOfBuffer()285   SizeOfBuffer() const {
286     return SizeInBits();
287   }
288 
289   // This mess is needed to expand the parameters_ tuple into individual
290   // arguments to the ElementView constructor.  If parameters_ has M elements,
291   // then:
292   //
293   // IndexOperatorHelper<false>::ConstructElement() calls
294   // IndexOperatorHelper<false, 0>::ConstructElement(), which calls
295   // IndexOperatorHelper<false, 0, 1>::ConstructElement(), and so on, up to
296   // IndexOperatorHelper<false, 0, 1, ..., M-1>::ConstructElement(), which calls
297   // IndexOperatorHelper<true, 0, 1, ..., M>::ConstructElement()
298   //
299   // That last call will resolve to the second, specialized version of
300   // IndexOperatorHelper.  That version's ConstructElement() uses
301   // `std::get<N>(parameters)...`, which will be expanded into
302   // `std::get<0>(parameters), std::get<1>(parameters), std::get<2>(parameters),
303   // ..., std::get<M>(parameters)`.
304   //
305   // If there are 0 parameters, then operator[]() will call
306   // IndexOperatorHelper<true>::ConstructElement(), which still works --
307   // `std::get<N>(parameters)...,` will be replaced by ``.
308   //
309   // In C++14, a lot of this can be replaced by std::index_sequence_of, and in
310   // C++17 it can be replaced with std::apply and a lambda.
311   //
312   // An alternate solution would be to force each parameterized view to have a
313   // constructor that accepts a tuple, instead of individual parameters, but
314   // that (further) complicates the matrix of constructors for view types.
315   template <bool, ::std::size_t... N>
316   struct IndexOperatorHelper {
ConstructElementIndexOperatorHelper317     static ElementView ConstructElement(
318         const ::std::tuple<ElementViewParameterTypes...> &parameters,
319         BufferType buffer, ::std::size_t index) {
320       return IndexOperatorHelper<
321           sizeof...(ElementViewParameterTypes) == 1 + sizeof...(N), N...,
322           sizeof...(N)>::ConstructElement(parameters, buffer, index);
323     }
324   };
325 
326   template </**/ ::std::size_t... N>
327   struct IndexOperatorHelper<true, N...> {
328     static ElementView ConstructElement(
329         const ::std::tuple<ElementViewParameterTypes...> &parameters,
330         BufferType buffer, ::std::size_t index) {
331       return ElementView(::std::get<N>(parameters)...,
332                          buffer.template GetOffsetStorage<kElementSize, 0>(
333                              kElementSize * index, kElementSize));
334     }
335   };
336 
337   ::std::tuple<ElementViewParameterTypes...> parameters_;
338   BufferType buffer_;
339 };
340 
341 // Optionally prints a shorthand representation of a BitArray in a comment.
342 template <class ElementView, class BufferType, ::std::size_t kElementSize,
343           ::std::size_t kAddressableUnitSize, class Stream>
344 void WriteShorthandArrayCommentToTextStream(
345     const GenericArrayView<ElementView, BufferType, kElementSize,
346                            kAddressableUnitSize> *array,
347     Stream *stream, const TextOutputOptions &options) {
348   // Intentionally empty.  Overload for specific element types.
349   // Avoid unused parameters error:
350   static_cast<void>(array);
351   static_cast<void>(stream);
352   static_cast<void>(options);
353 }
354 
355 // Overload for arrays of UInt.
356 // Prints out the elements as ASCII characters for arrays of UInt:8.
357 template <class BufferType, class BitViewType, class Stream,
358           ::std::size_t kElementSize, class Parameters,
359           class = typename ::std::enable_if<Parameters::kBits == 8>::type>
360 void WriteShorthandArrayCommentToTextStream(
361     const GenericArrayView<prelude::UIntView<Parameters, BitViewType>,
362                            BufferType, kElementSize, 8> *array,
363     Stream *stream, const TextOutputOptions &options) {
364   WriteShorthandAsciiArrayCommentToTextStream(array, stream, options);
365 }
366 
367 // Overload for arrays of UInt.
368 // Prints out the elements as ASCII characters for arrays of Int:8.
369 template <class BufferType, class BitViewType, class Stream,
370           ::std::size_t kElementSize, class Parameters,
371           class = typename ::std::enable_if<Parameters::kBits == 8>::type>
372 void WriteShorthandArrayCommentToTextStream(
373     const GenericArrayView<prelude::IntView<Parameters, BitViewType>,
374                            BufferType, kElementSize, 8> *array,
375     Stream *stream, const TextOutputOptions &options) {
376   WriteShorthandAsciiArrayCommentToTextStream(array, stream, options);
377 }
378 
379 }  // namespace support
380 }  // namespace emboss
381 
382 #endif  // EMBOSS_RUNTIME_CPP_EMBOSS_ARRAY_VIEW_H_
383