shared: add nm_utils_strbuf_seek_end() helper

This commit is contained in:
Thomas Haller 2018-09-06 15:20:21 +02:00
parent 0feeeaac63
commit 0a8248af10
3 changed files with 126 additions and 2 deletions

View file

@ -106,7 +106,7 @@ nm_utils_strbuf_append (char **buf, gsize *len, const char *format, ...)
retval = g_vsnprintf (p, *len, format, args);
va_end (args);
if (retval >= *len) {
if ((gsize) retval >= *len) {
*buf = &p[*len];
*len = 0;
} else {
@ -115,6 +115,88 @@ nm_utils_strbuf_append (char **buf, gsize *len, const char *format, ...)
}
}
/**
* nm_utils_strbuf_seek_end:
* @buf: the input/output buffer
* @len: the input/output lenght of the buffer.
*
* Commonly, one uses nm_utils_strbuf_append*(), to incrementally
* append strings to the buffer. However, sometimes we need to use
* existing API to write to the buffer.
* After doing so, we want to adjust the buffer counter.
* Essentially,
*
* g_snprintf (buf, len, ...);
* nm_utils_strbuf_seek_end (&buf, &len);
*
* is almost the same as
*
* nm_utils_strbuf_append (&buf, &len, ...);
*
* They only behave differently, if the string fits exactly
* into the buffer without truncation. The former cannot distinguish
* the two cases, while the latter can.
*/
void
nm_utils_strbuf_seek_end (char **buf, gsize *len)
{
gsize l;
char *end;
nm_assert (len);
nm_assert (buf && *buf);
if (*len == 0)
return;
end = memchr (*buf, 0, *len);
if (!end) {
/* hm, no NUL character within len bytes.
* Just NUL terminate the array and consume them
* all. */
*buf += *len;
(*buf)[-1] = '\0';
*len = 0;
return;
}
l = end - *buf;
nm_assert (l < *len);
*buf = end;
*len -= l;
if (*len == 1) {
/* the last character of a buffer is the '\0'. There are two
* cases why that may happen:
* - but string was truncated
* - the string fit exactly into the buffer.
* Here we cannot distinguish between the two, so assume the string
* was truncated and signal that by setting @len to 0 and pointing the
* buffer *past* the end (like all other nm_utils_strbuf_*() functions).
*
* Note that nm_utils_strbuf_append_str() can distinguish between
* the two cases, and leaves @len at 1, if the string was not actually
* truncated.
*
* For consistancy, it might be better not to do this and just
* seek to end of the buffer (not past it). However, that would mean,
* in a series of
* g_snprintf()
* nm_utils_strbuf_seek_end()
* the length would never reach zero, but stay at 1. With this,
* it reaches len 0 early.
* It seems better to declare the buffer as fully consumed and set
* the length to zero.
*
* If the caller does not care about truncation, then this behavior
* is more sensible. If the caller cares about truncation, it must
* check earlier (right when the truncation occures).
*/
(*buf)++;
*len = 0;
}
}
/*****************************************************************************/
/**

View file

@ -257,6 +257,7 @@ _nm_utils_strbuf_init (char *buf, gsize len, char **p_buf_ptr, gsize *p_buf_len)
void nm_utils_strbuf_append (char **buf, gsize *len, const char *format, ...) _nm_printf (3, 4);
void nm_utils_strbuf_append_c (char **buf, gsize *len, char c);
void nm_utils_strbuf_append_str (char **buf, gsize *len, const char *str);
void nm_utils_strbuf_seek_end (char **buf, gsize *len);
const char *nm_strquote (char *buf, gsize buf_len, const char *str);

View file

@ -1445,7 +1445,7 @@ test_nm_utils_strbuf_append (void)
t_buf = buf;
t_len = buf_len;
test_mode = nmtst_get_rand_int () % 4;
test_mode = nmtst_get_rand_int () % 5;
switch (test_mode) {
case 0:
@ -1466,6 +1466,47 @@ test_nm_utils_strbuf_append (void)
case 3:
nm_utils_strbuf_append (&t_buf, &t_len, "%s", str);
break;
case 4:
g_snprintf (t_buf, t_len, "%s", str);
if ( t_len > 0
&& strlen (str) >= buf_len
&& (nmtst_get_rand_int () % 2)) {
/* the string was truncated by g_snprintf(). That means, at the last position in the
* buffer is now NUL.
* Replace the NUL by the actual character, and check that nm_utils_strbuf_seek_end()
* does the right thing: NUL terminate the buffer and seek past the end of the buffer. */
g_assert_cmpmem (t_buf, t_len - 1, str, t_len - 1);
g_assert (t_buf[t_len - 1] == '\0');
g_assert (str[t_len - 1] != '\0');
t_buf[t_len - 1] = str[t_len - 1];
nm_utils_strbuf_seek_end (&t_buf, &t_len);
g_assert (t_len == 0);
g_assert (t_buf == &buf[buf_len]);
g_assert (t_buf[-1] == '\0');
} else {
nm_utils_strbuf_seek_end (&t_buf, &t_len);
if (strlen (str) + 1 == buf_len) {
/* Special case: we appended a string that fit into the buffer
* exactly, without truncation.
* If we would append the string via nm_utils_strbuf_append(),
* then it would have recognized that the string was not truncated
* and leave len==1, and pointing the buffer to the terminating NUL
* (at the very end, not past it).
*
* But nm_utils_strbuf_seek_end() cannot distinguish whether
* truncation occured, and assumes the buffer was indeed truncated.
*
* Assert for that, but also adjust the numbers, so that the assertions
* below pass (the assertions below theck for the nm_utils_strbuf_append()
* case). */
g_assert (t_len == 0);
g_assert (t_buf == &buf[buf_len]);
g_assert (t_buf[-1] == '\0');
t_len = 1;
t_buf--;
}
}
break;
}
/* Assert that the source-buffer is unmodified. */