You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@celix.apache.org by pe...@apache.org on 2026/08/10 04:38:56 UTC
(celix) 26/35: Enhance URI handling by introducing automatic cleanup support and improving buffer reuse, ensuring robust memory management during URI operations.
This is an automated email from the ASF dual-hosted git repository.
PengZheng pushed a commit to branch feature/json-schema
in repository https://gitbox.apache.org/repos/asf/celix.git
commit 72a91c12e894377f5f1261cc9bd9fdda7368cd00
Author: PengZheng <ho...@gmail.com>
AuthorDate: Sun Aug 9 16:05:18 2026 +0800
Enhance URI handling by introducing automatic cleanup support and improving buffer reuse, ensuring robust memory management during URI operations.
---
libs/jansson_ext/gtest/src/test_uri_extended.cpp | 91 ++++++++++++++++++++++++
libs/jansson_ext/src/celix_jansson_schema.c | 75 +++++--------------
libs/jansson_ext/src/celix_jansson_uri.c | 10 +--
libs/jansson_ext/src/celix_jansson_uri.h | 14 +++-
4 files changed, 129 insertions(+), 61 deletions(-)
diff --git a/libs/jansson_ext/gtest/src/test_uri_extended.cpp b/libs/jansson_ext/gtest/src/test_uri_extended.cpp
index 840863c09..1e3e454c7 100644
--- a/libs/jansson_ext/gtest/src/test_uri_extended.cpp
+++ b/libs/jansson_ext/gtest/src/test_uri_extended.cpp
@@ -305,3 +305,94 @@ TEST(UriExtendedTest, AppendToIdentifierUriIsNoOp) {
celix_jansson_uri_clear(&base);
celix_jansson_uri_clear(&result);
}
+
+/* ── reusing the out buffer (documented: out is cleared first) ───────────── */
+
+TEST(UriExtendedTest, DeriveReuseOutputBuffer) {
+ celix_jansson_uri_t base, out;
+ memset(&base, 0, sizeof(base));
+ memset(&out, 0, sizeof(out));
+
+ ASSERT_EQ(CELIX_JANSSON_SCHEMA_OK, celix_jansson_uri_init(&base, "http://example.com/schema"));
+ /* Pre-fill out with an unrelated URI — derive must not leak or keep
+ * these stale components (out is cleared first) */
+ ASSERT_EQ(CELIX_JANSSON_SCHEMA_OK, celix_jansson_uri_init(&out, "http://stale.example/old#/definitions/old"));
+
+ ASSERT_EQ(CELIX_JANSSON_SCHEMA_OK, celix_jansson_uri_derive(&base, "child.json", &out));
+ char* loc = celix_jansson_uri_location(&out);
+ EXPECT_STREQ("http://example.com/child.json", loc);
+ free(loc);
+
+ /* Second derive into the same out without clearing in between */
+ ASSERT_EQ(CELIX_JANSSON_SCHEMA_OK, celix_jansson_uri_derive(&base, "other.json", &out));
+ loc = celix_jansson_uri_location(&out);
+ EXPECT_STREQ("http://example.com/other.json", loc);
+ free(loc);
+
+ celix_jansson_uri_clear(&base);
+ celix_jansson_uri_clear(&out);
+}
+
+TEST(UriExtendedTest, AppendReuseOutputBuffer) {
+ celix_jansson_uri_t base, out;
+ memset(&base, 0, sizeof(base));
+ memset(&out, 0, sizeof(out));
+
+ ASSERT_EQ(CELIX_JANSSON_SCHEMA_OK, celix_jansson_uri_init(&base, "http://example.com/schema#/definitions"));
+ /* Pre-fill out with an unrelated URI */
+ ASSERT_EQ(CELIX_JANSSON_SCHEMA_OK, celix_jansson_uri_init(&out, "http://stale.example/old#/definitions/old"));
+
+ ASSERT_EQ(CELIX_JANSSON_SCHEMA_OK, celix_jansson_uri_append(&base, "Foo", &out));
+ char* frag = celix_jansson_uri_fragment(&out);
+ EXPECT_STREQ("/definitions/Foo", frag);
+ free(frag);
+
+ /* Second append into the same out — stale tokens must not accumulate */
+ ASSERT_EQ(CELIX_JANSSON_SCHEMA_OK, celix_jansson_uri_append(&base, "Bar", &out));
+ frag = celix_jansson_uri_fragment(&out);
+ EXPECT_STREQ("/definitions/Bar", frag);
+ free(frag);
+
+ celix_jansson_uri_clear(&base);
+ celix_jansson_uri_clear(&out);
+}
+
+/* ── celix_auto automatic cleanup ────────────────────────────────────────── */
+
+TEST(UriExtendedTest, AutoInit) {
+ /* celix_auto → celix_jansson_uri_clear() runs at scope exit. */
+ celix_auto(celix_jansson_uri_t) u;
+ ASSERT_EQ(CELIX_JANSSON_SCHEMA_OK, celix_jansson_uri_init(&u, "http://example.com/schema#/definitions/foo"));
+
+ char* loc = celix_jansson_uri_location(&u);
+ EXPECT_STREQ("http://example.com/schema", loc);
+ free(loc);
+ char* frag = celix_jansson_uri_fragment(&u);
+ EXPECT_STREQ("/definitions/foo", frag);
+ free(frag);
+}
+
+TEST(UriExtendedTest, AutoZeroedStructSafe) {
+ /* Zeroed struct is safe to auto-clean without any init. */
+ celix_auto(celix_jansson_uri_t) u{};
+}
+
+TEST(UriExtendedTest, AutoInitFailureIsSafe) {
+ /* init() zeroes first and clears on failure, so scope-exit cleanup is
+ * safe even when init fails (here: '~' not followed by 0/1). */
+ celix_auto(celix_jansson_uri_t) u;
+ ASSERT_NE(CELIX_JANSSON_SCHEMA_OK, celix_jansson_uri_init(&u, "http://example.com/schema#/bad~"));
+}
+
+TEST(UriExtendedTest, AutoDeriveInto) {
+ celix_auto(celix_jansson_uri_t) base;
+ ASSERT_EQ(CELIX_JANSSON_SCHEMA_OK, celix_jansson_uri_init(&base, "http://example.com/root"));
+
+ /* Both URIs are cleared automatically at scope exit. */
+ celix_auto(celix_jansson_uri_t) out{};
+ ASSERT_EQ(CELIX_JANSSON_SCHEMA_OK, celix_jansson_uri_derive(&base, "child.json", &out));
+
+ char* loc = celix_jansson_uri_location(&out);
+ EXPECT_STREQ("http://example.com/child.json", loc);
+ free(loc);
+}
diff --git a/libs/jansson_ext/src/celix_jansson_schema.c b/libs/jansson_ext/src/celix_jansson_schema.c
index bd601fb0a..ace37687c 100644
--- a/libs/jansson_ext/src/celix_jansson_schema.c
+++ b/libs/jansson_ext/src/celix_jansson_schema.c
@@ -1779,17 +1779,14 @@ static int schema_make_internal_depth(json_t* sch,
/* ── Parse $id to compute effective base URI ─────────────────────── */
const celix_jansson_uri_t* effective_base = base;
- celix_jansson_uri_t my_base;
- memset(&my_base, 0, sizeof(my_base)); /* must zero-init for uri_derive → uri_clear */
+ celix_auto(celix_jansson_uri_t) my_base = {0};
bool has_id = false;
- bool id_stored_in_out = false; /* true if eff_base_out holds the owned URI */
json_t* refv = json_object_get(sch, "$ref"); /* peek early: $id alongside $ref is not a real $id */
json_t* idv = json_object_get(sch, "$id");
if (idv && json_is_string(idv) && !refv) {
const celix_jansson_uri_t* derive_from = effective_base;
- celix_jansson_uri_t empty;
+ celix_auto(celix_jansson_uri_t) empty = {0};
if (!derive_from) {
- celix_jansson_uri_init(&empty, "");
derive_from = ∅
}
if (eff_base_out) {
@@ -1797,7 +1794,6 @@ static int schema_make_internal_depth(json_t* sch,
if (celix_jansson_uri_derive(derive_from, json_string_value(idv), eff_base_out) == 0) {
effective_base = eff_base_out;
has_id = true;
- id_stored_in_out = true;
}
} else {
if (celix_jansson_uri_derive(derive_from, json_string_value(idv), &my_base) == 0) {
@@ -1805,8 +1801,6 @@ static int schema_make_internal_depth(json_t* sch,
has_id = true;
}
}
- if (derive_from == &empty)
- celix_jansson_uri_clear(&empty);
}
if (json_is_boolean(sch))
@@ -1832,17 +1826,11 @@ static int schema_make_internal_depth(json_t* sch,
if (defs && json_is_object(defs)) {
char* base_loc = effective_base ? celix_jansson_uri_location(effective_base) : strdup("");
if (!base_loc) {
- /* Mirror the my_base cleanup of the other failing paths */
- if (has_id && !id_stored_in_out)
- celix_jansson_uri_clear(&my_base);
return CELIX_JANSSON_SCHEMA_ERROR_NOMEM;
}
celix_jansson_schema_file_t* sf = celix_jansson_schema_root_get_or_create_file(root, base_loc);
free(base_loc);
if (!sf) {
- /* Mirror the my_base cleanup of the other failing paths */
- if (has_id && !id_stored_in_out)
- celix_jansson_uri_clear(&my_base);
return CELIX_JANSSON_SCHEMA_ERROR_NOMEM;
}
const char* dk;
@@ -1851,8 +1839,6 @@ static int schema_make_internal_depth(json_t* sch,
celix_jansson_schema_node_t* ds;
int rc = schema_make_internal(dv, root, effective_base, &ds, depth + 1);
if (rc != CELIX_JANSSON_SCHEMA_OK) {
- if (has_id && !id_stored_in_out)
- celix_jansson_uri_clear(&my_base);
return rc;
}
if (ds) {
@@ -1876,14 +1862,12 @@ static int schema_make_internal_depth(json_t* sch,
return CELIX_JANSSON_SCHEMA_ERROR_INVALID_SCHEMA;
/* Resolve ref_str against the effective base URI */
- celix_jansson_uri_t ref_uri;
- memset(&ref_uri, 0, sizeof(ref_uri)); /* must zero-init for uri_derive → uri_clear */
+ celix_auto(celix_jansson_uri_t) ref_uri = {0};
if (effective_base) {
if (celix_jansson_uri_derive(effective_base, ref_str, &ref_uri) != 0)
return CELIX_JANSSON_SCHEMA_ERROR_NOMEM;
- } else {
- if (celix_jansson_uri_init(&ref_uri, ref_str) != 0)
- return CELIX_JANSSON_SCHEMA_ERROR_NOMEM; /* init clears ref_uri on failure */
+ } else if (celix_jansson_uri_update(&ref_uri, ref_str) != 0) {
+ return CELIX_JANSSON_SCHEMA_ERROR_NOMEM; /* ref_uri cleared at scope exit */
}
char* rloc = celix_jansson_uri_location(&ref_uri);
@@ -1891,7 +1875,6 @@ static int schema_make_internal_depth(json_t* sch,
if (!rloc || !rfra) {
free(rloc);
free(rfra);
- celix_jansson_uri_clear(&ref_uri);
return CELIX_JANSSON_SCHEMA_ERROR_NOMEM;
}
celix_jansson_schema_file_t* sf = celix_jansson_schema_root_get_or_create_file(root, rloc);
@@ -1919,7 +1902,6 @@ static int schema_make_internal_depth(json_t* sch,
celix_jansson_schema_unref(target);
free(rloc);
free(rfra);
- celix_jansson_uri_clear(&ref_uri);
return CELIX_JANSSON_SCHEMA_ERROR_NOMEM;
}
target->u.ref.id = uristr;
@@ -1929,14 +1911,16 @@ static int schema_make_internal_depth(json_t* sch,
}
if (!target && !plain_self_ref) {
- free(rloc); free(rfra); celix_jansson_uri_clear(&ref_uri);
+ free(rloc);
+ free(rfra);
return CELIX_JANSSON_SCHEMA_ERROR_NOMEM;
}
/* Create ref node — store derived URI as id */
celix_jansson_schema_node_t* rn = (celix_jansson_schema_node_t*)calloc(1, sizeof(*rn));
if (!rn) {
- free(rloc); free(rfra); celix_jansson_uri_clear(&ref_uri);
+ free(rloc);
+ free(rfra);
return CELIX_JANSSON_SCHEMA_ERROR_NOMEM;
}
rn->vtable = &vt_ref;
@@ -1949,7 +1933,6 @@ static int schema_make_internal_depth(json_t* sch,
celix_jansson_schema_unref(rn);
free(rloc);
free(rfra);
- celix_jansson_uri_clear(&ref_uri);
return CELIX_JANSSON_SCHEMA_ERROR_NOMEM;
}
if (target) {
@@ -1960,8 +1943,8 @@ static int schema_make_internal_depth(json_t* sch,
if (defv)
rn->default_value = json_deep_copy(defv);
- free(rloc); free(rfra);
- celix_jansson_uri_clear(&ref_uri);
+ free(rloc);
+ free(rfra);
/* No $id registration: draft-7 treats $id alongside $ref as not a real
* $id (JSON Reference: members other than $ref are ignored — see the
@@ -1974,19 +1957,15 @@ static int schema_make_internal_depth(json_t* sch,
if (rc == CELIX_JANSSON_SCHEMA_OK && has_id && *out) {
int ir = celix_jansson_schema_root_insert(root, &my_base, *out);
if (ir == CELIX_JANSSON_SCHEMA_ERROR_NOMEM) {
- /* The registry did not ref *out; unref it and clear it so the caller
- * does not take ownership on the error path */
+ /* The registry did not ref *out; unref it so the caller does not
+ * take ownership on the error path */
celix_jansson_schema_unref(*out);
*out = NULL;
- if (!id_stored_in_out)
- celix_jansson_uri_clear(&my_base);
return CELIX_JANSSON_SCHEMA_ERROR_NOMEM;
}
/* DUPLICATE_URI is benign: with $id:"" the registration above already
* happened and set_root_schema's root_insert must tolerate it as well */
}
- if (has_id && !id_stored_in_out)
- celix_jansson_uri_clear(&my_base);
return rc;
}
@@ -2046,7 +2025,7 @@ static celix_jansson_schema_node_t* resolve_document_fragment(
return NULL;
/* Build the base URI for this walk */
- celix_jansson_uri_t cur_base;
+ celix_auto(celix_jansson_uri_t) cur_base = {0};
if (sf->base_uri) {
if (celix_jansson_uri_init(&cur_base, sf->base_uri) != 0)
return NULL; /* init leaves cur_base cleared */
@@ -2080,7 +2059,6 @@ static celix_jansson_schema_node_t* resolve_document_fragment(
if (!token) {
/* strbuf_detach also returns NULL for an empty buffer, so an
* empty token (double slash) is rejected here as well. */
- celix_jansson_uri_clear(&cur_base);
return NULL;
}
@@ -2088,7 +2066,6 @@ static celix_jansson_schema_node_t* resolve_document_fragment(
if (!json_is_object(cur) && !json_is_array(cur)) {
free(token);
- celix_jansson_uri_clear(&cur_base);
return NULL;
}
@@ -2100,7 +2077,6 @@ static celix_jansson_schema_node_t* resolve_document_fragment(
for (const char* c = t; *c; c++) {
if (*c < '0' || *c > '9') {
free(token);
- celix_jansson_uri_clear(&cur_base);
return NULL;
}
}
@@ -2115,8 +2091,7 @@ static celix_jansson_schema_node_t* resolve_document_fragment(
(members_are_schemas || token_is_schema_position(token, child))) {
json_t* cid = json_object_get(child, "$id");
if (cid && json_is_string(cid)) {
- celix_jansson_uri_t new_base;
- memset(&new_base, 0, sizeof(new_base)); /* must zero-init for uri_derive → uri_clear */
+ celix_jansson_uri_t new_base = {0};
if (celix_jansson_uri_derive(&cur_base, json_string_value(cid), &new_base) == 0) {
celix_jansson_uri_clear(&cur_base);
cur_base = new_base;
@@ -2128,7 +2103,6 @@ static celix_jansson_schema_node_t* resolve_document_fragment(
if (!child) {
free(token);
- celix_jansson_uri_clear(&cur_base);
return NULL;
}
cur = child;
@@ -2141,12 +2115,11 @@ static celix_jansson_schema_node_t* resolve_document_fragment(
/* Compile the reached subtree with the tracked base */
celix_jansson_schema_node_t* sch = NULL;
int rc = schema_make_internal_depth(cur, root, &cur_base, NULL, &sch, depth + 1);
- celix_jansson_uri_clear(&cur_base);
if (rc != CELIX_JANSSON_SCHEMA_OK || !sch)
return NULL;
/* Register under the full URI so waiting refs get resolved */
- celix_jansson_uri_t full_uri;
+ celix_auto(celix_jansson_uri_t) full_uri = {0};
if (celix_jansson_uri_init(&full_uri, location) != 0) {
/* init leaves full_uri cleared; sch is owned by this frame */
celix_jansson_schema_unref(sch);
@@ -2167,14 +2140,12 @@ static celix_jansson_schema_node_t* resolve_document_fragment(
if (!push_ok) {
/* do not register under a partial fragment — it would wrongly
* satisfy a shorter-fragment waiting ref */
- celix_jansson_uri_clear(&full_uri);
celix_jansson_schema_unref(sch);
return NULL;
}
}
}
int ir = celix_jansson_schema_root_insert(root, &full_uri, sch);
- celix_jansson_uri_clear(&full_uri);
//LCOV_EXCL_START: reachable only on OOM inside root_insert's location/fragment
//allocation (the file cache-hit reasoning no longer applies); no current test drives this
if (ir == CELIX_JANSSON_SCHEMA_ERROR_NOMEM) {
@@ -2212,7 +2183,7 @@ static int compile_external_document(celix_jansson_schema_root_t* root, const ch
return rc ? rc : CELIX_JANSSON_SCHEMA_ERROR_LOADER;
}
- celix_jansson_uri_t retr;
+ celix_auto(celix_jansson_uri_t) retr = {0};
if (celix_jansson_uri_init(&retr, location) != 0) {
json_decref(doc); /* doc has not been transferred to sf->document */
return CELIX_JANSSON_SCHEMA_ERROR_NOMEM; /* init leaves retr cleared */
@@ -2221,7 +2192,6 @@ static int compile_external_document(celix_jansson_schema_root_t* root, const ch
rc = schema_make_internal_depth(doc, root, &retr, NULL, &sch, 0);
if (rc != CELIX_JANSSON_SCHEMA_OK || !sch) {
json_decref(doc);
- celix_jansson_uri_clear(&retr);
return rc ? rc : CELIX_JANSSON_SCHEMA_ERROR_NOMEM;
}
@@ -2233,7 +2203,6 @@ static int compile_external_document(celix_jansson_schema_root_t* root, const ch
/* The registry did not ref sch; propagate the OOM instead of returning a
* fake OK that would leave the placeholder unresolved forever */
json_decref(doc); /* doc has not been transferred to sf->document */
- celix_jansson_uri_clear(&retr);
celix_jansson_schema_unref(sch);
return CELIX_JANSSON_SCHEMA_ERROR_NOMEM;
}
@@ -2258,7 +2227,6 @@ static int compile_external_document(celix_jansson_schema_root_t* root, const ch
}
//LCOV_EXCL_STOP
- celix_jansson_uri_clear(&retr);
celix_jansson_schema_unref(sch);
return CELIX_JANSSON_SCHEMA_OK;
}
@@ -2511,7 +2479,7 @@ int celix_jansson_schema_root_validate(celix_jansson_schema_root_t* root,
/* Resolve initial_uri to a specific subschema, if provided */
if (initial_uri && initial_uri[0] != '\0' && strcmp(initial_uri, "#") != 0) {
- celix_jansson_uri_t uri;
+ celix_auto(celix_jansson_uri_t) uri = {0};
if (celix_jansson_uri_init(&uri, initial_uri) == 0) {
char* loc = celix_jansson_uri_location(&uri);
const char* frag = celix_jansson_uri_fragment(&uri);
@@ -2534,7 +2502,6 @@ int celix_jansson_schema_root_validate(celix_jansson_schema_root_t* root,
}
free(loc);
free((char*)frag);
- celix_jansson_uri_clear(&uri);
}
}
@@ -2668,14 +2635,12 @@ int celix_jansson_schema_set_root_schema(celix_jansson_schema_validator_t* v, js
return CELIX_JANSSON_SCHEMA_ERROR_NOMEM;
}
- celix_jansson_uri_t root_base;
- celix_jansson_uri_init(&root_base, "");
+ celix_auto(celix_jansson_uri_t) root_base = {0};
celix_jansson_schema_node_t* sch;
int err = schema_make_internal_depth(copy, root, NULL, &root_base, &sch, 0);
json_decref(copy);
if (err != CELIX_JANSSON_SCHEMA_OK) {
- celix_jansson_uri_clear(&root_base);
if (errmsg)
*errmsg = strdup(celix_jansson_schema_strerror(err));
return err;
@@ -2690,7 +2655,6 @@ int celix_jansson_schema_set_root_schema(celix_jansson_schema_validator_t* v, js
* failing paths below (root->root == NULL). */
celix_jansson_schema_unref(root->root);
root->root = NULL;
- celix_jansson_uri_clear(&root_base);
if (errmsg)
*errmsg = strdup(celix_jansson_schema_strerror(CELIX_JANSSON_SCHEMA_ERROR_NOMEM));
return CELIX_JANSSON_SCHEMA_ERROR_NOMEM;
@@ -2716,7 +2680,6 @@ int celix_jansson_schema_set_root_schema(celix_jansson_schema_validator_t* v, js
}
free(rloc);
}
- celix_jansson_uri_clear(&root_base);
err = resolve_external_refs(root);
if (err != CELIX_JANSSON_SCHEMA_OK && errmsg)
diff --git a/libs/jansson_ext/src/celix_jansson_uri.c b/libs/jansson_ext/src/celix_jansson_uri.c
index 53b996074..3393a8b61 100644
--- a/libs/jansson_ext/src/celix_jansson_uri.c
+++ b/libs/jansson_ext/src/celix_jansson_uri.c
@@ -62,7 +62,7 @@ int celix_jansson_uri_init(celix_jansson_uri_t* u, const char* uri_str) {
int celix_jansson_uri_update(celix_jansson_uri_t* u, const char* uri_str) {
if (!uri_str)
- return 0;
+ return 0; /* NULL is a no-op (keep current contents); "" clears instead */
/* Split at first '#' */
const char* hash = strchr(uri_str, '#');
@@ -213,7 +213,8 @@ int celix_jansson_uri_update(celix_jansson_uri_t* u, const char* uri_str) {
}
int celix_jansson_uri_derive(const celix_jansson_uri_t* base, const char* uri_str, celix_jansson_uri_t* out) {
- /* Start with a copy of the base */
+ /* Reset out first (defensive: safe if caller reuses the buffer);
+ * base is copied by the strdup block below */
celix_jansson_uri_clear(out);
/* Copy base components; each strdup is checked individually so no OOM
@@ -262,8 +263,9 @@ int celix_jansson_uri_derive(const celix_jansson_uri_t* base, const char* uri_st
}
int celix_jansson_uri_append(const celix_jansson_uri_t* u, const char* token, celix_jansson_uri_t* out) {
- /* Start with a copy; each strdup is checked individually so no OOM goes
- * unhandled (on failure out is left fully cleared) */
+ /* Reset out first (defensive: safe if caller reuses the buffer);
+ * u is copied by the strdup block below; each strdup is checked individually
+ * so no OOM goes unhandled (on failure out is left fully cleared) */
celix_jansson_uri_clear(out);
out->scheme = u->scheme ? strdup(u->scheme) : NULL;
if (u->scheme && !out->scheme) {
diff --git a/libs/jansson_ext/src/celix_jansson_uri.h b/libs/jansson_ext/src/celix_jansson_uri.h
index 968aec99b..e13139db0 100644
--- a/libs/jansson_ext/src/celix_jansson_uri.h
+++ b/libs/jansson_ext/src/celix_jansson_uri.h
@@ -21,6 +21,7 @@
#include <stdbool.h>
+#include "celix_cleanup.h"
#include "celix_jansson_schema.h"
#include "celix_jansson_pointer.h"
@@ -40,23 +41,30 @@ typedef struct celix_jansson_uri_t {
} celix_jansson_uri_t;
/** Initialize/parse a URI from a string. Returns JSS error code.
+ * NULL and "" both produce an empty URI (the struct is zeroed first).
* On failure (NOMEM) @p u is left fully cleared. */
int celix_jansson_uri_init(celix_jansson_uri_t* u, const char* uri_str);
/** Update a URI in-place by resolving @p uri_str against it.
+ * NULL keeps the current contents unchanged; "" clears everything
+ * (empty URI) — the two are NOT equivalent here.
* On failure (NOMEM) @p u may be left partially modified; callers must call
* celix_jansson_uri_clear() (init and derive already clear on failure). */
int celix_jansson_uri_update(celix_jansson_uri_t* u, const char* uri_str);
/**
* Derive a new URI by resolving @p uri_str relative to @p base.
- * @p out must be zero-initialized (or a fresh celix_jansson_uri_init result).
+ * A NULL @p uri_str copies @p base unchanged; "" resolves to an empty URI.
+ * @p out must be zero-initialized, or hold an existing URI to be reused
+ * (it is cleared first); never pass uninitialized stack memory.
*/
int celix_jansson_uri_derive(const celix_jansson_uri_t* base, const char* uri_str, celix_jansson_uri_t* out);
/**
* Append a JSON Pointer token to the URI (no-op if the URI has an
* identifier fragment).
+ * @p out must be zero-initialized, or hold an existing URI to be reused
+ * (it is cleared first); never pass uninitialized stack memory.
*/
int celix_jansson_uri_append(const celix_jansson_uri_t* u, const char* token, celix_jansson_uri_t* out);
@@ -81,6 +89,10 @@ bool celix_jansson_uri_equals(const celix_jansson_uri_t* a, const celix_jansson_
/** Free all memory held by a URI. */
void celix_jansson_uri_clear(celix_jansson_uri_t* u);
+/* Enables `celix_auto(celix_jansson_uri_t) u;` for scope-based automatic
+ * cleanup. Safe on zeroed structs (clear() frees NULLs and re-zeroes). */
+CELIX_DEFINE_AUTO_CLEANUP_CLEAR_FUNC(celix_jansson_uri_t, celix_jansson_uri_clear)
+
#ifdef __cplusplus
}
#endif