Skip to content

Commit

Permalink
Use mimalloc as the main memory allocator
Browse files Browse the repository at this point in the history
  • Loading branch information
dr8co committed Jun 16, 2024
1 parent 6c66717 commit 97892af
Show file tree
Hide file tree
Showing 16 changed files with 479 additions and 151 deletions.
53 changes: 37 additions & 16 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -62,26 +62,27 @@ add_executable(privacyShield)

# Add sources for the target
target_sources(privacyShield PRIVATE
"${CMAKE_CURRENT_SOURCE_DIR}/src/encryption/encryptDecrypt.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/src/encryption/encryptFiles.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/src/encryption/encryptStrings.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/src/passwordManager/passwordManager.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/src/passwordManager/passwords.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/src/main.cpp"
src/encryption/encryptDecrypt.cpp
src/encryption/encryptFiles.cpp
src/encryption/encryptStrings.cpp
src/passwordManager/passwordManager.cpp
src/passwordManager/passwords.cpp
src/main.cpp
)

# C++20 Modules
target_sources(privacyShield PRIVATE
FILE_SET CXX_MODULES FILES
"${CMAKE_CURRENT_SOURCE_DIR}/src/duplicateFinder/duplicateFinder.cppm"
"${CMAKE_CURRENT_SOURCE_DIR}/src/encryption/cryptoCipher.cppm"
"${CMAKE_CURRENT_SOURCE_DIR}/src/encryption/encryption.cppm"
"${CMAKE_CURRENT_SOURCE_DIR}/src/fileShredder/fileShredder.cppm"
"${CMAKE_CURRENT_SOURCE_DIR}/src/passwordManager/FuzzyMatcher.cppm"
"${CMAKE_CURRENT_SOURCE_DIR}/src/passwordManager/passwordManager.cppm"
"${CMAKE_CURRENT_SOURCE_DIR}/src/privacyTracks/privacyTracks.cppm"
"${CMAKE_CURRENT_SOURCE_DIR}/src/utils/utils.cppm"
"${CMAKE_CURRENT_SOURCE_DIR}/src/secureAllocator.cppm"
src/duplicateFinder/duplicateFinder.cppm
src/encryption/cryptoCipher.cppm
src/encryption/encryption.cppm
src/fileShredder/fileShredder.cppm
src/passwordManager/FuzzyMatcher.cppm
src/passwordManager/passwordManager.cppm
src/privacyTracks/privacyTracks.cppm
src/utils/utils.cppm
src/secureAllocator.cppm
src/mimallocSTL.cppm
)

# Find the required packages
Expand Down Expand Up @@ -110,6 +111,26 @@ if (NOT TARGET BLAKE3::blake3)

endif ()

# Mimalloc allocator
find_package(mimalloc 2.1 QUIET)
if (NOT TARGET mimalloc)
message(STATUS "mimalloc not found. Fetching from GitHub...")

FetchContent_Declare(
mimalloc
GIT_REPOSITORY https://github.com/microsoft/mimalloc.git
GIT_TAG v2.1.7
EXCLUDE_FROM_ALL
)
endif ()

set(MI_BUILD_TESTS OFF) # Do not build tests

FetchContent_MakeAvailable(mimalloc)
target_include_directories(privacyShield PRIVATE "${mimalloc_SOURCE_DIR}/include")
add_library(Mimalloc::mimalloc-static ALIAS mimalloc-static)
add_library(Mimalloc::mimalloc ALIAS mimalloc)

