diff --git a/.gitignore b/.gitignore index b34b1e7..fa75190 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,3 @@ -.idea .trunk .vc .vscode diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..13566b8 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml new file mode 100644 index 0000000..fcb6bdd --- /dev/null +++ b/.idea/codeStyles/Project.xml @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml new file mode 100644 index 0000000..79ee123 --- /dev/null +++ b/.idea/codeStyles/codeStyleConfig.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/.idea/copyright/ISC.xml b/.idea/copyright/ISC.xml new file mode 100644 index 0000000..feb3d7b --- /dev/null +++ b/.idea/copyright/ISC.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 0000000..22cdf9b --- /dev/null +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,11 @@ + + + + \ No newline at end of file diff --git a/.idea/klib.iml b/.idea/klib.iml new file mode 100644 index 0000000..f08604b --- /dev/null +++ b/.idea/klib.iml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..79b3c94 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..e33c9de --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/Arena.cc b/Arena.cc index 1072c67..978cc11 100644 --- a/Arena.cc +++ b/Arena.cc @@ -346,7 +346,7 @@ uint8_t & Arena::operator[](size_t index) { if (index > this->size) { -#if defined(DESKTOP_BUILD) and !defined(KLIB_NO_ASSERT) +#if defined(KLIB_DESKTOP_BUILD) and !defined(KLIB_NO_ASSERT) throw std::range_error("index out of range"); #else abort(); diff --git a/Buffer.cc b/Buffer.cc index 1da5bbf..2e58738 100644 --- a/Buffer.cc +++ b/Buffer.cc @@ -330,7 +330,7 @@ uint8_t & Buffer::operator[](size_t index) { if (index > this->length) { -#if defined(DESKTOP_BUILD) and !defined(KLIB_NO_ASSERT) +#if defined(KLIB_DESKTOP_BUILD) and !defined(KLIB_NO_ASSERT) throw std::range_error("array index out of bounds"); #else abort(); diff --git a/CMakeLists.txt b/CMakeLists.txt index 3976276..fd534f3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.25) project(klib LANGUAGES CXX - VERSION 0.0.1 + VERSION 0.1.0 DESCRIPTION "Kyle's C++ library") set(CMAKE_CXX_STANDARD 14) @@ -25,27 +25,29 @@ else () # nothing special for gcc at the moment endif () endif () -add_compile_options("-DDESKTOP_BUILD") +add_compile_definitions(DKLIB_DESKTOP_BUILD) +add_compile_definitions(KLIB_VERSION=${PROJECT_VERSION}) -set(HEADER_FILES - klib.h +set(HEADER_FILES klib.h Arena.h Buffer.h Dictionary.h Exceptions.h - Test.h + StringUtil.h TLV.h + Test.h WinHelpers.h) set(SOURCE_FILES Arena.cc Buffer.cc - Dictionary.cc - Exceptions.cc - Test.cc - TLV.cc Commander.cc Commander.h + Dictionary.cc + Exceptions.cc + StringUtil.cc + TLV.cc + Test.cc WinHelpers.cc) if (APPLE) @@ -76,6 +78,10 @@ add_executable(buffer_test bufferTest.cc) target_link_libraries(buffer_test klib) add_test(bufferTest buffer_test) +add_executable(stringutil_test stringutil_test.cc) +target_link_libraries(stringutil_test klib) +add_test(stringutilTest stringutil_test) + include(CMakePackageConfigHelpers) write_basic_package_version_file( klibConfig.cmake diff --git a/Dictionary.cc b/Dictionary.cc index 9e38127..c344410 100644 --- a/Dictionary.cc +++ b/Dictionary.cc @@ -2,7 +2,7 @@ #include #include "Dictionary.h" -#if defined(DESKTOP_BUILD) +#if defined(KLIB_DESKTOP_BUILD) #include #endif @@ -133,7 +133,7 @@ Dictionary::spaceAvailable(uint8_t klen, uint8_t vlen) std::ostream & operator<<(std::ostream &os, const Dictionary &dictionary) { -#if defined(DESKTOP_BUILD) +#if defined(KLIB_DESKTOP_BUILD) uint8_t *cursor = (dictionary.arena).NewCursor(); TLV::Record rec; diff --git a/Makefile b/Makefile index 6a69b5c..934b62c 100644 --- a/Makefile +++ b/Makefile @@ -5,7 +5,7 @@ SOURCES := $(wildcard *.cc) OBJS := Arena.o Dictionary.o TLV.o CXX := clang++ -CXXFLAGS := -g -std=c++14 -Werror -Wall -DDESKTOP_BUILD +CXXFLAGS := -g -std=c++14 -Werror -Wall -DKLIB_DESKTOP_BUILD .PHONY: all all: $(TARGET) $(TESTS) tags run-tests diff --git a/StringUtil.cc b/StringUtil.cc new file mode 100644 index 0000000..79b8017 --- /dev/null +++ b/StringUtil.cc @@ -0,0 +1,165 @@ +#include +#include + +#include "StringUtil.h" + + +namespace klib { +/// namespace U contains utilities. +namespace U { + +/// namespace S contains string-related functions. +namespace S { + + +/* +std::vector +SplitKeyValuePair(std::string line, std::string delimiter) +{ + std::string key; + std::string val; + + auto pos = line.find(delimiter); + if (pos == std::string::npos) { + key = line; + val = ""; + } else { + key = line.substr(0, pos); + val = line.substr(pos + 1, line.size() - pos - 2); + } + + TrimWhitespace(key); + TrimWhitespace(val); + return {key, val}; +} + + +std::vector +SplitKeyValuePair(std::string line, char delimiter) +{ + std::string key; + std::string val; + + auto pos = line.find(delimiter); + if (pos == std::string::npos) { + key = line; + val = ""; + } else { + key = line.substr(0, pos); + val = line.substr(pos + 1, line.size() - pos - 2); + } + + TrimWhitespace(key); + TrimWhitespace(val); + return {key, val}; +} +*/ + + +void +TrimLeadingWhitespace(std::string &s) +{ + s.erase(s.begin(), + std::find_if(s.begin(), s.end(), + [](unsigned char ch) { + return !std::isspace(ch); + })); +} + + +void +TrimTrailingWhitespace(std::string &s) +{ + s.erase(std::find_if(s.rbegin(), s.rend(), [](unsigned char ch) { + return !std::isspace(ch); + }).base(), s.end()); +} + + +void +TrimWhitespace(std::string &s) +{ + TrimLeadingWhitespace(s); + TrimTrailingWhitespace(s); +} + + +std::string +TrimLeadingWhitespaceDup(std::string s) +{ + TrimLeadingWhitespace(s); + return s; +} + + +std::string +TrimTrailingWhitespaceDup(std::string s) +{ + TrimTrailingWhitespace(s); + return s; +} + + +std::string +TrimWhitespaceDup(std::string s) +{ + TrimWhitespace(s); + return s; +} + + +std::vector +SplitN(std::string s, std::string delim, size_t maxCount) +{ + std::vector parts; + size_t ss = 0; + size_t se = 0; + + for (ss = 0; s.size() != 0 && ss < s.size(); ss++) { + se = s.find(delim, ss); + if ((maxCount > 0) && (parts.size() == maxCount - 1)) { + se = s.size(); + } else if (se == std::string::npos) { + se = s.size(); + } + + auto length = se - ss; + parts.push_back(s.substr(ss, length)); + ss = se; + } + + return parts; +} + + +std::ostream & +VectorToString(std::ostream &os, const std::vector &svec) +{ + os << "("; + os << svec.size(); + os << ")"; + os << "{"; + + for (size_t i = 0; i < svec.size(); i++) { + if (i > 0) os << ", "; + os << svec[i]; + } + + os << "}"; + return os; +} + + +std::string +VectorToString(const std::vector &svec) +{ + std::stringstream ss; + + VectorToString(ss, svec); + return ss.str(); +} + + +} // namespace S +} // namespace U +} // namespace klib diff --git a/StringUtil.h b/StringUtil.h new file mode 100644 index 0000000..dc205c9 --- /dev/null +++ b/StringUtil.h @@ -0,0 +1,105 @@ +/// +/// \file StringUtil.h +/// \author kyle (kyle@midgard) +/// \created 2023-10-14 +/// \brief StringUtil contains string utilities. +/// +/// \section COPYRIGHT +/// Copyright 2023 K. Isom +/// +/// 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. +/// + + +#include +#include +#include + + +#ifndef STRINGUTIL_H +#define STRINGUTIL_H + + +namespace klib { +/// namespace U contains utilities. + +namespace U { + +/// namespace S contains string-related functions. +namespace S { + + +/// Remove any whitespace at the beginning of the string. The string +/// is modified in-place. +void TrimLeadingWhitespace(std::string &s); + +/// Remove any whitespace at the end of the string. The string is +/// modified in-place. +void TrimTrailingWhitespace(std::string &s); + +/// Remove any whitespace at the beginning and end of the string. The +/// string is modified in-place. +void TrimWhitespace(std::string &s); + +/// Remove any whitespace at the beginning of the string. The original +/// string isn't modified, and a copy is returned. +std::string TrimLeadingWhitespaceDup(std::string s); + +/// Remove any whitespace at the end of the string. The original string +/// isn't modified, and a copy is returned. +std::string TrimTrailingWhitespaceDup(std::string s); + +/// Remove any whitespace at the beginning and end of the string. The +/// original string isn't modified, and a copy is returned. +std::string TrimWhitespaceDup(std::string s); + + +/// Split a line into key and value pairs. If the delimiter isn't found, +/// the line is returned as the first element in the pair, and the second +/// element will be empty. +/// +/// \param line A string representing a line in a file. +/// \param delimiter The string delimiter between the key and value. +/// \return The key and value, or {line, ""}. +std::vector SplitKeyValuePair(std::string line, std::string delimiter); + +/// Split a line into key and value pairs. If the delimiter isn't found, +/// the line is returned as the first element in the pair, and the second +/// element will be empty. +/// +/// \param line A string representing a line in a file. +/// \param delimiter The character delimiter between the key and value. +/// \return The key and value. +std::vector SplitKeyValuePair(std::string line, char delimiter); + + +std::vector Split(std::string, std::string delimiter); +std::vector Split(std::string, char delimiter); + +std::vector SplitN(std::string, std::string delimiter, size_t maxCount=0); +//std::vector SplitN(std::string, char delimiter, size_t size_t maxCount=0); + + +std::ostream &VectorToString(std::ostream &os, const std::vector &svec); +std::string VectorToString(const std::vector &svec); + + +} // namespace S +} // namespace U +} // namespace klib + + +#endif // STRINGUTIL_H + + diff --git a/Test.cc b/Test.cc index daab849..e4c0360 100644 --- a/Test.cc +++ b/Test.cc @@ -13,7 +13,7 @@ namespace klib { void -TestAssert(bool condition, std::string message = "Assertion failed.") +TestAssert(bool condition, std::string message) { #if defined(NDEBUG) || defined(KLIB_NO_ASSERT) if (!condition) { diff --git a/stringutil_test.cc b/stringutil_test.cc new file mode 100644 index 0000000..945350d --- /dev/null +++ b/stringutil_test.cc @@ -0,0 +1,117 @@ +/// +/// \file stringutil_test.cc +/// \author kyle +/// \created 10/14/23 +/// \brief Ensure the stringutil functions work. +/// +/// \section COPYRIGHT +/// Copyright 2023 K. Isom +/// +/// 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. +/// + + +#include +#include + +#include "StringUtil.h" +#include "Test.h" +using namespace klib; + + +static void +TestTrimming(std::string line, std::string lExpected, std::string rExpected, std::string expected) +{ + std::string result; + std::string message; + + result = U::S::TrimLeadingWhitespaceDup(line); + message = "TrimLeadingDup(\"" + line + "\"): '" + result + "'"; + TestAssert(result == lExpected, message); + + result = U::S::TrimTrailingWhitespaceDup(line); + message = "TrimTrailingDup(\"" + line + "\"): '" + result + "'"; + TestAssert(result == rExpected, message); + + result = U::S::TrimWhitespaceDup(line); + message = "TrimDup(\"" + line + "\"): '" + result + "'"; + TestAssert(result == expected, message); + + result = line; + U::S::TrimLeadingWhitespace(result); + message = "TrimLeadingDup(\"" + line + "\"): '" + result + "'"; + TestAssert(result == lExpected, message); + + result = line; + U::S::TrimTrailingWhitespace(result); + message = "TrimTrailingDup(\"" + line + "\"): '" + result + "'"; + TestAssert(result == rExpected, message); + + result = line; + U::S::TrimWhitespace(result); + message = "TrimDup(\"" + line + "\"): '" + result + "'"; + TestAssert(result == expected, message); +} + + +static std::string +vec2string(std::vector v) +{ + std::stringstream ss; + + ss << "("; + ss << v.size(); + ss << ")"; + ss << "{"; + + for (size_t i = 0; i < v.size(); i++) { + if (i > 0) ss << ", "; + ss << v[i]; + } + + ss << "}"; + return ss.str(); +} + + +static void +TestSplit(std::string line, std::string delim, size_t maxCount, std::vector expected) +{ + std::cout << "test split\n"; + std::cout << "\t line: \"" << line << "\"\n"; + std::cout << "\t delim: \"" << delim << "\"\n"; + std::cout << "\t count: " << maxCount << "\n"; + std::cout << "\texpect: " << vec2string(expected) << "\n"; + auto result = U::S::SplitN(line, delim, maxCount); + std::cout << "\tresult: " << U::S::VectorToString(result) << "\n"; + TestAssert(result == expected, U::S::VectorToString(result)); + std::cout << "OK!\n"; +} + + +int +main() +{ + TestTrimming(" foo\t ", "foo\t ", " foo", "foo"); + TestTrimming(" foo\tbar ", "foo\tbar ", " foo\tbar", "foo\tbar"); + + TestSplit("abc:def:ghij:klm", ":", 0, + std::vector{"abc", "def", "ghij", "klm"}); + TestSplit("abc:def:ghij:klm", ":", 3, + std::vector{"abc", "def", "ghij:klm"}); + TestSplit("abc:def:ghij:klm", ":", 2, + std::vector{"abc", "def:ghij:klm"}); + TestSplit("abc:def:ghij:klm", ":", 1, + std::vector{"abc:def:ghij:klm"}); +} \ No newline at end of file