소스 검색

Merge branch 'base64' of public/ls-standard-library into development

Lynar Studios - Public Repositories 2 년 전
부모
커밋
c4e5952fa7

+ 43 - 0
CMakeLists.txt

@@ -7,6 +7,7 @@ project(ls_std VERSION 2022.1.0)
 
 set(MODULE_NAME_BOXING ls_std_boxing)
 set(MODULE_NAME_CORE ls_std_core)
+set(MODULE_NAME_ENCODING ls_std_encoding)
 set(MODULE_NAME_EVENT ls_std_event)
 set(MODULE_NAME_IO ls_std_io)
 set(MODULE_NAME_LOGIC ls_std_logic)
@@ -70,6 +71,9 @@ set(SOURCE_FILES_CORE
         ${CMAKE_CURRENT_SOURCE_DIR}/source/ls_std/core/Class.cpp
         ${CMAKE_CURRENT_SOURCE_DIR}/source/ls_std/core/Version.cpp)
 
+set(SOURCE_FILES_ENCODING
+        ${CMAKE_CURRENT_SOURCE_DIR}/source/ls_std/encoding/Base64.cpp)
+
 set(SOURCE_FILES_EVENT
         ${CMAKE_CURRENT_SOURCE_DIR}/source/ls_std/event/serialization/SerializableJsonEvent.cpp
         ${CMAKE_CURRENT_SOURCE_DIR}/source/ls_std/event/Event.cpp
@@ -132,6 +136,9 @@ if (${LS_STD_BUILD_WITH_TESTS})
             ${CMAKE_CURRENT_SOURCE_DIR}/test/cases/core/LibraryVersionTest.cpp
             ${CMAKE_CURRENT_SOURCE_DIR}/test/cases/core/VersionTest.cpp)
 
