diff --git a/native/cpp/CommonCpp/NativeModules/CommUtilsModule.cpp b/native/cpp/CommonCpp/NativeModules/CommUtilsModule.cpp --- a/native/cpp/CommonCpp/NativeModules/CommUtilsModule.cpp +++ b/native/cpp/CommonCpp/NativeModules/CommUtilsModule.cpp @@ -1,4 +1,5 @@ #include "CommUtilsModule.h" +#include "../Tools/Base64.h" #include #include @@ -100,7 +101,13 @@ jsi::String CommUtilsModule::base64EncodeBuffer(jsi::Runtime &rt, jsi::Object data) { - return jsi::String::createFromAscii(rt, "unimplemented"); + auto arrayBuffer = data.getArrayBuffer(rt); + auto dataPtr = arrayBuffer.data(rt); + auto size = arrayBuffer.size(rt); + + auto bytes = std::vector{dataPtr, dataPtr + size}; + auto base64 = Base64::encode(bytes); + return jsi::String::createFromUtf8(rt, base64); } } // namespace comm diff --git a/native/cpp/CommonCpp/Tools/Base64.h b/native/cpp/CommonCpp/Tools/Base64.h new file mode 100644 --- /dev/null +++ b/native/cpp/CommonCpp/Tools/Base64.h @@ -0,0 +1,13 @@ +#pragma once + +#include +#include + +namespace comm { + +class Base64 { +public: + static std::string encode(const std::vector &data); +}; + +} // namespace comm diff --git a/native/cpp/CommonCpp/Tools/Base64.cpp b/native/cpp/CommonCpp/Tools/Base64.cpp new file mode 100644 --- /dev/null +++ b/native/cpp/CommonCpp/Tools/Base64.cpp @@ -0,0 +1,53 @@ +#include "Base64.h" + +#include +#include + +namespace comm { + +static constexpr std::array encode_table{ + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', + 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', + 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'}; + +static std::array encode_triplet(uint8_t a, uint8_t b, uint8_t c) { + constexpr uint32_t SIX_BIT_MASK = 0b111111; + const uint32_t concat_bits = (a << 16) | (b << 8) | c; + const auto x = encode_table[(concat_bits >> 18) & SIX_BIT_MASK]; + const auto y = encode_table[(concat_bits >> 12) & SIX_BIT_MASK]; + const auto z = encode_table[(concat_bits >> 6) & SIX_BIT_MASK]; + const auto w = encode_table[concat_bits & SIX_BIT_MASK]; + return {x, y, z, w}; +} + +std::string Base64::encode(const std::vector &data) { + const auto size = data.size(); + const auto remainder = size % 3; + const auto baseLength = size - remainder; + + // three bytes are encoded by 4 base64 chars + std::string encoded; + encoded.reserve(4 * ceil(size / 3)); + + for (std::size_t i = 0; i < baseLength; i += 3) { + const auto b64_chars = encode_triplet(data[i], data[i + 1], data[i + 2]); + std::copy(begin(b64_chars), end(b64_chars), back_inserter(encoded)); + } + + if (remainder == 1) { + const auto b64_chars = encode_triplet(data.back(), 0x00, 0x00); + encoded.push_back(b64_chars[0]); + encoded.push_back(b64_chars[1]); + encoded.append("=="); + } else if (remainder == 2) { + auto it = data.end() - 2; + const auto b64_chars = encode_triplet(*it, *(it + 1), 0x00); + std::copy_n(begin(b64_chars), 3, back_inserter(encoded)); + encoded.push_back('='); + } + return encoded; +} + +} // namespace comm diff --git a/native/cpp/CommonCpp/Tools/CMakeLists.txt b/native/cpp/CommonCpp/Tools/CMakeLists.txt --- a/native/cpp/CommonCpp/Tools/CMakeLists.txt +++ b/native/cpp/CommonCpp/Tools/CMakeLists.txt @@ -4,10 +4,12 @@ include(GNUInstallDirs) set(TOOLS_HDRS + "Base64.h" "WorkerThread.h" ) set(TOOLS_SRCS + "Base64.cpp" "WorkerThread.cpp" ) diff --git a/native/ios/Comm.xcodeproj/project.pbxproj b/native/ios/Comm.xcodeproj/project.pbxproj --- a/native/ios/Comm.xcodeproj/project.pbxproj +++ b/native/ios/Comm.xcodeproj/project.pbxproj @@ -39,6 +39,7 @@ 7FA2DCDF293E62F500991BA4 /* SWMansionIcons.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 7FA2DCDD293E62F500991BA4 /* SWMansionIcons.ttf */; }; 7FBB2A7629E94539002C6493 /* utilsJSI-generated.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7FBB2A7529E94539002C6493 /* utilsJSI-generated.cpp */; }; 7FBB2A7829E945C2002C6493 /* CommUtilsModule.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7FBB2A7329E944FD002C6493 /* CommUtilsModule.cpp */; }; + 7FBB2A7B29EEA2A4002C6493 /* Base64.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7FBB2A7A29EEA2A4002C6493 /* Base64.cpp */; }; 7FE4D9F5291DFE9300667BF6 /* commJSI-generated.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7FE4D9F4291DFE9300667BF6 /* commJSI-generated.cpp */; }; 8B38121629CE5742000C52E9 /* RustPromiseManager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8B38121529CE5742000C52E9 /* RustPromiseManager.cpp */; }; 8B652FA6295EAA5B009F8163 /* RustCallback.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8B652FA5295EAA5B009F8163 /* RustCallback.cpp */; }; @@ -168,6 +169,8 @@ 7FBB2A7429E9450E002C6493 /* CommUtilsModule.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CommUtilsModule.h; sourceTree = ""; }; 7FBB2A7529E94539002C6493 /* utilsJSI-generated.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = "utilsJSI-generated.cpp"; sourceTree = ""; }; 7FBB2A7729E94541002C6493 /* utilsJSI.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = utilsJSI.h; sourceTree = ""; }; + 7FBB2A7929EA752D002C6493 /* Base64.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Base64.h; sourceTree = ""; }; + 7FBB2A7A29EEA2A4002C6493 /* Base64.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = Base64.cpp; sourceTree = ""; }; 7FCEA2DC2444010B004017B1 /* Comm-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Comm-Bridging-Header.h"; sourceTree = ""; }; 7FCFD8BD1E81B8DF00629B0E /* Comm.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; name = Comm.entitlements; path = Comm/Comm.entitlements; sourceTree = ""; }; 7FE4D9F3291DFE9300667BF6 /* commJSI.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = commJSI.h; sourceTree = ""; }; @@ -357,6 +360,8 @@ 71BE84382636A944002849D2 /* Tools */ = { isa = PBXGroup; children = ( + 7FBB2A7A29EEA2A4002C6493 /* Base64.cpp */, + 7FBB2A7929EA752D002C6493 /* Base64.h */, 71B8CCBD26BD4DEB0040C0A2 /* CommSecureStore.h */, 718DE99C2653D41C00365824 /* WorkerThread.cpp */, 718DE99D2653D41C00365824 /* WorkerThread.h */, @@ -989,6 +994,7 @@ 7FE4D9F5291DFE9300667BF6 /* commJSI-generated.cpp in Sources */, 8B652FA6295EAA5B009F8163 /* RustCallback.cpp in Sources */, 71142A7726C2650B0039DCBD /* CommSecureStoreIOSWrapper.mm in Sources */, + 7FBB2A7B29EEA2A4002C6493 /* Base64.cpp in Sources */, CB38F2B1286C6C870010535C /* MessageOperationsUtilities.cpp in Sources */, 71CA4A64262DA8E500835C89 /* Logger.mm in Sources */, 71BF5B7F26BBDD7400EDE27D /* CryptoModule.cpp in Sources */,