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