Change in libosmocore[master]: utils.h: add OSMO_NAME_C_IMPL() macro

This is merely a historical archive of years 2008-2021, before the migration to mailman3.

A maintained and still updated list archive can be found at https://lists.osmocom.org/hyperkitty/list/gerrit-log@lists.osmocom.org/.

neels gerrit-no-reply at lists.osmocom.org
Mon Nov 4 18:13:50 UTC 2019


neels has uploaded this change for review. ( https://gerrit.osmocom.org/c/libosmocore/+/15957 )


Change subject: utils.h: add OSMO_NAME_C_IMPL() macro
......................................................................

utils.h: add OSMO_NAME_C_IMPL() macro

Provide a common implementation for foo_name_c() functions that base on
foo_name_buf() functions.

  char *foo_name_c(void *ctx, example_t arg)
  {
          OSMO_NAME_C_IMPL(ctx, 64, "ERROR", foo_name_buf, arg)
  }

Rationale: the most efficient way of composing strings that have optional parts
or require loops for composition is by writing to a ready char[], and this in
turn is easiest done by using OSMO_STRBUF_* API. Using such a basic name string
implementation which typically returns a length, I often want a more convenient
version that returns a char*, which can just be inlined in a "%s" string format
-- crucially: skipping string composition when inlined in a LOGP(). This common
implementation allows saving code dup, only the function signature is needed.

Why not include the function signature in the macro? The two sets of varargs
(1: signature args, 2: function call args) are hard to do. Also, having an
explicit signature is good for readability and code grepping / ctags.

Upcoming uses: in libosmocore in the mslookup (D-GSM) implementation
(osmo_mslookup_result_name_c()), and in osmo_msc's codec negotiation
implementation (sdp_audio_codecs_name_c(), sdp_msg_name_c(), ...).
I54b6c0810f181259da307078977d9ef3d90458c9 (libosmocore)
If3ce23cd5bab15e2ab4c52ef3e4c75979dffe931 (osmo-msc)

Change-Id: Ida5ba8d9640ea641aafef0236800f6d489d3d322
---
M include/osmocom/core/utils.h
M tests/utils/utils_test.c
M tests/utils/utils_test.ok
3 files changed, 157 insertions(+), 0 deletions(-)



  git pull ssh://gerrit.osmocom.org:29418/libosmocore refs/changes/57/15957/1

diff --git a/include/osmocom/core/utils.h b/include/osmocom/core/utils.h
index 601bb56..4eba987 100644
--- a/include/osmocom/core/utils.h
+++ b/include/osmocom/core/utils.h
@@ -3,6 +3,7 @@
 #include <stdbool.h>
 #include <stdint.h>
 #include <stdio.h>
+#include <string.h>
 
 #include <osmocom/core/backtrace.h>
 #include <osmocom/core/talloc.h>
@@ -269,4 +270,61 @@
 
 bool osmo_str_startswith(const char *str, const char *startswith_str);
 
+/*! Translate a buffer function to a talloc context function.
+ * This is the full function body of a char *foo_name_c(void *ctx, val...) function, implemented by an
+ * int foo_name_buf(buf, buflen, val...) function:
+ *
+ *    char *foo_name_c(void *ctx, example_t arg)
+ *    {
+ *            OSMO_NAME_C_IMPL(ctx, 64, "ERROR", foo_name_buf, arg)
+ *    }
+ *
+ * Return a talloc'd string containing the result of the given foo_name_buf() function, or ON_ERROR on error in the called
+ * foo_name_buf() function.
+ *
+ * If ON_ERROR is NULL, the function returns NULL on error rc from FUNC_BUF. Take care: returning NULL in printf() like
+ * formats (LOGP()) makes the program crash. If ON_ERROR is non-NULL, it must be a string constant, which is not
+ * returned directly, but written to an allocated string buffer first.
+ *
+ * \param[in] INITIAL_BUFSIZE  Which size to first talloc from ctx -- a larger size makes a reallocation less likely, a
+ * 	smaller size allocates less unused bytes, zero allocates once but still runs the string composition twice.
+ * \param[in] ON_ERROR  String constant to copy on error rc returned by FUNC_BUF, or NULL to return NULL.
+ * \param[in] FUNC_BUF  Name of a function with signature foo_buf(char *buf, size_t buflen, ...).
+ * \param[in] FUNC_BUF_ARGS  Additional arguments to pass to FUNC_BUF after the buf and buflen.
+ */
+#define OSMO_NAME_C_IMPL(CTX, INITIAL_BUFSIZE, ON_ERROR, FUNC_BUF, FUNC_BUF_ARGS...) \
+	size_t len = INITIAL_BUFSIZE; \
+	int needed; \
+	char *str = NULL; \
+	if (INITIAL_BUFSIZE > 0) { \
+		str = (char*)talloc_named_const(CTX, len, __func__); \
+		OSMO_ASSERT(str); \
+	} \
+	needed = FUNC_BUF(str, len, ## FUNC_BUF_ARGS); \
+	if (needed < 0) \
+		goto OSMO_NAME_C_on_error; \
+	if (needed < len) \
+		return str; \
+	len = needed + 1; \
+	if (str) { \
+		str = (char*)talloc_realloc_size(CTX, str, len); \
+		talloc_set_name_const(str, __func__); \
+	} else \
+		str = (char*)talloc_named_const(CTX, len, __func__); \
+	OSMO_ASSERT(str); \
+	needed = FUNC_BUF(str, len, ## FUNC_BUF_ARGS); \
+	if (needed < 0) \
+		goto OSMO_NAME_C_on_error; \
+	return str; \
+	OSMO_NAME_C_on_error: \
+	/* Re-using and re-sizing above allocated buf ends up in very complex code. Just free and strdup. */ \
+	if (str) \
+		talloc_free(str); \
+	if (!ON_ERROR) \
+		return NULL; \
+	str = talloc_strdup(CTX, ON_ERROR); \
+	OSMO_ASSERT(str); \
+	talloc_set_name_const(str, __func__); \
+	return str;
+
 /*! @} */
diff --git a/tests/utils/utils_test.c b/tests/utils/utils_test.c
index 70d017f..532bdbf 100644
--- a/tests/utils/utils_test.c
+++ b/tests/utils/utils_test.c
@@ -1058,6 +1058,87 @@
 	startswith_test_str("abc", "xyz", false);
 }
 
+static int foo_name_buf(char *buf, size_t buflen, const char *arg)
+{
+	if (!arg)
+		return -EINVAL;
+	return snprintf(buf, buflen, "%s", arg);
+}
+
+static char *foo_name_c(void *ctx, const char *arg)
+{
+        OSMO_NAME_C_IMPL(ctx, 10, "ERROR", foo_name_buf, arg)
+}
+
+static char *foo_name_c_null(void *ctx, const char *arg)
+{
+	OSMO_NAME_C_IMPL(ctx, 10, NULL, foo_name_buf, arg)
+}
+
+static char *foo_name_c_zero(void *ctx, const char *arg)
+{
+        OSMO_NAME_C_IMPL(ctx, 0, "ERROR", foo_name_buf, arg)
+}
+
+static char *foo_name_c_zero_null(void *ctx, const char *arg)
+{
+        OSMO_NAME_C_IMPL(ctx, 0, NULL, foo_name_buf, arg)
+}
+
+
+static void name_c_impl_test()
+{
+	char *test_strs[] = {
+		"test",
+		"longer than 10 chars",
+		NULL,
+	};
+	struct {
+		const char *label;
+		char *(*func)(void *, const char*);
+	} funcs[] = {
+		{
+			"OSMO_NAME_C_IMPL(10, \"ERROR\")",
+			foo_name_c,
+		},
+		{
+			"OSMO_NAME_C_IMPL(10, NULL)",
+			foo_name_c_null,
+		},
+		{
+			"OSMO_NAME_C_IMPL(0, \"ERROR\")",
+			foo_name_c_zero,
+		},
+		{
+			"OSMO_NAME_C_IMPL(0, NULL)",
+			foo_name_c_zero_null,
+		},
+	};
+
+	int i;
+	void *ctx = talloc_named_const(NULL, 0, __func__);
+	int allocs = talloc_total_blocks(ctx);
+
+	printf("\n%s\n", __func__);
+	for (i = 0; i < ARRAY_SIZE(test_strs); i++) {
+		char *test_str = test_strs[i];
+		int j;
+		printf("%2d: %s\n", i, osmo_quote_str(test_str, -1));
+		
+		for (j = 0; j < ARRAY_SIZE(funcs); j++) {
+			char *str = funcs[j].func(ctx, test_str);
+			printf("  %30s -> %s", funcs[j].label, osmo_quote_str(str, -1));
+			printf("  allocated %d", (int)talloc_total_blocks(ctx) - allocs);
+			if (str) {
+				printf("  %zu bytes, name '%s'", talloc_total_size(str), talloc_get_name(str));
+				talloc_free(str);
+			}
+			printf("\n");
+		}
+	}
+	talloc_free(ctx);
+}
+
 int main(int argc, char **argv)
 {
 	static const struct log_info log_info = {};
@@ -1078,5 +1159,6 @@
 	strbuf_test();
 	strbuf_test_nolen();
 	startswith_test();
+	name_c_impl_test();
 	return 0;
 }
diff --git a/tests/utils/utils_test.ok b/tests/utils/utils_test.ok
index c150a8d..b603647 100644
--- a/tests/utils/utils_test.ok
+++ b/tests/utils/utils_test.ok
@@ -360,3 +360,20 @@
 osmo_str_startswith("abc", "abc") == true
 osmo_str_startswith("abc", "abcd") == false
 osmo_str_startswith("abc", "xyz") == false
+
+name_c_impl_test
+ 0: "test"
+   OSMO_NAME_C_IMPL(10, "ERROR") -> "test"  allocated 1  10 bytes, name 'foo_name_c'
+      OSMO_NAME_C_IMPL(10, NULL) -> "test"  allocated 1  10 bytes, name 'foo_name_c_null'
+    OSMO_NAME_C_IMPL(0, "ERROR") -> "test"  allocated 1  5 bytes, name 'foo_name_c_zero'
+       OSMO_NAME_C_IMPL(0, NULL) -> "test"  allocated 1  5 bytes, name 'foo_name_c_zero_null'
+ 1: "longer than 10 chars"
+   OSMO_NAME_C_IMPL(10, "ERROR") -> "longer than 10 chars"  allocated 1  21 bytes, name 'foo_name_c'
+      OSMO_NAME_C_IMPL(10, NULL) -> "longer than 10 chars"  allocated 1  21 bytes, name 'foo_name_c_null'
+    OSMO_NAME_C_IMPL(0, "ERROR") -> "longer than 10 chars"  allocated 1  21 bytes, name 'foo_name_c_zero'
+       OSMO_NAME_C_IMPL(0, NULL) -> "longer than 10 chars"  allocated 1  21 bytes, name 'foo_name_c_zero_null'
+ 2: NULL
+   OSMO_NAME_C_IMPL(10, "ERROR") -> "ERROR"  allocated 1  6 bytes, name 'foo_name_c'
+      OSMO_NAME_C_IMPL(10, NULL) -> NULL  allocated 0
+    OSMO_NAME_C_IMPL(0, "ERROR") -> "ERROR"  allocated 1  6 bytes, name 'foo_name_c_zero'
+       OSMO_NAME_C_IMPL(0, NULL) -> NULL  allocated 0

-- 
To view, visit https://gerrit.osmocom.org/c/libosmocore/+/15957
To unsubscribe, or for help writing mail filters, visit https://gerrit.osmocom.org/settings

Gerrit-Project: libosmocore
Gerrit-Branch: master
Gerrit-Change-Id: Ida5ba8d9640ea641aafef0236800f6d489d3d322
Gerrit-Change-Number: 15957
Gerrit-PatchSet: 1
Gerrit-Owner: neels <nhofmeyr at sysmocom.de>
Gerrit-MessageType: newchange
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.osmocom.org/pipermail/gerrit-log/attachments/20191104/2a946d37/attachment.htm>


More information about the gerrit-log mailing list