diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..f0e9acc --- /dev/null +++ b/Makefile @@ -0,0 +1,48 @@ +WARNINGS= \ + -Werror \ + -Wall \ + -Wextra \ + -pedantic \ + -Wcast-align \ + -Wcast-qual \ + -Wctor-dtor-privacy \ + -Wdisabled-optimization \ + -Wformat=2 \ + -Winit-self \ + -Wlogical-op \ + -Wmissing-include-dirs \ + -Wnoexcept \ + -Wold-style-cast \ + -Woverloaded-virtual \ + -Wredundant-decls \ + -Wshadow \ + -Wsign-promo \ + -Wstrict-null-sentinel \ + -Wstrict-overflow=5 \ + -Wundef \ + -Wno-unused \ + -Wno-variadic-macros \ + -Wno-parentheses \ + -fdiagnostics-show-option + +test: base64-test-11 base64-test-17 + base64-test-11 + base64-test-17 + +base64-test-11: base64-11.o test-11.o + g++ base64-11.o test-11.o -o $@ + +base64-test-17: base64-17.o test-17.o + g++ base64-17.o test-17.o -o $@ + +base64-11.o: base64.cpp base64.h + g++ -std=c++11 $(WARNINGS) -c base64.cpp -o base64-11.o + +base64-17.o: base64.cpp base64.h + g++ -std=c++17 $(WARNINGS) -c base64.cpp -o base64-17.o + +test-11.o: test.cpp + g++ -std=c++11 $(WARNINGS) -c test.cpp -o test-11.o + +test-17.o: test.cpp + g++ -std=c++17 $(WARNINGS) -c test.cpp -o test-17.o diff --git a/base64.cpp b/base64.cpp index dac3393..34f6e76 100644 --- a/base64.cpp +++ b/base64.cpp @@ -5,7 +5,7 @@ More information at https://renenyffenegger.ch/notes/development/Base64/Encoding-and-decoding-base-64-with-cpp - Version: 2.rc.01 (release candidate) + Version: 2.rc.02 (release candidate) Copyright (C) 2004-2017, 2020 René Nyffenegger @@ -72,6 +72,26 @@ static std::string insert_linebreaks(std::string str, size_t distance) { return str; } +template +static std::string encode_with_line_breaks(String s) { + return insert_linebreaks(base64_encode(s, false), line_length); +} + +template +static std::string encode_pem(String s) { + return encode_with_line_breaks(s); +} + +template +static std::string encode_mime(String s) { + return encode_with_line_breaks(s); +} + +template +static std::string encode(String s, bool url) { + return base64_encode(reinterpret_cast(s.data()), s.length(), url); +} + std::string base64_encode(unsigned char const* bytes_to_encode, unsigned int in_len, bool url) { // // Replace question marks in base64_chars: @@ -123,8 +143,12 @@ std::string base64_encode(unsigned char const* bytes_to_encode, unsigned int in_ return ret; } - -std::string base64_decode(std::string const& encoded_string, bool remove_linebreaks) { +template +static std::string decode(String encoded_string, bool remove_linebreaks) { + // + // decode(…) is templated so that it can be used with String = const std::string& + // or std::string_view (requires at least C++17) + // if (remove_linebreaks) { @@ -181,14 +205,43 @@ std::string base64_decode(std::string const& encoded_string, bool remove_linebre return ret; } +std::string base64_decode(std::string const& s, bool remove_linebreaks) { + return decode(s, remove_linebreaks); +} + std::string base64_encode(std::string const& s, bool url) { - return base64_encode(reinterpret_cast(s.c_str()), s.length(), url); + return encode(s, url); } std::string base64_encode_pem (std::string const& s) { - return insert_linebreaks(base64_encode(s, false), 64); + return encode_pem(s); } std::string base64_encode_mime(std::string const& s) { - return insert_linebreaks(base64_encode(s, false), 76); + return encode_mime(s); +} + +#if __cplusplus >= 201703L +// +// Interface with std::string_view rather than const std::string& +// Requires C++17 +// Provided by Yannic Bonenberger (https://github.com/Yannic) +// + +std::string base64_encode(std::string_view s, bool url) { + return encode(s, url); +} + +std::string base64_encode_pem(std::string_view s) { + return encode_pem(s); } + +std::string base64_encode_mime(std::string_view s) { + return encode_mime(s); +} + +std::string base64_decode(std::string_view s, bool remove_linebreaks) { + return decode(s, remove_linebreaks); +} + +#endif // __cplusplus >= 201703L diff --git a/base64.h b/base64.h index 1197e48..1d5ca1a 100644 --- a/base64.h +++ b/base64.h @@ -1,6 +1,6 @@ // // base64 encoding and decoding with C++. -// Version: 2.rc.01 (release candidate) +// Version: 2.rc.02 (release candidate) // #ifndef BASE64_H_C0CE2A47_D10E_42C9_A27C_C883944E704A @@ -8,6 +8,10 @@ #include +#if __cplusplus >= 201703L +#include +#endif // __cplusplus >= 201703L + std::string base64_encode (std::string const& s, bool url = false); std::string base64_encode_pem (std::string const& s); std::string base64_encode_mime(std::string const& s); @@ -15,4 +19,17 @@ std::string base64_encode_mime(std::string const& s); std::string base64_decode(std::string const& s, bool remove_linebreaks = false); std::string base64_encode(unsigned char const*, unsigned int len, bool url = false); +#if __cplusplus >= 201703L +// +// Interface with std::string_view rather than const std::string& +// Requires C++17 +// Provided by Yannic Bonenberger (https://github.com/Yannic) +// +std::string base64_encode (std::string_view s, bool url = false); +std::string base64_encode_pem (std::string_view s); +std::string base64_encode_mime(std::string_view s); + +std::string base64_decode(std::string_view s, bool remove_linebreaks = false); +#endif // __cplusplus >= 201703L + #endif /* BASE64_H_C0CE2A47_D10E_42C9_A27C_C883944E704A */ diff --git a/test.cpp b/test.cpp index 36c3008..db0cc05 100644 --- a/test.cpp +++ b/test.cpp @@ -5,7 +5,7 @@ int main() { bool all_tests_passed = true; - const std::string orig = + const std::string orig = "René Nyffenegger\n" "http://www.renenyffenegger.ch\n" "passion for data\n"; @@ -102,6 +102,30 @@ int main() { all_tests_passed = false; } + + // -------------------------------------------------------------- + +#if __cplusplus >= 201703L + // + // Test the string_view interface (which required C++17) + // + std::string_view sv_orig = "foobarbaz"; + std::string_view sv_encoded = base64_encode(sv_orig); + + if (sv_encoded != "Zm9vYmFyYmF6") { + std::cout << "Failed to encode with string_view" << std::endl; + all_tests_passed = false; + } + + std::string_view sv_decoded = base64_decode(sv_encoded); + + if (sv_decoded != sv_orig) { + std::cout << "Failed to decode with string_view" << std::endl; + all_tests_passed = false; + } + +#endif + if (all_tests_passed) return 0; return 1; }