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...> ¶meters, 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...> ¶meters, 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