From af1b9fb8cb4f4f020309d5bf8c516daf1f151140 Mon Sep 17 00:00:00 2001 From: Jessica James Date: Tue, 9 Nov 2021 02:07:24 -0600 Subject: [PATCH] add basic split function & tests so I can copy this elsewhere --- src/include/split.hpp | 49 +++++++++ src/test/CMakeLists.txt | 2 +- src/test/split.cpp | 214 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 264 insertions(+), 1 deletion(-) create mode 100644 src/include/split.hpp create mode 100644 src/test/split.cpp diff --git a/src/include/split.hpp b/src/include/split.hpp new file mode 100644 index 0000000..3505235 --- /dev/null +++ b/src/include/split.hpp @@ -0,0 +1,49 @@ +// +// Created by jessi on 11/9/2021. +// + +#pragma once + +#include +#include + +namespace jessilib { + +/** Splits an input string into substrings */ +template> +constexpr ResultT split(const InStringT& in_string, typename InStringT::value_type in_delim) { + ResultT result; + // Nothing to return + if (in_string.empty()) { + return result; + } + + // If only MSVC didn't suck, we could use begin() and end()... + auto begin = in_string.data(); + auto end = in_string.data() + in_string.size(); + for (auto itr = begin; itr != end; ++itr) { + if (*itr == in_delim) { + // Push token to result + result.emplace_back(begin, itr - begin); + begin = itr + 1; + } + } + + // begin == end only if last character was in_delim + if (begin == end) { + result.emplace_back(); + } + else { + result.emplace_back(begin, end - begin); + } + + return result; +} + +/** Splits an input string into view substrings (same as split, but different default return type) */ +template>> +constexpr ResultT split_view(const InStringT& in_string, typename InStringT::value_type in_delim) { + return split(in_string, in_delim); +} + +} // namespace jessilib diff --git a/src/test/CMakeLists.txt b/src/test/CMakeLists.txt index 90a8ff2..648fa69 100644 --- a/src/test/CMakeLists.txt +++ b/src/test/CMakeLists.txt @@ -1,6 +1,6 @@ # Setup source files set(SOURCE_FILES - timer.cpp thread_pool.cpp util.cpp object.cpp parser.cpp config.cpp parsers/json.cpp unicode.cpp app_parameters.cpp io/color.cpp duration.cpp) + timer.cpp thread_pool.cpp util.cpp object.cpp parser.cpp config.cpp parsers/json.cpp unicode.cpp app_parameters.cpp io/color.cpp duration.cpp split.cpp) # Setup gtest set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) diff --git a/src/test/split.cpp b/src/test/split.cpp new file mode 100644 index 0000000..ac367fc --- /dev/null +++ b/src/test/split.cpp @@ -0,0 +1,214 @@ +/** + * Copyright (C) 2021 Jessica James. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Written by Jessica James + */ + +#include "split.hpp" +#include +#include +#include +#include "test.hpp" + +using namespace jessilib; +using namespace std::literals; + +// empty strings + +using char_types = ::testing::Types; +using text_char_types = ::testing::Types; + +template +class SplitSVTest : public ::testing::Test { +public: +}; +TYPED_TEST_SUITE(SplitSVTest, char_types); + +template +class SplitSringTest : public ::testing::Test { +public: +}; +TYPED_TEST_SUITE(SplitSringTest, char_types); + +template +class SplitViewSVTest : public ::testing::Test { +public: +}; +TYPED_TEST_SUITE(SplitViewSVTest, char_types); + +template +class SplitViewStringTest : public ::testing::Test { +public: +}; +TYPED_TEST_SUITE(SplitViewStringTest, char_types); + +template +std::basic_string make_word(size_t length = 8, T delim = static_cast(0)) { + std::basic_string result; + + if (length == 0) { + throw std::runtime_error{ "length == 0" }; + } + + result.push_back(delim + 1); + while (result.size() < length) { + auto chr = result.back() + 1; + if (chr == delim) { + ++chr; + } + result.push_back(chr); + } + + if (result.size() != length) { + std::string errmsg = std::to_string(result.size()) + " != " + std::to_string(length) + "; result.size() != length"; + throw std::runtime_error{ errmsg }; + } + + return result; +} + +template +std::basic_string_view make_word_view(size_t length = 8, T delim = static_cast(0)) { + static std::basic_string s_result; + s_result = make_word(length, delim); + return s_result; +} + +TYPED_TEST(SplitSVTest, empty) { + std::basic_string_view empty; + std::vector split_result = split(empty, static_cast(0)); + EXPECT_TRUE(split_result.empty()); +} + +TYPED_TEST(SplitSVTest, single_word) { + std::basic_string_view single_word = make_word_view(); + std::vector split_result = split(single_word, static_cast(0)); + EXPECT_EQ(split_result.size(), 1); + EXPECT_EQ(split_result[0].size(), 8); +} + +TYPED_TEST(SplitSVTest, single_word_trailing_delim) { + auto word = make_word(); + word += static_cast(0); + std::basic_string_view single_word = word; + std::vector split_result = split(single_word, static_cast(0)); + EXPECT_EQ(split_result.size(), 2); + EXPECT_EQ(split_result[0].size(), 8); + EXPECT_EQ(split_result[1].size(), 0); +} + +TYPED_TEST(SplitSVTest, single_word_prefix_delim) { + std::basic_string word; + word += static_cast(0); + word += make_word(); + std::basic_string_view single_word = word; + std::vector split_result = split(single_word, static_cast(0)); + EXPECT_EQ(split_result.size(), 2); + EXPECT_EQ(split_result[0].size(), 0); + EXPECT_EQ(split_result[1].size(), 8); +} + +TYPED_TEST(SplitSVTest, single_word_surround_delim) { + std::basic_string word; + word += static_cast(0); + word += make_word(); + word += static_cast(0); + std::basic_string_view single_word = word; + std::vector split_result = split(single_word, static_cast(0)); + EXPECT_EQ(split_result.size(), 3); + EXPECT_EQ(split_result[0].size(), 0); + EXPECT_EQ(split_result[1].size(), 8); + EXPECT_EQ(split_result[2].size(), 0); +} + +TYPED_TEST(SplitSVTest, two_words) { + auto word = make_word(); + word += static_cast(0); + word += make_word(); + std::basic_string_view words = word; + std::vector split_result = split(words, static_cast(0)); + EXPECT_EQ(split_result.size(), 2); + EXPECT_EQ(split_result[0].size(), 8); + EXPECT_EQ(split_result[1].size(), 8); +} + +TYPED_TEST(SplitSVTest, three_words) { + auto word = make_word(3); + word += static_cast(0); + word += make_word(5); + word += static_cast(0); + word += make_word(9); + std::basic_string_view words = word; + std::vector split_result = split(words, static_cast(0)); + EXPECT_EQ(split_result.size(), 3); + EXPECT_EQ(split_result[0].size(), 3); + EXPECT_EQ(split_result[1].size(), 5); + EXPECT_EQ(split_result[2].size(), 9); +} + +/** std::string split test, really just testing compilation */ + +TYPED_TEST(SplitSringTest, empty) { + std::basic_string empty; + std::vector split_result = split(empty, static_cast(0)); + EXPECT_TRUE(split_result.empty()); +} + +TYPED_TEST(SplitSringTest, single_word) { + std::basic_string single_word = make_word(); + std::vector split_result = split(single_word, static_cast(0)); + EXPECT_EQ(split_result.size(), 1); + EXPECT_EQ(split_result[0].size(), 8); +} + +/** Some basic tests for compiling with different containers */ + +TYPED_TEST(SplitSVTest, empty_deque) { + std::basic_string_view empty; + std::deque split_result = split>(empty, static_cast(0)); + EXPECT_TRUE(split_result.empty()); +} + +TYPED_TEST(SplitSVTest, empty_list) { + std::basic_string_view empty; + std::list split_result = split>(empty, static_cast(0)); + EXPECT_TRUE(split_result.empty()); +} + +/** SplitViewSVTest, really just compilation tests */ + +TYPED_TEST(SplitViewSVTest, empty) { + std::basic_string_view empty; + std::vector split_result = split_view(empty, static_cast(0)); + EXPECT_TRUE(split_result.empty()); +} + +TYPED_TEST(SplitViewSVTest, single_word) { + std::basic_string_view single_word = make_word_view(); + std::vector split_result = split_view(single_word, static_cast(0)); + EXPECT_EQ(split_result.size(), 1); +} + +TYPED_TEST(SplitViewStringTest, empty) { + std::basic_string empty; + std::vector> split_result = split_view(empty, static_cast(0)); + EXPECT_TRUE(split_result.empty()); +} + +TYPED_TEST(SplitViewStringTest, single_word) { + std::basic_string single_word = make_word(); + std::vector> split_result = split_view(single_word, static_cast(0)); + EXPECT_EQ(split_result.size(), 1); +}