mirror of https://gerrit.osmocom.org/libosmocore
add OSMO_STRBUF_PRINTF()
We are using macros like this or different workarounds in libmsc. In the course of implementing inter-MSC handover, I am encountering yet another such situation of appending multiple strings to a limited char buffer. Standardize. Add a unit test to utils_test.c. Change-Id: I2497514e26c5e7a5d88985fc7e58343be1a027b2
This commit is contained in:
parent
8667f7a815
commit
2cbe25f484
|
@ -145,4 +145,89 @@ uint32_t osmo_isqrt32(uint32_t x);
|
|||
|
||||
const char osmo_luhn(const char* in, int in_len);
|
||||
|
||||
/*! State for OSMO_STRBUF_APPEND() and OSMO_STRBUF_PRINTF(). See there for examples. */
|
||||
struct osmo_strbuf {
|
||||
/*! Point to the start of a string buffer. */
|
||||
char *buf;
|
||||
/*! Total sizeof() the buffer buf points at. */
|
||||
size_t len;
|
||||
/*! Current writing position in buf (end of the string written so far). */
|
||||
char *pos;
|
||||
/*! After all OSMO_STRBUF_APPEND operations, reflects the total number of characters that would be written had
|
||||
* buf been large enough. Like snprintf()'s return value, this does not include the terminating nul character.
|
||||
* Hence, to allocate an adequately sized buffer, add 1 to this number. */
|
||||
size_t chars_needed;
|
||||
};
|
||||
|
||||
/*! Append a string to a buffer, as printed by an snprintf()-like function and with similar bounds checking.
|
||||
* Make sure to never write past the end of the buffer, and collect the total size that would be needed.
|
||||
*
|
||||
* // an example function implementation to append: write N spaces.
|
||||
* int print_spaces(char *dst, size_t dst_len, int n)
|
||||
* {
|
||||
* int i;
|
||||
* if (n < 0)
|
||||
* return -EINVAL;
|
||||
* for (i = 0; i < n && i < dst_len; i++)
|
||||
* dst[i] = ' ';
|
||||
* if (dst_len)
|
||||
* dst[OSMO_MIN(dst_len - 1, n)] = '\0';
|
||||
* // return the n that we would have liked to write if space were available:
|
||||
* return n;
|
||||
* }
|
||||
*
|
||||
* // append above spaces as well as an snprintf()
|
||||
* void strbuf_example()
|
||||
* {
|
||||
* char buf[23];
|
||||
* struct osmo_strbuf sb = { .buf = buf, .len = sizeof(buf) };
|
||||
*
|
||||
* OSMO_STRBUF_APPEND(sb, print_spaces, 5);
|
||||
* OSMO_STRBUF_APPEND(sb, snprintf, "The answer is %d but what is the question?", 42);
|
||||
* OSMO_STRBUF_APPEND(sb, print_spaces, 423423);
|
||||
*
|
||||
* printf("%s\n", buf);
|
||||
* printf("would have needed %zu bytes\n", sb.chars_needed);
|
||||
* }
|
||||
*
|
||||
* \param[inout] STRBUF A struct osmo_strbuf instance.
|
||||
* \param[in] func A function with a signature of int func(char *dst, size_t dst_len [, args]) with semantics like
|
||||
* snprintf().
|
||||
* \param[in] args Arguments passed to func, if any.
|
||||
*/
|
||||
#define OSMO_STRBUF_APPEND(STRBUF, func, args...) do { \
|
||||
if (!(STRBUF).pos) \
|
||||
(STRBUF).pos = (STRBUF).buf; \
|
||||
size_t remain = (STRBUF).buf ? (STRBUF).len - ((STRBUF).pos - (STRBUF).buf) : 0; \
|
||||
int l = func((STRBUF).pos, remain, ##args); \
|
||||
if (l < 0 || l > remain) \
|
||||
(STRBUF).pos = (STRBUF).buf + (STRBUF).len; \
|
||||
else \
|
||||
(STRBUF).pos += l; \
|
||||
if (l > 0) \
|
||||
(STRBUF).chars_needed += l; \
|
||||
} while(0)
|
||||
|
||||
/*! Shortcut for OSMO_STRBUF_APPEND() invocation using snprintf().
|
||||
*
|
||||
* int strbuf_example2(char *buf, size_t buflen)
|
||||
* {
|
||||
* int i;
|
||||
* struct osmo_strbuf sb = { .buf = buf, .len = buflen };
|
||||
*
|
||||
* OSMO_STRBUF_PRINTF(sb, "T minus");
|
||||
* for (i = 10; i; i--)
|
||||
* OSMO_STRBUF_PRINTF(sb, " %d", i);
|
||||
* OSMO_STRBUF_PRINTF(sb, " ... Lift off!");
|
||||
*
|
||||
* return sb.chars_needed;
|
||||
* }
|
||||
*
|
||||
* \param[inout] STRBUF A struct osmo_strbuf instance.
|
||||
* \param[in] fmt Format string passed to snprintf.
|
||||
* \param[in] args Additional arguments passed to snprintf, if any.
|
||||
*/
|
||||
#define OSMO_STRBUF_PRINTF(STRBUF, fmt, args...) \
|
||||
OSMO_STRBUF_APPEND(STRBUF, snprintf, fmt, ##args)
|
||||
|
||||
/*! @} */
|
||||
|
|
|
@ -33,6 +33,7 @@
|
|||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <errno.h>
|
||||
#include <limits.h>
|
||||
|
||||
static void hexdump_test(void)
|
||||
{
|
||||
|
@ -936,6 +937,90 @@ static void osmo_str_tolowupper_test()
|
|||
OSMO_ASSERT(ok);
|
||||
}
|
||||
|
||||
/* Copy of the examples from OSMO_STRBUF_APPEND() */
|
||||
int print_spaces(char *dst, size_t dst_len, int argument)
|
||||
{
|
||||
int i;
|
||||
if (argument < 0)
|
||||
return -EINVAL;
|
||||
for (i = 0; i < argument && i < dst_len; i++)
|
||||
dst[i] = ' ';
|
||||
if (dst_len)
|
||||
dst[OSMO_MIN(dst_len - 1, argument)] = '\0';
|
||||
return argument;
|
||||
}
|
||||
|
||||
void strbuf_example(char *buf, size_t buflen)
|
||||
{
|
||||
struct osmo_strbuf sb = { .buf = buf, .len = buflen };
|
||||
|
||||
OSMO_STRBUF_APPEND(sb, print_spaces, 5);
|
||||
OSMO_STRBUF_APPEND(sb, snprintf, "The answer is %d but what is the question?", 42);
|
||||
OSMO_STRBUF_APPEND(sb, print_spaces, 423423);
|
||||
|
||||
printf("%s\n", buf);
|
||||
printf("would have needed %zu bytes\n", sb.chars_needed);
|
||||
}
|
||||
|
||||
/* Copy of the examples from OSMO_STRBUF_PRINTF() */
|
||||
int strbuf_example2(char *buf, size_t buflen)
|
||||
{
|
||||
int i;
|
||||
struct osmo_strbuf sb = { .buf = buf, .len = buflen };
|
||||
|
||||
OSMO_STRBUF_PRINTF(sb, "T minus");
|
||||
for (i = 10; i; i--)
|
||||
OSMO_STRBUF_PRINTF(sb, " %d", i);
|
||||
OSMO_STRBUF_PRINTF(sb, " ... Lift off!");
|
||||
|
||||
return sb.chars_needed;
|
||||
}
|
||||
|
||||
int strbuf_cascade(char *buf, size_t buflen)
|
||||
{
|
||||
struct osmo_strbuf sb = { .buf = buf, .len = buflen };
|
||||
|
||||
OSMO_STRBUF_APPEND(sb, strbuf_example2);
|
||||
OSMO_STRBUF_PRINTF(sb, " -- ");
|
||||
OSMO_STRBUF_APPEND(sb, strbuf_example2);
|
||||
OSMO_STRBUF_PRINTF(sb, " -- ");
|
||||
OSMO_STRBUF_APPEND(sb, strbuf_example2);
|
||||
|
||||
return sb.chars_needed;
|
||||
}
|
||||
|
||||
void strbuf_test()
|
||||
{
|
||||
char buf[256];
|
||||
int rc;
|
||||
printf("\n%s\n", __func__);
|
||||
|
||||
printf("OSMO_STRBUF_APPEND():\n");
|
||||
strbuf_example(buf, 23);
|
||||
|
||||
printf("\nOSMO_STRBUF_PRINTF():\n");
|
||||
rc = strbuf_example2(buf, 23);
|
||||
printf("1: (need %d chars, had size=23) %s\n", rc, buf);
|
||||
|
||||
rc = strbuf_example2(buf, rc);
|
||||
printf("2: (need %d chars, had size=%d) %s\n", rc, rc, buf);
|
||||
|
||||
rc = strbuf_example2(buf, rc + 1);
|
||||
printf("3: (need %d chars, had size=%d+1) %s\n", rc, rc, buf);
|
||||
|
||||
rc = strbuf_example2(buf, 0);
|
||||
snprintf(buf, sizeof(buf), "0x2b 0x2b 0x2b...");
|
||||
printf("4: (need %d chars, had size=0) %s\n", rc, buf);
|
||||
|
||||
rc = strbuf_example2(NULL, 99);
|
||||
printf("5: (need %d chars, had NULL buffer)\n", rc);
|
||||
|
||||
printf("\ncascade:\n");
|
||||
rc = strbuf_cascade(buf, sizeof(buf));
|
||||
printf("(need %d chars)\n%s\n", rc, buf);
|
||||
rc = strbuf_cascade(buf, 63);
|
||||
printf("(need %d chars, had size=63) %s\n", rc, buf);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
|
@ -954,5 +1039,6 @@ int main(int argc, char **argv)
|
|||
isqrt_test();
|
||||
osmo_sockaddr_to_str_and_uint_test();
|
||||
osmo_str_tolowupper_test();
|
||||
strbuf_test();
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -325,3 +325,20 @@ osmo_str_toupper_buf(28, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!@
|
|||
= 62, "ABCDEFGHIJKLMNOPQRSTUVWXYZA"
|
||||
osmo_str_toupper_buf(28, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!@#$%^&*()", in-place)
|
||||
= 27, "ABCDEFGHIJKLMNOPQRSTUVWXYZA"
|
||||
|
||||
strbuf_test
|
||||
OSMO_STRBUF_APPEND():
|
||||
The answer is 42
|
||||
would have needed 423470 bytes
|
||||
|
||||
OSMO_STRBUF_PRINTF():
|
||||
1: (need 42 chars, had size=23) T minus 10 9 8 7 6 5 4
|
||||
2: (need 42 chars, had size=42) T minus 10 9 8 7 6 5 4 3 2 1 ... Lift off
|
||||
3: (need 42 chars, had size=42+1) T minus 10 9 8 7 6 5 4 3 2 1 ... Lift off!
|
||||
4: (need 42 chars, had size=0) 0x2b 0x2b 0x2b...
|
||||
5: (need 42 chars, had NULL buffer)
|
||||
|
||||
cascade:
|
||||
(need 134 chars)
|
||||
T minus 10 9 8 7 6 5 4 3 2 1 ... Lift off! -- T minus 10 9 8 7 6 5 4 3 2 1 ... Lift off! -- T minus 10 9 8 7 6 5 4 3 2 1 ... Lift off!
|
||||
(need 134 chars, had size=63) T minus 10 9 8 7 6 5 4 3 2 1 ... Lift off! -- T minus 10 9 8 7
|
||||
|
|
Loading…
Reference in New Issue