| /* |
| * Copyright 2026 WebAssembly Community Group participants |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #ifndef wasm_support_span_h |
| #define wasm_support_span_h |
| |
| #include <algorithm> |
| #include <cassert> |
| #include <cstdint> |
| #include <iostream> |
| #include <limits> |
| |
| #include "support/inplace_vector.h" |
| |
| namespace wasm { |
| |
| // A span of values. |
| // |
| // Span{min, max} means [min, max], inclusive of both sides. To represent an |
| // empty span, we use min > max. |
| template<typename T> struct Span { |
| static constexpr T Min = std::numeric_limits<T>::lowest(); |
| static constexpr T Max = std::numeric_limits<T>::max(); |
| |
| T min = Min; |
| T max = Max; |
| |
| constexpr Span() = default; |
| constexpr Span(T min, T max) : min(min), max(max) {} |
| |
| // Set a single value as possible. |
| void set(T value) { min = max = value; } |
| |
| // To represent an empty span, we use min > max, an impossible span. |
| void setEmpty() { |
| *this = empty(); |
| assert(isEmpty()); |
| } |
| |
| bool isEmpty() const { return min > max; } |
| |
| static Span<T> empty() { return Span{Max, Min}; } |
| |
| void setFull() { |
| *this = Span(); |
| assert(isFull()); |
| } |
| |
| bool isFull() const { return min == Min && max == Max; } |
| |
| static Span<T> full() { return Span{}; } |
| |
| // Intersect this with another span, returning a (possibly empty) span. |
| Span<T> intersection(const Span& other) const { |
| if (isEmpty() || other.isEmpty()) { |
| return empty(); |
| } |
| return Span<T>{std::max(min, other.min), std::min(max, other.max)}; |
| } |
| |
| // Checks whether two spans have any overlap at all. |
| bool hasOverlap(const Span& other) const { |
| return !intersection(other).isEmpty(); |
| } |
| |
| // Check whether we contain another span (possibly being equal). |
| bool contains(const Span& other) const { |
| return intersection(other) == other; |
| } |
| |
| bool operator==(const Span& other) const { |
| if (isEmpty()) { |
| return other.isEmpty(); |
| } |
| return !other.isEmpty() && min == other.min && max == other.max; |
| } |
| bool operator!=(const Span& other) const { return !(*this == other); } |
| }; |
| |
| // A union of spans, which we assume are disjoint. |
| template<typename T, size_t N> |
| struct Spans : public inplace_vector<Span<T>, N> { |
| constexpr Spans() = default; |
| |
| // Initialize with Spans. |
| Spans(std::initializer_list<Span<T>> init) { |
| for (const auto& span : init) { |
| this->push_back(span); |
| } |
| } |
| |
| bool hasOverlap(const Spans<T, N>& other) const { |
| // There is overlap if any of our spans overlaps with any of other's. |
| for (const auto& span : *this) { |
| for (const auto& otherSpan : other) { |
| if (span.hasOverlap(otherSpan)) { |
| return true; |
| } |
| } |
| } |
| return false; |
| } |
| |
| bool contains(const Spans<T, N>& other) const { |
| // We contain other if each of their spans is contained in us. |
| for (const auto& otherSpan : other) { |
| // Because our spans are assumed to be disjoint, exactly one of our |
| // spans must contain otherSpan. |
| bool found = false; |
| for (const auto& span : *this) { |
| if (span.contains(otherSpan)) { |
| found = true; |
| break; |
| } |
| } |
| if (!found) { |
| return false; |
| } |
| } |
| return true; |
| } |
| }; |
| |
| // A useful set of 2 spans that can contain any integer value. 2 spans is enough |
| // to contain spans for any inequality, signed or unsigned: we represent numbers |
| // as unsigned internally, and so e.g. signed x <= 10 ends up as two disjoint |
| // spans, [0..10] and [2^31..MAX_INT]. |
| using SpansU2 = Spans<uint64_t, 2>; |
| |
| template<typename T> |
| inline std::ostream& operator<<(std::ostream& os, const Span<T>& span) { |
| if (span.isEmpty()) { |
| return os << "[empty]"; |
| } |
| return os << '[' << span.min << ", " << span.max << ']'; |
| } |
| |
| template<typename T, size_t N> |
| inline std::ostream& operator<<(std::ostream& os, const Spans<T, N>& spans) { |
| if (spans.empty()) { |
| return os << "{empty}"; |
| } |
| os << '{'; |
| for (size_t i = 0; i < spans.size(); ++i) { |
| if (i > 0) { |
| os << ", "; |
| } |
| os << spans[i]; |
| } |
| return os << '}'; |
| } |
| |
| } // namespace wasm |
| |
| #endif // wasm_support_span_h |