| /* | |
| * 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. | |
| */ | |
| #include <cstdint> | |
| #include <limits> | |
| #include <sstream> | |
| #include "support/iu64.h" | |
| #include "support/span.h" | |
| #include "gtest/gtest.h" | |
| using namespace wasm; | |
| // ============================================================================ | |
| // Generic Span<T> tests | |
| // ============================================================================ | |
| TEST(SpanTest, EmptySpanInt) { | |
| Span<int32_t> empty = Span<int32_t>::empty(); | |
| EXPECT_TRUE(empty.isEmpty()); | |
| EXPECT_FALSE(empty.isFull()); | |
| Span<int32_t> invalid(10, 5); | |
| EXPECT_TRUE(invalid.isEmpty()); | |
| EXPECT_FALSE(invalid.isFull()); | |
| EXPECT_EQ(empty, invalid); | |
| Span<int32_t> s; | |
| EXPECT_FALSE(s.isEmpty()); | |
| s.setEmpty(); | |
| EXPECT_TRUE(s.isEmpty()); | |
| EXPECT_EQ(s, empty); | |
| } | |
| TEST(SpanTest, FullSpanIntTypes) { | |
| // Signed 32-bit | |
| Span<int32_t> fullI32 = Span<int32_t>::full(); | |
| EXPECT_TRUE(fullI32.isFull()); | |
| EXPECT_FALSE(fullI32.isEmpty()); | |
| EXPECT_EQ(fullI32.min, std::numeric_limits<int32_t>::min()); | |
| EXPECT_EQ(fullI32.max, std::numeric_limits<int32_t>::max()); | |
| Span<int32_t> defI32; | |
| EXPECT_TRUE(defI32.isFull()); | |
| EXPECT_EQ(defI32, fullI32); | |
| // Unsigned 32-bit | |
| Span<uint32_t> fullU32 = Span<uint32_t>::full(); | |
| EXPECT_TRUE(fullU32.isFull()); | |
| EXPECT_FALSE(fullU32.isEmpty()); | |
| EXPECT_EQ(fullU32.min, 0u); | |
| EXPECT_EQ(fullU32.max, std::numeric_limits<uint32_t>::max()); | |
| // Signed 64-bit | |
| Span<int64_t> fullI64 = Span<int64_t>::full(); | |
| EXPECT_TRUE(fullI64.isFull()); | |
| EXPECT_FALSE(fullI64.isEmpty()); | |
| EXPECT_EQ(fullI64.min, std::numeric_limits<int64_t>::min()); | |
| EXPECT_EQ(fullI64.max, std::numeric_limits<int64_t>::max()); | |
| // Unsigned 64-bit | |
| Span<uint64_t> fullU64 = Span<uint64_t>::full(); | |
| EXPECT_TRUE(fullU64.isFull()); | |
| EXPECT_FALSE(fullU64.isEmpty()); | |
| EXPECT_EQ(fullU64.min, 0ull); | |
| EXPECT_EQ(fullU64.max, std::numeric_limits<uint64_t>::max()); | |
| } | |
| TEST(SpanTest, SetSingleValue) { | |
| Span<int32_t> s; | |
| s.set(42); | |
| EXPECT_EQ(s.min, 42); | |
| EXPECT_EQ(s.max, 42); | |
| EXPECT_FALSE(s.isEmpty()); | |
| EXPECT_FALSE(s.isFull()); | |
| EXPECT_EQ(s, Span<int32_t>(42, 42)); | |
| } | |
| TEST(SpanTest, SetFull) { | |
| Span<int32_t> s(10, 20); | |
| EXPECT_FALSE(s.isFull()); | |
| s.setFull(); | |
| EXPECT_TRUE(s.isFull()); | |
| EXPECT_EQ(s.min, std::numeric_limits<int32_t>::min()); | |
| EXPECT_EQ(s.max, std::numeric_limits<int32_t>::max()); | |
| } | |
| TEST(SpanTest, IntersectionInt) { | |
| Span<int32_t> a(1, 10); | |
| Span<int32_t> b(5, 15); | |
| Span<int32_t> ab = a.intersection(b); | |
| EXPECT_EQ(ab, Span<int32_t>(5, 10)); | |
| // Commutativity | |
| EXPECT_EQ(b.intersection(a), Span<int32_t>(5, 10)); | |
| // Touching at a single point | |
| Span<int32_t> c(10, 20); | |
| EXPECT_EQ(a.intersection(c), Span<int32_t>(10, 10)); | |
| // Disjoint | |
| Span<int32_t> d(11, 20); | |
| EXPECT_TRUE(a.intersection(d).isEmpty()); | |
| EXPECT_EQ(a.intersection(d), Span<int32_t>::empty()); | |
| // Contained | |
| Span<int32_t> e(3, 7); | |
| EXPECT_EQ(a.intersection(e), Span<int32_t>(3, 7)); | |
| // Identical | |
| EXPECT_EQ(a.intersection(a), a); | |
| // With empty | |
| EXPECT_TRUE(a.intersection(Span<int32_t>::empty()).isEmpty()); | |
| EXPECT_TRUE(Span<int32_t>::empty().intersection(a).isEmpty()); | |
| // With full | |
| EXPECT_EQ(a.intersection(Span<int32_t>::full()), a); | |
| EXPECT_EQ(Span<int32_t>::full().intersection(a), a); | |
| } | |
| TEST(SpanTest, HasOverlapInt) { | |
| Span<int32_t> a(1, 10); | |
| Span<int32_t> b(5, 15); | |
| Span<int32_t> c(10, 20); | |
| Span<int32_t> d(11, 20); | |
| EXPECT_TRUE(a.hasOverlap(b)); | |
| EXPECT_TRUE(b.hasOverlap(a)); | |
| EXPECT_TRUE(a.hasOverlap(c)); | |
| EXPECT_FALSE(a.hasOverlap(d)); | |
| EXPECT_FALSE(d.hasOverlap(a)); | |
| EXPECT_FALSE(a.hasOverlap(Span<int32_t>::empty())); | |
| EXPECT_TRUE(a.hasOverlap(Span<int32_t>::full())); | |
| EXPECT_FALSE(Span<int32_t>::empty().hasOverlap(Span<int32_t>::full())); | |
| } | |
| TEST(SpanTest, ContainsInt) { | |
| Span<int32_t> a(1, 10); | |
| Span<int32_t> b(3, 7); | |
| Span<int32_t> c(5, 15); | |
| Span<int32_t> d(11, 20); | |
| EXPECT_TRUE(a.contains(b)); | |
| EXPECT_FALSE(b.contains(a)); | |
| EXPECT_TRUE(a.contains(a)); | |
| EXPECT_FALSE(a.contains(c)); | |
| EXPECT_FALSE(a.contains(d)); | |
| EXPECT_TRUE(a.contains(Span<int32_t>::empty())); | |
| EXPECT_TRUE(Span<int32_t>::empty().contains(Span<int32_t>::empty())); | |
| EXPECT_FALSE(Span<int32_t>::empty().contains(a)); | |
| EXPECT_TRUE(Span<int32_t>::full().contains(a)); | |
| EXPECT_TRUE(Span<int32_t>::full().contains(Span<int32_t>::empty())); | |
| EXPECT_FALSE(a.contains(Span<int32_t>::full())); | |
| } | |
| TEST(SpanTest, StreamOutput) { | |
| auto toString = [](const auto& span) { | |
| std::ostringstream ss; | |
| ss << span; | |
| return ss.str(); | |
| }; | |
| EXPECT_EQ(toString(Span<int32_t>(1, 10)), "[1, 10]"); | |
| EXPECT_EQ(toString(Span<int32_t>::empty()), "[empty]"); | |
| EXPECT_EQ(toString(Span<int32_t>(10, 5)), "[empty]"); | |
| } | |
| // ============================================================================ | |
| // Span<IU64> tests (corner cases, sign mixing, large range) | |
| // ============================================================================ | |
| TEST(SpanIU64Test, FullAndLimits) { | |
| EXPECT_EQ(Span<IU64>::Min, IU64(std::numeric_limits<int64_t>::min())); | |
| EXPECT_EQ(Span<IU64>::Max, IU64(std::numeric_limits<uint64_t>::max())); | |
| Span<IU64> full = Span<IU64>::full(); | |
| EXPECT_TRUE(full.isFull()); | |
| EXPECT_FALSE(full.isEmpty()); | |
| EXPECT_EQ(full.min, IU64(std::numeric_limits<int64_t>::min())); | |
| EXPECT_EQ(full.max, IU64(std::numeric_limits<uint64_t>::max())); | |
| // Default constructed span is full | |
| Span<IU64> def; | |
| EXPECT_TRUE(def.isFull()); | |
| EXPECT_FALSE(def.isEmpty()); | |
| EXPECT_EQ(def, full); | |
| } | |
| TEST(SpanIU64Test, Empty) { | |
| Span<IU64> empty = Span<IU64>::empty(); | |
| EXPECT_TRUE(empty.isEmpty()); | |
| EXPECT_FALSE(empty.isFull()); | |
| Span<IU64> empty2{IU64(100), IU64(-100)}; | |
| EXPECT_TRUE(empty2.isEmpty()); | |
| EXPECT_FALSE(empty2.isFull()); | |
| EXPECT_EQ(empty, empty2); | |
| Span<IU64> empty3{IU64(uint64_t(1)), IU64(int64_t(-1))}; | |
| EXPECT_TRUE(empty3.isEmpty()); | |
| EXPECT_EQ(empty, empty3); | |
| } | |
| TEST(SpanIU64Test, SingletonsAtExtremes) { | |
| // Min int64 singleton | |
| Span<IU64> minI64{IU64(std::numeric_limits<int64_t>::min()), | |
| IU64(std::numeric_limits<int64_t>::min())}; | |
| EXPECT_FALSE(minI64.isEmpty()); | |
| EXPECT_FALSE(minI64.isFull()); | |
| EXPECT_EQ(minI64.min, IU64(std::numeric_limits<int64_t>::min())); | |
| EXPECT_EQ(minI64.max, IU64(std::numeric_limits<int64_t>::min())); | |
| // -1 singleton | |
| Span<IU64> negOne{IU64(-1), IU64(-1)}; | |
| EXPECT_FALSE(negOne.isEmpty()); | |
| // 0 singleton | |
| Span<IU64> zero{IU64(0), IU64(0)}; | |
| EXPECT_FALSE(zero.isEmpty()); | |
| // 1 singleton | |
| Span<IU64> one{IU64(1), IU64(1)}; | |
| EXPECT_FALSE(one.isEmpty()); | |
| // Max int64 singleton | |
| Span<IU64> maxI64{IU64(std::numeric_limits<int64_t>::max()), | |
| IU64(std::numeric_limits<int64_t>::max())}; | |
| EXPECT_FALSE(maxI64.isEmpty()); | |
| // 2^63 singleton (above int64_t max, into uint64_t territory) | |
| Span<IU64> highBit{IU64(uint64_t(1) << 63), IU64(uint64_t(1) << 63)}; | |
| EXPECT_FALSE(highBit.isEmpty()); | |
| // Max uint64 singleton | |
| Span<IU64> maxU64{IU64(std::numeric_limits<uint64_t>::max()), | |
| IU64(std::numeric_limits<uint64_t>::max())}; | |
| EXPECT_FALSE(maxU64.isEmpty()); | |
| } | |
| TEST(SpanIU64Test, CrossingZero) { | |
| Span<IU64> span{IU64(-10), IU64(10)}; | |
| EXPECT_FALSE(span.isEmpty()); | |
| EXPECT_FALSE(span.isFull()); | |
| // Contains points inside | |
| EXPECT_TRUE(span.contains(Span<IU64>(IU64(-10), IU64(-10)))); | |
| EXPECT_TRUE(span.contains(Span<IU64>(IU64(-5), IU64(5)))); | |
| EXPECT_TRUE(span.contains(Span<IU64>(IU64(0), IU64(0)))); | |
| EXPECT_TRUE(span.contains(Span<IU64>(IU64(10), IU64(10)))); | |
| // Does not contain points outside | |
| EXPECT_FALSE(span.contains(Span<IU64>(IU64(-11), IU64(-11)))); | |
| EXPECT_FALSE(span.contains(Span<IU64>(IU64(11), IU64(11)))); | |
| EXPECT_FALSE(span.contains(Span<IU64>(IU64(-15), IU64(5)))); | |
| EXPECT_FALSE(span.contains(Span<IU64>(IU64(-5), IU64(15)))); | |
| } | |
| TEST(SpanIU64Test, NegativeAndPositiveIntersections) { | |
| Span<IU64> neg{IU64(-100), IU64(-10)}; | |
| Span<IU64> pos{IU64(10), IU64(100)}; | |
| EXPECT_FALSE(neg.hasOverlap(pos)); | |
| EXPECT_FALSE(pos.hasOverlap(neg)); | |
| EXPECT_TRUE(neg.intersection(pos).isEmpty()); | |
| EXPECT_TRUE(pos.intersection(neg).isEmpty()); | |
| Span<IU64> touchNegZero{IU64(-10), IU64(0)}; | |
| Span<IU64> touchZeroPos{IU64(0), IU64(10)}; | |
| EXPECT_TRUE(touchNegZero.hasOverlap(touchZeroPos)); | |
| EXPECT_EQ(touchNegZero.intersection(touchZeroPos), | |
| Span<IU64>(IU64(0), IU64(0))); | |
| Span<IU64> overlap{IU64(-50), IU64(50)}; | |
| EXPECT_EQ(neg.intersection(overlap), Span<IU64>(IU64(-50), IU64(-10))); | |
| EXPECT_EQ(pos.intersection(overlap), Span<IU64>(IU64(10), IU64(50))); | |
| } | |
| TEST(SpanIU64Test, SignedUnsignedBoundary) { | |
| // Test around INT64_MAX and 2^63 | |
| int64_t maxI64 = std::numeric_limits<int64_t>::max(); | |
| uint64_t highBit = uint64_t(maxI64) + 1; // 0x8000000000000000ULL | |
| Span<IU64> s1(IU64(maxI64 - 100), IU64(highBit + 50)); | |
| Span<IU64> s2(IU64(highBit), IU64(highBit + 100)); | |
| EXPECT_TRUE(s1.hasOverlap(s2)); | |
| EXPECT_EQ(s1.intersection(s2), Span<IU64>(IU64(highBit), IU64(highBit + 50))); | |
| // Disjoint near 2^63 boundary | |
| Span<IU64> s3{IU64(maxI64 - 200), IU64(maxI64)}; | |
| Span<IU64> s4{IU64(highBit + 1), IU64(highBit + 100)}; | |
| EXPECT_FALSE(s3.hasOverlap(s4)); | |
| EXPECT_TRUE(s3.intersection(s4).isEmpty()); | |
| // Adjacent touching at 2^63 | |
| Span<IU64> s5{IU64(maxI64), IU64(highBit)}; | |
| Span<IU64> s6{IU64(highBit), IU64(highBit + 10)}; | |
| EXPECT_TRUE(s5.hasOverlap(s6)); | |
| EXPECT_EQ(s5.intersection(s6), Span<IU64>(IU64(highBit), IU64(highBit))); | |
| } | |
| TEST(SpanIU64Test, ExtremeBoundaries) { | |
| Span<IU64> minPart(IU64(std::numeric_limits<int64_t>::min()), | |
| IU64(std::numeric_limits<int64_t>::min() + 100)); | |
| Span<IU64> maxPart(IU64(std::numeric_limits<uint64_t>::max() - 100), | |
| IU64(std::numeric_limits<uint64_t>::max())); | |
| EXPECT_FALSE(minPart.hasOverlap(maxPart)); | |
| EXPECT_TRUE(minPart.intersection(maxPart).isEmpty()); | |
| Span<IU64> full = Span<IU64>::full(); | |
| EXPECT_TRUE(full.contains(minPart)); | |
| EXPECT_TRUE(full.contains(maxPart)); | |
| EXPECT_EQ(full.intersection(minPart), minPart); | |
| EXPECT_EQ(full.intersection(maxPart), maxPart); | |
| EXPECT_TRUE(full.hasOverlap(minPart)); | |
| EXPECT_TRUE(full.hasOverlap(maxPart)); | |
| Span<IU64> allNeg(IU64(std::numeric_limits<int64_t>::min()), IU64(-1)); | |
| Span<IU64> allNonNeg(IU64(0), IU64(std::numeric_limits<uint64_t>::max())); | |
| EXPECT_FALSE(allNeg.hasOverlap(allNonNeg)); | |
| EXPECT_TRUE(allNeg.intersection(allNonNeg).isEmpty()); | |
| EXPECT_TRUE(full.contains(allNeg)); | |
| EXPECT_TRUE(full.contains(allNonNeg)); | |
| } | |
| TEST(SpanIU64Test, SetAndMutate) { | |
| Span<IU64> s; | |
| EXPECT_TRUE(s.isFull()); | |
| s.set(IU64(-12345)); | |
| EXPECT_FALSE(s.isFull()); | |
| EXPECT_FALSE(s.isEmpty()); | |
| EXPECT_EQ(s.min, IU64(-12345)); | |
| EXPECT_EQ(s.max, IU64(-12345)); | |
| s.setEmpty(); | |
| EXPECT_TRUE(s.isEmpty()); | |
| s.setFull(); | |
| EXPECT_TRUE(s.isFull()); | |
| } |