+    set(TEST_FILES_ENCODING
+            ${CMAKE_CURRENT_SOURCE_DIR}/test/cases/encoding/Base64Test.cpp)
+
     set(TEST_FILES_EVENT
             ${CMAKE_CURRENT_SOURCE_DIR}/test/cases/event/serialization/SerializableJsonEventTest.cpp
             ${CMAKE_CURRENT_SOURCE_DIR}/test/cases/event/EventHandlerTest.cpp
@@ -204,6 +211,15 @@ if (${LS_STD_BUILD_WITH_TESTS})
     add_executable(${MODULE_NAME_CORE}_test ${TEST_FILES_CORE})
 endif ()
 
+##########################################################
+# Build Tests (encoding)
+##########################################################
+
+if (${LS_STD_BUILD_WITH_TESTS})
+    message("${MODULE_NAME_ENCODING}: Building tests...")
+    add_executable(${MODULE_NAME_ENCODING}_test ${TEST_FILES_ENCODING})
+endif ()
+
 ##########################################################
 # Build Tests (event)
 ##########################################################
@@ -249,6 +265,7 @@ if (${LS_STD_BUILD_WITH_TESTS})
     add_executable(${PROJECT_NAME}_test
             ${TEST_FILES_BOXING}
             ${TEST_FILES_CORE}
+            ${TEST_FILES_ENCODING}
             ${TEST_FILES_EVENT}
             ${TEST_FILES_IO}
             ${TEST_FILES_LOGIC}
@@ -288,6 +305,17 @@ if (${LS_STD_BUILD_STATIC})
     set_target_properties("${MODULE_NAME_CORE}" PROPERTIES DEBUG_POSTFIX "_d")
 endif ()
 
+##########################################################
+# Build Library (encoding)
+##########################################################
+
+message("${PROJECT_NAME}: Building ${MODULE_NAME_ENCODING} library version ${PROJECT_VERSION}...")
+
+if (${LS_STD_BUILD_STATIC})
+    add_library("${MODULE_NAME_ENCODING}" STATIC ${SOURCE_FILES_ENCODING})
+    set_target_properties("${MODULE_NAME_ENCODING}" PROPERTIES DEBUG_POSTFIX "_d")
+endif ()
+
 ##########################################################
 # Build Library (event)
 ##########################################################
@@ -359,6 +387,20 @@ if (${LS_STD_BUILD_WITH_TESTS})
             "${MODULE_NAME_CORE}")
 endif ()
 
+##########################################################
+# Linking (encoding)
+##########################################################
+
+if (${LS_STD_BUILD_WITH_TESTS})
+    message("${MODULE_NAME_ENCODING}: Linking libraries for test application...")
+    target_link_libraries(${MODULE_NAME_ENCODING}_test
+            gtest
+            gmock
+            gtest_main
+            "${MODULE_NAME_ENCODING}"
+            "${MODULE_NAME_CORE}")
+endif ()
+
 ##########################################################
 # Linking (event)
 ##########################################################
@@ -427,6 +469,7 @@ if (${LS_STD_BUILD_WITH_TESTS})
             gtest_main
             "${MODULE_NAME_CORE}"
             "${MODULE_NAME_BOXING}"
+            "${MODULE_NAME_ENCODING}"
             "${MODULE_NAME_EVENT}"
             "${MODULE_NAME_IO}"
             "${MODULE_NAME_LOGIC}"

+ 35 - 0
include/ls_std/core/interface/IEncoding.hpp

@@ -0,0 +1,35 @@
+/*
+ * Author:          Patrick-Christopher Mattulat
+ * Company:         Lynar Studios
+ * E-Mail:          webmaster@lynarstudios.com
+ * Created:         2022-01-03
+ * Changed:         2022-05-19
+ *
+ * */
+
+#ifndef LS_STD_I_ENCODING_HPP
+#define LS_STD_I_ENCODING_HPP
+
+#include <string>
+
+namespace ls
+{
+  namespace std
+  {
+    namespace core
+    {
+      namespace interface_type
+      {
+        class IEncoding
+        {
+          public:
+
+            virtual ::std::string encode(const ::std::string &_sequence) = 0;
+            virtual ::std::string decode(const ::std::string &_sequence) = 0;
+        };
+      }
+    }
+  }
+}
+
+#endif

+ 55 - 0
include/ls_std/encoding/Base64.hpp

@@ -0,0 +1,55 @@
+/*
+ * Author:          Patrick-Christopher Mattulat
+ * Company:         Lynar Studios
+ * E-Mail:          webmaster@lynarstudios.com
+ * Created:         2022-01-03
+ * Changed:         2022-05-20
+ *
+ * */
+
+#ifndef LS_STD_BASE64_HPP
+#define LS_STD_BASE64_HPP
+
+#include <ls_std/core/interface/IEncoding.hpp>
+#include <bitset>
+#include <vector>
+#include <unordered_map>
+
+namespace ls
+{
+  namespace std
+  {
+    namespace encoding
+    {
+      class Base64 : public ls::std::core::interface_type::IEncoding
+      {
+        public:
+
+          Base64() = default;
+          ~Base64() = default;
+
+          // implementation
+
+          ::std::string encode(const ::std::string &_sequence) override;
+          ::std::string decode(const ::std::string &_sequence) override;
+
+        private:
+
+          static ::std::string _applyEndingRule(::std::string _encodedString, size_t _sequenceSize);
+          static ::std::string _decodeByteQuadruple(const ::std::string &_quadruple);
+          static ::std::string _encodeByteTriple(const ::std::string &_byteTriple);
+          static uint32_t _extractBitSequence(uint32_t _bitMask, uint32_t _bitStorage);
+          static uint32_t _generateBitMask(uint32_t _maskValue, uint8_t _shiftValue);
+          static ::std::unordered_map<char, uint8_t> _getDecodingMap();
+          static ::std::unordered_map<uint8_t, char> _getEncodingMap();
+          static ::std::string _getNextByteQuadruple(const ::std::string &_sequence, size_t _index);
+          static ::std::string _getNextByteTriple(const ::std::string &_sequence, size_t _index);
+          static void _mergeBitSequence(uint32_t &_bitStorage, const uint32_t &_bitMask);
+          static uint32_t _toDecodingBitStorage(const ::std::string &_quadruple);
+          static uint32_t _toEncodingBitStorage(const ::std::string &_triple);
+      };
+    }
+  }
+}
+
+#endif

+ 2 - 1
include/ls_std/ls_std_core.hpp

@@ -3,7 +3,7 @@
  * Company:         Lynar Studios
  * E-Mail:          webmaster@lynarstudios.com
  * Created:         2022-05-13
- * Changed:         2022-05-16
+ * Changed:         2022-05-17
  *
  * */
 
@@ -20,6 +20,7 @@
 #include <ls_std/core/exception/NullPointerException.hpp>
 
 #include <ls_std/core/interface/IBoxing.hpp>
+#include <ls_std/core/interface/IEncoding.hpp>
 #include <ls_std/core/interface/IEventSubscriber.hpp>
 #include <ls_std/core/interface/IListener.hpp>
 #include <ls_std/core/interface/IReader.hpp>

+ 15 - 0
include/ls_std/ls_std_encoding.hpp

@@ -0,0 +1,15 @@
+/*
+ * Author:          Patrick-Christopher Mattulat
+ * Company:         Lynar Studios
+ * E-Mail:          webmaster@lynarstudios.com
+ * Created:         2022-05-17
+ * Changed:         2022-05-17
+ *
+ * */
+
+#ifndef LS_STD_LS_STD_ENCODING_HPP
+#define LS_STD_LS_STD_ENCODING_HPP
+
+#include <ls_std/encoding/Base64.hpp>
+
+#endif

+ 193 - 0
source/ls_std/encoding/Base64.cpp

@@ -0,0 +1,193 @@
+/*
+ * Author:          Patrick-Christopher Mattulat
+ * Company:         Lynar Studios
+ * E-Mail:          webmaster@lynarstudios.com
+ * Created:         2022-01-03
+ * Changed:         2022-05-20
+ *
+ * */
+
+#include <ls_std/encoding/Base64.hpp>
+#include <bitset>
+
+std::string ls::std::encoding::Base64::encode(const ::std::string &_sequence)
+{
+  ::std::string encodedString{};
+
+  for(size_t index = 0 ; index < _sequence.size() ; index += 3)
+  {
+    ::std::string byteTriple = ls::std::encoding::Base64::_getNextByteTriple(_sequence, index);
+    encodedString += ls::std::encoding::Base64::_encodeByteTriple(byteTriple);
+  }
+
+  return ls::std::encoding::Base64::_applyEndingRule(encodedString, _sequence.size());
+}
+
+std::string ls::std::encoding::Base64::decode(const ::std::string &_sequence)
+{
+  ::std::string decodedString{};
+
+  for (int index{} ; index < _sequence.size() ; index += 4)
+  {
+    ::std::string quadruple = ls::std::encoding::Base64::_getNextByteQuadruple(_sequence, index);
+    decodedString += ls::std::encoding::Base64::_decodeByteQuadruple(quadruple);
+  }
+
+  return decodedString;
+}
+
+::std::string ls::std::encoding::Base64::_applyEndingRule(::std::string _encodedString, size_t _sequenceSize)
+{
+  size_t size = _encodedString.size();
+
+  if (_sequenceSize % 3 == 1)
+  {
+    _encodedString[size - 2] = '=';
+    _encodedString[size - 1] = '=';
+  }
+
+  if (_sequenceSize % 3 == 2)
+  {
+    _encodedString[size - 1] = '=';
+  }
+
+  return _encodedString;
+}
+
+std::string ls::std::encoding::Base64::_decodeByteQuadruple(const ::std::string& _quadruple)
+{
+  ::std::string decodedText{};
+  uint8_t shiftValue = 16;
+  uint32_t bitStorage = ls::std::encoding::Base64::_toDecodingBitStorage(_quadruple);
+
+  for (uint8_t index = 0; index < ((uint8_t) _quadruple.size() - 1); index++)
+  {
+    uint32_t bitMask = ls::std::encoding::Base64::_generateBitMask(255, shiftValue);
+    uint32_t bitSequence = ls::std::encoding::Base64::_extractBitSequence(bitMask, bitStorage);
+    bitSequence = bitSequence >> shiftValue;
+
+    decodedText += (char) bitSequence;
+    shiftValue -= 8;
+  }
+
+  return decodedText;
+}
+
+std::string ls::std::encoding::Base64::_encodeByteTriple(const ::std::string& _byteTriple)
+{
+  ::std::string encodedText{};
+  uint32_t bitStorage = ls::std::encoding::Base64::_toEncodingBitStorage(_byteTriple);
+  static ::std::vector<uint32_t> bitMaskStorage = {16515072, 258048, 4032, 63};
+  static ::std::unordered_map<uint8_t, char> encodingMap = ls::std::encoding::Base64::_getEncodingMap();
+  uint8_t shiftValue = 18;
+
+  for (uint8_t bitMaskIndex = 0 ; bitMaskIndex < 4 ; bitMaskIndex++)
+  {
+    uint32_t extractedBitSequence = ls::std::encoding::Base64::_extractBitSequence(bitMaskStorage[bitMaskIndex], bitStorage);
+    extractedBitSequence = extractedBitSequence >> shiftValue;
+    encodedText += encodingMap[(uint8_t) extractedBitSequence];
+    shiftValue -= 6;
+  }
+
+  return encodedText;
+}
+
+uint32_t ls::std::encoding::Base64::_extractBitSequence(uint32_t _bitMask, uint32_t _bitStorage)
+{
+  return _bitStorage & _bitMask;
+}
+
+uint32_t ls::std::encoding::Base64::_generateBitMask(uint32_t _maskValue, uint8_t _shiftValue)
+{
+  if(_shiftValue == 0)
+  {
+    return _maskValue;
+  }
+
+  if(_shiftValue < 0)
+  {
+    return _maskValue >> _shiftValue;
+  }
+
+  return _maskValue << _shiftValue;
+}
+
+std::unordered_map<char, uint8_t> ls::std::encoding::Base64::_getDecodingMap()
+{
+  static ::std::unordered_map<char, uint8_t> decodingMap =
+  {
+    {'A', 0}, {'B', 1}, {'C', 2}, {'D', 3}, {'E', 4}, {'F', 5}, {'G', 6}, {'H', 7},
+    {'I', 8}, {'J', 9}, {'K', 10}, {'L', 11}, {'M', 12}, {'N', 13}, {'O', 14}, {'P', 15},
+    {'Q', 16}, {'R', 17}, {'S', 18}, {'T', 19}, {'U', 20}, {'V', 21}, {'W', 22}, {'X', 23},
+    {'Y', 24}, {'Z', 25}, {'a', 26}, {'b', 27}, {'c', 28}, {'d', 29}, {'e', 30}, {'f', 31},
+    {'g', 32}, {'h', 33}, {'i', 34}, {'j', 35}, {'k', 36}, {'l', 37}, {'m', 38}, {'n', 39},
+    {'o', 40}, {'p', 41}, {'q', 42}, {'r', 43}, {'s', 44}, {'t', 45}, {'u', 46}, {'v', 47},
+    {'w', 48}, {'x', 49}, {'y', 50}, {'z', 51}, {'0', 52}, {'1', 53}, {'2', 54}, {'3', 55},
+    {'4', 56}, {'5', 57}, {'6', 58}, {'7', 59}, {'8', 60}, {'9', 61}, {'+', 62}, {'/', 63}
+  };
+
+  return decodingMap;
+}
+
+std::unordered_map<uint8_t, char> ls::std::encoding::Base64::_getEncodingMap()
+{
+  static ::std::unordered_map<uint8_t, char> encodingMap =
+  {
+    {0, 'A'}, {1, 'B'}, {2, 'C'}, {3, 'D'}, {4, 'E'}, {5, 'F'}, {6, 'G'}, {7, 'H'},
+    {8, 'I'}, {9, 'J'}, {10, 'K'}, {11, 'L'}, {12, 'M'}, {13, 'N'}, {14, 'O'}, {15, 'P'},
+    {16, 'Q'}, {17, 'R'}, {18, 'S'}, {19, 'T'}, {20, 'U'}, {21, 'V'}, {22, 'W'}, {23, 'X'},
+    {24, 'Y'}, {25, 'Z'}, {26, 'a'}, {27, 'b'}, {28, 'c'}, {29, 'd'}, {30, 'e'}, {31, 'f'},
+    {32, 'g'}, {33, 'h'}, {34, 'i'}, {35, 'j'}, {36, 'k'}, {37, 'l'}, {38, 'm'}, {39, 'n'},
+    {40, 'o'}, {41, 'p'}, {42, 'q'}, {43, 'r'}, {44, 's'}, {45, 't'}, {46, 'u'}, {47, 'v'},
+    {48, 'w'}, {49, 'x'}, {50, 'y'}, {51, 'z'}, {52, '0'}, {53, '1'}, {54, '2'}, {55, '3'},
+    {56, '4'}, {57, '5'}, {58, '6'}, {59, '7'}, {60, '8'}, {61, '9'}, {62, '+'}, {63, '/'},
+  };
+
+  return encodingMap;
+}
+
+std::string ls::std::encoding::Base64::_getNextByteQuadruple(const ::std::string &_sequence, size_t _index)
+{
+  return _sequence.substr(_index, 4);
+}
+
+std::string ls::std::encoding::Base64::_getNextByteTriple(const ::std::string &_sequence, size_t _index)
+{
+  return _sequence.substr(_index, 3);
+}
+
+void ls::std::encoding::Base64::_mergeBitSequence(uint32_t &_bitStorage, const uint32_t &_bitMask)
+{
+  _bitStorage = _bitStorage | _bitMask;
+}
+
+uint32_t ls::std::encoding::Base64::_toDecodingBitStorage(const ::std::string &_quadruple)
+{
+  uint32_t bitStorage{};
+  uint8_t letterCounter = 1;
+  ::std::unordered_map<char, uint8_t> decodingMap = ls::std::encoding::Base64::_getDecodingMap();
+
+  for(char letter : _quadruple)
+  {
+    uint32_t bitMask = ls::std::encoding::Base64::_generateBitMask(decodingMap[(char) letter], (4 - letterCounter) * 6); // must be hardcoded - even in case of less than 4 characters, so that conversion is correct
+    ls::std::encoding::Base64::_mergeBitSequence(bitStorage, bitMask);
+    ++letterCounter;
+  }
+
+  return bitStorage;
+}
+
+uint32_t ls::std::encoding::Base64::_toEncodingBitStorage(const ::std::string &_triple)
+{
+  uint32_t bitStorage{};
+  uint8_t shiftValue = 16;
+
+  for(char letter : _triple)
+  {
+    uint32_t bitMask = ls::std::encoding::Base64::_generateBitMask((uint8_t) letter, shiftValue);
+    ls::std::encoding::Base64::_mergeBitSequence(bitStorage, bitMask);
+    shiftValue -= 8;
+  }
+
+  return bitStorage;
+}

+ 48 - 0
test/cases/encoding/Base64Test.cpp

@@ -0,0 +1,48 @@
+/*
+ * Author:          Patrick-Christopher Mattulat
+ * Company:         Lynar Studios
+ * E-Mail:          webmaster@lynarstudios.com
+ * Created:         2022-01-08
+ * Changed:         2022-05-20
+ *
+ * */
+
+#include <gtest/gtest.h>
+#include <ls_std/ls_std_encoding.hpp>
+
+namespace
+{
+  class Base64Test : public ::testing::Test
+  {
+    protected:
+
+      Base64Test() = default;
+      ~Base64Test() override = default;
+
+      void SetUp() override
+      {}
+
+      void TearDown() override
+      {}
+  };
+
+  TEST_F(Base64Test, encode)
+  {
+    ls::std::encoding::Base64 base64{};
+
+    ASSERT_STREQ("YWJj", base64.encode("abc").c_str());
+    ASSERT_STREQ("YWJjZGU=", base64.encode("abcde").c_str());
+    ASSERT_STREQ("SGVsbG8gQysrIQ==", base64.encode("Hello C++!").c_str());
+    ASSERT_STREQ("UG9seWZvbiB6d2l0c2NoZXJuZCBhw59lbiBNw6R4Y2hlbnMgVsO2Z2VsIFLDvGJlbiwgSm9naHVydCB1bmQgUXVhcms=", base64.encode("Polyfon zwitschernd aßen Mäxchens Vögel Rüben, Joghurt und Quark").c_str());
+  }
+
+  TEST_F(Base64Test, decode)
+  {
+    ls::std::encoding::Base64 base64{};
+
+    ASSERT_STREQ("abc", base64.decode("YWJj").c_str());
+    ASSERT_STREQ("abcde", base64.decode("YWJjZGU=").c_str());
+    ASSERT_STREQ("Hello C++!", base64.decode("SGVsbG8gQysrIQ==").c_str());
+    ASSERT_STREQ("Polyfon zwitschernd aßen Mäxchens Vögel Rüben, Joghurt und Quark", base64.decode("UG9seWZvbiB6d2l0c2NoZXJuZCBhw59lbiBNw6R4Y2hlbnMgVsO2Z2VsIFLDvGJlbiwgSm9naHVydCB1bmQgUXVhcms=").c_str());
+  }
+}