diff --git a/SolARFramework.pri b/SolARFramework.pri index 1e499dc3..4b7dddf8 100644 --- a/SolARFramework.pri +++ b/SolARFramework.pri @@ -127,9 +127,11 @@ interfaces/api/tracking/IOpticalFlowEstimator.h \ interfaces/core/Log.h \ interfaces/core/Timer.h \ interfaces/core/Messages.h \ +interfaces/core/ScopedTempDir.h \ interfaces/core/SerializationDefinitions.h \ interfaces/core/SolARFramework.h \ interfaces/core/SolARFrameworkDefinitions.h \ +interfaces/core/ZipBufferUtils.h \ interfaces/datastructure/BufferInternal.hpp \ interfaces/datastructure/CameraDefinitions.h \ interfaces/datastructure/CameraParametersCollection.h \ @@ -190,7 +192,9 @@ src/api/map/IRectifyMap.cpp \ src/datastructure/RelocalizationInformation.cpp \ src/datastructure/StorageCapabilities.cpp \ src/core/Log.cpp \ +src/core/ScopedTempDir.cpp \ src/core/SolARFramework.cpp \ +src/core/ZipBufferUtils.cpp \ src/datastructure/CameraParametersCollection.cpp \ src/datastructure/CloudPoint.cpp \ src/datastructure/CoordinateSystem.cpp \ diff --git a/interfaces/core/ScopedTempDir.h b/interfaces/core/ScopedTempDir.h new file mode 100644 index 00000000..cde16008 --- /dev/null +++ b/interfaces/core/ScopedTempDir.h @@ -0,0 +1,57 @@ +/** + * @copyright Copyright (c) 2026 B-com http://www.b-com.com/ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SOLAR_SCOPEDTEMPDIR_H +#define SOLAR_SCOPEDTEMPDIR_H + +#include +#include + +namespace SolAR { +namespace util { + +/** + * @class ScopedTempDir + * @brief Create a temporary directory + * + */ +class ScopedTempDir { + +public: + + ScopedTempDir(); + + ~ScopedTempDir(); + + // Delete copy operations to prevent double deletion + ScopedTempDir(const ScopedTempDir&) = delete; + ScopedTempDir& operator=(const ScopedTempDir&) = delete; + ScopedTempDir(ScopedTempDir&&) = delete; + ScopedTempDir& operator=(ScopedTempDir&&) = delete; + + const std::filesystem::path& getPath() const; + const std::string getStringPath() const; + +private: + + std::filesystem::path m_tempPath; // Temporary working directory used to copy, zip or unzip data + +}; + +} // end of namespace util +} // end of namespace SolAR + +#endif // SOLAR_SCOPEDTEMPDIR_H diff --git a/interfaces/core/ZipBufferUtils.h b/interfaces/core/ZipBufferUtils.h new file mode 100644 index 00000000..3be4048f --- /dev/null +++ b/interfaces/core/ZipBufferUtils.h @@ -0,0 +1,60 @@ +/** + * @copyright Copyright (c) 2026 B-com http://www.b-com.com/ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SOLAR_ZIPBUFFERUTILS_H +#define SOLAR_ZIPBUFFERUTILS_H + +#include "core/Messages.h" +#include +#include + +namespace SolAR { +namespace util { + +/** + * @class ZipBufferUtils + * @brief Defines methods to zip/unzip data to/from a binary buffer + * + */ + +class ZipBufferUtils { + +public: + + /// @brief zip the content of the original path and store the binary result in the output buffer + /// @param[in] originalPath path to data to zip + /// @param[out] compressedZipBuffer output buffer containing the zip data + /// @return + /// * FrameworkReturnCode::_SUCCESS if the process succeeds + /// * else FrameworkReturnCode::_ERROR_ + static FrameworkReturnCode compress(const std::string & originalPath, + std::vector & compressedZipBuffer); + + /// @brief unzip the content of the input buffer and store the result in the destination path + /// @param[in] compressedZipBuffer input buffer containing the zip data + /// @param[in] destinationPath path for unzipped data + /// @return + /// * FrameworkReturnCode::_SUCCESS if the process succeeds + /// * else FrameworkReturnCode::_ERROR_ + static FrameworkReturnCode extract(const std::vector & compressedZipBuffer, + const std::string & destinationPath); + +}; + +} // end of namespace util +} // end of namespace SolAR + +#endif // SOLAR_ZIPBUFFERUTILS_H diff --git a/src/core/ScopedTempDir.cpp b/src/core/ScopedTempDir.cpp new file mode 100644 index 00000000..abc12aea --- /dev/null +++ b/src/core/ScopedTempDir.cpp @@ -0,0 +1,41 @@ +#include "core/ScopedTempDir.h" +#include + +namespace fs = std::filesystem; + +using namespace SolAR; +using namespace SolAR::util; + +ScopedTempDir::ScopedTempDir() +{ + fs::path base_path = fs::temp_directory_path(); + + // Generate a random name for temporary subdirectory + std::random_device rd; + std::mt19937_64 gen(rd()); + std::uniform_int_distribution dis; + do { + std::string random_name = "tmp_" + std::to_string(dis(gen)); + m_tempPath = base_path / random_name; + } while (fs::exists(m_tempPath)); + + // Create the temporary subdirectory + std::error_code ec; + fs::create_directories(m_tempPath, ec); +} + +ScopedTempDir::~ScopedTempDir() +{ + // Delete the temporary directory (and its contents) + std::error_code ec; fs::remove_all(m_tempPath, ec); +} + +const fs::path& ScopedTempDir::getPath() const +{ + return m_tempPath; +} + +const std::string ScopedTempDir::getStringPath() const +{ + return m_tempPath.string(); +} diff --git a/src/core/ZipBufferUtils.cpp b/src/core/ZipBufferUtils.cpp new file mode 100644 index 00000000..27c811ca --- /dev/null +++ b/src/core/ZipBufferUtils.cpp @@ -0,0 +1,121 @@ +#include "core/ZipBufferUtils.h" +#include "core/ScopedTempDir.h" +#include "core/Log.h" + +namespace fs = std::filesystem; + +using namespace SolAR; +using namespace SolAR::util; + +FrameworkReturnCode ZipBufferUtils::compress(const std::string & originalPath, + std::vector & compressedZipBuffer) +{ + LOG_DEBUG("ZipBufferUtils::compress - Original path: {}", originalPath); + + compressedZipBuffer.clear(); + + // Get a temporary working directory + ScopedTempDir workingDir; + + LOG_DEBUG("ZipBufferUtils::compress - Working temporary path: {}", workingDir.getStringPath()); + + fs::path op(originalPath); + + try { + // Check original path + if (!fs::is_directory(op)) { + LOG_ERROR("ZipBufferUtils::compress - The original path is not a directory: {}", originalPath); + return FrameworkReturnCode::_ERROR_; + } + if (fs::is_empty(op)) { + LOG_WARNING("ZipBufferUtils::compress - The original path is empty: {}", originalPath); + return FrameworkReturnCode::_SUCCESS; + } + } + catch (const fs::filesystem_error & e) { + LOG_ERROR("ZipBufferUtils::compress - The following exception has been caught {}", e.what()); + return FrameworkReturnCode::_ERROR_; + } + + // Try to zip the original path content + std::string command = "cd " + originalPath +"; zip -r " + workingDir.getStringPath() + "/data.zip ."; + if (std::system(command.c_str()) != 0) { + LOG_ERROR("ZipBufferUtils::compress - Error occured while trying to zip the working directory content: {}", workingDir.getStringPath()); + return FrameworkReturnCode::_ERROR_; + } + + // Open the resulting zip file + std::string zipFile = workingDir.getStringPath() + "/data.zip"; + std::ifstream file(zipFile, std::ios::binary); + if (!file.is_open()) { + LOG_ERROR("ZipBufferUtils::compress - Cannot open the zip binary file: {}", zipFile); + return FrameworkReturnCode::_ERROR_; + } + + // Get its size + file.seekg(0, std::ios::end); + std::streampos fileSize = file.tellg(); + file.seekg(0, std::ios::beg); + if (fileSize == 0) { + LOG_ERROR("ZipBufferUtils::compress - Empty zip binary file: {}", zipFile); + return FrameworkReturnCode::_ERROR_; + } + LOG_DEBUG("ZipBufferUtils::compress - Zip file size ({}): {}", zipFile, fmt::streamed(fileSize)); + + // Read the data and put it in the output buffer + compressedZipBuffer.resize(fileSize); + file.read(reinterpret_cast(compressedZipBuffer.data()), compressedZipBuffer.size()); + + return FrameworkReturnCode::_SUCCESS; +} + +FrameworkReturnCode ZipBufferUtils::extract(const std::vector & compressedZipBuffer, + const std::string & destinationPath) +{ + LOG_DEBUG("ZipBufferUtils::extract - Destination path: {}", destinationPath); + + if (compressedZipBuffer.empty()) { + LOG_WARNING("ZipBufferUtils::extract - Empty input buffer"); + return FrameworkReturnCode::_SUCCESS; + } + + try { + fs::path dp(destinationPath); + + // Check destination path + if (!fs::is_directory(dp)) { + LOG_ERROR("ZipBufferUtils::extract - The destination path is not a directory: {}", destinationPath); + return FrameworkReturnCode::_ERROR_; + } + + // Get a temporary working directory + ScopedTempDir workingDir; + + LOG_DEBUG("ZipBufferUtils::extract - Working temporary path: {}", workingDir.getStringPath()); + + // Create the zip file from the input buffer + std::string zipFile = workingDir.getStringPath() + "/data.zip"; + std::ofstream file(zipFile, std::ios::out | std::ios::binary); + if (!file.is_open()) { + LOG_ERROR("ZipBufferUtils::extract - Cannot create/open zip file: {}", zipFile); + return FrameworkReturnCode::_ERROR_; + } + + // Write the compressed data + file.write(reinterpret_cast(compressedZipBuffer.data()), compressedZipBuffer.size()); + file.close(); + + // Try to unzip the file content + std::string command = "unzip " + workingDir.getStringPath() + "/data.zip -d " + destinationPath; + if (std::system(command.c_str()) != 0) { + LOG_ERROR("ZipBufferUtils::extract - Error occured while trying to unzip the compressed data file: {}", zipFile); + return FrameworkReturnCode::_ERROR_; + } + + return FrameworkReturnCode::_SUCCESS; + } + catch (const fs::filesystem_error & e) { + LOG_ERROR("ZipBufferUtils::extract - The following exception has been caught {}", e.what()); + return FrameworkReturnCode::_ERROR_; + } +}