add cx_strat()

Thu, 25 Dec 2025 11:39:26 +0100

author
Mike Becker <universe@uap-core.de>
date
Thu, 25 Dec 2025 11:39:26 +0100
changeset 1668
3ffdfe1776b4
parent 1667
608cc0b25352
child 1669
d416628d6c7d

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);

mercurial