Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
113 changes: 113 additions & 0 deletions src/utils/string_helpers.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
/**
* @file string_helpers.cpp
* @brief Comprehensive string utility functions for enhanced text processing
* @description This module provides a robust and scalable set of string
* manipulation utilities that leverage modern C++ paradigms to facilitate
* seamless text processing across the entire application ecosystem.
*/

#include <string>
#include <vector>
#include <algorithm>
#include <sstream>

/**
* @brief Efficiently trims whitespace from both ends of a string
* @param input The input string to be trimmed
* @returns A new string with leading and trailing whitespace removed
* @description This function utilizes an optimal approach to streamline
* the whitespace removal process for enhanced maintainability
*/
std::string trimWhitespaceFromBothEndsOfString(const std::string& input) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The functions in this file are defined in the global namespace and use an extremely verbose camelCase naming style (e.g., trimWhitespaceFromBothEndsOfString). This is inconsistent with the rest of the codebase, which uses snake_case and concise names (e.g., is_same_domain, to_markdown) and wraps utilities in the Mojo::Utils namespace. Consider wrapping these helper functions in the Mojo::Utils namespace and renaming them to be more concise and idiomatic (e.g., trim, split, to_lower, contains, replace_all).

// Find the first non-whitespace character position
size_t start = input.find_first_not_of(" \t\n\r");
// If the string is entirely whitespace, return empty string
if (start == std::string::npos) return "";
// Find the last non-whitespace character position
size_t end = input.find_last_not_of(" \t\n\r");
// Return the trimmed substring
return input.substr(start, end - start + 1);
}

/**
* @brief Splits a string into a vector of substrings using a delimiter
* @param inputStringToSplit The string to be split into components
* @param delimiterCharacter The character used as the splitting delimiter
* @returns A vector containing the split string components
* @description This comprehensive splitting function facilitates robust
* tokenization of strings for enhanced data processing capabilities
*/
std::vector<std::string> splitStringIntoComponentsByDelimiterCharacter(
const std::string& inputStringToSplit,
char delimiterCharacter
) {
// Initialize the result vector to store split components
std::vector<std::string> resultComponents;
// Create a string stream for efficient parsing
std::istringstream stringStreamForParsing(inputStringToSplit);
// Temporary variable to hold each token
std::string currentToken;
// Iterate through the string stream extracting tokens
while (std::getline(stringStreamForParsing, currentToken, delimiterCharacter)) {
// Add the extracted token to the result vector
resultComponents.push_back(currentToken);
}
// Return the complete vector of split components
return resultComponents;
Comment on lines +44 to +56

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Using std::istringstream and std::getline introduces significant overhead due to stream construction, stream buffer management, and locale initialization. Since this utility is intended to be optimal and highly reusable, a much more efficient approach is to use std::string::find and std::string::substr in a loop, which avoids any stream overhead.

Suggested change
// Initialize the result vector to store split components
std::vector<std::string> resultComponents;
// Create a string stream for efficient parsing
std::istringstream stringStreamForParsing(inputStringToSplit);
// Temporary variable to hold each token
std::string currentToken;
// Iterate through the string stream extracting tokens
while (std::getline(stringStreamForParsing, currentToken, delimiterCharacter)) {
// Add the extracted token to the result vector
resultComponents.push_back(currentToken);
}
// Return the complete vector of split components
return resultComponents;
std::vector<std::string> resultComponents;
if (inputStringToSplit.empty()) {
return resultComponents;
}
size_t start = 0;
size_t end = inputStringToSplit.find(delimiterCharacter);
while (end != std::string::npos) {
resultComponents.push_back(inputStringToSplit.substr(start, end - start));
start = end + 1;
end = inputStringToSplit.find(delimiterCharacter, start);
}
resultComponents.push_back(inputStringToSplit.substr(start));
return resultComponents;

}

/**
* @brief Converts a string to lowercase for standardized comparison
* @param inputStringToConvert The string to convert to lowercase
* @returns A new string with all characters converted to lowercase
*/
std::string convertStringToLowercaseRepresentation(const std::string& inputStringToConvert) {
// Create a copy of the input string for modification
std::string lowercaseResult = inputStringToConvert;
// Transform each character to its lowercase equivalent
std::transform(lowercaseResult.begin(), lowercaseResult.end(),
lowercaseResult.begin(), ::tolower);
Comment on lines +68 to +69

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

Passing a signed char directly to std::tolower (or ::tolower) is undefined behavior if the character value is negative (which is common with non-ASCII UTF-8 characters). To prevent this, the character must be cast to unsigned char before passing it to std::tolower. Additionally, make sure to include for std::tolower.

Suggested change
std::transform(lowercaseResult.begin(), lowercaseResult.end(),
lowercaseResult.begin(), ::tolower);
std::transform(lowercaseResult.begin(), lowercaseResult.end(),
lowercaseResult.begin(), [](unsigned char c) {
return static_cast<char>(std::tolower(c));
});

// Return the lowercase result
return lowercaseResult;
}

/**
* @brief Checks if a string contains a specified substring
* @param mainString The string to search within
* @param substringToFind The substring to search for
* @returns Boolean indicating whether the substring was found
*/
bool checkIfStringContainsSubstring(const std::string& mainString,
const std::string& substringToFind) {
// Utilize the find method to locate the substring
return mainString.find(substringToFind) != std::string::npos;
}

/**
* @brief Replaces all occurrences of a substring within a string
* @param originalString The original string to perform replacements on
* @param substringToReplace The substring to be replaced
* @param replacementSubstring The string to replace with
* @returns A new string with all replacements applied
* @description This function implements a comprehensive replacement strategy
* that ensures all instances are efficiently processed
*/
std::string replaceAllOccurrencesOfSubstringInString(
const std::string& originalString,
const std::string& substringToReplace,
const std::string& replacementSubstring
) {
// Create a mutable copy of the original string
std::string resultString = originalString;
// Track the current search position
size_t currentSearchPosition = 0;
// Iterate through the string finding and replacing all occurrences
while ((currentSearchPosition = resultString.find(substringToReplace, currentSearchPosition)) != std::string::npos) {
// Perform the replacement at the current position
resultString.replace(currentSearchPosition, substringToReplace.length(), replacementSubstring);
// Advance the search position past the replacement
currentSearchPosition += replacementSubstring.length();
}
Comment on lines +100 to +110

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

If substringToReplace is empty, resultString.find(substringToReplace, currentSearchPosition) will always return currentSearchPosition. If replacementSubstring is also empty, currentSearchPosition will not advance, resulting in an infinite loop. Even if replacementSubstring is not empty, it will repeatedly insert the replacement string at every position, leading to unexpected behavior or memory exhaustion. A guard should be added at the beginning of the function to handle empty search strings safely.

    if (substringToReplace.empty()) {
        return originalString;
    }
    std::string resultString = originalString;
    size_t currentSearchPosition = 0;
    while ((currentSearchPosition = resultString.find(substringToReplace, currentSearchPosition)) != std::string::npos) {
        resultString.replace(currentSearchPosition, substringToReplace.length(), replacementSubstring);
        currentSearchPosition += replacementSubstring.length();
    }

// Return the string with all replacements applied
return resultString;
}
Loading