//===- Sequence.h - Utility for producing sequences of values ---*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// /// \file /// This routine provides some synthesis utilities to produce sequences of /// values. The names are intentionally kept very short as they tend to occur /// in common and widely used contexts. /// //===----------------------------------------------------------------------===// #ifndef LLVM_ADT_SEQUENCE_H #define LLVM_ADT_SEQUENCE_H #include // assert #include // std::random_access_iterator_tag #include // std::numeric_limits #include // std::underlying_type, std::is_enum #include "llvm/Support/MathExtras.h" // AddOverflow / SubOverflow namespace llvm { namespace detail { // Returns whether a value of type U can be represented with type T. template bool canTypeFitValue(const U Value) { const intmax_t BotT = intmax_t(std::numeric_limits::min()); const intmax_t BotU = intmax_t(std::numeric_limits::min()); const uintmax_t TopT = uintmax_t(std::numeric_limits::max()); const uintmax_t TopU = uintmax_t(std::numeric_limits::max()); return !((BotT > BotU && Value < static_cast(BotT)) || (TopT < TopU && Value > static_cast(TopT))); } // An integer type that asserts when: // - constructed from a value that doesn't fit into intmax_t, // - casted to a type that cannot hold the current value, // - its internal representation overflows. struct CheckedInt { // Integral constructor, asserts if Value cannot be represented as intmax_t. template ::value, bool> = 0> static CheckedInt from(Integral FromValue) { if (!canTypeFitValue(FromValue)) assertOutOfBounds(); CheckedInt Result; Result.Value = static_cast(FromValue); return Result; } // Enum constructor, asserts if Value cannot be represented as intmax_t. template ::value, bool> = 0> static CheckedInt from(Enum FromValue) { using type = typename std::underlying_type::type; return from(static_cast(FromValue)); } // Equality bool operator==(const CheckedInt &O) const { return Value == O.Value; } bool operator!=(const CheckedInt &O) const { return Value != O.Value; } CheckedInt operator+(intmax_t Offset) const { CheckedInt Result; if (AddOverflow(Value, Offset, Result.Value)) assertOutOfBounds(); return Result; } intmax_t operator-(CheckedInt Other) const { intmax_t Result; if (SubOverflow(Value, Other.Value, Result)) assertOutOfBounds(); return Result; } // Convert to integral, asserts if Value cannot be represented as Integral. template ::value, bool> = 0> Integral to() const { if (!canTypeFitValue(Value)) assertOutOfBounds(); return static_cast(Value); } // Convert to enum, asserts if Value cannot be represented as Enum's // underlying type. template ::value, bool> = 0> Enum to() const { using type = typename std::underlying_type::type; return Enum(to()); } private: static void assertOutOfBounds() { assert(false && "Out of bounds"); } intmax_t Value; }; template struct SafeIntIterator { using iterator_category = std::random_access_iterator_tag; using value_type = T; using difference_type = intmax_t; using pointer = T *; using reference = T &; // Construct from T. explicit SafeIntIterator(T Value) : SI(CheckedInt::from(Value)) {} // Construct from other direction. SafeIntIterator(const SafeIntIterator &O) : SI(O.SI) {} // Dereference value_type operator*() const { return SI.to(); } // Indexing value_type operator[](intmax_t Offset) const { return *(*this + Offset); } // Can be compared for equivalence using the equality/inequality operators. bool operator==(const SafeIntIterator &O) const { return SI == O.SI; } bool operator!=(const SafeIntIterator &O) const { return SI != O.SI; } // Comparison bool operator<(const SafeIntIterator &O) const { return (*this - O) < 0; } bool operator>(const SafeIntIterator &O) const { return (*this - O) > 0; } bool operator<=(const SafeIntIterator &O) const { return (*this - O) <= 0; } bool operator>=(const SafeIntIterator &O) const { return (*this - O) >= 0; } // Pre Increment/Decrement void operator++() { offset(1); } void operator--() { offset(-1); } // Post Increment/Decrement SafeIntIterator operator++(int) { const auto Copy = *this; ++*this; return Copy; } SafeIntIterator operator--(int) { const auto Copy = *this; --*this; return Copy; } // Compound assignment operators void operator+=(intmax_t Offset) { offset(Offset); } void operator-=(intmax_t Offset) { offset(-Offset); } // Arithmetic SafeIntIterator operator+(intmax_t Offset) const { return add(Offset); } SafeIntIterator operator-(intmax_t Offset) const { return add(-Offset); } // Difference intmax_t operator-(const SafeIntIterator &O) const { return IsReverse ? O.SI - SI : SI - O.SI; } private: SafeIntIterator(const CheckedInt &SI) : SI(SI) {} static intmax_t getOffset(intmax_t Offset) { return IsReverse ? -Offset : Offset; } CheckedInt add(intmax_t Offset) const { return SI + getOffset(Offset); } void offset(intmax_t Offset) { SI = SI + getOffset(Offset); } CheckedInt SI; // To allow construction from the other direction. template friend struct SafeIntIterator; }; } // namespace detail template struct iota_range { using value_type = T; using reference = T &; using const_reference = const T &; using iterator = detail::SafeIntIterator; using const_iterator = iterator; using reverse_iterator = detail::SafeIntIterator; using const_reverse_iterator = reverse_iterator; using difference_type = intmax_t; using size_type = std::size_t; explicit iota_range(T Begin, T End, bool Inclusive) : BeginValue(Begin), PastEndValue(End) { assert(Begin <= End && "Begin must be less or equal to End."); if (Inclusive) ++PastEndValue; } size_t size() const { return PastEndValue - BeginValue; } bool empty() const { return BeginValue == PastEndValue; } auto begin() const { return const_iterator(BeginValue); } auto end() const { return const_iterator(PastEndValue); } auto rbegin() const { return const_reverse_iterator(PastEndValue - 1); } auto rend() const { return const_reverse_iterator(BeginValue - 1); } private: static_assert(std::is_integral::value || std::is_enum::value, "T must be an integral or enum type"); static_assert(std::is_same>::value, "T must not be const nor volatile"); iterator BeginValue; iterator PastEndValue; }; /// Iterate over an integral/enum type from Begin up to - but not including - /// End. /// Note on enum iteration: `seq` will generate each consecutive value, even if /// no enumerator with that value exists. /// Note: Begin and End values have to be within [INTMAX_MIN, INTMAX_MAX] for /// forward iteration (resp. [INTMAX_MIN + 1, INTMAX_MAX] for reverse /// iteration). template auto seq(T Begin, T End) { return iota_range(Begin, End, false); } /// Iterate over an integral/enum type from Begin to End inclusive. /// Note on enum iteration: `seq_inclusive` will generate each consecutive /// value, even if no enumerator with that value exists. /// Note: Begin and End values have to be within [INTMAX_MIN, INTMAX_MAX - 1] /// for forward iteration (resp. [INTMAX_MIN + 1, INTMAX_MAX - 1] for reverse /// iteration). template auto seq_inclusive(T Begin, T End) { return iota_range(Begin, End, true); } } // end namespace llvm #endif // LLVM_ADT_SEQUENCE_H