From 47f133de71b55bb760613b6ee39e8f9d41bfad8a Mon Sep 17 00:00:00 2001 From: jakcron Date: Sat, 28 Sep 2024 14:58:52 +0800 Subject: [PATCH 01/11] Address vs project file issues (#37) --- .../libtoolchain/libtoolchain.vcxproj | 29 +- .../libtoolchain/libtoolchain.vcxproj.filters | 684 +++++++++--------- 2 files changed, 372 insertions(+), 341 deletions(-) diff --git a/build/visualstudio/libtoolchain/libtoolchain.vcxproj b/build/visualstudio/libtoolchain/libtoolchain.vcxproj index 5107cc31..02ebb33e 100644 --- a/build/visualstudio/libtoolchain/libtoolchain.vcxproj +++ b/build/visualstudio/libtoolchain/libtoolchain.vcxproj @@ -129,7 +129,6 @@ - @@ -140,8 +139,7 @@ - - + @@ -152,6 +150,7 @@ + @@ -166,12 +165,12 @@ - - - + + + @@ -213,6 +212,7 @@ + @@ -236,11 +236,17 @@ + + + + + + @@ -248,7 +254,6 @@ - @@ -262,9 +267,9 @@ + - @@ -272,17 +277,20 @@ + + - + + @@ -350,14 +358,13 @@ + - - diff --git a/build/visualstudio/libtoolchain/libtoolchain.vcxproj.filters b/build/visualstudio/libtoolchain/libtoolchain.vcxproj.filters index 3a0155ba..3b315cbc 100644 --- a/build/visualstudio/libtoolchain/libtoolchain.vcxproj.filters +++ b/build/visualstudio/libtoolchain/libtoolchain.vcxproj.filters @@ -19,6 +19,9 @@ Source Files\cli + + Source Files\crypto + Source Files\crypto @@ -76,6 +79,9 @@ Source Files\crypto + + Source Files\crypto + Source Files\crypto @@ -91,9 +97,6 @@ Source Files\crypto - - Source Files\crypto - Source Files\crypto @@ -112,10 +115,10 @@ Source Files\crypto - + Source Files\crypto - + Source Files\crypto @@ -124,10 +127,10 @@ Source Files\crypto - + Source Files\crypto - + Source Files\crypto @@ -178,15 +181,15 @@ Source Files\io - - Source Files\io - Source Files\io Source Files\io + + Source Files\io + Source Files\io @@ -220,13 +223,13 @@ Source Files\io - + Source Files\io - + Source Files\io - + Source Files\io @@ -239,465 +242,486 @@ Source Files\os - Source Files\string - - - Source Files\crypto + Source Files\os - - Header Files + + Header Files\tc + + + Header Files\tc + + + Header Files\tc + + + Header Files\tc + + + Header Files\tc + + + Header Files\tc + + + Header Files\tc + + + Header Files\tc - - Header Files\tc + + Header Files\tc - - Header Files\tc + + Header Files\tc - - Header Files\tc + + Header Files\tc - - Header Files\tc + + Header Files\tc - - Header Files\tc + + Header Files\tc - - Header Files\tc + + Header Files\tc - - Header Files\tc + + Header Files\tc - - Header Files\tc + + Header Files\tc - - Header Files\tc + + Header Files\tc - - Header Files\tc + + Header Files\tc - - Header Files\tc + + Header Files\tc\bn - - Header Files\tc + + Header Files\tc\bn - - Header Files\tc + + Header Files\tc\bn - - Header Files\tc + + Header Files\tc\bn - - Header Files\tc + + Header Files\tc\bn - - Header Files\tc + + Header Files\tc - - Header Files\tc + + Header Files\tc\cli - - Header Files\tc + + Header Files\tc\cli - - Header Files\tc + + Header Files\tc - - Header Files\tc + + Header Files\tc\crypto - - Header Files\tc + + Header Files\tc\crypto - - Header Files\tc + + Header Files\tc\crypto - - Header Files\tc + + Header Files\tc\crypto - - Header Files\tc\bn + + Header Files\tc\crypto - - Header Files\tc\bn + + Header Files\tc\crypto - - Header Files\tc\bn + + Header Files\tc\crypto - - Header Files\tc\bn + + Header Files\tc\crypto - - Header Files\tc\bn + + Header Files\tc\crypto - - Header Files\tc + + Header Files\tc\crypto - - Header Files\tc\cli + + Header Files\tc\crypto - - Header Files\tc\cli + + Header Files\tc\crypto - - Header Files\tc\crypto + + Header Files\tc\crypto - - Header Files\tc\crypto + + Header Files\tc\crypto - - Header Files\tc\crypto + + Header Files\tc\crypto - - Header Files\tc\crypto + + Header Files\tc\crypto - - Header Files\tc\crypto + + Header Files\tc\crypto - - Header Files\tc\crypto + + Header Files\tc\crypto - - Header Files\tc\crypto + + Header Files\tc\crypto - - Header Files\tc\crypto + + Header Files\tc\crypto - - Header Files\tc\crypto + + Header Files\tc\crypto - - Header Files\tc\crypto + + Header Files\tc\crypto - - Header Files\tc\crypto + + Header Files\tc\crypto - - Header Files\tc\crypto + + Header Files\tc\crypto - - Header Files\tc\crypto + + Header Files\tc\crypto - - Header Files\tc\crypto + + Header Files\tc\crypto - - Header Files\tc\crypto + + Header Files\tc\crypto - - Header Files\tc\crypto + + Header Files\tc\crypto - - Header Files\tc\crypto + + Header Files\tc\crypto - - Header Files\tc\crypto + + Header Files\tc\crypto - - Header Files\tc\crypto + + Header Files\tc\crypto - - Header Files\tc\crypto + + Header Files\tc\crypto - - Header Files\tc\crypto + + Header Files\tc\crypto - - Header Files\tc\crypto + + Header Files\tc\crypto - - Header Files\tc\crypto + + Header Files\tc\crypto - - Header Files\tc\crypto + + Header Files\tc\crypto - - Header Files\tc\crypto + + Header Files\tc\crypto - - Header Files\tc\crypto + + Header Files\tc\crypto - - Header Files\tc\crypto + + Header Files\tc\crypto - - Header Files\tc\crypto + + Header Files\tc\crypto - - Header Files\tc\crypto + + Header Files\tc\crypto - - Header Files\tc\crypto + + Header Files\tc\crypto - - Header Files\tc\crypto + + Header Files\tc\crypto - - Header Files\tc\crypto + + Header Files\tc\crypto - - Header Files\tc\crypto + + Header Files\tc\crypto - - Header Files\tc\crypto + + Header Files\tc\crypto - - Header Files\tc\crypto + + Header Files\tc\crypto - - Header Files\tc\crypto + + Header Files\tc\crypto - - Header Files\tc\crypto + + Header Files\tc\crypto - - Header Files\tc\crypto + + Header Files\tc\crypto - - Header Files\tc\crypto + + Header Files\tc\crypto - - Header Files\tc\crypto + + Header Files\tc\crypto - - Header Files\tc\crypto + + Header Files\tc\crypto - - Header Files\tc\crypto + + Header Files\tc\crypto - - Header Files\tc\crypto + + Header Files\tc\crypto - - Header Files\tc\crypto + + Header Files\tc\crypto - - Header Files\tc\crypto + + Header Files\tc\crypto - - Header Files\tc\crypto + + Header Files\tc\crypto - - Header Files\tc\crypto + + Header Files\tc\crypto - - Header Files\tc\crypto + + Header Files\tc\crypto\detail - - Header Files\tc\crypto + + Header Files\tc\crypto\detail - - Header Files\tc\crypto + + Header Files\tc\crypto\detail - - Header Files\tc\crypto + + Header Files\tc\crypto\detail - - Header Files\tc\crypto + + Header Files\tc\crypto\detail - - Header Files\tc\crypto + + Header Files\tc\crypto\detail - - Header Files\tc\crypto + + Header Files\tc\crypto\detail - - Header Files\tc\crypto + + Header Files\tc\crypto\detail - - Header Files\tc\crypto + + Header Files\tc\crypto\detail - - Header Files\tc\crypto + + Header Files\tc\crypto\detail - - Header Files\tc\crypto\detail + + Header Files\tc\crypto\detail - - Header Files\tc\crypto\detail + + Header Files\tc\crypto\detail - - Header Files\tc\crypto\detail + + Header Files\tc\crypto\detail - - Header Files\tc\crypto\detail + + Header Files\tc\crypto\detail - - Header Files\tc\crypto\detail + + Header Files\tc\crypto\detail - - Header Files\tc\crypto\detail + + Header Files\tc\crypto\detail - - Header Files\tc\crypto\detail + + Header Files\tc\crypto\detail - - Header Files\tc\crypto\detail + + Header Files\tc\crypto\detail - - Header Files\tc\crypto\detail + + Header Files\tc\crypto\detail - - Header Files\tc\crypto\detail + + Header Files\tc\crypto\detail - - Header Files\tc\crypto\detail + + Header Files\tc\crypto\detail - - Header Files\tc\crypto\detail + + Header Files\tc - - Header Files\tc\crypto\detail + + Header Files\tc\io - - Header Files\tc\crypto\detail + + Header Files\tc\io - - Header Files\tc\crypto\detail + + Header Files\tc\io - - Header Files\tc\crypto\detail + + Header Files\tc\io - - Header Files\tc\crypto\detail + + Header Files\tc\io - - Header Files\tc\crypto\detail + + Header Files\tc\io - - Header Files\tc\crypto\detail + + Header Files\tc\io - - Header Files\tc\crypto\detail + + Header Files\tc\io - - Header Files\tc\io + + Header Files\tc\io - - Header Files\tc\io + + Header Files\tc\io - - Header Files\tc\io + + Header Files\tc\io - - Header Files\tc\io + + Header Files\tc\io - - Header Files\tc\io + + Header Files\tc\io - - Header Files\tc\io + + Header Files\tc\io - - Header Files\tc\io + + Header Files\tc\io - - Header Files\tc\io + + Header Files\tc\io - - Header Files\tc\io + + Header Files\tc\io - - Header Files\tc\io + + Header Files\tc\io - - Header Files\tc\io + + Header Files\tc\io - - Header Files\tc\io + + Header Files\tc\io - - Header Files\tc\io + + Header Files\tc\io - - Header Files\tc\io + + Header Files\tc\io - - Header Files\tc\io + + Header Files\tc\io - - Header Files\tc\io + + Header Files\tc\io - - Header Files\tc\io + + Header Files\tc\io - - Header Files\tc\io + + Header Files\tc\io - - Header Files\tc\io + + Header Files\tc\io - - Header Files\tc\io + + Header Files\tc\io - - Header Files\tc\io + + Header Files\tc\io - - Header Files\tc\io + + Header Files\tc\io - - Header Files\tc\io + + Header Files\tc\io - - Header Files\tc\io + + Header Files\tc\io - - Header Files\tc\io + + Header Files\tc\io - - Header Files\tc\io + + Header Files\tc\io - - Header Files\tc\io + + Header Files\tc\io - - Header Files\tc\io + + Header Files\tc\io - - Header Files\tc\io + + Header Files\tc\io - - Header Files\tc\io + + Header Files\tc\io - - Header Files\tc\io + + Header Files\tc\io - - Header Files\tc\io + + Header Files\tc\io - - Header Files\tc\io + + Header Files\tc\io - - Header Files\tc\io + + Header Files\tc - - Header Files\tc\io + + Header Files\tc\os - - Header Files\tc\io + + Header Files\tc\os - - Header Files\tc\os + + Header Files\tc - - Header Files\tc\os + + Header Files\tc\string - - Header Files\tc\string + + Header Files\tc\string - - Header Files\tc\string\detail + + Header Files\tc\string - - Header Files\tc\string\detail + + Header Files\tc - - Header Files\tc\crypto + + Header Files\tc + + + Header Files @@ -716,14 +740,20 @@ {1790ccfc-ff8d-42cf-a41b-c546f7c7e03a} + + {07fdaf97-dd39-4b5e-8679-5f3a45ff300e} + {db3218f8-61c4-444c-9758-eaf0a3fc7cb5} + + {2a9cc5ad-5ee9-4baf-b63c-d7af02b7309b} + {f110e29c-e832-412d-b35c-fe09f44cb48b} - - {07fdaf97-dd39-4b5e-8679-5f3a45ff300e} + + {68b83858-efc6-489d-a672-8eef14f0f2bc} {7cb67e0a-2201-4709-a0fa-7da68aaf8bb4} @@ -734,6 +764,9 @@ {fc4541ce-957c-4976-880e-5189f1d8c46e} + + {e80dca43-1a2b-4037-8751-245a3b07d04a} + {b6397317-93c5-48d9-a2bd-107761509eb7} @@ -743,14 +776,5 @@ {d8bfe354-3e2c-40e9-8023-5cdbf8fd877d} - - {2a9cc5ad-5ee9-4baf-b63c-d7af02b7309b} - - - {68b83858-efc6-489d-a672-8eef14f0f2bc} - - - {e80dca43-1a2b-4037-8751-245a3b07d04a} - \ No newline at end of file From d814e842f165cecf4dfc2a58455c343a772a89e1 Mon Sep 17 00:00:00 2001 From: jakcron Date: Sat, 28 Sep 2024 19:25:55 +0800 Subject: [PATCH 02/11] Fix bug in LocalFileSystem test code where test dir was not deleted. --- test/io_LocalFileSystem_TestClass.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/test/io_LocalFileSystem_TestClass.cpp b/test/io_LocalFileSystem_TestClass.cpp index c6e32a56..b0e78e16 100644 --- a/test/io_LocalFileSystem_TestClass.cpp +++ b/test/io_LocalFileSystem_TestClass.cpp @@ -1338,6 +1338,9 @@ void io_LocalFileSystem_TestClass::test_GetCanonicalPath_DoesExist() // restore pre-test directory local_fs.setWorkingDirectory(pre_test_dir); + + // remove directory made in root dir + local_fs.removeDirectory(base_dir_relative_path); } catch (const std::exception& e) { @@ -1446,6 +1449,9 @@ void io_LocalFileSystem_TestClass::test_GetCanonicalPath_NotExist() // restore pre-test directory local_fs.setWorkingDirectory(pre_test_dir); + + // remove directory made in root dir + local_fs.removeDirectory(base_dir_relative_path); } catch (const std::exception& e) { From 8d1cdb71b98796a83799f001d9911d39e5949e81 Mon Sep 17 00:00:00 2001 From: jakcron Date: Sun, 29 Sep 2024 15:17:02 +0800 Subject: [PATCH 03/11] Add Base64Util --- .../libtoolchain/libtoolchain.vcxproj | 3 + .../libtoolchain/libtoolchain.vcxproj.filters | 15 + include/tc/encode.h | 13 + include/tc/encode/Base64Util.h | 47 +++ makefile | 2 +- src/encode/Base64Util.cpp | 298 ++++++++++++++++++ 6 files changed, 377 insertions(+), 1 deletion(-) create mode 100644 include/tc/encode.h create mode 100644 include/tc/encode/Base64Util.h create mode 100644 src/encode/Base64Util.cpp diff --git a/build/visualstudio/libtoolchain/libtoolchain.vcxproj b/build/visualstudio/libtoolchain/libtoolchain.vcxproj index 02ebb33e..7561e968 100644 --- a/build/visualstudio/libtoolchain/libtoolchain.vcxproj +++ b/build/visualstudio/libtoolchain/libtoolchain.vcxproj @@ -231,6 +231,8 @@ + + @@ -343,6 +345,7 @@ + diff --git a/build/visualstudio/libtoolchain/libtoolchain.vcxproj.filters b/build/visualstudio/libtoolchain/libtoolchain.vcxproj.filters index 3b315cbc..5e4631d0 100644 --- a/build/visualstudio/libtoolchain/libtoolchain.vcxproj.filters +++ b/build/visualstudio/libtoolchain/libtoolchain.vcxproj.filters @@ -178,6 +178,9 @@ Source Files\crypto\detail + + Source Files\io + Source Files\io @@ -570,6 +573,12 @@ Header Files\tc + + Header Files\tc\encode + + + Header Files\tc + Header Files\tc\io @@ -742,6 +751,9 @@ {07fdaf97-dd39-4b5e-8679-5f3a45ff300e} + + + {2b186524-afb8-4bab-bd5a-7fb9ee579131} {db3218f8-61c4-444c-9758-eaf0a3fc7cb5} @@ -767,6 +779,9 @@ {e80dca43-1a2b-4037-8751-245a3b07d04a} + + {aba2432e-d6dc-403e-84f4-4e2c7d24e681} + {b6397317-93c5-48d9-a2bd-107761509eb7} diff --git a/include/tc/encode.h b/include/tc/encode.h new file mode 100644 index 00000000..991a7bba --- /dev/null +++ b/include/tc/encode.h @@ -0,0 +1,13 @@ + /** + * @file encode.h + * @brief Declaration of the encode/decode library + */ +#pragma once +#include +#include + + /** + * @namespace tc::encode + * @brief Namespace of the encode/decode library + */ +#include \ No newline at end of file diff --git a/include/tc/encode/Base64Util.h b/include/tc/encode/Base64Util.h new file mode 100644 index 00000000..54167e95 --- /dev/null +++ b/include/tc/encode/Base64Util.h @@ -0,0 +1,47 @@ + /** + * @file Base64Util.h + * @brief Declaration of tc::encode::Base64Util + * @author Jack (jakcron) + * @version 0.1 + * @date 2024/09/29 + **/ +#pragma once +#include +#include + +namespace tc { namespace encode { + + /** + * @class Base64Util + * @brief A collection of utilities to encode/decode binary data/strings to base64 and vice-versa. + **/ +class Base64Util +{ +public: + static tc::ByteData encodeDataAsBase64Data(const byte_t* data, size_t size); + static tc::ByteData encodeDataAsBase64Data(const tc::ByteData& data); + + static std::string encodeDataAsBase64String(const byte_t* data, size_t size); + static std::string encodeDataAsBase64String(const tc::ByteData& data); + + static tc::ByteData encodeStringAsBase64Data(const char* data, size_t size); + static tc::ByteData encodeStringAsBase64Data(const std::string& data); + + static std::string encodeStringAsBase64String(const char* data, size_t size); + static std::string encodeStringAsBase64String(const std::string& data); + + static tc::ByteData decodeBase64DataAsData(const byte_t* data, size_t size); + static tc::ByteData decodeBase64DataAsData(const tc::ByteData& data); + + static std::string decodeBase64DataAsString(const byte_t* data, size_t size); + static std::string decodeBase64DataAsString(const tc::ByteData& data); + + static tc::ByteData decodeBase64StringAsData(const char* data, size_t size); + static tc::ByteData decodeBase64StringAsData(const std::string& data); + + static std::string decodeBase64StringAsString(const char* data, size_t size); + static std::string decodeBase64StringAsString(const std::string& data); +private: +}; + +}} // namespace tc::encode diff --git a/makefile b/makefile index 3b4109a9..a0fce2df 100644 --- a/makefile +++ b/makefile @@ -8,7 +8,7 @@ PROJECT_NAME = libtoolchain # Project Relative Paths PROJECT_PATH = $(CURDIR) PROJECT_SRC_PATH = src -PROJECT_SRC_SUBDIRS = $(PROJECT_SRC_PATH) $(PROJECT_SRC_PATH)/io $(PROJECT_SRC_PATH)/string $(PROJECT_SRC_PATH)/cli $(PROJECT_SRC_PATH)/os $(PROJECT_SRC_PATH)/crypto $(PROJECT_SRC_PATH)/crypto/detail +PROJECT_SRC_SUBDIRS = $(PROJECT_SRC_PATH) $(PROJECT_SRC_PATH)/io $(PROJECT_SRC_PATH)/string $(PROJECT_SRC_PATH)/cli $(PROJECT_SRC_PATH)/os $(PROJECT_SRC_PATH)/encode $(PROJECT_SRC_PATH)/crypto $(PROJECT_SRC_PATH)/crypto/detail PROJECT_INCLUDE_PATH = include PROJECT_TESTSRC_PATH = test PROJECT_TESTSRC_SUBDIRS = $(PROJECT_TESTSRC_PATH) diff --git a/src/encode/Base64Util.cpp b/src/encode/Base64Util.cpp new file mode 100644 index 00000000..6e4b705f --- /dev/null +++ b/src/encode/Base64Util.cpp @@ -0,0 +1,298 @@ +#include + +#include + +tc::ByteData tc::encode::Base64Util::encodeDataAsBase64Data(const byte_t* data, size_t size) +{ + return tc::ByteData(); +} +tc::ByteData tc::encode::Base64Util::encodeDataAsBase64Data(const tc::ByteData& data) +{ + return encodeDataAsBase64Data(data.data(), data.size()); +} + +std::string tc::encode::Base64Util::encodeDataAsBase64String(const byte_t* data, size_t size) +{ + tc::ByteData encoded = encodeDataAsBase64Data(data, size); + return std::string((char*)data.data(), data.size()); +} +std::string tc::encode::Base64Util::encodeDataAsBase64String(const tc::ByteData& data) +{ + return encodeDataAsBase64String(data.data(), data.size()); +} + +tc::ByteData tc::encode::Base64Util::encodeStringAsBase64Data(const char* data, size_t size) +{ + return tc::ByteData(); +} +tc::ByteData tc::encode::Base64Util::encodeStringAsBase64Data(const std::string& data) +{ + +} + +std::string tc::encode::Base64Util::encodeStringAsBase64String(const char* data, size_t size) +{ + +} +std::string tc::encode::Base64Util::encodeStringAsBase64String(const std::string& data) +{ + +} + +tc::ByteData tc::encode::Base64Util::decodeBase64DataAsData(const byte_t* data, size_t size) +{ + +} +tc::ByteData tc::encode::Base64Util::decodeBase64DataAsData(const tc::ByteData& data) +{ + +} + +std::string tc::encode::Base64Util::decodeBase64DataAsString(const byte_t* data, size_t size) +{ + +} +std::string tc::encode::Base64Util::decodeBase64DataAsString(const tc::ByteData& data) +{ + +} + +tc::ByteData tc::encode::Base64Util::decodeBase64StringAsData(const char* data, size_t size) +{ + +} +tc::ByteData tc::encode::Base64Util::decodeBase64StringAsData(const std::string& data) +{ + +} + +std::string tc::encode::Base64Util::decodeBase64StringAsString(const char* data, size_t size) +{ + +} +std::string tc::encode::Base64Util::decodeBase64StringAsString(const std::string& data) +{ + +} + + +/* +inline int charToByte(char chr) +{ + if (chr >= 'a' && chr <= 'f') + return (chr - 'a') + 0xa; + else if (chr >= 'A' && chr <= 'F') + return (chr - 'A') + 0xa; + else if (chr >= '0' && chr <= '9') + return chr - '0'; + return -1; +} + +tc::ByteData tc::cli::FormatUtil::hexStringToBytes(const std::string& str) +{ + size_t size = str.size(); + if ((size % 2)) + { + return tc::ByteData(); + } + + auto bytes = tc::ByteData(size/2); + + for (size_t i = 0; i < bytes.size(); i++) + { + int byte = 0; + + byte = charToByte(str[i * 2]); + if (byte == -1) + return tc::ByteData(); + + bytes.data()[i] = byte_t((byte & 0xf) << 4); + + byte = charToByte(str[(i * 2) + 1]); + if (byte == -1) + return tc::ByteData(); + + bytes.data()[i] |= byte_t((byte & 0xf) << 0); + } + + return bytes; +} + +std::string tc::cli::FormatUtil::formatBytesAsStringWithLineLimit(const byte_t* data, size_t len, bool upper_case, const std::string& delimiter, size_t row_len, size_t indent_len, bool print_first_indent) +{ + // create indentation string + std::string indent_str = ""; + for (size_t i = 0; i < indent_len; i++) + { + indent_str += " "; + } + + const byte_t* original_data = data; + + // create output string + std::string output_str = ""; + + for (size_t print_len = 0; len > 0; len -= print_len, data += print_len) + { + if (data != original_data || print_first_indent) + { + output_str += indent_str; + } + + print_len = std::min(len, row_len); + + output_str += formatBytesAsString(data, print_len, upper_case, delimiter); + + output_str += fmt::format("\n"); + } + + return output_str; +} + +std::string tc::cli::FormatUtil::formatBytesAsString(const byte_t* data, size_t size, bool upper_case, const std::string& delimiter) +{ + // create output string + std::string output_str; + + for (size_t i = 0; i < size; i++) + { + output_str += fmt::format((upper_case ? "{:02X}" : "{:02x}"), data[i]); + if (i+1 < size) + { + output_str += delimiter; + } + } + + return output_str; +} + +std::string tc::cli::FormatUtil::formatBytesAsString(const tc::ByteData& data, bool upper_case, const std::string& delimiter) +{ + return formatBytesAsString(data.data(), data.size(), upper_case, delimiter); +} + + +std::string tc::cli::FormatUtil::formatListWithLineLimit(const std::vector& str_list, size_t row_len, size_t indent_len, bool print_first_indent) +{ + if (str_list.size() == 0) + { + return ""; + } + + // create output string + std::string output_str = ""; + + // create indentation string + std::string indent_str = ""; + for (size_t i = 0; i < indent_len; i++) + { + indent_str += " "; + } + + // create delimiter string + std::string delimiter_str = ", "; + + size_t printed_len = 0; + for (auto itr = str_list.begin(); itr != str_list.end(); itr++) + { + // format the strings + // wrap the line after row_len multples + if (printed_len > row_len || printed_len == 0) + { + // don't print the new line if this is the first string + if (itr != str_list.begin()) + { + output_str += fmt::format("{:s}\n", delimiter_str); + } + + // print indent if this isn't the first string or the user has opted into printing the indent regardless + if (itr != str_list.begin() || print_first_indent) + { + output_str += indent_str; + } + + // reset printed_len + printed_len = 0; + } + // within a line we want to separate the next string from the last one with a comma and a space + else + { + //ss << delimiter_str; + output_str += delimiter_str; + } + + // print string + output_str += *itr; + + // note the length of the string printed + printed_len += itr->size() + delimiter_str.size(); + } + output_str += fmt::format("\n"); + + return output_str; +} + +std::string tc::cli::FormatUtil::formatBytesAsHxdHexString(const byte_t* data, size_t size, size_t bytes_per_row, size_t byte_group_size) +{ + if (size == 0 || bytes_per_row == 0 || byte_group_size == 0) + { + return ""; + } + + // create output string + std::string output_str = ""; + + // iterate over blocks + for (size_t i = 0; size > 0; i++) + { + size_t row_print_len = std::min(size, bytes_per_row); + + output_str += fmt::format("{:08x} | ", uint64_t(i) * uint64_t(bytes_per_row)); + + // for block i print each byte + for (size_t j = 0; j < bytes_per_row; j++) + { + if (j < row_print_len) + { + output_str += fmt::format("{:02X}", data[(i * bytes_per_row) + j]); + } + else + { + output_str += " "; + } + + + if (((j+1) % byte_group_size) == 0) + { + output_str += " "; + } + } + + output_str += " "; + + for (size_t j = 0; j < bytes_per_row; j++) + { + if (j < row_print_len) + { + byte_t byte = data[(i * bytes_per_row) + j]; + output_str += fmt::format("{:c}", (iscntrl(byte) == 0 && byte < 0x7f) ? (char)byte : '.'); + } + else + { + output_str += " "; + } + } + + output_str += fmt::format("\n"); + + size -= row_print_len; + } + + return output_str; +} + +std::string tc::cli::FormatUtil::formatBytesAsHxdHexString(const byte_t* data, size_t size) +{ + return formatBytesAsHxdHexString(data, size, 0x10, 1); +} +*/ \ No newline at end of file From 6d13b4344d72ad87c804a128cc466330974a7442 Mon Sep 17 00:00:00 2001 From: Jack Date: Sun, 29 Sep 2024 15:59:49 +0800 Subject: [PATCH 04/11] Begin adding unit tests --- .../libtoolchain-test.vcxproj | 2 + .../libtoolchain-test.vcxproj.filters | 6 + include/tc.h | 1 + src/encode/Base64Util.cpp | 292 +++-------- test/encode_Base64Util_TestClass.cpp | 452 ++++++++++++++++++ test/encode_Base64Util_TestClass.h | 48 ++ test/main.cpp | 4 + 7 files changed, 568 insertions(+), 237 deletions(-) create mode 100644 test/encode_Base64Util_TestClass.cpp create mode 100644 test/encode_Base64Util_TestClass.h diff --git a/build/visualstudio/libtoolchain-test/libtoolchain-test.vcxproj b/build/visualstudio/libtoolchain-test/libtoolchain-test.vcxproj index 2d035579..e50b20b4 100644 --- a/build/visualstudio/libtoolchain-test/libtoolchain-test.vcxproj +++ b/build/visualstudio/libtoolchain-test/libtoolchain-test.vcxproj @@ -205,6 +205,7 @@ + @@ -315,6 +316,7 @@ + diff --git a/build/visualstudio/libtoolchain-test/libtoolchain-test.vcxproj.filters b/build/visualstudio/libtoolchain-test/libtoolchain-test.vcxproj.filters index f303ecdf..10e0c0c8 100644 --- a/build/visualstudio/libtoolchain-test/libtoolchain-test.vcxproj.filters +++ b/build/visualstudio/libtoolchain-test/libtoolchain-test.vcxproj.filters @@ -255,6 +255,9 @@ Source Files + + Source Files + Source Files @@ -581,6 +584,9 @@ Header Files + + Header Files + Header Files diff --git a/include/tc.h b/include/tc.h index 08106f50..578d9076 100644 --- a/include/tc.h +++ b/include/tc.h @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include diff --git a/src/encode/Base64Util.cpp b/src/encode/Base64Util.cpp index 6e4b705f..fb713160 100644 --- a/src/encode/Base64Util.cpp +++ b/src/encode/Base64Util.cpp @@ -2,10 +2,36 @@ #include +#include + +inline std::string byteDataAsString(const tc::ByteData& data) +{ + if (data.size() == 0) + return std::string(); + + return std::string((char*)data.data(), data.size()); +} + tc::ByteData tc::encode::Base64Util::encodeDataAsBase64Data(const byte_t* data, size_t size) { - return tc::ByteData(); + if (data == nullptr && size != 0) + { + return tc::ByteData(); + } + + size_t enc_len = 0; + + mbedtls_base64_encode(nullptr, enc_len, &enc_len, data, size); + + tc::ByteData enc_data = tc::ByteData(enc_len); + + int mbedtls_ret = mbedtls_base64_encode(enc_data.data(), enc_data.size(), &enc_len, data, size); + if (mbedtls_ret != 0) + return tc::ByteData(); + + return enc_data; } + tc::ByteData tc::encode::Base64Util::encodeDataAsBase64Data(const tc::ByteData& data) { return encodeDataAsBase64Data(data.data(), data.size()); @@ -13,8 +39,7 @@ tc::ByteData tc::encode::Base64Util::encodeDataAsBase64Data(const tc::ByteData& std::string tc::encode::Base64Util::encodeDataAsBase64String(const byte_t* data, size_t size) { - tc::ByteData encoded = encodeDataAsBase64Data(data, size); - return std::string((char*)data.data(), data.size()); + return byteDataAsString(encodeDataAsBase64Data(data, size)); } std::string tc::encode::Base64Util::encodeDataAsBase64String(const tc::ByteData& data) { @@ -23,276 +48,69 @@ std::string tc::encode::Base64Util::encodeDataAsBase64String(const tc::ByteData& tc::ByteData tc::encode::Base64Util::encodeStringAsBase64Data(const char* data, size_t size) { - return tc::ByteData(); + return encodeDataAsBase64Data((const byte_t*)data, size); } tc::ByteData tc::encode::Base64Util::encodeStringAsBase64Data(const std::string& data) { - + return encodeStringAsBase64Data(data.c_str(), data.size()); } std::string tc::encode::Base64Util::encodeStringAsBase64String(const char* data, size_t size) { - + return byteDataAsString(encodeStringAsBase64Data(data, size)); } std::string tc::encode::Base64Util::encodeStringAsBase64String(const std::string& data) { - + return byteDataAsString(encodeStringAsBase64Data(data)); } tc::ByteData tc::encode::Base64Util::decodeBase64DataAsData(const byte_t* data, size_t size) { + if (data == nullptr && size != 0) + { + return tc::ByteData(); + } + + size_t dec_len = 0; + + mbedtls_base64_decode(nullptr, dec_len, &dec_len, data, size); + + tc::ByteData dec_data = tc::ByteData(dec_len); + int mbedtls_ret = mbedtls_base64_decode(dec_data.data(), dec_data.size(), &dec_len, data, size); + if (mbedtls_ret != 0) + return tc::ByteData(); + + return dec_data; } tc::ByteData tc::encode::Base64Util::decodeBase64DataAsData(const tc::ByteData& data) { - + return decodeBase64DataAsData(data.data(), data.size()); } std::string tc::encode::Base64Util::decodeBase64DataAsString(const byte_t* data, size_t size) { - + return byteDataAsString(decodeBase64DataAsData(data, size)); } std::string tc::encode::Base64Util::decodeBase64DataAsString(const tc::ByteData& data) { - + return byteDataAsString(decodeBase64DataAsData(data)); } tc::ByteData tc::encode::Base64Util::decodeBase64StringAsData(const char* data, size_t size) { - + return decodeBase64DataAsData((const byte_t*)data, size); } tc::ByteData tc::encode::Base64Util::decodeBase64StringAsData(const std::string& data) { - + return decodeBase64DataAsData((const byte_t*)data.c_str(), data.size()); } std::string tc::encode::Base64Util::decodeBase64StringAsString(const char* data, size_t size) { - + return byteDataAsString(decodeBase64DataAsData((const byte_t*)data, size)); } std::string tc::encode::Base64Util::decodeBase64StringAsString(const std::string& data) { - -} - - -/* -inline int charToByte(char chr) -{ - if (chr >= 'a' && chr <= 'f') - return (chr - 'a') + 0xa; - else if (chr >= 'A' && chr <= 'F') - return (chr - 'A') + 0xa; - else if (chr >= '0' && chr <= '9') - return chr - '0'; - return -1; -} - -tc::ByteData tc::cli::FormatUtil::hexStringToBytes(const std::string& str) -{ - size_t size = str.size(); - if ((size % 2)) - { - return tc::ByteData(); - } - - auto bytes = tc::ByteData(size/2); - - for (size_t i = 0; i < bytes.size(); i++) - { - int byte = 0; - - byte = charToByte(str[i * 2]); - if (byte == -1) - return tc::ByteData(); - - bytes.data()[i] = byte_t((byte & 0xf) << 4); - - byte = charToByte(str[(i * 2) + 1]); - if (byte == -1) - return tc::ByteData(); - - bytes.data()[i] |= byte_t((byte & 0xf) << 0); - } - - return bytes; -} - -std::string tc::cli::FormatUtil::formatBytesAsStringWithLineLimit(const byte_t* data, size_t len, bool upper_case, const std::string& delimiter, size_t row_len, size_t indent_len, bool print_first_indent) -{ - // create indentation string - std::string indent_str = ""; - for (size_t i = 0; i < indent_len; i++) - { - indent_str += " "; - } - - const byte_t* original_data = data; - - // create output string - std::string output_str = ""; - - for (size_t print_len = 0; len > 0; len -= print_len, data += print_len) - { - if (data != original_data || print_first_indent) - { - output_str += indent_str; - } - - print_len = std::min(len, row_len); - - output_str += formatBytesAsString(data, print_len, upper_case, delimiter); - - output_str += fmt::format("\n"); - } - - return output_str; -} - -std::string tc::cli::FormatUtil::formatBytesAsString(const byte_t* data, size_t size, bool upper_case, const std::string& delimiter) -{ - // create output string - std::string output_str; - - for (size_t i = 0; i < size; i++) - { - output_str += fmt::format((upper_case ? "{:02X}" : "{:02x}"), data[i]); - if (i+1 < size) - { - output_str += delimiter; - } - } - - return output_str; -} - -std::string tc::cli::FormatUtil::formatBytesAsString(const tc::ByteData& data, bool upper_case, const std::string& delimiter) -{ - return formatBytesAsString(data.data(), data.size(), upper_case, delimiter); -} - - -std::string tc::cli::FormatUtil::formatListWithLineLimit(const std::vector& str_list, size_t row_len, size_t indent_len, bool print_first_indent) -{ - if (str_list.size() == 0) - { - return ""; - } - - // create output string - std::string output_str = ""; - - // create indentation string - std::string indent_str = ""; - for (size_t i = 0; i < indent_len; i++) - { - indent_str += " "; - } - - // create delimiter string - std::string delimiter_str = ", "; - - size_t printed_len = 0; - for (auto itr = str_list.begin(); itr != str_list.end(); itr++) - { - // format the strings - // wrap the line after row_len multples - if (printed_len > row_len || printed_len == 0) - { - // don't print the new line if this is the first string - if (itr != str_list.begin()) - { - output_str += fmt::format("{:s}\n", delimiter_str); - } - - // print indent if this isn't the first string or the user has opted into printing the indent regardless - if (itr != str_list.begin() || print_first_indent) - { - output_str += indent_str; - } - - // reset printed_len - printed_len = 0; - } - // within a line we want to separate the next string from the last one with a comma and a space - else - { - //ss << delimiter_str; - output_str += delimiter_str; - } - - // print string - output_str += *itr; - - // note the length of the string printed - printed_len += itr->size() + delimiter_str.size(); - } - output_str += fmt::format("\n"); - - return output_str; -} - -std::string tc::cli::FormatUtil::formatBytesAsHxdHexString(const byte_t* data, size_t size, size_t bytes_per_row, size_t byte_group_size) -{ - if (size == 0 || bytes_per_row == 0 || byte_group_size == 0) - { - return ""; - } - - // create output string - std::string output_str = ""; - - // iterate over blocks - for (size_t i = 0; size > 0; i++) - { - size_t row_print_len = std::min(size, bytes_per_row); - - output_str += fmt::format("{:08x} | ", uint64_t(i) * uint64_t(bytes_per_row)); - - // for block i print each byte - for (size_t j = 0; j < bytes_per_row; j++) - { - if (j < row_print_len) - { - output_str += fmt::format("{:02X}", data[(i * bytes_per_row) + j]); - } - else - { - output_str += " "; - } - - - if (((j+1) % byte_group_size) == 0) - { - output_str += " "; - } - } - - output_str += " "; - - for (size_t j = 0; j < bytes_per_row; j++) - { - if (j < row_print_len) - { - byte_t byte = data[(i * bytes_per_row) + j]; - output_str += fmt::format("{:c}", (iscntrl(byte) == 0 && byte < 0x7f) ? (char)byte : '.'); - } - else - { - output_str += " "; - } - } - - output_str += fmt::format("\n"); - - size -= row_print_len; - } - - return output_str; -} - -std::string tc::cli::FormatUtil::formatBytesAsHxdHexString(const byte_t* data, size_t size) -{ - return formatBytesAsHxdHexString(data, size, 0x10, 1); -} -*/ \ No newline at end of file + return byteDataAsString(decodeBase64DataAsData((const byte_t*)data.c_str(), data.size())); +} \ No newline at end of file diff --git a/test/encode_Base64Util_TestClass.cpp b/test/encode_Base64Util_TestClass.cpp new file mode 100644 index 00000000..e5810b83 --- /dev/null +++ b/test/encode_Base64Util_TestClass.cpp @@ -0,0 +1,452 @@ +#include "encode_Base64Util_TestClass.h" + +#include + +//--------------------------------------------------------- + +encode_Base64Util_TestClass::encode_Base64Util_TestClass() : + mTestTag("tc::encode::Base64Util"), + mTestResults() +{ +} + +void encode_Base64Util_TestClass::runAllTests(void) +{ + testHexStringToBytes(); + testFormatBytesAsString(); + testFormatBytesAsStringWithLineLimit(); + testFormatListWithLineLimit(); + testFormatBytesAsHxdHexString(); +} + +const std::string& encode_Base64Util_TestClass::getTestTag() const +{ + return mTestTag; +} + +const std::vector& encode_Base64Util_TestClass::getTestResults() const +{ + return mTestResults; +} + +//--------------------------------------------------------- + +void encode_Base64Util_TestClass::testHexStringToBytes() +{ + TestResult test; + test.test_name = "testHexStringToBytes"; + test.result = "NOT RUN"; + test.comments = ""; + + try + { + struct TestCase + { + std::string test_name; + std::string in_string; + tc::ByteData out_data; + }; + + // create manual tests + std::vector tests = + { + { "empty string", "", tc::ByteData()}, + { "unaligned string" ,"1", tc::ByteData()}, + { "unaligned larger string" ,"123456789", tc::ByteData()}, + { "multi-byte" ,"00112233445566778899aabbccddeeff010203", tc::ByteData({0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, 0x01, 0x02, 0x03})}, + }; + + // add programatically determined tests + for (size_t i = 0; i < 0xff; i++) + { + std::string all_lower, all_upper, upper_lower, lower_upper; + + size_t upper_num = (i >> 4) & 0xf; + size_t lower_num = (i) & 0xf; + + if (upper_num <= 9) + { + all_lower += char('0' + upper_num); + all_upper += char('0' + upper_num); + upper_lower += char('0' + upper_num); + lower_upper += char('0' + upper_num); + } + else + { + all_lower += char('a' + upper_num - 10); + all_upper += char('A' + upper_num - 10); + upper_lower += char('A' + upper_num - 10); + lower_upper += char('a' + upper_num - 10); + } + + if (lower_num <= 9) + { + all_lower += char('0' + lower_num); + all_upper += char('0' + lower_num); + upper_lower += char('0' + lower_num); + lower_upper += char('0' + lower_num); + } + else + { + all_lower += char('a' + lower_num - 10); + all_upper += char('A' + lower_num - 10); + upper_lower += char('a' + lower_num - 10); + lower_upper += char('A' + lower_num - 10); + } + + tests.push_back({"all_lower_" + all_lower, all_lower, tc::ByteData({(byte_t)i})}); + tests.push_back({"all_upper_" + all_lower, all_upper, tc::ByteData({(byte_t)i})}); + tests.push_back({"upper_lower_" + all_lower, upper_lower, tc::ByteData({(byte_t)i})}); + tests.push_back({"lower_upper_" + all_lower, lower_upper, tc::ByteData({(byte_t)i})}); + } + + // run tests + for (auto test = tests.begin(); test != tests.end(); test++) + { + tc::ByteData out = tc::encode::Base64Util::hexStringToBytes(test->in_string); + + if (out.size() != test->out_data.size()) + { + throw tc::TestException(fmt::format("Test({:s}) to convert str({:s}) returned ByteData with wrong size: {:d} (should be: {:d})", test->test_name, test->in_string, out.size(), test->out_data.size())); + } + + if (out.size() != 0 && memcmp(out.data(), test->out_data.data(), out.size()) != 0) + { + throw tc::TestException(fmt::format("Test({:s}) to convert str({:s}) returned ByteData with wrong data", test->test_name, test->in_string)); + } + } + + // record result + test.result = "PASS"; + test.comments = ""; + } + catch (const tc::TestException& e) + { + // record result + test.result = "FAIL"; + test.comments = e.what(); + } + catch (const std::exception& e) + { + // record result + test.result = "UNHANDLED EXCEPTION"; + test.comments = e.what(); + } + + // add result to list + mTestResults.push_back(std::move(test)); +} + +void encode_Base64Util_TestClass::testFormatBytesAsString() +{ + TestResult test; + test.test_name = "testFormatBytesAsString"; + test.result = "NOT RUN"; + test.comments = ""; + + try + { + // test recipe + struct TestCase + { + std::string test_name; + tc::ByteData in_data; + bool in_is_uppercase; + std::string in_delimiter; + std::string out_string; + }; + + // create tests + std::vector tests = + { + {"empty data", tc::ByteData(), false, "", ""}, + {"8byte lowercase no delim", tc::ByteData({0x00, 0xaa, 0x11, 0xbb, 0x22, 0xcc, 0x33, 0xdd}), false, "", "00aa11bb22cc33dd"}, + {"8byte lowercase ' ' delim", tc::ByteData({0x00, 0xaa, 0x11, 0xbb, 0x22, 0xcc, 0x33, 0xdd}), false, " ", "00 aa 11 bb 22 cc 33 dd"}, + {"8byte lowercase ':' delim", tc::ByteData({0x00, 0xaa, 0x11, 0xbb, 0x22, 0xcc, 0x33, 0xdd}), false, ":", "00:aa:11:bb:22:cc:33:dd"}, + {"8byte uppercase no delim", tc::ByteData({0x00, 0xaa, 0x11, 0xbb, 0x22, 0xcc, 0x33, 0xdd}), true, "", "00AA11BB22CC33DD"}, + {"8byte uppercase ' ' delim", tc::ByteData({0x00, 0xaa, 0x11, 0xbb, 0x22, 0xcc, 0x33, 0xdd}), true, " ", "00 AA 11 BB 22 CC 33 DD"}, + {"8byte uppercase ':' delim", tc::ByteData({0x00, 0xaa, 0x11, 0xbb, 0x22, 0xcc, 0x33, 0xdd}), true, ":", "00:AA:11:BB:22:CC:33:DD"}, + }; + + + // run tests + for (auto test = tests.begin(); test != tests.end(); test++) + { + std::string res = tc::encode::Base64Util::formatBytesAsString(test->in_data.data(), test->in_data.size(), test->in_is_uppercase, test->in_delimiter); + + if (res != test->out_string) + { + throw tc::TestException(fmt::format("Test({:s}) Failed to format data correctly. Output: \"{:s}\", Expected: \"{:s}\"", test->test_name, res, test->out_string)); + } + } + + // record result + test.result = "PASS"; + test.comments = ""; + } + catch (const tc::TestException& e) + { + // record result + test.result = "FAIL"; + test.comments = e.what(); + } + catch (const std::exception& e) + { + // record result + test.result = "UNHANDLED EXCEPTION"; + test.comments = e.what(); + } + + // add result to list + mTestResults.push_back(std::move(test)); +} + +void encode_Base64Util_TestClass::testFormatBytesAsStringWithLineLimit() +{ + TestResult test; + test.test_name = "testFormatBytesAsStringWithLineLimit"; + test.result = "NOT RUN"; + test.comments = ""; + + try + { + // test recipe + struct TestCase + { + std::string test_name; + tc::ByteData in_data; + size_t in_row_len; + size_t in_indent_len; + std::string out_string; + }; + + // create tests + std::vector tests = + { + {"empty data", tc::ByteData(), 0, 0, ""}, + {"size:8 row:8 indent:0", tc::ByteData({0x00, 0xaa, 0x11, 0xbb, 0x22, 0xcc, 0x33, 0xdd}), 8, 0, "00aa11bb22cc33dd\n"}, + {"size:8 row:4 indent:0", tc::ByteData({0x00, 0xaa, 0x11, 0xbb, 0x22, 0xcc, 0x33, 0xdd}), 4, 0, "00aa11bb\n22cc33dd\n"}, + {"size:8 row:3 indent:0", tc::ByteData({0x00, 0xaa, 0x11, 0xbb, 0x22, 0xcc, 0x33, 0xdd}), 3, 0, "00aa11\nbb22cc\n33dd\n"}, + {"size:8 row:3 indent:2", tc::ByteData({0x00, 0xaa, 0x11, 0xbb, 0x22, 0xcc, 0x33, 0xdd}), 3, 2, " 00aa11\n bb22cc\n 33dd\n"}, + {"size:8 row:1 indent:3", tc::ByteData({0x00, 0xaa, 0x11, 0xbb, 0x22, 0xcc, 0x33, 0xdd}), 1, 3, " 00\n aa\n 11\n bb\n 22\n cc\n 33\n dd\n"}, + }; + + + // run tests + for (auto test = tests.begin(); test != tests.end(); test++) + { + std::string res = tc::encode::Base64Util::formatBytesAsStringWithLineLimit(test->in_data.data(), test->in_data.size(), false, "", test->in_row_len, test->in_indent_len); + + if (res != test->out_string) + { + std::string& expected = test->out_string; + + // replace literal new lines so they can be printed on one line for debugging + for (size_t pos = res.find('\n', 0); pos != std::string::npos; pos = res.find('\n', pos+1)) + { + res.replace(pos, 1, "\\n"); + } + + // replace literal new lines so they can be printed on one line for debugging + for (size_t pos = expected.find('\n', 0); pos != std::string::npos; pos = expected.find('\n', pos+1)) + { + expected.replace(pos, 1, "\\n"); + } + + throw tc::TestException(fmt::format("Test({:s}) Failed to format data correctly. Output: \"{:s}\", Expected: \"{:s}\"", test->test_name, res, expected)); + } + } + + // record result + test.result = "PASS"; + test.comments = ""; + } + catch (const tc::TestException& e) + { + // record result + test.result = "FAIL"; + test.comments = e.what(); + } + catch (const std::exception& e) + { + // record result + test.result = "UNHANDLED EXCEPTION"; + test.comments = e.what(); + } + + // add result to list + mTestResults.push_back(std::move(test)); +} + +void encode_Base64Util_TestClass::testFormatListWithLineLimit() +{ + TestResult test; + test.test_name = "testFormatListWithLineLimit"; + test.result = "NOT RUN"; + test.comments = ""; + + try + { + // test recipe + struct TestCase + { + std::string test_name; + std::vector in_list; + size_t in_row_len; + size_t in_indent_len; + std::string out_string; + }; + + // create tests + std::vector tests = + { + {"empty", {}, 0, 0, ""}, + {"empty list", {}, 40, 0, ""}, + {"list of 4, row_len 20", {"Astr", "Bstr", "Cstr", "Dstr"}, 20, 0, "Astr, Bstr, Cstr, Dstr\n"}, + {"list of 4, row_len 8", {"Astr", "Bstr", "Cstr", "Dstr"}, 8, 0, "Astr, Bstr, \nCstr, Dstr\n"}, + {"list of 4, row_len 8, indent=2", {"Astr", "Bstr", "Cstr", "Dstr"}, 8, 2, " Astr, Bstr, \n Cstr, Dstr\n"}, + {"list of 4, row_len 4", {"Astr", "Bstr", "Cstr", "Dstr"}, 4, 0, "Astr, \nBstr, \nCstr, \nDstr\n"}, + {"list of 4, row_len 2", {"Astr", "Bstr", "Cstr", "Dstr"}, 2, 0, "Astr, \nBstr, \nCstr, \nDstr\n"}, + {"list of 4, row_len 1", {"Astr", "Bstr", "Cstr", "Dstr"}, 1, 0, "Astr, \nBstr, \nCstr, \nDstr\n"}, + {"list of 4, row_len 0", {"Astr", "Bstr", "Cstr", "Dstr"}, 0, 0, "Astr, \nBstr, \nCstr, \nDstr\n"}, + }; + + + // run tests + for (auto test = tests.begin(); test != tests.end(); test++) + { + std::string res = tc::encode::Base64Util::formatListWithLineLimit(test->in_list, test->in_row_len, test->in_indent_len); + + if (res != test->out_string) + { + std::string& expected = test->out_string; + + // replace literal new lines so they can be printed on one line for debugging + for (size_t pos = res.find('\n', 0); pos != std::string::npos; pos = res.find('\n', pos+1)) + { + res.replace(pos, 1, "\\n"); + } + + // replace literal new lines so they can be printed on one line for debugging + for (size_t pos = expected.find('\n', 0); pos != std::string::npos; pos = expected.find('\n', pos+1)) + { + expected.replace(pos, 1, "\\n"); + } + + throw tc::TestException(fmt::format("Test({:s}) Failed to format data correctly. Output: \"{:s}\", Expected: \"{:s}\"", test->test_name, res, expected)); + } + } + + // record result + test.result = "PASS"; + test.comments = ""; + } + catch (const tc::TestException& e) + { + // record result + test.result = "FAIL"; + test.comments = e.what(); + } + catch (const std::exception& e) + { + // record result + test.result = "UNHANDLED EXCEPTION"; + test.comments = e.what(); + } + + // add result to list + mTestResults.push_back(std::move(test)); +} + +void encode_Base64Util_TestClass::testFormatBytesAsHxdHexString() +{ + TestResult test; + test.test_name = "testFormatBytesAsHxdHexString"; + test.result = "NOT RUN"; + test.comments = ""; + + try + { + // test recipe + struct TestCase + { + std::string test_name; + tc::ByteData in_data; + size_t in_bytes_per_row; + size_t in_byte_group_size; + std::string out_string; + }; + + // create tests + std::vector tests = + { + {"empty all", {}, 0, 0, ""}, + {"empty data", {}, 16, 1, ""}, + {"empty bytes_per_row", {0x00, 0x01}, 0, 1, ""}, + {"empty byte_group_size", {0x00, 0x01}, 16, 0, ""}, + {"little data", {0x00, 0x01}, 16, 1, "00000000 | 00 01 .. \n"}, + {"full row data", {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f}, 16, 1, "00000000 | 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F ................\n"}, + {"2 row ascii", {0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4e, 0x4f, 0x50, 0x51, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66}, 16, 1, "00000000 | 41 42 43 44 45 46 47 48 49 4A 4B 4C 4E 4F 50 51 ABCDEFGHIJKLNOPQ\n00000010 | 51 52 53 54 55 56 57 58 59 5A 61 62 63 64 65 66 QRSTUVWXYZabcdef\n"}, + {"full row data, byte_group_size=2", {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f}, 16, 2, "00000000 | 0001 0203 0405 0607 0809 0A0B 0C0D 0E0F ................\n"}, + {"full row data, byte_group_size=3", {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f}, 16, 3, "00000000 | 000102 030405 060708 090A0B 0C0D0E 0F ................\n"}, + {"full row data, byte_group_size=4", {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f}, 16, 4, "00000000 | 00010203 04050607 08090A0B 0C0D0E0F ................\n"}, + {"full row data, byte_group_size=5", {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f}, 16, 5, "00000000 | 0001020304 0506070809 0A0B0C0D0E 0F ................\n"}, + {"full row data, byte_group_size=6", {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f}, 16, 6, "00000000 | 000102030405 060708090A0B 0C0D0E0F ................\n"}, + {"full row data, byte_group_size=7", {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f}, 16, 7, "00000000 | 00010203040506 0708090A0B0C0D 0E0F ................\n"}, + {"full row data, byte_group_size=8", {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f}, 16, 8, "00000000 | 0001020304050607 08090A0B0C0D0E0F ................\n"}, + {"full row data, byte_group_size=9", {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f}, 16, 9, "00000000 | 000102030405060708 090A0B0C0D0E0F ................\n"}, + {"full row data, byte_group_size=10", {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f}, 16, 10, "00000000 | 00010203040506070809 0A0B0C0D0E0F ................\n"}, + {"full row data, byte_group_size=11", {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f}, 16, 11, "00000000 | 000102030405060708090A 0B0C0D0E0F ................\n"}, + {"full row data, byte_group_size=12", {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f}, 16, 12, "00000000 | 000102030405060708090A0B 0C0D0E0F ................\n"}, + {"full row data, byte_group_size=13", {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f}, 16, 13, "00000000 | 000102030405060708090A0B0C 0D0E0F ................\n"}, + {"full row data, byte_group_size=14", {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f}, 16, 14, "00000000 | 000102030405060708090A0B0C0D 0E0F ................\n"}, + {"full row data, byte_group_size=15", {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f}, 16, 15, "00000000 | 000102030405060708090A0B0C0D0E 0F ................\n"}, + {"full row data, byte_group_size=16", {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f}, 16, 16, "00000000 | 000102030405060708090A0B0C0D0E0F ................\n"}, + }; + + + // run tests + for (auto test = tests.begin(); test != tests.end(); test++) + { + std::string res = tc::encode::Base64Util::formatBytesAsHxdHexString(test->in_data.data(), test->in_data.size(), test->in_bytes_per_row, test->in_byte_group_size); + + if (res != test->out_string) + { + std::string& expected = test->out_string; + + // replace literal new lines so they can be printed on one line for debugging + for (size_t pos = res.find('\n', 0); pos != std::string::npos; pos = res.find('\n', pos+1)) + { + res.replace(pos, 1, "\\n"); + } + + // replace literal new lines so they can be printed on one line for debugging + for (size_t pos = expected.find('\n', 0); pos != std::string::npos; pos = expected.find('\n', pos+1)) + { + expected.replace(pos, 1, "\\n"); + } + + throw tc::TestException(fmt::format("Test({:s}) Failed to format data correctly. Output: \"{:s}\", Expected: \"{:s}\"", test->test_name, res, expected)); + } + } + + // record result + test.result = "PASS"; + test.comments = ""; + } + catch (const tc::TestException& e) + { + // record result + test.result = "FAIL"; + test.comments = e.what(); + } + catch (const std::exception& e) + { + // record result + test.result = "UNHANDLED EXCEPTION"; + test.comments = e.what(); + } + + // add result to list + mTestResults.push_back(std::move(test)); +} \ No newline at end of file diff --git a/test/encode_Base64Util_TestClass.h b/test/encode_Base64Util_TestClass.h new file mode 100644 index 00000000..dbe14dfc --- /dev/null +++ b/test/encode_Base64Util_TestClass.h @@ -0,0 +1,48 @@ +#pragma once +#include "ITestClass.h" +#include + +class encode_Base64Util_TestClass : public ITestClass +{ +public: + encode_Base64Util_TestClass(); + + // this will run the tests + void runAllTests(); + + // this is the label for this test (for filtering purposes) + const std::string& getTestTag() const; + + // this is where the test results are written + const std::vector& getTestResults() const; +private: + std::string mTestTag; + std::vector mTestResults; + + /* + static tc::ByteData encodeDataAsBase64Data(const byte_t* data, size_t size); + + static std::string encodeDataAsBase64String(const byte_t* data, size_t size); + + static tc::ByteData encodeStringAsBase64Data(const char* data, size_t size); + + static std::string encodeStringAsBase64String(const char* data, size_t size); + + static tc::ByteData decodeBase64DataAsData(const byte_t* data, size_t size); + + static std::string decodeBase64DataAsString(const byte_t* data, size_t size); + + static tc::ByteData decodeBase64StringAsData(const char* data, size_t size); + + static std::string decodeBase64StringAsString(const char* data, size_t size); + */ + + void testEncodeDataAsBase64Data(); + void testEncodeDataAsBase64String(); + void testEncodeStringAsBase64Data(); + void testEncodeStringAsBase64String(); + void testDecodeBase64DataAsData(); + void testDecodeBase64DataAsString(); + void testDecodeBase64StringAsData(); + void testDecodeBase64StringAsString(); +}; \ No newline at end of file diff --git a/test/main.cpp b/test/main.cpp index aa099a11..e7d00466 100644 --- a/test/main.cpp +++ b/test/main.cpp @@ -3,6 +3,7 @@ #include "string_TranscodeUtil_TestClass.h" #include "cli_FormatUtil_TestClass.h" #include "cli_OptionParser_TestClass.h" +#include "encode_Base64Util_TestClass.h" #include "bn_binaryutils_TestClass.h" #include "bn_pad_TestClass.h" #include "bn_string_TestClass.h" @@ -235,6 +236,9 @@ int main(int argc, char** argv) runTest(global_test_results, include_test_regex, exclude_test_regex, include_slow_tests); runTest(global_test_results, include_test_regex, exclude_test_regex, include_slow_tests); + // namespace tc::encode + runTest(global_test_results, include_test_regex, exclude_test_regex, include_slow_tests); + // namespace tc::bn runTest(global_test_results, include_test_regex, exclude_test_regex, include_slow_tests); runTest(global_test_results, include_test_regex, exclude_test_regex, include_slow_tests); From b4a97b4f062ed879b92da8fc9f2640d6a30e332e Mon Sep 17 00:00:00 2001 From: Jack Date: Sun, 29 Sep 2024 18:51:34 +0800 Subject: [PATCH 05/11] Revise Base64Util --- include/tc/encode/Base64Util.h | 29 ++++-------- src/encode/Base64Util.cpp | 82 ++++++++++------------------------ 2 files changed, 31 insertions(+), 80 deletions(-) diff --git a/include/tc/encode/Base64Util.h b/include/tc/encode/Base64Util.h index 54167e95..31069ecb 100644 --- a/include/tc/encode/Base64Util.h +++ b/include/tc/encode/Base64Util.h @@ -18,30 +18,17 @@ namespace tc { namespace encode { class Base64Util { public: - static tc::ByteData encodeDataAsBase64Data(const byte_t* data, size_t size); - static tc::ByteData encodeDataAsBase64Data(const tc::ByteData& data); + static std::string encodeDataAsBase64(const byte_t* data, size_t size); + static std::string encodeDataAsBase64(const tc::ByteData& data); - static std::string encodeDataAsBase64String(const byte_t* data, size_t size); - static std::string encodeDataAsBase64String(const tc::ByteData& data); + static std::string encodeStringAsBase64(const char* data, size_t size); + static std::string encodeStringAsBase64(const std::string& data); - static tc::ByteData encodeStringAsBase64Data(const char* data, size_t size); - static tc::ByteData encodeStringAsBase64Data(const std::string& data); - - static std::string encodeStringAsBase64String(const char* data, size_t size); - static std::string encodeStringAsBase64String(const std::string& data); - - static tc::ByteData decodeBase64DataAsData(const byte_t* data, size_t size); - static tc::ByteData decodeBase64DataAsData(const tc::ByteData& data); - - static std::string decodeBase64DataAsString(const byte_t* data, size_t size); - static std::string decodeBase64DataAsString(const tc::ByteData& data); - - static tc::ByteData decodeBase64StringAsData(const char* data, size_t size); - static tc::ByteData decodeBase64StringAsData(const std::string& data); + static tc::ByteData decodeBase64AsData(const char* data, size_t size); + static tc::ByteData decodeBase64AsData(const std::string& data); - static std::string decodeBase64StringAsString(const char* data, size_t size); - static std::string decodeBase64StringAsString(const std::string& data); -private: + static std::string decodeBase64AsString(const char* data, size_t size); + static std::string decodeBase64AsString(const std::string& data); }; }} // namespace tc::encode diff --git a/src/encode/Base64Util.cpp b/src/encode/Base64Util.cpp index fb713160..7e3418ed 100644 --- a/src/encode/Base64Util.cpp +++ b/src/encode/Base64Util.cpp @@ -9,14 +9,15 @@ inline std::string byteDataAsString(const tc::ByteData& data) if (data.size() == 0) return std::string(); - return std::string((char*)data.data(), data.size()); + return std::string((char*)data.data()); } -tc::ByteData tc::encode::Base64Util::encodeDataAsBase64Data(const byte_t* data, size_t size) + +std::string tc::encode::Base64Util::encodeDataAsBase64(const byte_t* data, size_t size) { if (data == nullptr && size != 0) { - return tc::ByteData(); + return ""; } size_t enc_len = 0; @@ -27,90 +28,53 @@ tc::ByteData tc::encode::Base64Util::encodeDataAsBase64Data(const byte_t* data, int mbedtls_ret = mbedtls_base64_encode(enc_data.data(), enc_data.size(), &enc_len, data, size); if (mbedtls_ret != 0) - return tc::ByteData(); + enc_data = tc::ByteData(); - return enc_data; + return byteDataAsString(enc_data); } - -tc::ByteData tc::encode::Base64Util::encodeDataAsBase64Data(const tc::ByteData& data) +std::string tc::encode::Base64Util::encodeDataAsBase64(const tc::ByteData& data) { - return encodeDataAsBase64Data(data.data(), data.size()); + return encodeDataAsBase64(data.data(), data.size()); } -std::string tc::encode::Base64Util::encodeDataAsBase64String(const byte_t* data, size_t size) +std::string tc::encode::Base64Util::encodeStringAsBase64(const char* str, size_t size) { - return byteDataAsString(encodeDataAsBase64Data(data, size)); + return encodeDataAsBase64((const byte_t*)str, size); } -std::string tc::encode::Base64Util::encodeDataAsBase64String(const tc::ByteData& data) +std::string tc::encode::Base64Util::encodeStringAsBase64(const std::string& str) { - return encodeDataAsBase64String(data.data(), data.size()); + return encodeDataAsBase64((const byte_t*)str.c_str(), str.size()); } -tc::ByteData tc::encode::Base64Util::encodeStringAsBase64Data(const char* data, size_t size) +tc::ByteData tc::encode::Base64Util::decodeBase64AsData(const char* str, size_t size) { - return encodeDataAsBase64Data((const byte_t*)data, size); -} -tc::ByteData tc::encode::Base64Util::encodeStringAsBase64Data(const std::string& data) -{ - return encodeStringAsBase64Data(data.c_str(), data.size()); -} - -std::string tc::encode::Base64Util::encodeStringAsBase64String(const char* data, size_t size) -{ - return byteDataAsString(encodeStringAsBase64Data(data, size)); -} -std::string tc::encode::Base64Util::encodeStringAsBase64String(const std::string& data) -{ - return byteDataAsString(encodeStringAsBase64Data(data)); -} - -tc::ByteData tc::encode::Base64Util::decodeBase64DataAsData(const byte_t* data, size_t size) -{ - if (data == nullptr && size != 0) + if (str == nullptr && size != 0) { return tc::ByteData(); } size_t dec_len = 0; - mbedtls_base64_decode(nullptr, dec_len, &dec_len, data, size); + mbedtls_base64_decode(nullptr, dec_len, &dec_len, (const byte_t*)str, size); tc::ByteData dec_data = tc::ByteData(dec_len); - int mbedtls_ret = mbedtls_base64_decode(dec_data.data(), dec_data.size(), &dec_len, data, size); + int mbedtls_ret = mbedtls_base64_decode(dec_data.data(), dec_data.size(), &dec_len, (const byte_t*)str, size); if (mbedtls_ret != 0) - return tc::ByteData(); + dec_data = tc::ByteData(); return dec_data; } -tc::ByteData tc::encode::Base64Util::decodeBase64DataAsData(const tc::ByteData& data) -{ - return decodeBase64DataAsData(data.data(), data.size()); -} - -std::string tc::encode::Base64Util::decodeBase64DataAsString(const byte_t* data, size_t size) -{ - return byteDataAsString(decodeBase64DataAsData(data, size)); -} -std::string tc::encode::Base64Util::decodeBase64DataAsString(const tc::ByteData& data) +tc::ByteData tc::encode::Base64Util::decodeBase64AsData(const std::string& str) { - return byteDataAsString(decodeBase64DataAsData(data)); + return decodeBase64AsData(str.c_str(), str.size()); } -tc::ByteData tc::encode::Base64Util::decodeBase64StringAsData(const char* data, size_t size) -{ - return decodeBase64DataAsData((const byte_t*)data, size); -} -tc::ByteData tc::encode::Base64Util::decodeBase64StringAsData(const std::string& data) -{ - return decodeBase64DataAsData((const byte_t*)data.c_str(), data.size()); -} - -std::string tc::encode::Base64Util::decodeBase64StringAsString(const char* data, size_t size) +std::string tc::encode::Base64Util::decodeBase64AsString(const char* str, size_t size) { - return byteDataAsString(decodeBase64DataAsData((const byte_t*)data, size)); + return byteDataAsString(decodeBase64AsData(str, size)); } -std::string tc::encode::Base64Util::decodeBase64StringAsString(const std::string& data) +std::string tc::encode::Base64Util::decodeBase64AsString(const std::string& str) { - return byteDataAsString(decodeBase64DataAsData((const byte_t*)data.c_str(), data.size())); + return byteDataAsString(decodeBase64AsData(str)); } \ No newline at end of file From c64ed642fde38c8a80b2da656d374710b9ee3d21 Mon Sep 17 00:00:00 2001 From: Jack Date: Sun, 29 Sep 2024 18:51:41 +0800 Subject: [PATCH 06/11] Implement unit test --- test/encode_Base64Util_TestClass.cpp | 375 +++++++++++---------------- test/encode_Base64Util_TestClass.h | 30 +-- 2 files changed, 151 insertions(+), 254 deletions(-) diff --git a/test/encode_Base64Util_TestClass.cpp b/test/encode_Base64Util_TestClass.cpp index e5810b83..c51da3d6 100644 --- a/test/encode_Base64Util_TestClass.cpp +++ b/test/encode_Base64Util_TestClass.cpp @@ -12,11 +12,10 @@ encode_Base64Util_TestClass::encode_Base64Util_TestClass() : void encode_Base64Util_TestClass::runAllTests(void) { - testHexStringToBytes(); - testFormatBytesAsString(); - testFormatBytesAsStringWithLineLimit(); - testFormatListWithLineLimit(); - testFormatBytesAsHxdHexString(); + testEncodeDataAsBase64(); + testEncodeStringAsBase64(); + testDecodeBase64AsData(); + testDecodeBase64AsString(); } const std::string& encode_Base64Util_TestClass::getTestTag() const @@ -31,88 +30,56 @@ const std::vector& encode_Base64Util_TestClass::getTestR //--------------------------------------------------------- -void encode_Base64Util_TestClass::testHexStringToBytes() +void encode_Base64Util_TestClass::testEncodeDataAsBase64() { TestResult test; - test.test_name = "testHexStringToBytes"; + test.test_name = "testEncodeDataAsBase64"; test.result = "NOT RUN"; test.comments = ""; - try + try { struct TestCase { std::string test_name; std::string in_string; - tc::ByteData out_data; + tc::ByteData in_data; + std::string out_base64; }; - // create manual tests + // create happy path tests std::vector tests = { - { "empty string", "", tc::ByteData()}, - { "unaligned string" ,"1", tc::ByteData()}, - { "unaligned larger string" ,"123456789", tc::ByteData()}, - { "multi-byte" ,"00112233445566778899aabbccddeeff010203", tc::ByteData({0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, 0x01, 0x02, 0x03})}, + { "empty data", std::string(), tc::ByteData(), std::string()}, + { "single space", " ", tc::cli::FormatUtil::hexStringToBytes("20"), "IA=="}, + { "ascii string", "The quick brown fox jumps over the lazy dog.", tc::cli::FormatUtil::hexStringToBytes("54686520717569636b2062726f776e20666f78206a756d7073206f76657220746865206c617a7920646f672e"), "VGhlIHF1aWNrIGJyb3duIGZveCBqdW1wcyBvdmVyIHRoZSBsYXp5IGRvZy4="}, + { "512 bytes binary data", std::string(), tc::cli::FormatUtil::hexStringToBytes("85d278fb18be42a62a56b495d4ab56a803fad5a1c66153b0c8a8628c3222fe55c500fa9721d5bfebff31ec59dcf810ac4bd0d2939ae045e7fcc34a432b45aca93e2a7a35b8208e75e1477d02bfd2175ae4a2d68dbf1a882249ca3f2b36586db700fb47e954f9233c9a2227e850957bbb1f3da6e9b4693d38e09434a691451b897c8b1fbbd5de4921e1f84fc136dd1b9297ad5e065556cd2ce52ec6eed8133d83bfc4e54c7d715de5e2b7eed9898b9b5eebbd5d07ae22f83d87218f7fe7355b5b6379e4a3eb27d48a9e042aa637063aac9a0b7bc104758bee61321183c0bb1ffb8ceb8c3d0cbbd5a55fe7809df89bf0883d8d7c762673fbfc5fceb6b14b4b1a828da5059cb090a2286ae7efc64b09e033d70cb6a528c54cea3d90f35b8fdfd73a1e85604c827cf5719b488e37b8cda2e2baba5cec43b1d031e7e4a47f3baedb00c037779a6f0e9fab5c9c3c84236458378847acd174083570c628074417ebba551853299eea400dfa95a8aff5f1fd9c314225365023ee31a03930c0029d57feb81417d57f9f45f225faa3790c0b239891c82151a7449507cab70376975ba9f2e68f5e37544f848faf875b9e24dccb9c556aae2a57a7f369581d50dfdf06fdff27fd2107db080c4d9e1c100427a1493ddb5e80e43f943bbbad9113448b658a5a5cdefd70e57d0b28e2bd942a978179eb88d6661b30f2b5b346fff1d0a5c9b93d2d"), "hdJ4+xi+QqYqVrSV1KtWqAP61aHGYVOwyKhijDIi/lXFAPqXIdW/6/8x7Fnc+BCsS9DSk5rgRef8w0pDK0WsqT4qejW4II514Ud9Ar/SF1rkotaNvxqIIknKPys2WG23APtH6VT5IzyaIifoUJV7ux89pum0aT044JQ0ppFFG4l8ix+71d5JIeH4T8E23RuSl61eBlVWzSzlLsbu2BM9g7/E5Ux9cV3l4rfu2YmLm17rvV0HriL4PYchj3/nNVtbY3nko+sn1IqeBCqmNwY6rJoLe8EEdYvuYTIRg8C7H/uM64w9DLvVpV/ngJ34m/CIPY18diZz+/xfzraxS0sago2lBZywkKIoaufvxksJ4DPXDLalKMVM6j2Q81uP39c6HoVgTIJ89XGbSI43uM2i4rq6XOxDsdAx5+Skfzuu2wDAN3eabw6fq1ycPIQjZFg3iEes0XQINXDGKAdEF+u6VRhTKZ7qQA36laiv9fH9nDFCJTZQI+4xoDkwwAKdV/64FBfVf59F8iX6o3kMCyOYkcghUadElQfKtwN2l1up8uaPXjdUT4SPr4dbniTcy5xVaq4qV6fzaVgdUN/fBv3/J/0hB9sIDE2eHBAEJ6FJPdtegOQ/lDu7rZETRItlilpc3v1w5X0LKOK9lCqXgXnriNZmGzDytbNG//HQpcm5PS0="}, }; - // add programatically determined tests - for (size_t i = 0; i < 0xff; i++) + // run error path tests { - std::string all_lower, all_upper, upper_lower, lower_upper; - - size_t upper_num = (i >> 4) & 0xf; - size_t lower_num = (i) & 0xf; + std::string out = tc::encode::Base64Util::encodeDataAsBase64(nullptr, 0xdeadbeef); - if (upper_num <= 9) - { - all_lower += char('0' + upper_num); - all_upper += char('0' + upper_num); - upper_lower += char('0' + upper_num); - lower_upper += char('0' + upper_num); - } - else + if (out != "") { - all_lower += char('a' + upper_num - 10); - all_upper += char('A' + upper_num - 10); - upper_lower += char('A' + upper_num - 10); - lower_upper += char('a' + upper_num - 10); + throw tc::TestException(fmt::format("encodeDataAsBase64() did not return an empty string for invalid input: data=nullptr, size=0xdeadbeef")); } - - if (lower_num <= 9) - { - all_lower += char('0' + lower_num); - all_upper += char('0' + lower_num); - upper_lower += char('0' + lower_num); - lower_upper += char('0' + lower_num); - } - else - { - all_lower += char('a' + lower_num - 10); - all_upper += char('A' + lower_num - 10); - upper_lower += char('a' + lower_num - 10); - lower_upper += char('A' + lower_num - 10); - } - - tests.push_back({"all_lower_" + all_lower, all_lower, tc::ByteData({(byte_t)i})}); - tests.push_back({"all_upper_" + all_lower, all_upper, tc::ByteData({(byte_t)i})}); - tests.push_back({"upper_lower_" + all_lower, upper_lower, tc::ByteData({(byte_t)i})}); - tests.push_back({"lower_upper_" + all_lower, lower_upper, tc::ByteData({(byte_t)i})}); } - // run tests + // run happy path tests for (auto test = tests.begin(); test != tests.end(); test++) { - tc::ByteData out = tc::encode::Base64Util::hexStringToBytes(test->in_string); + std::string out_1 = tc::encode::Base64Util::encodeDataAsBase64(test->in_data); + std::string out_2 = tc::encode::Base64Util::encodeDataAsBase64(test->in_data.data(), test->in_data.size()); - if (out.size() != test->out_data.size()) + if (out_1 != test->out_base64) { - throw tc::TestException(fmt::format("Test({:s}) to convert str({:s}) returned ByteData with wrong size: {:d} (should be: {:d})", test->test_name, test->in_string, out.size(), test->out_data.size())); + throw tc::TestException(fmt::format("Test({:s}) to convert data({:s}) to base64, returned \"{:s}\" (should be: \"{:s}\")", test->test_name, tc::cli::FormatUtil::formatBytesAsString(test->in_data, false, ""), out_1, test->out_base64)); } - if (out.size() != 0 && memcmp(out.data(), test->out_data.data(), out.size()) != 0) + if (out_2 != test->out_base64) { - throw tc::TestException(fmt::format("Test({:s}) to convert str({:s}) returned ByteData with wrong data", test->test_name, test->in_string)); + throw tc::TestException(fmt::format("Test({:s}) to convert data({:s}), size({:d}) to base64, returned \"{:s}\" (should be: \"{:s}\")", test->test_name, tc::cli::FormatUtil::formatBytesAsString(test->in_data.data(), test->in_data.size(), false, ""), test->in_data.size(), out_2, test->out_base64)); } } @@ -137,46 +104,57 @@ void encode_Base64Util_TestClass::testHexStringToBytes() mTestResults.push_back(std::move(test)); } -void encode_Base64Util_TestClass::testFormatBytesAsString() +void encode_Base64Util_TestClass::testEncodeStringAsBase64() { TestResult test; - test.test_name = "testFormatBytesAsString"; + test.test_name = "testEncodeStringAsBase64"; test.result = "NOT RUN"; test.comments = ""; - try + try { - // test recipe struct TestCase { std::string test_name; + std::string in_string; tc::ByteData in_data; - bool in_is_uppercase; - std::string in_delimiter; - std::string out_string; + std::string out_base64; }; - // create tests + // create happy path tests std::vector tests = { - {"empty data", tc::ByteData(), false, "", ""}, - {"8byte lowercase no delim", tc::ByteData({0x00, 0xaa, 0x11, 0xbb, 0x22, 0xcc, 0x33, 0xdd}), false, "", "00aa11bb22cc33dd"}, - {"8byte lowercase ' ' delim", tc::ByteData({0x00, 0xaa, 0x11, 0xbb, 0x22, 0xcc, 0x33, 0xdd}), false, " ", "00 aa 11 bb 22 cc 33 dd"}, - {"8byte lowercase ':' delim", tc::ByteData({0x00, 0xaa, 0x11, 0xbb, 0x22, 0xcc, 0x33, 0xdd}), false, ":", "00:aa:11:bb:22:cc:33:dd"}, - {"8byte uppercase no delim", tc::ByteData({0x00, 0xaa, 0x11, 0xbb, 0x22, 0xcc, 0x33, 0xdd}), true, "", "00AA11BB22CC33DD"}, - {"8byte uppercase ' ' delim", tc::ByteData({0x00, 0xaa, 0x11, 0xbb, 0x22, 0xcc, 0x33, 0xdd}), true, " ", "00 AA 11 BB 22 CC 33 DD"}, - {"8byte uppercase ':' delim", tc::ByteData({0x00, 0xaa, 0x11, 0xbb, 0x22, 0xcc, 0x33, 0xdd}), true, ":", "00:AA:11:BB:22:CC:33:DD"}, + { "empty data", std::string(), tc::ByteData(), std::string()}, + { "single space", " ", tc::cli::FormatUtil::hexStringToBytes("20"), "IA=="}, + { "ascii string", "The quick brown fox jumps over the lazy dog.", tc::cli::FormatUtil::hexStringToBytes("54686520717569636b2062726f776e20666f78206a756d7073206f76657220746865206c617a7920646f672e"), "VGhlIHF1aWNrIGJyb3duIGZveCBqdW1wcyBvdmVyIHRoZSBsYXp5IGRvZy4="}, + // skip binary test as not a string + //{ "512 bytes binary data", std::string(), tc::cli::FormatUtil::hexStringToBytes("85d278fb18be42a62a56b495d4ab56a803fad5a1c66153b0c8a8628c3222fe55c500fa9721d5bfebff31ec59dcf810ac4bd0d2939ae045e7fcc34a432b45aca93e2a7a35b8208e75e1477d02bfd2175ae4a2d68dbf1a882249ca3f2b36586db700fb47e954f9233c9a2227e850957bbb1f3da6e9b4693d38e09434a691451b897c8b1fbbd5de4921e1f84fc136dd1b9297ad5e065556cd2ce52ec6eed8133d83bfc4e54c7d715de5e2b7eed9898b9b5eebbd5d07ae22f83d87218f7fe7355b5b6379e4a3eb27d48a9e042aa637063aac9a0b7bc104758bee61321183c0bb1ffb8ceb8c3d0cbbd5a55fe7809df89bf0883d8d7c762673fbfc5fceb6b14b4b1a828da5059cb090a2286ae7efc64b09e033d70cb6a528c54cea3d90f35b8fdfd73a1e85604c827cf5719b488e37b8cda2e2baba5cec43b1d031e7e4a47f3baedb00c037779a6f0e9fab5c9c3c84236458378847acd174083570c628074417ebba551853299eea400dfa95a8aff5f1fd9c314225365023ee31a03930c0029d57feb81417d57f9f45f225faa3790c0b239891c82151a7449507cab70376975ba9f2e68f5e37544f848faf875b9e24dccb9c556aae2a57a7f369581d50dfdf06fdff27fd2107db080c4d9e1c100427a1493ddb5e80e43f943bbbad9113448b658a5a5cdefd70e57d0b28e2bd942a978179eb88d6661b30f2b5b346fff1d0a5c9b93d2d"), "hdJ4+xi+QqYqVrSV1KtWqAP61aHGYVOwyKhijDIi/lXFAPqXIdW/6/8x7Fnc+BCsS9DSk5rgRef8w0pDK0WsqT4qejW4II514Ud9Ar/SF1rkotaNvxqIIknKPys2WG23APtH6VT5IzyaIifoUJV7ux89pum0aT044JQ0ppFFG4l8ix+71d5JIeH4T8E23RuSl61eBlVWzSzlLsbu2BM9g7/E5Ux9cV3l4rfu2YmLm17rvV0HriL4PYchj3/nNVtbY3nko+sn1IqeBCqmNwY6rJoLe8EEdYvuYTIRg8C7H/uM64w9DLvVpV/ngJ34m/CIPY18diZz+/xfzraxS0sago2lBZywkKIoaufvxksJ4DPXDLalKMVM6j2Q81uP39c6HoVgTIJ89XGbSI43uM2i4rq6XOxDsdAx5+Skfzuu2wDAN3eabw6fq1ycPIQjZFg3iEes0XQINXDGKAdEF+u6VRhTKZ7qQA36laiv9fH9nDFCJTZQI+4xoDkwwAKdV/64FBfVf59F8iX6o3kMCyOYkcghUadElQfKtwN2l1up8uaPXjdUT4SPr4dbniTcy5xVaq4qV6fzaVgdUN/fBv3/J/0hB9sIDE2eHBAEJ6FJPdtegOQ/lDu7rZETRItlilpc3v1w5X0LKOK9lCqXgXnriNZmGzDytbNG//HQpcm5PS0="}, }; + // run error path tests + { + std::string out = tc::encode::Base64Util::encodeStringAsBase64(nullptr, 0xdeadbeef); + + if (out != "") + { + throw tc::TestException(fmt::format("encodeStringAsBase64() did not return an empty string for invalid input: str=nullptr, size=0xdeadbeef")); + } + } - // run tests + // run happy path tests for (auto test = tests.begin(); test != tests.end(); test++) { - std::string res = tc::encode::Base64Util::formatBytesAsString(test->in_data.data(), test->in_data.size(), test->in_is_uppercase, test->in_delimiter); + std::string out_1 = tc::encode::Base64Util::encodeStringAsBase64(test->in_string); + std::string out_2 = tc::encode::Base64Util::encodeStringAsBase64(test->in_string.c_str(), test->in_string.size()); - if (res != test->out_string) + if (out_1 != test->out_base64) { - throw tc::TestException(fmt::format("Test({:s}) Failed to format data correctly. Output: \"{:s}\", Expected: \"{:s}\"", test->test_name, res, test->out_string)); + throw tc::TestException(fmt::format("Test({:s}) to convert str({:s}) to base64, returned \"{:s}\" (should be: \"{:s}\")", test->test_name, test->in_string, out_1, test->out_base64)); + } + + if (out_2 != test->out_base64) + { + throw tc::TestException(fmt::format("Test({:s}) to convert str({:s}), size({:d}) to base64, returned \"{:s}\" (should be: \"{:s}\")", test->test_name, test->in_string.c_str(), test->in_string.size(), test->in_data.size(), out_2, test->out_base64)); } } @@ -201,139 +179,90 @@ void encode_Base64Util_TestClass::testFormatBytesAsString() mTestResults.push_back(std::move(test)); } -void encode_Base64Util_TestClass::testFormatBytesAsStringWithLineLimit() +void encode_Base64Util_TestClass::testDecodeBase64AsData() { TestResult test; - test.test_name = "testFormatBytesAsStringWithLineLimit"; + test.test_name = "testDecodeBase64AsData"; test.result = "NOT RUN"; test.comments = ""; - try + try { - // test recipe struct TestCase { std::string test_name; + std::string in_string; tc::ByteData in_data; - size_t in_row_len; - size_t in_indent_len; - std::string out_string; + std::string out_base64; }; - // create tests + // create happy path tests std::vector tests = { - {"empty data", tc::ByteData(), 0, 0, ""}, - {"size:8 row:8 indent:0", tc::ByteData({0x00, 0xaa, 0x11, 0xbb, 0x22, 0xcc, 0x33, 0xdd}), 8, 0, "00aa11bb22cc33dd\n"}, - {"size:8 row:4 indent:0", tc::ByteData({0x00, 0xaa, 0x11, 0xbb, 0x22, 0xcc, 0x33, 0xdd}), 4, 0, "00aa11bb\n22cc33dd\n"}, - {"size:8 row:3 indent:0", tc::ByteData({0x00, 0xaa, 0x11, 0xbb, 0x22, 0xcc, 0x33, 0xdd}), 3, 0, "00aa11\nbb22cc\n33dd\n"}, - {"size:8 row:3 indent:2", tc::ByteData({0x00, 0xaa, 0x11, 0xbb, 0x22, 0xcc, 0x33, 0xdd}), 3, 2, " 00aa11\n bb22cc\n 33dd\n"}, - {"size:8 row:1 indent:3", tc::ByteData({0x00, 0xaa, 0x11, 0xbb, 0x22, 0xcc, 0x33, 0xdd}), 1, 3, " 00\n aa\n 11\n bb\n 22\n cc\n 33\n dd\n"}, + { "empty data", std::string(), tc::ByteData(), std::string()}, + { "single space", " ", tc::cli::FormatUtil::hexStringToBytes("20"), "IA=="}, + { "ascii string", "The quick brown fox jumps over the lazy dog.", tc::cli::FormatUtil::hexStringToBytes("54686520717569636b2062726f776e20666f78206a756d7073206f76657220746865206c617a7920646f672e"), "VGhlIHF1aWNrIGJyb3duIGZveCBqdW1wcyBvdmVyIHRoZSBsYXp5IGRvZy4="}, + { "512 bytes binary data", std::string(), tc::cli::FormatUtil::hexStringToBytes("85d278fb18be42a62a56b495d4ab56a803fad5a1c66153b0c8a8628c3222fe55c500fa9721d5bfebff31ec59dcf810ac4bd0d2939ae045e7fcc34a432b45aca93e2a7a35b8208e75e1477d02bfd2175ae4a2d68dbf1a882249ca3f2b36586db700fb47e954f9233c9a2227e850957bbb1f3da6e9b4693d38e09434a691451b897c8b1fbbd5de4921e1f84fc136dd1b9297ad5e065556cd2ce52ec6eed8133d83bfc4e54c7d715de5e2b7eed9898b9b5eebbd5d07ae22f83d87218f7fe7355b5b6379e4a3eb27d48a9e042aa637063aac9a0b7bc104758bee61321183c0bb1ffb8ceb8c3d0cbbd5a55fe7809df89bf0883d8d7c762673fbfc5fceb6b14b4b1a828da5059cb090a2286ae7efc64b09e033d70cb6a528c54cea3d90f35b8fdfd73a1e85604c827cf5719b488e37b8cda2e2baba5cec43b1d031e7e4a47f3baedb00c037779a6f0e9fab5c9c3c84236458378847acd174083570c628074417ebba551853299eea400dfa95a8aff5f1fd9c314225365023ee31a03930c0029d57feb81417d57f9f45f225faa3790c0b239891c82151a7449507cab70376975ba9f2e68f5e37544f848faf875b9e24dccb9c556aae2a57a7f369581d50dfdf06fdff27fd2107db080c4d9e1c100427a1493ddb5e80e43f943bbbad9113448b658a5a5cdefd70e57d0b28e2bd942a978179eb88d6661b30f2b5b346fff1d0a5c9b93d2d"), "hdJ4+xi+QqYqVrSV1KtWqAP61aHGYVOwyKhijDIi/lXFAPqXIdW/6/8x7Fnc+BCsS9DSk5rgRef8w0pDK0WsqT4qejW4II514Ud9Ar/SF1rkotaNvxqIIknKPys2WG23APtH6VT5IzyaIifoUJV7ux89pum0aT044JQ0ppFFG4l8ix+71d5JIeH4T8E23RuSl61eBlVWzSzlLsbu2BM9g7/E5Ux9cV3l4rfu2YmLm17rvV0HriL4PYchj3/nNVtbY3nko+sn1IqeBCqmNwY6rJoLe8EEdYvuYTIRg8C7H/uM64w9DLvVpV/ngJ34m/CIPY18diZz+/xfzraxS0sago2lBZywkKIoaufvxksJ4DPXDLalKMVM6j2Q81uP39c6HoVgTIJ89XGbSI43uM2i4rq6XOxDsdAx5+Skfzuu2wDAN3eabw6fq1ycPIQjZFg3iEes0XQINXDGKAdEF+u6VRhTKZ7qQA36laiv9fH9nDFCJTZQI+4xoDkwwAKdV/64FBfVf59F8iX6o3kMCyOYkcghUadElQfKtwN2l1up8uaPXjdUT4SPr4dbniTcy5xVaq4qV6fzaVgdUN/fBv3/J/0hB9sIDE2eHBAEJ6FJPdtegOQ/lDu7rZETRItlilpc3v1w5X0LKOK9lCqXgXnriNZmGzDytbNG//HQpcm5PS0="}, }; - - // run tests - for (auto test = tests.begin(); test != tests.end(); test++) + // run error path tests { - std::string res = tc::encode::Base64Util::formatBytesAsStringWithLineLimit(test->in_data.data(), test->in_data.size(), false, "", test->in_row_len, test->in_indent_len); + tc::ByteData out = tc::encode::Base64Util::decodeBase64AsData(nullptr, 0xdeadbeef); - if (res != test->out_string) + if (out.data() != nullptr || out.size() != 0) { - std::string& expected = test->out_string; - - // replace literal new lines so they can be printed on one line for debugging - for (size_t pos = res.find('\n', 0); pos != std::string::npos; pos = res.find('\n', pos+1)) - { - res.replace(pos, 1, "\\n"); - } - - // replace literal new lines so they can be printed on one line for debugging - for (size_t pos = expected.find('\n', 0); pos != std::string::npos; pos = expected.find('\n', pos+1)) - { - expected.replace(pos, 1, "\\n"); - } - - throw tc::TestException(fmt::format("Test({:s}) Failed to format data correctly. Output: \"{:s}\", Expected: \"{:s}\"", test->test_name, res, expected)); + throw tc::TestException(fmt::format("decodeBase64AsData() did not return an empty tc::ByteData for invalid input: str=nullptr, size=0xdeadbeef")); } } + { + tc::ByteData out = tc::encode::Base64Util::decodeBase64AsData("not base 64"); - // record result - test.result = "PASS"; - test.comments = ""; - } - catch (const tc::TestException& e) - { - // record result - test.result = "FAIL"; - test.comments = e.what(); - } - catch (const std::exception& e) - { - // record result - test.result = "UNHANDLED EXCEPTION"; - test.comments = e.what(); - } + if (out.data() != nullptr || out.size() != 0) + { + throw tc::TestException(fmt::format("decodeBase64AsData() did not return an empty tc::ByteData for invalid input: str=\"not base 64\"")); + } + } - // add result to list - mTestResults.push_back(std::move(test)); -} + // run happy path tests + for (auto test = tests.begin(); test != tests.end(); test++) + { + tc::ByteData out_1 = tc::encode::Base64Util::decodeBase64AsData(test->out_base64); + tc::ByteData out_2 = tc::encode::Base64Util::decodeBase64AsData(test->out_base64.data(), test->out_base64.size()); -void encode_Base64Util_TestClass::testFormatListWithLineLimit() -{ - TestResult test; - test.test_name = "testFormatListWithLineLimit"; - test.result = "NOT RUN"; - test.comments = ""; + // size should match + if (out_1.size() != test->in_data.size()) + { + throw tc::TestException(fmt::format("Test({:s}) to convert str({:s}) from base64, size was \"{:d}\" (should be: \"{:d}\")", test->test_name, test->out_base64, out_1.size(), test->in_data.size())); + } - try - { - // test recipe - struct TestCase - { - std::string test_name; - std::vector in_list; - size_t in_row_len; - size_t in_indent_len; - std::string out_string; - }; + // if expected data was nullptr, so should actual + if (test->in_data.data() == nullptr && out_1.data() != nullptr) + { + throw tc::TestException(fmt::format("Test({:s}) to convert str({:s}) from base64, ret.data() should have been null", test->test_name, test->out_base64)); + } - // create tests - std::vector tests = - { - {"empty", {}, 0, 0, ""}, - {"empty list", {}, 40, 0, ""}, - {"list of 4, row_len 20", {"Astr", "Bstr", "Cstr", "Dstr"}, 20, 0, "Astr, Bstr, Cstr, Dstr\n"}, - {"list of 4, row_len 8", {"Astr", "Bstr", "Cstr", "Dstr"}, 8, 0, "Astr, Bstr, \nCstr, Dstr\n"}, - {"list of 4, row_len 8, indent=2", {"Astr", "Bstr", "Cstr", "Dstr"}, 8, 2, " Astr, Bstr, \n Cstr, Dstr\n"}, - {"list of 4, row_len 4", {"Astr", "Bstr", "Cstr", "Dstr"}, 4, 0, "Astr, \nBstr, \nCstr, \nDstr\n"}, - {"list of 4, row_len 2", {"Astr", "Bstr", "Cstr", "Dstr"}, 2, 0, "Astr, \nBstr, \nCstr, \nDstr\n"}, - {"list of 4, row_len 1", {"Astr", "Bstr", "Cstr", "Dstr"}, 1, 0, "Astr, \nBstr, \nCstr, \nDstr\n"}, - {"list of 4, row_len 0", {"Astr", "Bstr", "Cstr", "Dstr"}, 0, 0, "Astr, \nBstr, \nCstr, \nDstr\n"}, - }; + // if the data was matching and non-zero, they should also match + if (memcmp(out_2.data(), test->in_data.data(), test->in_data.size()) != 0) + { + throw tc::TestException(fmt::format("Test({:s}) to convert str({:s}) from base64, returned \"{:s}\" (should be: \"{:s}\")", test->test_name, test->out_base64, tc::cli::FormatUtil::formatBytesAsString(out_2, false, ""), tc::cli::FormatUtil::formatBytesAsString(test->in_data, false, ""))); + } + // size should match + if (out_2.size() != test->in_data.size()) + { + throw tc::TestException(fmt::format("Test({:s}) to convert str({:s}), size({:d}) from base64, size was \"{:d}\" (should be: \"{:d}\")", test->test_name, test->out_base64.c_str(), test->out_base64.size(), out_2.size(), test->in_data.size())); + } - // run tests - for (auto test = tests.begin(); test != tests.end(); test++) - { - std::string res = tc::encode::Base64Util::formatListWithLineLimit(test->in_list, test->in_row_len, test->in_indent_len); + // if expected data was nullptr, so should actual + if (test->in_data.data() == nullptr && out_2.data() != nullptr) + { + throw tc::TestException(fmt::format("Test({:s}) to convert str({:s}), size({:d}) from base64, ret.data() should have been null", test->test_name, test->out_base64.c_str(), test->out_base64.size())); + } - if (res != test->out_string) + // if the data was matching and non-zero, they should also match + if (memcmp(out_2.data(), test->in_data.data(), test->in_data.size()) != 0) { - std::string& expected = test->out_string; - - // replace literal new lines so they can be printed on one line for debugging - for (size_t pos = res.find('\n', 0); pos != std::string::npos; pos = res.find('\n', pos+1)) - { - res.replace(pos, 1, "\\n"); - } - - // replace literal new lines so they can be printed on one line for debugging - for (size_t pos = expected.find('\n', 0); pos != std::string::npos; pos = expected.find('\n', pos+1)) - { - expected.replace(pos, 1, "\\n"); - } - - throw tc::TestException(fmt::format("Test({:s}) Failed to format data correctly. Output: \"{:s}\", Expected: \"{:s}\"", test->test_name, res, expected)); + throw tc::TestException(fmt::format("Test({:s}) to convert str({:s}), size({:d}) from base64, returned \"{:s}\" (should be: \"{:s}\")", test->test_name, test->out_base64.c_str(), test->out_base64.size(), tc::cli::FormatUtil::formatBytesAsString(out_1, false, ""), tc::cli::FormatUtil::formatBytesAsString(test->in_data, false, ""))); } } @@ -358,75 +287,65 @@ void encode_Base64Util_TestClass::testFormatListWithLineLimit() mTestResults.push_back(std::move(test)); } -void encode_Base64Util_TestClass::testFormatBytesAsHxdHexString() +void encode_Base64Util_TestClass::testDecodeBase64AsString() { TestResult test; - test.test_name = "testFormatBytesAsHxdHexString"; + test.test_name = "testDecodeBase64AsString"; test.result = "NOT RUN"; test.comments = ""; - try + try { - // test recipe struct TestCase { std::string test_name; + std::string in_string; tc::ByteData in_data; - size_t in_bytes_per_row; - size_t in_byte_group_size; - std::string out_string; + std::string out_base64; }; - // create tests + // create happy path tests std::vector tests = { - {"empty all", {}, 0, 0, ""}, - {"empty data", {}, 16, 1, ""}, - {"empty bytes_per_row", {0x00, 0x01}, 0, 1, ""}, - {"empty byte_group_size", {0x00, 0x01}, 16, 0, ""}, - {"little data", {0x00, 0x01}, 16, 1, "00000000 | 00 01 .. \n"}, - {"full row data", {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f}, 16, 1, "00000000 | 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F ................\n"}, - {"2 row ascii", {0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4e, 0x4f, 0x50, 0x51, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66}, 16, 1, "00000000 | 41 42 43 44 45 46 47 48 49 4A 4B 4C 4E 4F 50 51 ABCDEFGHIJKLNOPQ\n00000010 | 51 52 53 54 55 56 57 58 59 5A 61 62 63 64 65 66 QRSTUVWXYZabcdef\n"}, - {"full row data, byte_group_size=2", {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f}, 16, 2, "00000000 | 0001 0203 0405 0607 0809 0A0B 0C0D 0E0F ................\n"}, - {"full row data, byte_group_size=3", {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f}, 16, 3, "00000000 | 000102 030405 060708 090A0B 0C0D0E 0F ................\n"}, - {"full row data, byte_group_size=4", {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f}, 16, 4, "00000000 | 00010203 04050607 08090A0B 0C0D0E0F ................\n"}, - {"full row data, byte_group_size=5", {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f}, 16, 5, "00000000 | 0001020304 0506070809 0A0B0C0D0E 0F ................\n"}, - {"full row data, byte_group_size=6", {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f}, 16, 6, "00000000 | 000102030405 060708090A0B 0C0D0E0F ................\n"}, - {"full row data, byte_group_size=7", {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f}, 16, 7, "00000000 | 00010203040506 0708090A0B0C0D 0E0F ................\n"}, - {"full row data, byte_group_size=8", {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f}, 16, 8, "00000000 | 0001020304050607 08090A0B0C0D0E0F ................\n"}, - {"full row data, byte_group_size=9", {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f}, 16, 9, "00000000 | 000102030405060708 090A0B0C0D0E0F ................\n"}, - {"full row data, byte_group_size=10", {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f}, 16, 10, "00000000 | 00010203040506070809 0A0B0C0D0E0F ................\n"}, - {"full row data, byte_group_size=11", {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f}, 16, 11, "00000000 | 000102030405060708090A 0B0C0D0E0F ................\n"}, - {"full row data, byte_group_size=12", {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f}, 16, 12, "00000000 | 000102030405060708090A0B 0C0D0E0F ................\n"}, - {"full row data, byte_group_size=13", {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f}, 16, 13, "00000000 | 000102030405060708090A0B0C 0D0E0F ................\n"}, - {"full row data, byte_group_size=14", {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f}, 16, 14, "00000000 | 000102030405060708090A0B0C0D 0E0F ................\n"}, - {"full row data, byte_group_size=15", {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f}, 16, 15, "00000000 | 000102030405060708090A0B0C0D0E 0F ................\n"}, - {"full row data, byte_group_size=16", {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f}, 16, 16, "00000000 | 000102030405060708090A0B0C0D0E0F ................\n"}, + { "empty data", std::string(), tc::ByteData(), std::string()}, + { "single space", " ", tc::cli::FormatUtil::hexStringToBytes("20"), "IA=="}, + { "ascii string", "The quick brown fox jumps over the lazy dog.", tc::cli::FormatUtil::hexStringToBytes("54686520717569636b2062726f776e20666f78206a756d7073206f76657220746865206c617a7920646f672e"), "VGhlIHF1aWNrIGJyb3duIGZveCBqdW1wcyBvdmVyIHRoZSBsYXp5IGRvZy4="}, + // skip binary test as not a string + //{ "512 bytes binary data", std::string(), tc::cli::FormatUtil::hexStringToBytes("85d278fb18be42a62a56b495d4ab56a803fad5a1c66153b0c8a8628c3222fe55c500fa9721d5bfebff31ec59dcf810ac4bd0d2939ae045e7fcc34a432b45aca93e2a7a35b8208e75e1477d02bfd2175ae4a2d68dbf1a882249ca3f2b36586db700fb47e954f9233c9a2227e850957bbb1f3da6e9b4693d38e09434a691451b897c8b1fbbd5de4921e1f84fc136dd1b9297ad5e065556cd2ce52ec6eed8133d83bfc4e54c7d715de5e2b7eed9898b9b5eebbd5d07ae22f83d87218f7fe7355b5b6379e4a3eb27d48a9e042aa637063aac9a0b7bc104758bee61321183c0bb1ffb8ceb8c3d0cbbd5a55fe7809df89bf0883d8d7c762673fbfc5fceb6b14b4b1a828da5059cb090a2286ae7efc64b09e033d70cb6a528c54cea3d90f35b8fdfd73a1e85604c827cf5719b488e37b8cda2e2baba5cec43b1d031e7e4a47f3baedb00c037779a6f0e9fab5c9c3c84236458378847acd174083570c628074417ebba551853299eea400dfa95a8aff5f1fd9c314225365023ee31a03930c0029d57feb81417d57f9f45f225faa3790c0b239891c82151a7449507cab70376975ba9f2e68f5e37544f848faf875b9e24dccb9c556aae2a57a7f369581d50dfdf06fdff27fd2107db080c4d9e1c100427a1493ddb5e80e43f943bbbad9113448b658a5a5cdefd70e57d0b28e2bd942a978179eb88d6661b30f2b5b346fff1d0a5c9b93d2d"), "hdJ4+xi+QqYqVrSV1KtWqAP61aHGYVOwyKhijDIi/lXFAPqXIdW/6/8x7Fnc+BCsS9DSk5rgRef8w0pDK0WsqT4qejW4II514Ud9Ar/SF1rkotaNvxqIIknKPys2WG23APtH6VT5IzyaIifoUJV7ux89pum0aT044JQ0ppFFG4l8ix+71d5JIeH4T8E23RuSl61eBlVWzSzlLsbu2BM9g7/E5Ux9cV3l4rfu2YmLm17rvV0HriL4PYchj3/nNVtbY3nko+sn1IqeBCqmNwY6rJoLe8EEdYvuYTIRg8C7H/uM64w9DLvVpV/ngJ34m/CIPY18diZz+/xfzraxS0sago2lBZywkKIoaufvxksJ4DPXDLalKMVM6j2Q81uP39c6HoVgTIJ89XGbSI43uM2i4rq6XOxDsdAx5+Skfzuu2wDAN3eabw6fq1ycPIQjZFg3iEes0XQINXDGKAdEF+u6VRhTKZ7qQA36laiv9fH9nDFCJTZQI+4xoDkwwAKdV/64FBfVf59F8iX6o3kMCyOYkcghUadElQfKtwN2l1up8uaPXjdUT4SPr4dbniTcy5xVaq4qV6fzaVgdUN/fBv3/J/0hB9sIDE2eHBAEJ6FJPdtegOQ/lDu7rZETRItlilpc3v1w5X0LKOK9lCqXgXnriNZmGzDytbNG//HQpcm5PS0="}, }; + // run error path tests + { + std::string out = tc::encode::Base64Util::decodeBase64AsString(nullptr, 0xdeadbeef); - // run tests + if (out != "") + { + throw tc::TestException(fmt::format("decodeBase64AsString() did not return an empty string for invalid input: str=nullptr, size=0xdeadbeef")); + } + } + { + std::string out = tc::encode::Base64Util::decodeBase64AsString("not base 64"); + + if (out != "") + { + throw tc::TestException(fmt::format("decodeBase64AsString() did not return an empty string for invalid input: str=\"not base 64\"")); + } + } + + // run happy path tests for (auto test = tests.begin(); test != tests.end(); test++) { - std::string res = tc::encode::Base64Util::formatBytesAsHxdHexString(test->in_data.data(), test->in_data.size(), test->in_bytes_per_row, test->in_byte_group_size); + std::string out_1 = tc::encode::Base64Util::decodeBase64AsString(test->out_base64); + std::string out_2 = tc::encode::Base64Util::decodeBase64AsString(test->out_base64.data(), test->out_base64.size()); + + if (out_1 != test->in_string) + { + throw tc::TestException(fmt::format("Test({:s}) to convert str({:s}) from base64, returned \"{:s}\" (should be: \"{:s}\")", test->test_name, test->out_base64, out_1, test->in_string)); + } - if (res != test->out_string) + if (out_2 != test->in_string) { - std::string& expected = test->out_string; - - // replace literal new lines so they can be printed on one line for debugging - for (size_t pos = res.find('\n', 0); pos != std::string::npos; pos = res.find('\n', pos+1)) - { - res.replace(pos, 1, "\\n"); - } - - // replace literal new lines so they can be printed on one line for debugging - for (size_t pos = expected.find('\n', 0); pos != std::string::npos; pos = expected.find('\n', pos+1)) - { - expected.replace(pos, 1, "\\n"); - } - - throw tc::TestException(fmt::format("Test({:s}) Failed to format data correctly. Output: \"{:s}\", Expected: \"{:s}\"", test->test_name, res, expected)); + throw tc::TestException(fmt::format("Test({:s}) to convert str({:s}), size({:d}) from base64, returned \"{:s}\" (should be: \"{:s}\")", test->test_name, test->out_base64.c_str(), test->out_base64.size(), test->in_data.size(), out_2, test->in_string)); } } diff --git a/test/encode_Base64Util_TestClass.h b/test/encode_Base64Util_TestClass.h index dbe14dfc..bea9f60d 100644 --- a/test/encode_Base64Util_TestClass.h +++ b/test/encode_Base64Util_TestClass.h @@ -19,30 +19,8 @@ class encode_Base64Util_TestClass : public ITestClass std::string mTestTag; std::vector mTestResults; - /* - static tc::ByteData encodeDataAsBase64Data(const byte_t* data, size_t size); - - static std::string encodeDataAsBase64String(const byte_t* data, size_t size); - - static tc::ByteData encodeStringAsBase64Data(const char* data, size_t size); - - static std::string encodeStringAsBase64String(const char* data, size_t size); - - static tc::ByteData decodeBase64DataAsData(const byte_t* data, size_t size); - - static std::string decodeBase64DataAsString(const byte_t* data, size_t size); - - static tc::ByteData decodeBase64StringAsData(const char* data, size_t size); - - static std::string decodeBase64StringAsString(const char* data, size_t size); - */ - - void testEncodeDataAsBase64Data(); - void testEncodeDataAsBase64String(); - void testEncodeStringAsBase64Data(); - void testEncodeStringAsBase64String(); - void testDecodeBase64DataAsData(); - void testDecodeBase64DataAsString(); - void testDecodeBase64StringAsData(); - void testDecodeBase64StringAsString(); + void testEncodeDataAsBase64(); + void testEncodeStringAsBase64(); + void testDecodeBase64AsData(); + void testDecodeBase64AsString(); }; \ No newline at end of file From b48faf5d5037eb3da2fd3889688743ae639f0eb8 Mon Sep 17 00:00:00 2001 From: Jack Date: Sun, 29 Sep 2024 19:16:05 +0800 Subject: [PATCH 07/11] Base64Util::decodeBase64AsString() only supports ASCII strings. --- src/encode/Base64Util.cpp | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/src/encode/Base64Util.cpp b/src/encode/Base64Util.cpp index 7e3418ed..305e97f4 100644 --- a/src/encode/Base64Util.cpp +++ b/src/encode/Base64Util.cpp @@ -6,12 +6,24 @@ inline std::string byteDataAsString(const tc::ByteData& data) { - if (data.size() == 0) - return std::string(); + std::string str = ""; + + if (data.size() > 0) + { + str = std::string((const char*)data.data()); + } - return std::string((char*)data.data()); -} + for (size_t i = 0; i < str.size(); i++) + { + if ( ! std::isprint(static_cast(str[i])) ) + { + str = ""; + break; + } + } + return str; +} std::string tc::encode::Base64Util::encodeDataAsBase64(const byte_t* data, size_t size) { From 6d1b26800f746336b9334373462a1dab32eb5a47 Mon Sep 17 00:00:00 2001 From: Jack Date: Sun, 29 Sep 2024 19:16:21 +0800 Subject: [PATCH 08/11] Add Base64Util documentation --- include/tc/encode/Base64Util.h | 94 +++++++++++++++++++++++++++++++--- 1 file changed, 88 insertions(+), 6 deletions(-) diff --git a/include/tc/encode/Base64Util.h b/include/tc/encode/Base64Util.h index 31069ecb..31f02079 100644 --- a/include/tc/encode/Base64Util.h +++ b/include/tc/encode/Base64Util.h @@ -18,17 +18,99 @@ namespace tc { namespace encode { class Base64Util { public: + /** + * @brief Encode byte array as base64 string. + * + * @param[in] data Byte array to encode. + * @param[in] size Size of byte array @p data. + * + * @return Converted byte array as base64 string. + * + * @post String returned will be empty if the byte array cannot be encoded. + **/ static std::string encodeDataAsBase64(const byte_t* data, size_t size); + + /** + * @brief Encode byte array as base64 string. + * + * @param[in] data tc::ByteData containing byte array to encode. + * + * @return Converted byte array as base64 string. + * + * @post String returned will be empty if the byte array cannot be encoded. + **/ static std::string encodeDataAsBase64(const tc::ByteData& data); - static std::string encodeStringAsBase64(const char* data, size_t size); - static std::string encodeStringAsBase64(const std::string& data); + /** + * @brief Encode string as base64 string. + * + * @param[in] str String to encode. + * @param[in] size Size in bytes of string @p str. + * + * @return Converted string as base64 string. + * + * @post String returned will be empty if the string cannot be encoded. + **/ + static std::string encodeStringAsBase64(const char* str, size_t size); + + /** + * @brief Encode string as base64 string. + * + * @param[in] str String to encode. + * + * @return Converted string as base64 string. + * + * @post String returned will be empty if the string cannot be encoded. + **/ + static std::string encodeStringAsBase64(const std::string& str); - static tc::ByteData decodeBase64AsData(const char* data, size_t size); - static tc::ByteData decodeBase64AsData(const std::string& data); + /** + * @brief Decode base64 string to bytes. + * + * @param[in] str Base64 string to decode. + * @param[in] size Size of string @p str. + * + * @return Converted base64 string as bytes. + * + * @post String returned will be empty if the base64 string cannot be decoded. + **/ + static tc::ByteData decodeBase64AsData(const char* str, size_t size); + + /** + * @brief Decode base64 string to bytes. + * + * @param[in] str Base64 string to decode. + * + * @return Converted base64 string as bytes. + * + * @post String returned will be empty if the base64 string cannot be decoded. + **/ + static tc::ByteData decodeBase64AsData(const std::string& str); - static std::string decodeBase64AsString(const char* data, size_t size); - static std::string decodeBase64AsString(const std::string& data); + /** + * @brief Decode base64 string to ASCII string. + * @note this is provided for completeness, and only supports ASCII strings + * + * @param[in] str Base64 string to decode. + * @param[in] size Size of string @p str. + * + * @return Converted base64 string as ASCII string. + * + * @post String returned will be empty if the base64 string cannot be decoded. + **/ + static std::string decodeBase64AsString(const char* str, size_t size); + + /** + * @brief Decode base64 string to ASCII string. + * @note this is provided for completeness, and only supports ASCII strings + * + * @param[in] str Base64 string to decode. + * + * @return Converted base64 string as ASCII string. + * + * @post String returned will be empty if the base64 string cannot be decoded. + **/ + static std::string decodeBase64AsString(const std::string& str); }; }} // namespace tc::encode From b26b0f10ad0608189af5b0c0bd0b0d816e075fcb Mon Sep 17 00:00:00 2001 From: Jack Date: Sun, 29 Sep 2024 19:17:36 +0800 Subject: [PATCH 09/11] Remove one layer of calls to decodeBase64AsString() --- src/encode/Base64Util.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/encode/Base64Util.cpp b/src/encode/Base64Util.cpp index 305e97f4..19842190 100644 --- a/src/encode/Base64Util.cpp +++ b/src/encode/Base64Util.cpp @@ -44,6 +44,7 @@ std::string tc::encode::Base64Util::encodeDataAsBase64(const byte_t* data, size_ return byteDataAsString(enc_data); } + std::string tc::encode::Base64Util::encodeDataAsBase64(const tc::ByteData& data) { return encodeDataAsBase64(data.data(), data.size()); @@ -53,6 +54,7 @@ std::string tc::encode::Base64Util::encodeStringAsBase64(const char* str, size_t { return encodeDataAsBase64((const byte_t*)str, size); } + std::string tc::encode::Base64Util::encodeStringAsBase64(const std::string& str) { return encodeDataAsBase64((const byte_t*)str.c_str(), str.size()); @@ -77,6 +79,7 @@ tc::ByteData tc::encode::Base64Util::decodeBase64AsData(const char* str, size_t return dec_data; } + tc::ByteData tc::encode::Base64Util::decodeBase64AsData(const std::string& str) { return decodeBase64AsData(str.c_str(), str.size()); @@ -86,7 +89,8 @@ std::string tc::encode::Base64Util::decodeBase64AsString(const char* str, size_t { return byteDataAsString(decodeBase64AsData(str, size)); } + std::string tc::encode::Base64Util::decodeBase64AsString(const std::string& str) { - return byteDataAsString(decodeBase64AsData(str)); + return byteDataAsString(decodeBase64AsData(str.data(), str.size())); } \ No newline at end of file From 91ff06b68d6e4b7b28812274726f5e215cdf08ea Mon Sep 17 00:00:00 2001 From: Jack Date: Sun, 13 Oct 2024 16:20:06 +0900 Subject: [PATCH 10/11] Bump version number to v0.8.0 --- Doxyfile | 2 +- README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Doxyfile b/Doxyfile index e00f81f2..266509b8 100644 --- a/Doxyfile +++ b/Doxyfile @@ -38,7 +38,7 @@ PROJECT_NAME = libtoolchain # could be handy for archiving the generated documentation or if some version # control system is used. -PROJECT_NUMBER = v0.7.0 +PROJECT_NUMBER = v0.8.0 # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer a diff --git a/README.md b/README.md index 19ef4b86..194b0b04 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ ![Platform](https://img.shields.io/badge/platform-linux:%20x86__64,%20i386%20%7C%20windows:%20x86__64,%20i386%20%7C%20macOS:%20x86__64,%20arm64-lightgrey.svg) -![Version](https://img.shields.io/badge/version-0.7.0%20%7C%20prerelease-green.svg) +![Version](https://img.shields.io/badge/version-0.8.0%20%7C%20prerelease-green.svg) Library to ease the development of toolchain applications. From 4c6c501edd0092ceed3ec2510e6b70996b00e38f Mon Sep 17 00:00:00 2001 From: Jack Date: Mon, 14 Oct 2024 10:01:14 +0900 Subject: [PATCH 11/11] Update version in docs URL. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 194b0b04..219eb5f3 100644 --- a/README.md +++ b/README.md @@ -41,7 +41,7 @@ For GNU/unix systems `make` can be used. For native Windows, Visual Studio proje See more [here](./BUILDING.md). # Documentation -API Documentation is generated using Doxygen, and located at docs/index.html. Alternatively documentation for the current stable version is available at https://jakcron.github.io/libtoolchain-docs/v0.7. +API Documentation is generated using Doxygen, and located at docs/index.html. Alternatively documentation for the current stable version is available at https://jakcron.github.io/libtoolchain-docs/v0.8. # License This source code is made available under the [MIT license](./LICENSE).