blob: e5038efc58e6cef547de7c06c6fad828b36be375 [file] [edit]
/*
* 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