From 3697471d7f5b4316cffe758eb675f795cbca9ae5 Mon Sep 17 00:00:00 2001 From: Nitish Singh <93253740+nitishhsinghhh@users.noreply.github.com> Date: Fri, 1 May 2026 18:22:21 +0530 Subject: [PATCH 01/14] Added clang format --- .../CppLib/src/AlternatingCaseConversion.cpp | 13 +-- .../CppLib/src/CapitalizeWordsConversion.cpp | 6 +- .../CppLib/src/ConversionResult.cpp | 75 +++++++------ .../CppLib/src/InvertWordsConversion.cpp | 3 +- .../CppLib/src/ProcessStringDLL.cpp | 102 +++++++++--------- .../CppLib/src/RemoveSpacesConversion.cpp | 3 +- .../CppLib/src/RemoveVowelsConversion.cpp | 3 +- .../CppLib/src/SentenceCaseConversion.cpp | 3 +- 8 files changed, 105 insertions(+), 103 deletions(-) diff --git a/backend/CaseConversionAPI/CppLib/src/AlternatingCaseConversion.cpp b/backend/CaseConversionAPI/CppLib/src/AlternatingCaseConversion.cpp index e9229b7..4ddb38f 100644 --- a/backend/CaseConversionAPI/CppLib/src/AlternatingCaseConversion.cpp +++ b/backend/CaseConversionAPI/CppLib/src/AlternatingCaseConversion.cpp @@ -26,11 +26,12 @@ /*********************************************************************/ #include "AlternatingCaseConversion.hpp" +#include "ConversionResult.hpp" #include "LowerCaseConversion.hpp" #include "UpperCaseConversion.hpp" -#include "ConversionResult.hpp" -ConversionResult AlternatingCaseConversion::convert(const std::string &input) const { +ConversionResult +AlternatingCaseConversion::convert(const std::string &input) const { LowerCaseConversion lowerConv; UpperCaseConversion upperConv; @@ -44,17 +45,17 @@ ConversionResult AlternatingCaseConversion::convert(const std::string &input) co if (upper) { auto resObj = upperConv.convert(temp); - finalResult += resObj.get_c_str(); + finalResult += resObj.get_c_str(); } else { auto resObj = lowerConv.convert(temp); - finalResult += resObj.get_c_str(); + finalResult += resObj.get_c_str(); } upper = !upper; } else { - finalResult += c; + finalResult += c; if (c == ' ') { - upper = true; + upper = true; } } } diff --git a/backend/CaseConversionAPI/CppLib/src/CapitalizeWordsConversion.cpp b/backend/CaseConversionAPI/CppLib/src/CapitalizeWordsConversion.cpp index 36893bb..3075e3a 100644 --- a/backend/CaseConversionAPI/CppLib/src/CapitalizeWordsConversion.cpp +++ b/backend/CaseConversionAPI/CppLib/src/CapitalizeWordsConversion.cpp @@ -30,7 +30,8 @@ #include "UpperCaseConversion.hpp" #include -ConversionResult CapitalizeWordsConversion::convert(const std::string &input) const { +ConversionResult +CapitalizeWordsConversion::convert(const std::string &input) const { LowerCaseConversion lowerConv; UpperCaseConversion upperConv; @@ -44,7 +45,8 @@ ConversionResult CapitalizeWordsConversion::convert(const std::string &input) co } // Convert whole word to lowercase first - word = ConversionResult(lowerConv.convert(word)).get_c_str(); // Get C-string from ConversionResult + word = ConversionResult(lowerConv.convert(word)) + .get_c_str(); // Get C-string from ConversionResult // Capitalize first letter using UpperCaseConversion std::string firstChar(1, word[0]); diff --git a/backend/CaseConversionAPI/CppLib/src/ConversionResult.cpp b/backend/CaseConversionAPI/CppLib/src/ConversionResult.cpp index d6c31d6..bdfb96a 100644 --- a/backend/CaseConversionAPI/CppLib/src/ConversionResult.cpp +++ b/backend/CaseConversionAPI/CppLib/src/ConversionResult.cpp @@ -34,80 +34,77 @@ /* Constructor */ /*********************************************************************/ -ConversionResult::ConversionResult(const char* input) { - if (input) { - data = new char[std::strlen(input) + 1]; - std::strcpy(data, input); - } else { - data = nullptr; - } +ConversionResult::ConversionResult(const char *input) { + if (input) { + data = new char[std::strlen(input) + 1]; + std::strcpy(data, input); + } else { + data = nullptr; + } } /*********************************************************************/ /* Destructor */ /*********************************************************************/ -ConversionResult::~ConversionResult() { - delete[] data; -} +ConversionResult::~ConversionResult() { delete[] data; } /*********************************************************************/ /* Copy Constructor */ /*********************************************************************/ -ConversionResult::ConversionResult(const ConversionResult& other) { - if (other.data) { - data = new char[std::strlen(other.data) + 1]; - std::strcpy(data, other.data); - } else { - data = nullptr; - } +ConversionResult::ConversionResult(const ConversionResult &other) { + if (other.data) { + data = new char[std::strlen(other.data) + 1]; + std::strcpy(data, other.data); + } else { + data = nullptr; + } } /*********************************************************************/ /* Copy Assignment */ /*********************************************************************/ -ConversionResult& ConversionResult::operator=(const ConversionResult& other) { - if (this != &other) { - delete[] data; +ConversionResult &ConversionResult::operator=(const ConversionResult &other) { + if (this != &other) { + delete[] data; - if (other.data) { - data = new char[std::strlen(other.data) + 1]; - std::strcpy(data, other.data); - } else { - data = nullptr; - } + if (other.data) { + data = new char[std::strlen(other.data) + 1]; + std::strcpy(data, other.data); + } else { + data = nullptr; } - return *this; + } + return *this; } /*********************************************************************/ /* Move Constructor */ /*********************************************************************/ -ConversionResult::ConversionResult(ConversionResult&& other) noexcept +ConversionResult::ConversionResult(ConversionResult &&other) noexcept : data(other.data) { - other.data = nullptr; + other.data = nullptr; } /*********************************************************************/ /* Move Assignment */ /*********************************************************************/ -ConversionResult& ConversionResult::operator=(ConversionResult&& other) noexcept { - if (this != &other) { - delete[] data; - data = other.data; - other.data = nullptr; - } - return *this; +ConversionResult & +ConversionResult::operator=(ConversionResult &&other) noexcept { + if (this != &other) { + delete[] data; + data = other.data; + other.data = nullptr; + } + return *this; } /*********************************************************************/ /* Accessor */ /*********************************************************************/ -const char* ConversionResult::get_c_str() const { - return data; -} \ No newline at end of file +const char *ConversionResult::get_c_str() const { return data; } \ No newline at end of file diff --git a/backend/CaseConversionAPI/CppLib/src/InvertWordsConversion.cpp b/backend/CaseConversionAPI/CppLib/src/InvertWordsConversion.cpp index dcfb5bc..26eb875 100644 --- a/backend/CaseConversionAPI/CppLib/src/InvertWordsConversion.cpp +++ b/backend/CaseConversionAPI/CppLib/src/InvertWordsConversion.cpp @@ -27,7 +27,8 @@ #include #include -ConversionResult InvertWordsConversion::convert(const std::string &input) const { +ConversionResult +InvertWordsConversion::convert(const std::string &input) const { std::istringstream iss(input); std::ostringstream oss; std::string word; diff --git a/backend/CaseConversionAPI/CppLib/src/ProcessStringDLL.cpp b/backend/CaseConversionAPI/CppLib/src/ProcessStringDLL.cpp index dc6745e..b91591b 100644 --- a/backend/CaseConversionAPI/CppLib/src/ProcessStringDLL.cpp +++ b/backend/CaseConversionAPI/CppLib/src/ProcessStringDLL.cpp @@ -130,61 +130,59 @@ static bool mapConversionType(ConversionChoice choice, ConversionType &type) { extern "C" { -API const char *processStringDLL(const char *input, int choiceInt, const char *traceId) { - try { - if (!input) return safeError("ERROR_NULL_INPUT"); - - size_t inputLength = std::strlen(input); - if (inputLength > MAX_INPUT_SIZE) { - return safeError("ERROR_BUFFER_OVERFLOW_LIMIT_5MB"); - } - - if (choiceInt < 0) { - return safeError("ERROR_NEGATIVE_CONVERSION_CHOICE"); - } - - ConversionChoice choice = static_cast(choiceInt); - ConversionType type; - - if (!mapConversionType(choice, type)) { - return safeError("ERROR_INVALID_CONVERSION_CHOICE"); - } - - Client client; - if (traceId) { - client.setTraceId(traceId); - } - - // 1. Create the engine and execute - client.setStrategy(StringConversionFactory::create(type)); - - // 2. Capture the result in a local scope to ensure its lifetime - ConversionResult result = client.execute(std::string(input)); - const char* rawPtr = result.get_c_str(); - - if (!rawPtr) - return safeError("ERROR_ENGINE_RETURNED_NULL"); - - // 3. Perform a DEEP COPY immediately into malloc'd memory - size_t resLen = std::strlen(rawPtr); - char *output = static_cast(std::malloc(resLen + 1)); - - if (!output) - return safeError("FATAL_ALLOCATION_FAILURE"); - - std::memcpy(output, rawPtr, resLen + 1); - - // 4. The 'result' object will be destroyed after this return, - // but 'output' is now independent on the heap. - return output; - - } catch (...) { - return safeError("ERROR_INTERNAL_EXCEPTION"); +API const char *processStringDLL(const char *input, int choiceInt, + const char *traceId) { + try { + if (!input) + return safeError("ERROR_NULL_INPUT"); + + size_t inputLength = std::strlen(input); + if (inputLength > MAX_INPUT_SIZE) { + return safeError("ERROR_BUFFER_OVERFLOW_LIMIT_5MB"); } + + if (choiceInt < 0) { + return safeError("ERROR_NEGATIVE_CONVERSION_CHOICE"); + } + + ConversionChoice choice = static_cast(choiceInt); + ConversionType type; + + if (!mapConversionType(choice, type)) { + return safeError("ERROR_INVALID_CONVERSION_CHOICE"); + } + + Client client; + if (traceId) { + client.setTraceId(traceId); + } + + client.setStrategy(StringConversionFactory::create(type)); + + ConversionResult result = client.execute(std::string(input)); + const char *rawPtr = result.get_c_str(); + + if (!rawPtr) + return safeError("ERROR_ENGINE_RETURNED_NULL"); + + size_t resLen = std::strlen(rawPtr); + char *output = static_cast(std::malloc(resLen + 1)); + + if (!output) + return safeError("FATAL_ALLOCATION_FAILURE"); + + std::memcpy(output, rawPtr, resLen + 1); + + return output; + + } catch (...) { + return safeError("ERROR_INTERNAL_EXCEPTION"); + } } -API void freeString(char *str) { - if (str) std::free(str); +API void freeString(char *str) { + if (str) + std::free(str); } } // extern "C" \ No newline at end of file diff --git a/backend/CaseConversionAPI/CppLib/src/RemoveSpacesConversion.cpp b/backend/CaseConversionAPI/CppLib/src/RemoveSpacesConversion.cpp index 46e0ae6..7d15c14 100644 --- a/backend/CaseConversionAPI/CppLib/src/RemoveSpacesConversion.cpp +++ b/backend/CaseConversionAPI/CppLib/src/RemoveSpacesConversion.cpp @@ -26,7 +26,8 @@ #include "RemoveSpacesConversion.hpp" #include -ConversionResult RemoveSpacesConversion::convert(const std::string &input) const { +ConversionResult +RemoveSpacesConversion::convert(const std::string &input) const { std::string result = input; result.erase(std::remove(result.begin(), result.end(), ' '), result.end()); return ConversionResult(result.c_str()); diff --git a/backend/CaseConversionAPI/CppLib/src/RemoveVowelsConversion.cpp b/backend/CaseConversionAPI/CppLib/src/RemoveVowelsConversion.cpp index 20db859..86a3170 100644 --- a/backend/CaseConversionAPI/CppLib/src/RemoveVowelsConversion.cpp +++ b/backend/CaseConversionAPI/CppLib/src/RemoveVowelsConversion.cpp @@ -26,7 +26,8 @@ #include "RemoveVowelsConversion.hpp" #include "LowerCaseConversion.hpp" -ConversionResult RemoveVowelsConversion::convert(const std::string &input) const { +ConversionResult +RemoveVowelsConversion::convert(const std::string &input) const { LowerCaseConversion lowerConv; std::string lower = ConversionResult(lowerConv.convert(input)).get_c_str(); std::string result; diff --git a/backend/CaseConversionAPI/CppLib/src/SentenceCaseConversion.cpp b/backend/CaseConversionAPI/CppLib/src/SentenceCaseConversion.cpp index 7877fb7..b26cdda 100644 --- a/backend/CaseConversionAPI/CppLib/src/SentenceCaseConversion.cpp +++ b/backend/CaseConversionAPI/CppLib/src/SentenceCaseConversion.cpp @@ -29,7 +29,8 @@ #include "LowerCaseConversion.hpp" #include "UpperCaseConversion.hpp" -ConversionResult SentenceCaseConversion::convert(const std::string &input) const { +ConversionResult +SentenceCaseConversion::convert(const std::string &input) const { if (input.empty()) { return ConversionResult(input.c_str()); } From cdc482fb096853ee8eaa83759e61560f133b4797 Mon Sep 17 00:00:00 2001 From: Nitish Singh <93253740+nitishhsinghhh@users.noreply.github.com> Date: Fri, 1 May 2026 18:44:26 +0530 Subject: [PATCH 02/14] refactor: replace insecure strcpy with memcpy in ConversionResult Optimized RAII ownership model for M2 architecture and resolved clang-analyzer security violations (CWE-119). --- .../CppLib/src/ConversionResult.cpp | 22 +++++++++++-------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/backend/CaseConversionAPI/CppLib/src/ConversionResult.cpp b/backend/CaseConversionAPI/CppLib/src/ConversionResult.cpp index bdfb96a..9bd6623 100644 --- a/backend/CaseConversionAPI/CppLib/src/ConversionResult.cpp +++ b/backend/CaseConversionAPI/CppLib/src/ConversionResult.cpp @@ -37,7 +37,8 @@ ConversionResult::ConversionResult(const char *input) { if (input) { data = new char[std::strlen(input) + 1]; - std::strcpy(data, input); + size_t length = std::strlen(input); + std::memcpy(data, input, length + 1); } else { data = nullptr; } @@ -55,8 +56,9 @@ ConversionResult::~ConversionResult() { delete[] data; } ConversionResult::ConversionResult(const ConversionResult &other) { if (other.data) { - data = new char[std::strlen(other.data) + 1]; - std::strcpy(data, other.data); + size_t length = std::strlen(other.data); + data = new char[length + 1]; + std::memcpy(data, other.data, length + 1); } else { data = nullptr; } @@ -68,14 +70,16 @@ ConversionResult::ConversionResult(const ConversionResult &other) { ConversionResult &ConversionResult::operator=(const ConversionResult &other) { if (this != &other) { - delete[] data; - + char* new_data = nullptr; if (other.data) { - data = new char[std::strlen(other.data) + 1]; - std::strcpy(data, other.data); - } else { - data = nullptr; + size_t length = std::strlen(other.data); + new_data = new char[length + 1]; + std::memcpy(new_data, other.data, length + 1); } + + // Clean up old data and assign new + delete[] data; + data = new_data; } return *this; } From bef2df6ec4df833dd7eff2369fdb94268c2b5c20 Mon Sep 17 00:00:00 2001 From: Nitish Singh <93253740+nitishhsinghhh@users.noreply.github.com> Date: Fri, 1 May 2026 18:48:32 +0530 Subject: [PATCH 03/14] refactor: replace insecure strcpy with memcpy in ConversionResult Optimized RAII ownership model for M2 architecture and resolved clang-analyzer security violations (CWE-119). --- backend/CaseConversionAPI/CppLib/src/ConversionResult.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/CaseConversionAPI/CppLib/src/ConversionResult.cpp b/backend/CaseConversionAPI/CppLib/src/ConversionResult.cpp index 9bd6623..919182a 100644 --- a/backend/CaseConversionAPI/CppLib/src/ConversionResult.cpp +++ b/backend/CaseConversionAPI/CppLib/src/ConversionResult.cpp @@ -70,13 +70,13 @@ ConversionResult::ConversionResult(const ConversionResult &other) { ConversionResult &ConversionResult::operator=(const ConversionResult &other) { if (this != &other) { - char* new_data = nullptr; + char *new_data = nullptr; if (other.data) { size_t length = std::strlen(other.data); new_data = new char[length + 1]; std::memcpy(new_data, other.data, length + 1); } - + // Clean up old data and assign new delete[] data; data = new_data; From 53f45e0f416a38e9b2eaef13fa835459f78a4277 Mon Sep 17 00:00:00 2001 From: Nitish Singh <93253740+nitishhsinghhh@users.noreply.github.com> Date: Sun, 3 May 2026 03:06:20 +0530 Subject: [PATCH 04/14] All three OS build --- .../CaseConversionAPI/CppLib/CMakeLists.txt | 4 +- .../CppLib/Scripts/orchestrate-native.md | 106 ++++++++++++++++++ .../CppLib/Scripts/orchestrate-native.sh | 98 ++++++++++++---- 3 files changed, 187 insertions(+), 21 deletions(-) create mode 100644 backend/CaseConversionAPI/CppLib/Scripts/orchestrate-native.md diff --git a/backend/CaseConversionAPI/CppLib/CMakeLists.txt b/backend/CaseConversionAPI/CppLib/CMakeLists.txt index e1bfd28..b51cd8e 100644 --- a/backend/CaseConversionAPI/CppLib/CMakeLists.txt +++ b/backend/CaseConversionAPI/CppLib/CMakeLists.txt @@ -1,6 +1,8 @@ cmake_minimum_required(VERSION 3.14) -cmake_policy(SET CMP0135 NEW) +if(POLICY CMP0135) + cmake_policy(SET CMP0135 NEW) +endif() # Add this near the top, after project() if(CMAKE_BUILD_TYPE STREQUAL "Debug" OR NOT CMAKE_BUILD_TYPE) diff --git a/backend/CaseConversionAPI/CppLib/Scripts/orchestrate-native.md b/backend/CaseConversionAPI/CppLib/Scripts/orchestrate-native.md new file mode 100644 index 0000000..f9ae92b --- /dev/null +++ b/backend/CaseConversionAPI/CppLib/Scripts/orchestrate-native.md @@ -0,0 +1,106 @@ +# C++ Core Orchestration Guide (Monorepo) + +This guide provides the end-to-end instructions for building, optimizing, and testing the high-performance C++ engine across different environments using the orchestrate-native.sh script. + +## 1. macOS (Local / Native) + +Targeting Apple M2 Silicon for maximum performance. + +### Setup + +1. Install Tooling: Ensure you have CMake and the Apple Command Line Tools. + +```Bash +brew install cmake +xcode-select --install +``` + +2. Permissions: Make the script executable. + +```Bash +chmod +x backend/CaseConversionAPI/CppLib/Scripts/orchestrate-native.sh +``` + +Run + +Run directly from the project root: + +```Bash +./backend/CaseConversionAPI/CppLib/Scripts/orchestrate-native.sh macos-latest +``` + +### The Fix: Purge the Cache + +You must delete the existing build artifacts before switching environments. Run this in your Mac terminal: + +```Bash +# 1. Clear the corrupted cache +rm -rf backend/CaseConversionAPI/CppLib/build/* + +# 2. Run the script again +./backend/CaseConversionAPI/CppLib/Scripts/orchestrate-native.sh macos-latest +``` + +## 2. Linux (Docker) + +Targeting a clean Ubuntu environment for cloud-native validation. + +Setup (Initial Login) + +Build the Environment: + +```Bash +docker build -t cpp-linux-env -f - < /dev/null && pwd )" +if [ "$#" -ne 1 ]; then + echo "Usage: $0 " + echo "Example: $0 macos-latest | ubuntu-latest | windows-latest" + exit 1 +fi + +OS_TARGET=$(echo "$1" | tr '[:upper:]' '[:lower:]') +echo "Targeting Matrix OS: $OS_TARGET" -# Target the CppLib root (one level up from Scripts/) +# ------------------------------- +# 2. Path Synchronization +# ------------------------------- +SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )" CPP_ROOT="$SCRIPT_DIR/.." cd "$CPP_ROOT" # ------------------------------- -# 2. Compilation Layer +# 3. OS-Specific Configuration +# ------------------------------- +# Initialize CMAKE_ARGS as an array to handle spaces safely +CMAKE_ARGS=("-DCMAKE_BUILD_TYPE=Release") + +if [[ "$OS_TARGET" == *"macos"* ]]; then + echo "Setting up Apple Silicon / macOS optimization..." + CMAKE_ARGS+=("-DARCH_ARM64=ON" "-DUSE_PCORES=ON") + +elif [[ "$OS_TARGET" == *"ubuntu"* || "$OS_TARGET" == *"linux"* ]]; then + echo "Setting up Linux / GCC optimization..." + CMAKE_ARGS+=("-DCMAKE_CXX_FLAGS=-O3 -march=native" "-G" "Unix Makefiles") + +elif [[ "$OS_TARGET" == *"windows"* ]]; then + # Check if we are inside the Docker container (where MinGW lives) + if command -v x86_64-w64-mingw32-gcc &> /dev/null; then + echo "Setting up MinGW / Windows cross-compilation..." + CMAKE_ARGS+=("-G" "Unix Makefiles") + CMAKE_ARGS+=("-DCMAKE_SYSTEM_NAME=Windows") + CMAKE_ARGS+=("-DCMAKE_C_COMPILER=x86_64-w64-mingw32-gcc") + CMAKE_ARGS+=("-DCMAKE_CXX_COMPILER=x86_64-w64-mingw32-g++") + else + echo "Setting up MSVC / Windows optimization..." + CMAKE_ARGS+=("-G" "Visual Studio 17 2022" "-A" "x64") + fi + +else + echo "Warning: Unrecognized OS target. Using generic build flags." +fi +# ------------------------------- +# 4. Compilation Layer # ------------------------------- -echo "===== Configuring & Building C++ Core =====" +echo "===== Configuring & Building C++ Core ($OS_TARGET) =====" -# Create build directory relative to CppLib root mkdir -p build cd build -# Configure and Build -cmake .. -DCMAKE_BUILD_TYPE=Release -cmake --build . --config Release --parallel +# Use the array expansion syntax to preserve argument boundaries +cmake .. "${CMAKE_ARGS[@]}" + +# Detect available cores for max performance +NUM_CORES=$(nproc 2>/dev/null || sysctl -n hw.ncpu || echo 1) +echo "Utilizing $NUM_CORES cores for parallel build..." + +cmake --build . --config Release --parallel "$NUM_CORES" # ------------------------------- -# 3. Execution Layer +# 5. Execution Layer # ------------------------------- -echo -e "\n===== Running Core Tests =====" -# This is the primary validation for your C++ logic -./runTests +# Skip execution if we are cross-compiling Windows binaries on a Linux host +if [[ "$OS_TARGET" == *"windows"* ]] && [[ "$(uname)" == "Linux" ]]; then + echo -e "\n[Notice] Windows binary built successfully. Skipping execution on Linux host." +else + echo -e "\n===== Running Core Tests =====" + + if [ -f "./runTests" ]; then + ./runTests + elif [ -f "./runTests.exe" ]; then + ./runTests.exe + elif [ -f "./Release/runTests.exe" ]; then + ./Release/runTests.exe + else + echo "Error: Test binary not found. Checking directory: $(pwd)" + ls -R + exit 1 + fi +fi # ------------------------------- -# 4. Workspace Restoration +# 6. Workspace Restoration # ------------------------------- -# Return to the monorepo root cd ../../../.. echo -e "\n===== Completed. Back in project root: $(pwd) =====" \ No newline at end of file From 31299e8202fee3f18e0843830afa42b6a44bcfb7 Mon Sep 17 00:00:00 2001 From: Nitish Singh <93253740+nitishhsinghhh@users.noreply.github.com> Date: Sun, 3 May 2026 04:22:03 +0530 Subject: [PATCH 05/14] style: fix clang-format violations in ProcessStringDLL --- backend/CaseConversionAPI/CppLib/src/ProcessStringDLL.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/backend/CaseConversionAPI/CppLib/src/ProcessStringDLL.cpp b/backend/CaseConversionAPI/CppLib/src/ProcessStringDLL.cpp index b91591b..d728a3d 100644 --- a/backend/CaseConversionAPI/CppLib/src/ProcessStringDLL.cpp +++ b/backend/CaseConversionAPI/CppLib/src/ProcessStringDLL.cpp @@ -181,8 +181,9 @@ API const char *processStringDLL(const char *input, int choiceInt, } API void freeString(char *str) { - if (str) + if (str) { std::free(str); + } } } // extern "C" \ No newline at end of file From 5f1ca0d32cfa007c646d982e65d11d1dbd9a702f Mon Sep 17 00:00:00 2001 From: Nitish Singh <93253740+nitishhsinghhh@users.noreply.github.com> Date: Sun, 3 May 2026 22:16:48 +0530 Subject: [PATCH 06/14] ci: add automated GitHub Actions cache cleanup workflow --- .github/workflows/cleanup-cache.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/cleanup-cache.yml b/.github/workflows/cleanup-cache.yml index 3047fc3..91be4aa 100644 --- a/.github/workflows/cleanup-cache.yml +++ b/.github/workflows/cleanup-cache.yml @@ -25,6 +25,10 @@ on: - cron: '0 0 * * *' # Daily at midnight UTC workflow_dispatch: {} +permissions: + actions: write # Required to delete caches + contents: read # Required to read repository metadata + jobs: cleanup: runs-on: ubuntu-latest From 3a8ebd707e1dcff58041b14c8749c441891f423b Mon Sep 17 00:00:00 2001 From: Nitish Singh <93253740+nitishhsinghhh@users.noreply.github.com> Date: Sun, 3 May 2026 22:35:01 +0530 Subject: [PATCH 07/14] ci: add automated GitHub Actions cache cleanup workflow --- .github/workflows/cleanup-cache.yml | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/.github/workflows/cleanup-cache.yml b/.github/workflows/cleanup-cache.yml index 91be4aa..9717b08 100644 --- a/.github/workflows/cleanup-cache.yml +++ b/.github/workflows/cleanup-cache.yml @@ -25,45 +25,46 @@ on: - cron: '0 0 * * *' # Daily at midnight UTC workflow_dispatch: {} +# Global permissions to fix "Exit Code 4" and CodeQL alerts permissions: - actions: write # Required to delete caches - contents: read # Required to read repository metadata + actions: write # Crucial: Allows the 'gh' CLI to delete caches + contents: read # Standard: Allows reading repo metadata jobs: cleanup: runs-on: ubuntu-latest steps: - # ------------------------------------------------------------ - # Install GitHub CLI Cache Extension - # ------------------------------------------------------------ - name: Install Cache Extension run: | gh extension install actions/gh-actions-cache - # ------------------------------------------------------------ - # Cleanup Stale Caches (>7 days old) - # ------------------------------------------------------------ - name: Delete Old Caches env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GH_TOKEN: ${{ secrets.SECRET_GITHUB_TOKEN }} run: | REPO=${{ github.repository }} - echo "Fetching cache list..." + echo "Fetching cache list for $REPO..." + # Fetch the list of caches gh actions-cache list -R $REPO --limit 1000 > caches.txt while read -r line; do + # Extract Cache Key and Last Used Timestamp KEY=$(echo $line | awk '{print $1}') LAST_USED=$(echo $line | awk '{print $3" "$4}') + # Convert to Timestamps LAST_USED_TS=$(date -d "$LAST_USED" +%s || echo 0) NOW_TS=$(date +%s) + # Calculate Age in Days AGE=$(( (NOW_TS - LAST_USED_TS) / 86400 )) if [ "$AGE" -gt 7 ]; then - echo "Deleting cache: $KEY (Age: $AGE days)" + echo "Deleting stale cache: $KEY (Age: $AGE days)" gh actions-cache delete "$KEY" -R $REPO --confirm + else + echo "Skipping active cache: $KEY (Age: $AGE days)" fi done < caches.txt \ No newline at end of file From 3105952773e70eb7be5763c15aca4494217ddf79 Mon Sep 17 00:00:00 2001 From: Nitish Singh <93253740+nitishhsinghhh@users.noreply.github.com> Date: Mon, 4 May 2026 01:00:15 +0530 Subject: [PATCH 08/14] Resolves CodeQL missing-workflow-permissions alert by defining an explicit permissions block. This fixes the authorization failure (exit code 4) encountered when the GitHub CLI attempts to prune stale artifacts. --- .github/workflows/cleanup-cache.yml | 5 ++- ITERATIONS/README.md | 49 +++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 3 deletions(-) diff --git a/.github/workflows/cleanup-cache.yml b/.github/workflows/cleanup-cache.yml index 9717b08..a67fe61 100644 --- a/.github/workflows/cleanup-cache.yml +++ b/.github/workflows/cleanup-cache.yml @@ -25,10 +25,9 @@ on: - cron: '0 0 * * *' # Daily at midnight UTC workflow_dispatch: {} -# Global permissions to fix "Exit Code 4" and CodeQL alerts permissions: - actions: write # Crucial: Allows the 'gh' CLI to delete caches - contents: read # Standard: Allows reading repo metadata + actions: write + contents: read jobs: cleanup: diff --git a/ITERATIONS/README.md b/ITERATIONS/README.md index c2ff28c..05be4eb 100644 --- a/ITERATIONS/README.md +++ b/ITERATIONS/README.md @@ -88,3 +88,52 @@ docker run --rm -i \ -v $(pwd):/results \ grafana/k6 run --summary-export=/results/result.json - VUs (Concurrency) + 1 4 8 20 100 +``` + +--- + +### Key Takeaways for 1.5M / 100 VUs + +- Massive Concurrency: Even when the system is under extreme artificial pressure (100 VUs fighting for 8 physical cores), the engine maintains a throughput that can support over 35,000 active humans simultaneously. + +- Infrastructure Efficiency: Processing 1.5 million requests with zero failures on a fanless MacBook Air M2 demonstrates that the native C++ core eliminates the overhead typically associated with high-frequency string processing in managed languages. + +- Hardware Saturation: The jump in p95 latency to 41ms (from 2.66ms at lower loads) indicates that while the CPU is fully saturated, the system remains highly responsive for web standards—95% of users still experience "sub-50ms" response times. + +### Final Verdict + +This engine, running on consumer-grade Apple Silicon, is capable of powering a Tier-1 Web Service backend. It provides the throughput of a distributed cluster within a single, highly optimized polyglot process. From 457bd6c3100c962f52eb80a6e25be656e22eecd3 Mon Sep 17 00:00:00 2001 From: Nitish Singh <93253740+nitishhsinghhh@users.noreply.github.com> Date: Mon, 4 May 2026 11:23:33 +0530 Subject: [PATCH 09/14] ci: finalize cache cleanup workflow with correct permissions and token scope --- .github/workflows/cleanup-cache.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/cleanup-cache.yml b/.github/workflows/cleanup-cache.yml index a67fe61..a2962c6 100644 --- a/.github/workflows/cleanup-cache.yml +++ b/.github/workflows/cleanup-cache.yml @@ -40,7 +40,8 @@ jobs: - name: Delete Old Caches env: - GH_TOKEN: ${{ secrets.SECRET_GITHUB_TOKEN }} + # Change SECRET_GITHUB_TOKEN to GITHUB_TOKEN + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | REPO=${{ github.repository }} From 4d6c7634499b1ccdc42800b04d63f34b7370e23d Mon Sep 17 00:00:00 2001 From: Nitish Singh <93253740+nitishhsinghhh@users.noreply.github.com> Date: Mon, 4 May 2026 11:28:12 +0530 Subject: [PATCH 10/14] ci: finalize cache cleanup workflow --- .github/workflows/cleanup-cache.yml | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/.github/workflows/cleanup-cache.yml b/.github/workflows/cleanup-cache.yml index a2962c6..8ff046c 100644 --- a/.github/workflows/cleanup-cache.yml +++ b/.github/workflows/cleanup-cache.yml @@ -32,6 +32,8 @@ permissions: jobs: cleanup: runs-on: ubuntu-latest + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} steps: - name: Install Cache Extension @@ -39,26 +41,18 @@ jobs: gh extension install actions/gh-actions-cache - name: Delete Old Caches - env: - # Change SECRET_GITHUB_TOKEN to GITHUB_TOKEN - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | REPO=${{ github.repository }} - echo "Fetching cache list for $REPO..." - # Fetch the list of caches + gh actions-cache list -R $REPO --limit 1000 > caches.txt while read -r line; do - # Extract Cache Key and Last Used Timestamp KEY=$(echo $line | awk '{print $1}') LAST_USED=$(echo $line | awk '{print $3" "$4}') - # Convert to Timestamps LAST_USED_TS=$(date -d "$LAST_USED" +%s || echo 0) NOW_TS=$(date +%s) - - # Calculate Age in Days AGE=$(( (NOW_TS - LAST_USED_TS) / 86400 )) if [ "$AGE" -gt 7 ]; then From d3456b2b593a2a8b1baf35c27b6705cef0f1c62e Mon Sep 17 00:00:00 2001 From: Nitish Singh <93253740+nitishhsinghhh@users.noreply.github.com> Date: Mon, 4 May 2026 15:08:27 +0530 Subject: [PATCH 11/14] ci: finalize cache cleanup workflow --- .github/workflows/cleanup-cache.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/cleanup-cache.yml b/.github/workflows/cleanup-cache.yml index 8ff046c..507e9b9 100644 --- a/.github/workflows/cleanup-cache.yml +++ b/.github/workflows/cleanup-cache.yml @@ -45,7 +45,7 @@ jobs: REPO=${{ github.repository }} echo "Fetching cache list for $REPO..." - gh actions-cache list -R $REPO --limit 1000 > caches.txt + gh actions-cache list -R $REPO --limit 100 > caches.txt while read -r line; do KEY=$(echo $line | awk '{print $1}') From a3f32613a24b007c7d110a970c84cd49830b53b0 Mon Sep 17 00:00:00 2001 From: Nitish Singh <93253740+nitishhsinghhh@users.noreply.github.com> Date: Mon, 4 May 2026 15:27:09 +0530 Subject: [PATCH 12/14] ci: finalize cache cleanup workflow --- .github/workflows/cleanup-cache.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/cleanup-cache.yml b/.github/workflows/cleanup-cache.yml index 507e9b9..922b331 100644 --- a/.github/workflows/cleanup-cache.yml +++ b/.github/workflows/cleanup-cache.yml @@ -22,7 +22,7 @@ name: Cache Cleanup on: schedule: - - cron: '0 0 * * *' # Daily at midnight UTC + - cron: '30 18 * * *' # Midnight IST (18:30 UTC) workflow_dispatch: {} permissions: From b1d7aa86fc486231b279d1222fb2ad4c62f59c75 Mon Sep 17 00:00:00 2001 From: Nitish Singh <93253740+nitishhsinghhh@users.noreply.github.com> Date: Mon, 4 May 2026 16:44:02 +0530 Subject: [PATCH 13/14] Minor DLL changes --- .../CppLib/include/ProcessStringDLL.hpp | 2 +- .../CppLib/src/ProcessStringDLL.cpp | 17 ++++++++++------- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/backend/CaseConversionAPI/CppLib/include/ProcessStringDLL.hpp b/backend/CaseConversionAPI/CppLib/include/ProcessStringDLL.hpp index 97b2dc8..1456b92 100644 --- a/backend/CaseConversionAPI/CppLib/include/ProcessStringDLL.hpp +++ b/backend/CaseConversionAPI/CppLib/include/ProcessStringDLL.hpp @@ -47,7 +47,7 @@ extern "C" { #endif /** - * @brief COnverts input string based on choice and returns a newly allocated + * @brief Converts input string based on choice and returns a newly allocated * C-string. * * @param input C-string input. diff --git a/backend/CaseConversionAPI/CppLib/src/ProcessStringDLL.cpp b/backend/CaseConversionAPI/CppLib/src/ProcessStringDLL.cpp index d728a3d..e8d8e1b 100644 --- a/backend/CaseConversionAPI/CppLib/src/ProcessStringDLL.cpp +++ b/backend/CaseConversionAPI/CppLib/src/ProcessStringDLL.cpp @@ -78,7 +78,7 @@ static const char *safeError(const char *msg) { // Conversion Mapping (Internal - C++ only) //=================================================================== -static bool mapConversionType(ConversionChoice choice, ConversionType &type) { +static bool mapConversionType(ConversionChoice choice, ConversionType &type) noexcept{ switch (choice) { case ConversionChoice::Alternating: type = ConversionType::Alternating; @@ -133,9 +133,10 @@ extern "C" { API const char *processStringDLL(const char *input, int choiceInt, const char *traceId) { try { - if (!input) + if (!input) { return safeError("ERROR_NULL_INPUT"); - + } + size_t inputLength = std::strlen(input); if (inputLength > MAX_INPUT_SIZE) { return safeError("ERROR_BUFFER_OVERFLOW_LIMIT_5MB"); @@ -162,15 +163,17 @@ API const char *processStringDLL(const char *input, int choiceInt, ConversionResult result = client.execute(std::string(input)); const char *rawPtr = result.get_c_str(); - if (!rawPtr) + if (!rawPtr) { return safeError("ERROR_ENGINE_RETURNED_NULL"); - + } + size_t resLen = std::strlen(rawPtr); char *output = static_cast(std::malloc(resLen + 1)); - if (!output) + if (!output) { return safeError("FATAL_ALLOCATION_FAILURE"); - + } + std::memcpy(output, rawPtr, resLen + 1); return output; From 8a643126f2ab3526b911f45a2e223938b1eae4a6 Mon Sep 17 00:00:00 2001 From: Nitish Singh <93253740+nitishhsinghhh@users.noreply.github.com> Date: Mon, 4 May 2026 18:13:22 +0530 Subject: [PATCH 14/14] Resolved merge conflicts for handover --- .../CaseConversionAPI/CppLib/src/ProcessStringDLL.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/backend/CaseConversionAPI/CppLib/src/ProcessStringDLL.cpp b/backend/CaseConversionAPI/CppLib/src/ProcessStringDLL.cpp index e8d8e1b..0e69ac5 100644 --- a/backend/CaseConversionAPI/CppLib/src/ProcessStringDLL.cpp +++ b/backend/CaseConversionAPI/CppLib/src/ProcessStringDLL.cpp @@ -78,7 +78,8 @@ static const char *safeError(const char *msg) { // Conversion Mapping (Internal - C++ only) //=================================================================== -static bool mapConversionType(ConversionChoice choice, ConversionType &type) noexcept{ +static bool mapConversionType(ConversionChoice choice, + ConversionType &type) noexcept { switch (choice) { case ConversionChoice::Alternating: type = ConversionType::Alternating; @@ -136,7 +137,7 @@ API const char *processStringDLL(const char *input, int choiceInt, if (!input) { return safeError("ERROR_NULL_INPUT"); } - + size_t inputLength = std::strlen(input); if (inputLength > MAX_INPUT_SIZE) { return safeError("ERROR_BUFFER_OVERFLOW_LIMIT_5MB"); @@ -166,14 +167,14 @@ API const char *processStringDLL(const char *input, int choiceInt, if (!rawPtr) { return safeError("ERROR_ENGINE_RETURNED_NULL"); } - + size_t resLen = std::strlen(rawPtr); char *output = static_cast(std::malloc(resLen + 1)); if (!output) { return safeError("FATAL_ALLOCATION_FAILURE"); } - + std::memcpy(output, rawPtr, resLen + 1); return output;