Foundations
This commit is contained in:
@@ -0,0 +1,193 @@
|
||||
#include <ctype.h>
|
||||
#include <c_StrUtil.h>
|
||||
#include <c_Memory.h>
|
||||
|
||||
#include "c_ArrayList.h"
|
||||
|
||||
// Reverses a string in-place
|
||||
void c_StrUtil_Reverse(char* str) {
|
||||
if (!str) return;
|
||||
const c_size_t len = strlen(str);
|
||||
if (len <= 1) return;
|
||||
|
||||
c_size_t i = 0;
|
||||
c_size_t j = len - 1;
|
||||
while (i < j) {
|
||||
char temp = str[i];
|
||||
str[i] = str[j];
|
||||
str[j] = temp;
|
||||
i++;
|
||||
j--;
|
||||
}
|
||||
}
|
||||
|
||||
// Converts a string to lowercase into a destination buffer
|
||||
c_err_t c_StrUtil_ToLower(char* out_dest, const char* src, c_size_t dest_capacity) {
|
||||
if (!out_dest || !src || dest_capacity == 0) return C_ERR_PARAM;
|
||||
|
||||
c_size_t i = 0;
|
||||
while (src[i] != '\0') {
|
||||
if (i >= dest_capacity - 1) {
|
||||
out_dest[i] = '\0';
|
||||
return C_ERR_FAIL;
|
||||
}
|
||||
out_dest[i] = (char)tolower((unsigned char)src[i]);
|
||||
i++;
|
||||
}
|
||||
out_dest[i] = '\0';
|
||||
return C_ERR_OK;
|
||||
}
|
||||
|
||||
// Converts a string to uppercase into a destination buffer
|
||||
c_err_t c_StrUtil_ToUpper(char* out_dest, const char* src, c_size_t dest_capacity) {
|
||||
if (!out_dest || !src || dest_capacity == 0) return C_ERR_PARAM;
|
||||
|
||||
c_size_t i = 0;
|
||||
while (src[i] != '\0') {
|
||||
if (i >= dest_capacity - 1) {
|
||||
out_dest[i] = '\0';
|
||||
return C_ERR_FAIL;
|
||||
}
|
||||
out_dest[i] = (char)toupper((unsigned char)src[i]);
|
||||
i++;
|
||||
}
|
||||
out_dest[i] = '\0';
|
||||
return C_ERR_OK;
|
||||
}
|
||||
|
||||
// Removes leading and trailing whitespaces into a destination buffer
|
||||
c_err_t c_StrUtil_Trim(char* out_dest, const char* src, c_size_t dest_capacity) {
|
||||
if (!out_dest || !src || dest_capacity == 0) return C_ERR_PARAM;
|
||||
|
||||
// Find first non-whitespace character
|
||||
while (*src && isspace((unsigned char)*src)) {
|
||||
src++;
|
||||
}
|
||||
|
||||
// Find the end of the string
|
||||
c_size_t len = strlen(src);
|
||||
while (len > 0 && isspace((unsigned char)src[len - 1])) {
|
||||
len--;
|
||||
}
|
||||
|
||||
if (len >= dest_capacity) {
|
||||
return C_ERR_FAIL;
|
||||
}
|
||||
|
||||
// Copy trimmed substring
|
||||
memmove(out_dest, src, len);
|
||||
out_dest[len] = '\0';
|
||||
return C_ERR_OK;
|
||||
}
|
||||
|
||||
c_bool_t c_StrUtil_StartsWith(const char* str, const char* prefix) {
|
||||
if (!str || !prefix) return C_FALSE;
|
||||
return strncmp(str, prefix, strlen(prefix)) == 0 ? C_TRUE : C_FALSE;
|
||||
}
|
||||
|
||||
c_bool_t c_StrUtil_EndsWith(const char* str, const char* suffix) {
|
||||
if (!str || !suffix) return C_FALSE;
|
||||
c_size_t str_len = strlen(str);
|
||||
c_size_t suf_len = strlen(suffix);
|
||||
if (suf_len > str_len) return C_FALSE;
|
||||
return strcmp(str + str_len - suf_len, suffix) == 0 ? C_TRUE : C_FALSE;
|
||||
}
|
||||
|
||||
// Safely extracts a substring with full bounds verification
|
||||
c_err_t c_StrUtil_Substring(char* out_dest, const char* src, c_size_t start, c_size_t len, c_size_t dest_capacity) {
|
||||
if (!out_dest || !src || dest_capacity == 0) return C_ERR_PARAM;
|
||||
|
||||
c_size_t src_len = strlen(src);
|
||||
if (start > src_len) return C_ERR_OUTOFBOUND;
|
||||
|
||||
// Adjust requested length if it overflows source string boundaries
|
||||
if (start + len > src_len) {
|
||||
len = src_len - start;
|
||||
}
|
||||
|
||||
if (len >= dest_capacity) {
|
||||
return C_ERR_OUTOFBOUND;
|
||||
}
|
||||
|
||||
memcpy(out_dest, src + start, len);
|
||||
out_dest[len] = '\0';
|
||||
return C_ERR_OK;
|
||||
}
|
||||
|
||||
// Replaces occurrences of a substring inside a buffer safely
|
||||
c_err_t c_StrUtil_Replace(char* out_dest, const char* src, const char* find, const char* replace_with, c_size_t dest_capacity) {
|
||||
if (!out_dest || !src || !find || !replace_with || dest_capacity == 0) return C_ERR_PARAM;
|
||||
|
||||
c_size_t find_len = strlen(find);
|
||||
c_size_t replace_len = strlen(replace_with);
|
||||
c_size_t dest_len = 0;
|
||||
|
||||
if (find_len == 0) return C_ERR_PARAM;
|
||||
|
||||
while (*src) {
|
||||
// If match found, inject replacement string
|
||||
if (strncmp(src, find, find_len) == 0) {
|
||||
if (dest_len + replace_len >= dest_capacity) {
|
||||
out_dest[dest_len] = '\0';
|
||||
return C_ERR_OUTOFBOUND;
|
||||
}
|
||||
strcpy(out_dest + dest_len, replace_with);
|
||||
dest_len += replace_len;
|
||||
src += find_len;
|
||||
} else {
|
||||
// Otherwise, inject individual source character
|
||||
if (dest_len + 1 >= dest_capacity) {
|
||||
out_dest[dest_len] = '\0';
|
||||
return C_ERR_OUTOFBOUND;
|
||||
}
|
||||
out_dest[dest_len] = *src;
|
||||
dest_len++;
|
||||
src++;
|
||||
}
|
||||
}
|
||||
out_dest[dest_len] = '\0';
|
||||
return C_ERR_OK;
|
||||
}
|
||||
|
||||
c_err_t c_StrUtil_Split(c_ArrayList_t* out_list, const char* src, const char* delimiter) {
|
||||
if (!out_list || !src || !delimiter) return C_ERR_PARAM;
|
||||
if (out_list->obj_size != sizeof(c_StrToken_t)) return C_ERR_PARAM; // Type safety assert
|
||||
|
||||
const c_size_t delim_len = strlen(delimiter);
|
||||
if (delim_len == 0) return C_ERR_PARAM;
|
||||
|
||||
const char* current = src;
|
||||
const char* next_match;
|
||||
|
||||
while ((next_match = strstr(current, delimiter)) != NULL) {
|
||||
size_t token_len = next_match - current;
|
||||
|
||||
if (token_len > 0) {
|
||||
c_StrToken_t new_token;
|
||||
// Bound checking to ensure long strings don't cause buffer overflows
|
||||
size_t copy_len = (token_len >= C_STR_TOKEN_MAX_LEN) ? (C_STR_TOKEN_MAX_LEN - 1) : token_len;
|
||||
|
||||
memcpy(new_token.text, current, copy_len);
|
||||
new_token.text[copy_len] = '\0';
|
||||
|
||||
// Deep-copy token structure payload straight into the dynamic ArrayList
|
||||
c_ArrayList_Add(out_list, &new_token);
|
||||
}
|
||||
current = next_match + delim_len;
|
||||
}
|
||||
|
||||
// Capture the final token remaining after the last delimiter match
|
||||
if (*current != '\0') {
|
||||
c_StrToken_t new_token;
|
||||
size_t token_len = strlen(current);
|
||||
size_t copy_len = (token_len >= C_STR_TOKEN_MAX_LEN) ? (C_STR_TOKEN_MAX_LEN - 1) : token_len;
|
||||
|
||||
memcpy(new_token.text, current, copy_len);
|
||||
new_token.text[copy_len] = '\0';
|
||||
|
||||
c_ArrayList_Add(out_list, &new_token);
|
||||
}
|
||||
|
||||
return C_ERR_OK;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user