blob: 06d61c84b6aba9c8053531075827d7c13c034d55 [file] [log] [blame]
// SPDX-License-Identifier: Apache-2.0
/** @file
MemSpan unit tests.
*/
#include "catch.hpp"
#include <iostream>
#include <vector>
#include "swoc/MemSpan.h"
#include "swoc/TextView.h"
#include "swoc/MemArena.h"
using swoc::MemSpan;
using swoc::TextView;
using namespace swoc::literals;
TEST_CASE("MemSpan", "[libswoc][MemSpan]")
{
int32_t idx[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
char buff[1024];
MemSpan<char> span(buff, sizeof(buff));
MemSpan<char> left = span.prefix(512);
memset(span, ' ');
REQUIRE(left.size() == 512);
REQUIRE(span.size() == 1024);
span.remove_prefix(512);
REQUIRE(span.size() == 512);
REQUIRE(left.end() == span.begin());
left.assign(buff, sizeof(buff));
span = left.suffix(768);
REQUIRE(span.size() == 768);
left.remove_suffix(768);
REQUIRE(left.end() == span.begin());
REQUIRE(left.size() + span.size() == 1024);
MemSpan<int32_t> idx_span(idx);
REQUIRE(idx_span.size() == 11);
REQUIRE(idx_span.data_size() == sizeof(idx));
REQUIRE(idx_span.data() == idx);
auto sp2 = idx_span.rebind<int16_t>();
REQUIRE(sp2.data_size() == idx_span.data_size());
REQUIRE(sp2.size() == 2 * idx_span.size());
REQUIRE(sp2[0] == 0);
REQUIRE(sp2[1] == 0);
// exactly one of { le, be } must be true.
bool le = sp2[2] == 1 && sp2[3] == 0;
bool be = sp2[2] == 0 && sp2[3] == 1;
REQUIRE(le != be);
auto idx2 = sp2.rebind<int32_t>(); // still the same if converted back to original?
REQUIRE(idx_span.is_same(idx2));
// Verify attempts to rebind on non-integral sized arrays fails.
span.assign(buff, 1022);
REQUIRE(span.data_size() == 1022);
REQUIRE(span.size() == 1022);
auto vs = span.rebind<void>();
REQUIRE_THROWS_AS(span.rebind<uint32_t>(), std::invalid_argument);
REQUIRE_THROWS_AS(vs.rebind<uint32_t>(), std::invalid_argument);
vs.rebind<void>(); // check for void -> void rebinding.
// Check for defaulting to a void rebind.
vs = span.rebind();
REQUIRE(vs.size() == 1022);
// Check for assignment to void.
vs = span;
REQUIRE(vs.size() == 1022);
// Test array constructors.
MemSpan<char> a{buff};
REQUIRE(a.size() == sizeof(buff));
REQUIRE(a.data() == buff);
float floats[] = {1.1, 2.2, 3.3, 4.4, 5.5};
MemSpan<float> fspan{floats};
REQUIRE(fspan.size() == 5);
REQUIRE(fspan[3] == 4.4f);
MemSpan<float> f2span{floats, floats + 5};
REQUIRE(fspan.data() == f2span.data());
REQUIRE(fspan.size() == f2span.size());
REQUIRE(fspan.is_same(f2span));
unsigned char ucb[512];
MemSpan ucspan{ucb};
memset(ucspan, 0);
REQUIRE(ucspan[0] == 0);
REQUIRE(ucspan[511] == 0);
REQUIRE(ucspan[111] == 0);
REQUIRE(ucb[0] == 0);
REQUIRE(ucb[511] == 0);
ucspan.remove_suffix(1);
ucspan.remove_prefix(1);
memset(ucspan, '@');
REQUIRE(ucspan[0] == '@');
REQUIRE(ucspan[509] == '@');
REQUIRE(ucb[0] == 0);
REQUIRE(ucb[511] == 0);
REQUIRE(ucb[510] == '@');
};
TEST_CASE("MemSpan construct", "[libswoc][MemSpan]") {
static unsigned counter = 0;
struct Thing {
Thing(TextView s) : _s(s) {
++counter;
}
~Thing() {
--counter;
}
unsigned _n = 56;
std::string _s;
};
char buff[sizeof(Thing) * 7];
auto span{MemSpan(buff).rebind<Thing>()};
span.make("default"_tv);
REQUIRE(counter == span.length());
REQUIRE(span[2]._s == "default");
REQUIRE(span[4]._n == 56);
span.destroy();
REQUIRE(counter == 0);
}
TEST_CASE("MemSpan<void>", "[libswoc][MemSpan]")
{
TextView tv = "bike shed";
char buff[1024];
MemSpan<void> span(buff, sizeof(buff));
MemSpan<void const> cspan(span);
MemSpan<void const> ccspan(tv.data(), tv.size());
CHECK_FALSE(cspan.is_same(ccspan));
ccspan = span;
// auto bad_span = ccspan.rebind<uint8_t>(); // should not compile.
auto left = span.prefix(512);
REQUIRE(left.size() == 512);
REQUIRE(span.size() == 1024);
span.remove_prefix(512);
REQUIRE(span.size() == 512);
REQUIRE(left.data_end() == span.data());
left.assign(buff, sizeof(buff));
span = left.suffix(700);
REQUIRE(span.size() == 700);
left.remove_suffix(700);
REQUIRE(left.data_end() == span.data());
REQUIRE(left.size() + span.size() == 1024);
MemSpan<void> a(buff, sizeof(buff));
MemSpan<void> b;
b = a.align<int>();
REQUIRE(b.data() == a.data());
REQUIRE(b.size() == a.size());
b = a.suffix(a.size() - 2).align<int>();
REQUIRE(b.data() != a.data());
REQUIRE(b.size() != a.size());
auto i = a.rebind<int>();
REQUIRE(b.data() == i.data() + 1);
b = a.suffix(a.size() - 2).align(alignof(int));
REQUIRE(b.data() == i.data() + 1);
REQUIRE(b.rebind<int>().size() == i.size() - 1);
};
TEST_CASE("MemSpan conversions", "[libswoc][MemSpan]")
{
std::array<int, 10> a1;
std::string_view sv{"Evil Dave"};
swoc::TextView tv{sv};
std::string str{sv};
auto const & ra1 = a1;
auto ms1 = MemSpan<int>(a1); // construct from array
auto ms2 = MemSpan(a1); // construct from array, deduction guide
REQUIRE(ms2.size() == a1.size());
auto ms3 = MemSpan<int const>(ra1); // construct from const array
REQUIRE(ms3.size() == ra1.size());
[[maybe_unused]] auto ms4 = MemSpan(ra1); // construct from const array, deduction guided.
// Construct a span of constant from a const ref to an array with non-const type.
MemSpan<int const> ms5 { ra1 };
REQUIRE(ms5.size() == ra1.size());
// Construct a span of constant from a ref to an array with non-const type.
MemSpan<int const> ms6 { a1 };
MemSpan<void> va1{a1};
REQUIRE(va1.size() == a1.size() * sizeof(*(a1.data())));
MemSpan<void const> cva1{a1};
REQUIRE(cva1.size() == a1.size() * sizeof(*(a1.data())));
[[maybe_unused]] MemSpan<int const> c1 = ms1; // Conversion from T to T const.
MemSpan<char const> c2{sv.data(), sv.size()};
[[maybe_unused]] MemSpan<void const> vc2{c2};
// Generic construction from STL containers.
MemSpan<char const> c3{sv};
[[maybe_unused]] MemSpan<char> c7{str};
[[maybe_unused]] MemSpan<void> c4{str};
auto const & cstr {str};
MemSpan<char const> c8{cstr};
REQUIRE(c8.size() == cstr.size());
// [[maybe_unused]] MemSpan<char> c9{cstr}; // should not compile, const container to non-const span.
[[maybe_unused]] MemSpan<void const> c5{str};
[[maybe_unused]] MemSpan<void const> c6{sv};
[[maybe_unused]] MemSpan c10{sv};
[[maybe_unused]] MemSpan c11{tv};
char const * args[] = { "alpha" , "bravo" , "charlie", "delta"};
MemSpan<char const *> span_args { args };
MemSpan<char const *> span2_args { span_args };
REQUIRE(span_args.size() == 4);
REQUIRE(span2_args.size() == 4);
auto f = [&]() -> TextView { return sv; };
MemSpan fs1{f()};
auto fc = [&]() -> TextView const { return sv; };
MemSpan fs2{fc()};
}
TEST_CASE("MemSpan arena", "[libswoc][MemSpan]") {
swoc::MemArena a;
struct Thing {
size_t _n = 0;
void * _ptr = nullptr;
};
auto span = a.alloc(sizeof(Thing)).rebind<Thing>();
MemSpan<void> raw = span;
REQUIRE(raw.size() == sizeof(Thing));
MemSpan<void const> craw = raw;
REQUIRE(raw.size() == craw.size());
craw = span;
REQUIRE(raw.size() == craw.size());
REQUIRE(raw.rebind<Thing>().length() == 1);
}