Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 51 additions & 31 deletions src/openvic-simulation/core/string/StringLiteral.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,16 +46,19 @@ namespace OpenVic {

value_type _data[N];

[[nodiscard]] constexpr string_literal() noexcept = default;
[[nodiscard]] constexpr string_literal() noexcept {
_data[size()] = '\0';
}

[[nodiscard]] constexpr string_literal(const value_type (&literal)[N]) noexcept {
for (auto i = 0u; i != N; i++) {
for (size_type i = 0u; i != N; i++) {
_data[i] = literal[i];
}
}

[[nodiscard]] constexpr string_literal(value_type c) noexcept : _data {} {
[[nodiscard]] constexpr string_literal(value_type c) noexcept {
_data[0] = c;
_data[size()] = '\0';
}

[[nodiscard]] constexpr const_iterator begin() const noexcept {
Expand Down Expand Up @@ -94,7 +97,7 @@ namespace OpenVic {
}

[[nodiscard]] constexpr const_reference operator[](size_type pos) const noexcept {
return as_string_view()[pos];
return _data[pos];
}

[[nodiscard]] constexpr bool empty() const {
Expand Down Expand Up @@ -136,63 +139,71 @@ namespace OpenVic {
return ends_with(other.as_string_view());
}

[[nodiscard]] constexpr auto clear() const noexcept {
return string_literal<0, value_type, traits_type> {};
[[nodiscard]] constexpr string_literal<1, value_type, traits_type> clear() const noexcept {
return string_literal<1, value_type, traits_type> {};
}

[[nodiscard]] constexpr auto push_back(value_type c) const noexcept {
return *this + string_literal { c };
[[nodiscard]] constexpr string_literal<size() + 2, value_type, traits_type> push_back(value_type c) const noexcept {
return *this + string_literal<2, value_type, traits_type> { c };
}

[[nodiscard]] constexpr string_literal<size(), value_type, traits_type> pop_back() const noexcept {
string_literal<size(), value_type, traits_type> result {};
for (size_type i = 0u; i != size(); i++) {
for (size_type i = 0u; i != size() - 1; i++) {
result._data[i] = _data[i];
}
result._data[size() - 1] = '\0';
return result;
}

template<size_type count>
[[nodiscard]] constexpr string_literal<N + count, value_type, traits_type> append(value_type ch) const noexcept {
string_literal<N + count, value_type, traits_type> result {};
for (auto i = 0u; i != N; i++) {
string_literal<N + count, value_type, traits_type> result;
for (size_type i = 0u; i != size(); i++) {
result._data[i] = _data[i];
}

for (auto i = N; i != N + count; i++) {
for (size_type i = size(); i != size() + count; i++) {
result._data[i] = ch;
}
result._data[size() + count] = '\0';

return result;
}

template<size_type N2>
[[nodiscard]] constexpr auto append(string_literal<N2, value_type, traits_type> str) const noexcept {
[[nodiscard]] constexpr string_literal<size() + N2, value_type, traits_type> append( //
string_literal<N2, value_type, traits_type> str
) const noexcept {
return *this + str;
}

template<size_type N2>
[[nodiscard]] constexpr auto append(const value_type (&literal)[N2]) const noexcept {
[[nodiscard]] constexpr string_literal<size() + N2, value_type, traits_type> append( //
const value_type (&literal)[N2]
) const noexcept {
return *this + literal;
}

template<size_type pos = 0, size_type count = npos>
[[nodiscard]] constexpr auto substr() const noexcept {
static_assert(pos <= N, "pos must be less than or equal to N");
constexpr size_type result_size = std::min(count - pos, N - pos);
[[nodiscard]] constexpr decltype(auto) substr() //
const noexcept {
static_assert(pos <= size(), "pos must be less than or equal to size");
constexpr size_type result_size = std::min(count, size() - pos) + 1;

string_literal<result_size, value_type, traits_type> result {};
for (size_type i = 0u, i2 = pos; i != result_size; i++, i2++) {
string_literal<result_size, value_type, traits_type> result;
for (size_type i = 0u, i2 = pos; i != result.size(); i++, i2++) {
result._data[i] = _data[i2];
}
result._data[result.size()] = '\0';
return result;
}

[[nodiscard]] constexpr auto substr() const noexcept {
return substr<>();
[[nodiscard]] constexpr string_literal const& substr() const noexcept {
return *this;
}

[[nodiscard]] constexpr std::basic_string_view<CharT, Traits> substr( //
[[nodiscard]] constexpr std::basic_string_view<value_type, traits_type> substr( //
size_type pos, size_type count = npos
) const noexcept {
return as_string_view().substr(pos, count);
Expand All @@ -207,13 +218,13 @@ namespace OpenVic {
return as_string_view().find(literal, pos, N2 - 1);
}

[[nodiscard]] constexpr size_type rfind(std::string_view str, size_type pos = 0) const noexcept {
[[nodiscard]] constexpr size_type rfind(std::string_view str, size_type pos = npos) const noexcept {
return as_string_view().rfind(str, pos);
}

template<size_type N2>
[[nodiscard]] constexpr size_type rfind(const value_type (&literal)[N2], size_type pos = 0) const noexcept {
return as_string_view().find(literal, pos, N2 - 1);
[[nodiscard]] constexpr size_type rfind(const value_type (&literal)[N2], size_type pos = npos) const noexcept {
return as_string_view().rfind(literal, pos, N2 - 1);
}

[[nodiscard]] constexpr int compare(std::string_view str) const noexcept {
Expand Down Expand Up @@ -246,9 +257,11 @@ namespace OpenVic {
}

template<size_type N2>
[[nodiscard]] constexpr auto operator+(string_literal<N2, value_type, traits_type> const& other) const noexcept {
string_literal<size() + N2, value_type, traits_type> result {};
for (size_type i = 0u; i != N; i++) {
[[nodiscard]] constexpr string_literal<size() + N2, value_type, traits_type> operator+( //
string_literal<N2, value_type, traits_type> const& other
) const noexcept {
string_literal<size() + N2, value_type, traits_type> result;
for (size_type i = 0u; i != size(); i++) {
result._data[i] = _data[i];
}

Expand All @@ -259,12 +272,14 @@ namespace OpenVic {
}

template<size_type N2>
[[nodiscard]] constexpr auto operator+(const value_type (&rhs)[N2]) const noexcept {
[[nodiscard]] constexpr string_literal<size() + N2, value_type, traits_type> operator+( //
const value_type (&rhs)[N2]
) const noexcept {
return *this + _to_string(rhs);
}

template<size_type N2>
[[nodiscard]] friend constexpr auto operator+( //
[[nodiscard]] friend constexpr string_literal<size() + N2, value_type, traits_type> operator+( //
const value_type (&lhs)[N2], string_literal<N, value_type, traits_type> rhs
) noexcept {
return _to_string(lhs) + rhs;
Expand All @@ -273,6 +288,11 @@ namespace OpenVic {
template<std::size_t N, typename CharT = char, class Traits = std::char_traits<CharT>>
string_literal(const CharT (&)[N]) -> string_literal<N, CharT, Traits>;

// Size of 2 to include null terminator
template<typename CharT = char, class Traits = std::char_traits<CharT>>
string_literal(CharT) -> string_literal<2, CharT, Traits>;

// Size of 1 to include null terminator
template<typename CharT = char, class Traits = std::char_traits<CharT>>
string_literal(CharT) -> string_literal<1, CharT, Traits>;
string_literal() -> string_literal<1, CharT, Traits>;
}
181 changes: 181 additions & 0 deletions tests/src/core/string/StringLiteral.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
#include "openvic-simulation/core/string/StringLiteral.hpp"

#include <cstddef>
#include <iterator>
#include <string_view>

#include <range/v3/algorithm/equal.hpp>
#include <range/v3/view/drop.hpp>

#include "Helper.hpp" // IWYU pragma: keep
#include <snitch/snitch_append.hpp>
#include <snitch/snitch_macros_check.hpp>
#include <snitch/snitch_macros_constexpr.hpp>
#include <snitch/snitch_macros_misc.hpp>
#include <snitch/snitch_macros_test_case.hpp>
#include <snitch/snitch_string.hpp>

using namespace OpenVic;
using namespace std::string_view_literals;

namespace snitch {
template<std::size_t N, typename CharT, class Traits>
[[nodiscard]] inline static constexpr bool append( //
snitch::small_string_span ss, OpenVic::string_literal<N, CharT, Traits> const& s
) noexcept {
return append(ss, s.as_string_view());
}
}

TEST_CASE("string_literal Constructor methods", "[string_literal][string_literal-constructor]") {
static constexpr string_literal empty {};
CONSTEXPR_CHECK(empty.empty());

static constexpr string_literal char_value = 'c';
CONSTEXPR_CHECK(char_value.size() == 1);
CONSTEXPR_CHECK(char_value[0] == 'c');

static constexpr string_literal value = "value";
CONSTEXPR_CHECK(value.size() == 5);
CONSTEXPR_CHECK(value[0] == 'v');
CONSTEXPR_CHECK(value[1] == 'a');
CONSTEXPR_CHECK(value[2] == 'l');
CONSTEXPR_CHECK(value[3] == 'u');
CONSTEXPR_CHECK(value[4] == 'e');
}

TEST_CASE("string_literal Operators", "[string_literal][string_literal-operators]") {
static constexpr string_literal check1 = "check1";
CONSTEXPR_CHECK(check1[0] == 'c');
CONSTEXPR_CHECK(check1[1] == 'h');
CONSTEXPR_CHECK(check1[2] == 'e');
CONSTEXPR_CHECK(check1[3] == 'c');
CONSTEXPR_CHECK(check1[4] == 'k');
CONSTEXPR_CHECK(check1[5] == '1');
CONSTEXPR_CHECK(check1 == check1);

static constexpr string_literal check2 = "check2";
CONSTEXPR_CHECK(check1 != check2);
CONSTEXPR_CHECK(static_cast<std::string_view>(check2) == "check2"sv);
CONSTEXPR_CHECK(static_cast<char const*>(check2) == "check2"sv);
CONSTEXPR_CHECK((check1 + check2) == "check1check2"sv);
CONSTEXPR_CHECK((check1 + "check2") == "check1check2"sv);
CONSTEXPR_CHECK(("check1" + check2) == "check1check2"sv);
}

TEST_CASE("string_literal Iterator methods", "[string_literal][string_literal-iterator]") {
static constexpr string_literal iterator = "iterator";
CONSTEXPR_CHECK(*iterator.begin() == 'i');
CONSTEXPR_CHECK(*(iterator.begin() + 1) == 't');
CONSTEXPR_CHECK(*(iterator.begin() + 2) == 'e');
CONSTEXPR_CHECK(*(iterator.begin() + 3) == 'r');
CONSTEXPR_CHECK(*(iterator.begin() + 4) == 'a');
CONSTEXPR_CHECK(*(iterator.begin() + 5) == 't');
CONSTEXPR_CHECK(*(iterator.begin() + 6) == 'o');
CONSTEXPR_CHECK(*(iterator.begin() + 7) == 'r');
CONSTEXPR_CHECK((iterator.begin() + 8) == iterator.end());

CONSTEXPR_CHECK(*iterator.rbegin() == 'r');
CONSTEXPR_CHECK(*(iterator.rbegin() + 1) == 'o');
CONSTEXPR_CHECK(*(iterator.rbegin() + 2) == 't');
CONSTEXPR_CHECK(*(iterator.rbegin() + 3) == 'a');
CONSTEXPR_CHECK(*(iterator.rbegin() + 4) == 'r');
CONSTEXPR_CHECK(*(iterator.rbegin() + 5) == 'e');
CONSTEXPR_CHECK(*(iterator.rbegin() + 6) == 't');
CONSTEXPR_CHECK(*(iterator.rbegin() + 7) == 'i');
CONSTEXPR_CHECK((iterator.rbegin() + 8) == iterator.rend());

CONSTEXPR_CHECK(std::reverse_iterator { iterator.begin() } == iterator.rend());
CONSTEXPR_CHECK(std::reverse_iterator { iterator.end() } == iterator.rbegin());
CONSTEXPR_CHECK(iterator.cbegin() == iterator.begin());
CONSTEXPR_CHECK(iterator.cend() == iterator.end());
CONSTEXPR_CHECK(iterator.crbegin() == iterator.rbegin());
CONSTEXPR_CHECK(iterator.crend() == iterator.rend());
}

TEST_CASE("string_literal Access methods", "[string_literal][string_literal-access]") {
static constexpr string_literal access = "access";
CONSTEXPR_CHECK(access.at(0) == 'a');
CONSTEXPR_CHECK(access.at(1) == 'c');
CONSTEXPR_CHECK(access.at(2) == 'c');
CONSTEXPR_CHECK(access.at(3) == 'e');
CONSTEXPR_CHECK(access.at(4) == 's');
CONSTEXPR_CHECK(access.at(5) == 's');

CONSTEXPR_CHECK(access.front() == 'a');
CONSTEXPR_CHECK(access.back() == 's');

CONSTEXPR_CHECK(access.data()[0] == 'a');
CONSTEXPR_CHECK(access.data()[1] == 'c');
CONSTEXPR_CHECK(access.data()[2] == 'c');
CONSTEXPR_CHECK(access.data()[3] == 'e');
CONSTEXPR_CHECK(access.data()[4] == 's');
CONSTEXPR_CHECK(access.data()[5] == 's');

CONSTEXPR_CHECK(access.c_str() == "access"sv);
CONSTEXPR_CHECK(access.c_str()[6] == '\0');
}

TEST_CASE("string_literal Inspect methods", "[string_literal][string_literal-Inspect]") {
static constexpr string_literal inspect = "inspect";
CONSTEXPR_CHECK(inspect.compare("inspect"sv) == 0);
CONSTEXPR_CHECK(inspect.compare("inspect") == 0);
CONSTEXPR_CHECK(inspect.compare("notinspect"sv) < 0);
CONSTEXPR_CHECK(inspect.compare("notinspect") < 0);
CONSTEXPR_CHECK(inspect.compare("aotinspect"sv) > 0);
CONSTEXPR_CHECK(inspect.compare("aotinspect") > 0);

CONSTEXPR_CHECK(inspect.starts_with("in"sv));
CONSTEXPR_CHECK(inspect.starts_with('i'));
CONSTEXPR_CHECK(inspect.starts_with("in"));
CONSTEXPR_CHECK_FALSE(inspect.starts_with("et"sv));
CONSTEXPR_CHECK_FALSE(inspect.starts_with('b'));
CONSTEXPR_CHECK_FALSE(inspect.starts_with("da"));

static constexpr string_literal in = "in";
CONSTEXPR_CHECK(inspect.starts_with(in));

static constexpr string_literal not_inspect = "not inspect";
CONSTEXPR_CHECK_FALSE(inspect.starts_with(not_inspect));

CONSTEXPR_CHECK(inspect.ends_with("ect"sv)); // spellchecker:disable-line
CONSTEXPR_CHECK(inspect.ends_with('t'));
CONSTEXPR_CHECK(inspect.ends_with("pect"));
CONSTEXPR_CHECK_FALSE(inspect.ends_with("nb"sv));
CONSTEXPR_CHECK_FALSE(inspect.ends_with('q'));
CONSTEXPR_CHECK_FALSE(inspect.ends_with("rp"));

static constexpr string_literal ct = "ct";
CONSTEXPR_CHECK(inspect.ends_with(ct));

CONSTEXPR_CHECK_FALSE(inspect.ends_with(not_inspect));

CONSTEXPR_CHECK(inspect.find("ec") == 4);
CONSTEXPR_CHECK(inspect.find("ec"sv) == 4);
CONSTEXPR_CHECK(inspect.find("ec", 3) == 4);
CONSTEXPR_CHECK(inspect.find("ec"sv, 2) == 4);
CONSTEXPR_CHECK(inspect.find("non") == inspect.npos);
CONSTEXPR_CHECK(inspect.find("non"sv) == inspect.npos);

CONSTEXPR_CHECK(inspect.rfind("ec") == 4);
CONSTEXPR_CHECK(inspect.rfind("ec"sv) == 4);
CONSTEXPR_CHECK(inspect.rfind("ns", 1) == 1);
CONSTEXPR_CHECK(inspect.rfind("ns"sv, 2) == 1);
CONSTEXPR_CHECK(inspect.rfind("non") == inspect.npos);
CONSTEXPR_CHECK(inspect.rfind("non"sv) == inspect.npos);
}

TEST_CASE("string_literal Modifier methods", "[string_literal][string_literal-modifier]") {
static constexpr string_literal modifier = "modifier";
CONSTEXPR_CHECK(modifier.clear().empty());
CONSTEXPR_CHECK(modifier.push_back('a') == "modifiera"sv);
CONSTEXPR_CHECK(modifier.pop_back() == "modifie"sv);
CONSTEXPR_CHECK(modifier.append<3>('b') == "modifierbbb"sv);
CONSTEXPR_CHECK(modifier.append("hey") == "modifierhey"sv);
CONSTEXPR_CHECK(modifier.append(modifier) == "modifiermodifier"sv);
CONSTEXPR_CHECK(modifier.substr<1, 3>() == "odi"sv);
CONSTEXPR_CHECK(modifier.substr<1>() == "odifier"sv);
CONSTEXPR_CHECK(modifier.substr() == modifier);
CONSTEXPR_CHECK(modifier.substr(2, 3) == "dif"sv);
CONSTEXPR_CHECK(modifier.substr(3) == "ifier"sv);
}