Thu, 25 Dec 2025 11:39:26 +0100
add cx_strat()
| CHANGELOG | file | annotate | diff | comparison | revisions | |
| docs/Writerside/topics/about.md | file | annotate | diff | comparison | revisions | |
| docs/Writerside/topics/string.h.md | file | annotate | diff | comparison | revisions | |
| src/cx/string.h | file | annotate | diff | comparison | revisions | |
| src/json.c | file | annotate | diff | comparison | revisions | |
| src/properties.c | file | annotate | diff | comparison | revisions | |
| src/string.c | file | annotate | diff | comparison | revisions | |
| tests/test_string.c | file | annotate | diff | comparison | revisions |
--- a/CHANGELOG Thu Dec 25 11:10:13 2025 +0100 +++ b/CHANGELOG Thu Dec 25 11:39:26 2025 +0100 @@ -11,6 +11,7 @@ * adds line continuation support to CxProperties / CxPropertiesConfig * adds cx_hash_key_as_string() * adds support for CxHashKey pointers in CX_HASH_KEY() and all map functions + * adds cx_strat() * adds cx_bstr() and cx_bstr_m() * adds cxBufferMaximumCapacity() * adds cxBufferAppendString()
--- a/docs/Writerside/topics/about.md Thu Dec 25 11:10:13 2025 +0100 +++ b/docs/Writerside/topics/about.md Thu Dec 25 11:39:26 2025 +0100 @@ -38,6 +38,7 @@ * adds line continuation support to CxProperties / CxPropertiesConfig * adds cx_hash_key_as_string() * adds support for CxHashKey pointers in CX_HASH_KEY() and all map functions +* adds cx_strat() * adds cx_bstr() and cx_bstr_m() * adds cxBufferMaximumCapacity() * adds cxBufferAppendString()
--- a/docs/Writerside/topics/string.h.md Thu Dec 25 11:10:13 2025 +0100 +++ b/docs/Writerside/topics/string.h.md Thu Dec 25 11:39:26 2025 +0100 @@ -150,6 +150,8 @@ ```C #include <cx/string.h> +char cx_strat(cxstring str, off_t index); + cxstring cx_strchr(cxstring string, int chr); cxstring cx_strrchr(cxstring string, int chr); @@ -175,6 +177,10 @@ cxmutstr cx_strtrim_m(cxmutstr string); ``` +The function `cx_strat()` returns the character at the specified `index`. +When the `index` is negative, the characters are counted from the end of the string. +When the `index` is out of bounds, the zero-character is returned. + The functions `cx_strchr()`, `cx_strrchr()`, and `cx_strstr()`, behave like their stdlib counterparts. The function `cx_strsubs()` returns the substring starting at the specified `start` index,
--- a/src/cx/string.h Thu Dec 25 11:10:13 2025 +0100 +++ b/src/cx/string.h Thu Dec 25 11:39:26 2025 +0100 @@ -531,6 +531,44 @@ CX_EXPORT cxmutstr cx_strsubsl_m(cxmutstr string, size_t start, size_t length); /** + * Returns the character at the specified index offset. + * + * Internal function - do not use. + * + * @param str the string + * @param index the index offset + * @return the character at the index + * @see cx_strat() + */ +CX_INLINE char cx_strat_(cxstring str, off_t index) { + size_t i; + if (index >= 0) { + i = index; + } else { + i = (size_t) (str.length + index); + } + if (i >= str.length) { + return '\0'; + } + return str.ptr[i]; +} + +/** + * Returns the character at the specified index offset. + * + * When the @p index is negative, the character is counted from the end of the + * string where -1 denotes the last character in the string. + * + * When the @p index is out of bounds, the function returns zero. + * + * @param str the string + * @param index the index offset + * @return the character at the index + * @see cx_strat() + */ +#define cx_strat(str, index) cx_strat_(cx_strcast(str), index) + +/** * Returns a substring starting at the location of the first occurrence of the * specified character. *
--- a/src/json.c Thu Dec 25 11:10:13 2025 +0100 +++ b/src/json.c Thu Dec 25 11:39:26 2025 +0100 @@ -175,7 +175,7 @@ size_t token_part_start = json->buffer.pos; bool escape_end_of_string = ttype == CX_JSON_TOKEN_STRING - && json->uncompleted_content.ptr[json->uncompleted_content.length-1] == '\\'; + && cx_strat(json->uncompleted_content, -1) == '\\'; for (size_t i = json->buffer.pos; i < json->buffer.size; i++) { char c = json->buffer.space[i];
--- a/src/properties.c Thu Dec 25 11:10:13 2025 +0100 +++ b/src/properties.c Thu Dec 25 11:39:26 2025 +0100 @@ -112,7 +112,7 @@ cxstring nl = cx_strchr(input, '\n'); while (nl.length > 0) { // check for line continuation - char previous = nl.ptr > input.ptr ? nl.ptr[-1] : prop->buffer.space[prop->buffer.size-1]; + char previous = nl.ptr > input.ptr ? nl.ptr[-1] : cx_strat(cx_bstr(&prop->buffer), -1); if (previous == continuation) { // this nl is a line continuation, check the next newline nl = cx_strchr(cx_strsubs(nl, 1), '\n');
--- a/src/string.c Thu Dec 25 11:10:13 2025 +0100 +++ b/src/string.c Thu Dec 25 11:39:26 2025 +0100 @@ -505,11 +505,11 @@ cxstring cx_strtrim(cxstring string) { cxstring result = string; - while (result.length > 0 && isspace((unsigned char)(result.ptr[0]))) { + while (isspace((unsigned char)cx_strat(result, 0))) { result.ptr++; result.length--; } - while (result.length > 0 && isspace((unsigned char)result.ptr[result.length - 1])) { + while (isspace((unsigned char)cx_strat(result, -1))) { result.length--; } return result;
--- a/tests/test_string.c Thu Dec 25 11:10:13 2025 +0100 +++ b/tests/test_string.c Thu Dec 25 11:39:26 2025 +0100 @@ -220,6 +220,31 @@ } } +CX_TEST(test_strat) { + cxstring str = cx_str("Hello, World!"); + + CX_TEST_DO { + // the entire string + for (size_t i = 0; i < str.length; i++) { + CX_TEST_ASSERT(cx_strat(str, i) == str.ptr[i]); + } + // the entire string backwards + for (off_t i = 1; i <= (off_t) str.length; i++) { + CX_TEST_ASSERT(cx_strat(str, -i) == str.ptr[str.length-i]); + } + // out of bounds (positive) + CX_TEST_ASSERT(cx_strat(str, 12) == '!'); + CX_TEST_ASSERT(cx_strat(str, 13) == '\0'); + CX_TEST_ASSERT(cx_strat(str, 14) == '\0'); + CX_TEST_ASSERT(cx_strat(str, 651273) == '\0'); + // out of bounds (negative) + CX_TEST_ASSERT(cx_strat(str, -13) == 'H'); + CX_TEST_ASSERT(cx_strat(str, -14) == '\0'); + CX_TEST_ASSERT(cx_strat(str, -15) == '\0'); + CX_TEST_ASSERT(cx_strat(str, -651273) == '\0'); + } +} + CX_TEST(test_strchr) { cxstring str = cx_str("I will find you - and I will kill you"); @@ -1511,6 +1536,7 @@ cx_test_register(suite, test_strcpy); cx_test_register(suite, test_strlen); cx_test_register(suite, test_strsubs); + cx_test_register(suite, test_strat); cx_test_register(suite, test_strchr); cx_test_register(suite, test_strrchr); cx_test_register(suite, test_strstr);