# Fetch Isocline from GitHub
FetchContent_Declare(
isocline
Expand Down Expand Up @@ -148,12 +169,12 @@ endif ()

# Link libraries
target_link_libraries(privacyShield PRIVATE

OpenSSL::Crypto
Sodium::sodium
Gcrypt::Gcrypt
BLAKE3::blake3
ISOCline::isocline
Mimalloc::mimalloc-static
)

# Install the binary (optional), with 0755 permissions
Expand Down
25 changes: 13 additions & 12 deletions src/duplicateFinder/duplicateFinder.cppm
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ module;

export module duplicateFinder;
import utils;
import mimallocSTL;

namespace fs = std::filesystem;

Expand All @@ -38,17 +39,17 @@ constexpr std::size_t CHUNK_SIZE = 4096; ///< Read and process files in chunks o

/// \brief Represents a file by its path (canonical) and hash.
struct FileInfo {
std::string path; ///< the path to the file.
std::string hash; ///< the file's BLAKE3 hash
miSTL::string path; ///< the path to the file.
miSTL::string hash; ///< the file's BLAKE3 hash
};

/// \brief Calculates the 256-bit BLAKE3 hash of a file.
/// \param filePath path to the file.
/// \return Base64-encoded hash of the file.
/// \throws std::runtime_error if the file cannot be opened.
std::string calculateBlake3(const std::string &filePath) {
miSTL::string calculateBlake3(const miSTL::string &filePath) {
// Open the file
std::ifstream file(filePath, std::ios::binary);
std::ifstream file(filePath.c_str(), std::ios::binary);
if (!file) {
if (std::error_code ec; fs::exists(filePath, ec))
throw std::runtime_error(std::format("Failed to open '{}' for hashing.", filePath));
Expand All @@ -72,7 +73,7 @@ std::string calculateBlake3(const std::string &filePath) {
blake3_hasher_update(&hasher, buffer.data(), remainingBytes);

// Finalize the hash calculation
std::vector<unsigned char> digest(BLAKE3_OUT_LEN);
miSTL::vector<unsigned char> digest(BLAKE3_OUT_LEN);
blake3_hasher_finalize(&hasher, digest.data(), BLAKE3_OUT_LEN);

return base64Encode(digest);
Expand All @@ -93,7 +94,7 @@ inline void handleAccessError(const std::string_view filename) {
/// \brief recursively traverses a directory and collects file information.
/// \param directoryPath the directory to process.
/// \param files a vector to store the information from the files found in the directory.
void traverseDirectory(const fs::path &directoryPath, std::vector<FileInfo> &files) {
void traverseDirectory(const fs::path &directoryPath, miSTL::vector<FileInfo> &files) {
std::error_code ec;

for (const auto &entry: fs::recursive_directory_iterator(directoryPath,
Expand All @@ -109,7 +110,7 @@ void traverseDirectory(const fs::path &directoryPath, std::vector<FileInfo> &fil
continue;
}
// Make sure we can read the entry
if (isReadable(entry.path())) [[likely]] {
if (isReadable(entry.path().string().c_str())) [[likely]] {
// process only regular files
if (entry.is_regular_file()) [[likely]] {
FileInfo fileInfo;
Expand All @@ -133,22 +134,22 @@ void traverseDirectory(const fs::path &directoryPath, std::vector<FileInfo> &fil
/// \param files the files to process.
/// \param start the index where processing starts.
/// \param end the index where processing ends.
void calculateHashes(std::vector<FileInfo> &files, const std::size_t start, const std::size_t end) {
void calculateHashes(miSTL::vector<FileInfo> &files, const std::size_t start, const std::size_t end) {
// Check if the range is valid
if (start > end || end > files.size())
throw std::range_error("Invalid range.");

// Calculate hashes for the files in the range
for (std::size_t i = start; i < end; ++i)
files[i].hash = calculateBlake3(files[i].path);
files[i].hash = calculateBlake3(files[i].path).c_str();
}

/// \brief finds duplicate files (by content) in a directory.
/// \param directoryPath the directory to process.
/// \return True if duplicates are found, else False.
std::size_t findDuplicates(const fs::path &directoryPath) {
// Collect file information
std::vector<FileInfo> files;
miSTL::vector<FileInfo> files;
traverseDirectory(directoryPath, files);
const std::size_t filesProcessed = files.size();
if (filesProcessed < 2) return 0;
Expand All @@ -158,7 +159,7 @@ std::size_t findDuplicates(const fs::path &directoryPath) {
const unsigned int numThreads{n ? n : 8}; // Use 8 threads if hardware_concurrency() fails

// Divide the files among the threads
std::vector<std::jthread> threads;
miSTL::vector<std::jthread> threads;
const std::size_t filesPerThread = filesProcessed / numThreads;
std::size_t start = 0;

Expand All @@ -174,7 +175,7 @@ std::size_t findDuplicates(const fs::path &directoryPath) {
for (auto &thread: threads) thread.join();

// A hash map to map the files to their corresponding hashes
std::unordered_map<std::string, std::vector<std::string> > hashMap;
miSTL::unordered_map<miSTL::string, miSTL::vector<miSTL::string> > hashMap;

// Iterate over files and identify duplicates
for (const auto &[filePath, hash]: files)
Expand Down
25 changes: 13 additions & 12 deletions src/encryption/encryptDecrypt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ module;

import utils;
import secureAllocator;
import mimallocSTL;
import passwordManager;

module encryption;
Expand Down Expand Up @@ -63,14 +64,14 @@ constexpr struct {
/// \brief Formats a file size into a human-readable string.
/// \param size The file size as an unsigned integer.
/// \return A string representing the formatted file size.
std::string formatFileSize(const std::uintmax_t &size) {
miSTL::string formatFileSize(const std::uintmax_t &size) {
int i{};
auto mantissa = static_cast<double>(size);
for (; mantissa >= 1024.; mantissa /= 1024., ++i) {
}
mantissa = std::ceil(mantissa * 10.) / 10.;
std::string result = std::to_string(mantissa) + "BKMGTPE"[i];
return i == 0 ? result : result + "B (" + std::to_string(size) + ')';
miSTL::string result { std::to_string(mantissa) + "BKMGTPE"[i]};
return i == 0 ? result : result + "B (" + std::to_string(size).c_str() + ')';
}

/// \brief Checks for issues with the input file, that may hinder encryption/decryption.
Expand Down Expand Up @@ -101,7 +102,7 @@ void checkInputFile(const fs::path &inFile, const OperationMode &mode) {
std::format("{} is not a regular file.", inFile.string())); // Encrypted files are regular
}
// Check if the input file is readable
if (auto file = inFile.string(); !isReadable(file))
if (auto file = inFile.string(); !isReadable(file.c_str()))
throw std::runtime_error(std::format("{} is not readable.", file));
}

Expand Down Expand Up @@ -170,7 +171,7 @@ inline void checkOutputFile(const fs::path &inFile, fs::path &outFile, const Ope
throw std::runtime_error("Operation aborted.");

// Determine if the output file can be written if it exists
if (auto file = weakly_canonical(outFile).string(); !(isWritable(file) && isReadable(file)))
if (auto file = weakly_canonical(outFile).string(); !(isWritable(file.c_str()) && isReadable(file.c_str())))
throw std::runtime_error(std::format("{} is not writable/readable.", file));
}
}
Expand Down Expand Up @@ -211,7 +212,7 @@ inline void copyLastWrite(const std::string_view srcFile, const std::string_view
/// \param password the password to use for encryption/decryption.
/// \param algo the algorithm to use for encryption/decryption.
/// \param mode the mode of operation: encryption or decryption.
void fileEncryptionDecryption(const std::string &inputFileName, const std::string &outputFileName,
void fileEncryptionDecryption(const miSTL::string &inputFileName, const miSTL::string &outputFileName,
const privacy::string &password, const Algorithms &algo, const OperationMode &mode) {
// The mode must be valid: must be either encryption or decryption
if (mode != OperationMode::Encryption && mode != OperationMode::Decryption) [[unlikely]] {
Expand All @@ -221,7 +222,7 @@ void fileEncryptionDecryption(const std::string &inputFileName, const std::strin

try {
/// Encrypts/decrypts a file based on the passed mode and algorithm.
auto encryptDecrypt = [&](const std::string &algorithm) -> void {
auto encryptDecrypt = [&](const miSTL::string &algorithm) -> void {
if (mode == OperationMode::Encryption) // Encryption
encryptFile(inputFileName, outputFileName, password, algorithm);
else // Decryption
Expand Down Expand Up @@ -275,7 +276,7 @@ void fileEncryptionDecryption(const std::string &inputFileName, const std::strin
/// \brief Encrypts and decrypts files.
void encryptDecrypt() {
// I'm using hashmaps as an alternative to multiple if-else statements
const std::unordered_map<int, Algorithms> algoChoice = {
const miSTL::unordered_map<int, Algorithms> algoChoice = {
{0, Algorithms::AES}, // Default
{1, Algorithms::AES},
{2, Algorithms::Camellia},
Expand All @@ -284,7 +285,7 @@ void encryptDecrypt() {
{5, Algorithms::Twofish}
};

const std::unordered_map<Algorithms, std::string_view> algoDescription = {
const miSTL::unordered_map<Algorithms, std::string_view> algoDescription = {
{Algorithms::AES, "256-bit AES in CBC mode"},
{Algorithms::Camellia, "256-bit Camellia in CBC mode"},
{Algorithms::Aria, "256-bit Aria in CBC mode"},
Expand All @@ -303,8 +304,8 @@ void encryptDecrypt() {

if (const int choice = getResponseInt("Enter your choice: "); choice == 1 || choice == 2) {
try {
std::string pre = choice == 1 ? "En" : "De"; // the prefix string
std::string pre_l{pre}; // the prefix in lowercase
miSTL::string pre = choice == 1 ? "En" : "De"; // the prefix string
miSTL::string pre_l{pre}; // the prefix in lowercase

// Transform the prefix to lowercase
std::ranges::transform(pre_l.begin(), pre_l.end(), pre_l.begin(),
Expand Down Expand Up @@ -370,7 +371,7 @@ void encryptDecrypt() {
printColoredOutput('c', "{}", algoDescription.find(cipher)->second);
printColoredOutputln('g', "...");

fileEncryptionDecryption(canonical(inputPath).string(), weakly_canonical(outputPath).string(),
fileEncryptionDecryption(canonical(inputPath).string().c_str(), weakly_canonical(outputPath).string().c_str(),
password, cipher, static_cast<OperationMode>(choice));
std::println("");
} catch (const std::exception &ex) {
Expand Down
31 changes: 16 additions & 15 deletions src/encryption/encryptFiles.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ module;

import cryptoCipher;
import secureAllocator;
import mimallocSTL;

module encryption;

Expand All @@ -45,7 +46,7 @@ privacy::vector<unsigned char> generateSalt(const int saltSize) {
std::mutex m;
privacy::vector<unsigned char> salt(saltSize);

if (std::scoped_lock<std::mutex> lock(m); RAND_bytes(salt.data(), saltSize) != 1) {
if (std::scoped_lock lock(m); RAND_bytes(salt.data(), saltSize) != 1) {
std::cerr << "Failed to seed OpenSSL's CSPRNG properly."
"\nPlease check your system's randomness utilities." << std::endl;

Expand Down Expand Up @@ -134,15 +135,15 @@ deriveKey(const privacy::string &password, const privacy::vector<unsigned char>
/// \details Encryption mode: CBC.
/// \details Key derivation function: PBKDF2 with BLAKE2b512 as the digest function (salted).
/// \details The IV is generated randomly with a CSPRNG and prepended to the encrypted file.
void encryptFile(const std::string &inputFile, const std::string &outputFile, const privacy::string &password,
const std::string &algo) {
void encryptFile(const miSTL::string &inputFile, const miSTL::string &outputFile, const privacy::string &password,
const miSTL::string &algo) {
// Open the input file for reading
std::ifstream inFile(inputFile, std::ios::binary);
std::ifstream inFile(inputFile.c_str(), std::ios::binary);
if (!inFile)
throw std::runtime_error(std::format("Failed to open '{}' for reading.", inputFile));

// Open the output file for writing
std::ofstream outFile(outputFile, std::ios::binary | std::ios::trunc);
std::ofstream outFile(outputFile.c_str(), std::ios::binary | std::ios::trunc);
if (!outFile)
throw std::runtime_error(std::format("Failed to open '{}' for writing.", outputFile));

Expand Down Expand Up @@ -227,15 +228,15 @@ void encryptFile(const std::string &inputFile, const std::string &outputFile, co
/// \param algo The cipher algorithm used to encrypt the file.
///
/// \throws std::runtime_error if the decryption fails, and for other (documented) errors.
void decryptFile(const std::string &inputFile, const std::string &outputFile, const privacy::string &password,
const std::string &algo) {
void decryptFile(const miSTL::string &inputFile, const miSTL::string &outputFile, const privacy::string &password,
const miSTL::string &algo) {
// Open the input file for reading
std::ifstream inFile(inputFile, std::ios::binary);
std::ifstream inFile(inputFile.c_str(), std::ios::binary);
if (!inFile)
throw std::runtime_error(std::format("Failed to open '{}' for reading.", inputFile));

// Open the output file for writing
std::ofstream outFile(outputFile, std::ios::binary | std::ios::trunc);
std::ofstream outFile(outputFile.c_str(), std::ios::binary | std::ios::trunc);
if (!outFile)
throw std::runtime_error(std::format("Failed to open '{}' for writing.", outputFile));

Expand Down Expand Up @@ -343,15 +344,15 @@ inline void throwSafeError(const gcry_error_t &err, const std::string_view messa
/// using PBKDF2 with BLAKE2b-512 as the hash function.
/// \details The IV(nonce) is randomly generated and stored in the output file.
void
encryptFileWithMoreRounds(const std::string &inputFilePath, const std::string &outputFilePath,
encryptFileWithMoreRounds(const miSTL::string &inputFilePath, const miSTL::string &outputFilePath,
const privacy::string &password, const gcry_cipher_algos &algorithm) {
// Open the input file for reading
std::ifstream inputFile(inputFilePath, std::ios::binary);
std::ifstream inputFile(inputFilePath.c_str(), std::ios::binary);
if (!inputFile)
throw std::runtime_error(std::format("Failed to open '{}' for reading.", inputFilePath));

// Open the output file for writing
std::ofstream outputFile(outputFilePath, std::ios::binary | std::ios::trunc);
std::ofstream outputFile(outputFilePath.c_str(), std::ios::binary | std::ios::trunc);
if (!outputFile)
throw std::runtime_error(std::format("Failed to open '{}' for writing.", outputFilePath));

Expand Down Expand Up @@ -426,15 +427,15 @@ encryptFileWithMoreRounds(const std::string &inputFilePath, const std::string &o
///
/// \throws std::runtime_error if the decryption fails, and for other (documented) errors.
void
decryptFileWithMoreRounds(const std::string &inputFilePath, const std::string &outputFilePath,
decryptFileWithMoreRounds(const miSTL::string &inputFilePath, const miSTL::string &outputFilePath,
const privacy::string &password, const gcry_cipher_algos &algorithm) {
// Open the input file for reading
std::ifstream inputFile(inputFilePath, std::ios::binary);
std::ifstream inputFile(inputFilePath.c_str(), std::ios::binary);
if (!inputFile)
throw std::runtime_error(std::format("Failed to open '{}' for reading.", inputFilePath));

// Open the output file for writing
std::ofstream outputFile(outputFilePath, std::ios::binary | std::ios::trunc);
std::ofstream outputFile(outputFilePath.c_str(), std::ios::binary | std::ios::trunc);
if (!outputFile)
throw std::runtime_error(std::format("Failed to open '{}' for writing.", outputFilePath));

Expand Down
Loading

0 comments on commit 97892af

Please sign in to comment.