On 12/31/19 10:53 AM, Bruno Haible wrote: > Hi Tim, > >>> - providing primitives for string allocation reduces the amount of buffer >>> overflow bugs that otherwise occur in this area. [1] >>> [1] https://lists.gnu.org/archive/html/bug-gnulib/2019-09/msg00031.html > >> here is a string concatenation function without ellipsis, analogue to >> writev() and struct iovec - just a suggestion. Instead of 'struct >> strvec' a new string_t type would be handy. >> >> #include <stddef.h> >> #include <stdio.h> >> #include <stdlib.h> >> #include <string.h> >> >> struct strvec { >> char *strv_base; >> size_t strv_len; >> }; >> >> __attribute__ ((nonnull (1))) >> char *concat_stringv(const struct strvec *strv) >> { >> const struct strvec *str; >> size_t len = 0; >> char *buf; >> >> for (str = strv; str->strv_base; str++) >> len += str->strv_len; >> >> if (!(buf = malloc(len + 1))) >> return buf; >> >> len = 0; >> for (str = strv; str->strv_base; len += str->strv_len, str++) >> memcpy(buf + len, str->strv_base, str->strv_len); >> >> buf[len] = 0; >> >> return buf; >> } >> >> void main(void) >> { >> char *s = concat_stringv((struct strvec []) { >> { "a", 1 }, >> { "b", 1 }, >> { NULL } >> }); > > This looks good. It brings us one step closer to the stated goal [1]. > > Would you like to contribute such a 'string-alloc' module that, together with > 'strdup' and 'asprintf', removes most needs to create a string's contents > "by hand"?
When time allows, I would like to make up a module. Though IMO the design of the function doesn't allow to reuse an existing buffer (e.g. a scratch buffer on the stack). Since malloc() etc are pretty costly, you often want to avoid it as much as possible. Like e.g. /* Use given stack buffer, fallback to malloc() if too short */ char sbuf[256]; char *s = concat_stringv_stack(sbuf, sizeof (sbuf), (struct strvec []) { { "a", 1 }, { "b", 1 }, { NULL } }); ... do things with s ... if (s != sbuf) free (s); Sometimes you want to reuse an existing malloc'ed buffer: /* Use existing heap buffer, use realloc() if too short */ char *buf = malloc(N); char *buf = concat_stringv_reuse(buf, N, (struct strvec []) { { "a", 1 }, { "b", 1 }, { NULL } }); ... do things with s ... free (buf); You might also be interested in the size of the created string to avoid a superfluous strlen(). So the need for more specialized functions makes it all more and more complex. During the development of Libwget/Wget2 we needed all of the above (and more) and finally came up with a good compromise (well, good for us). We created a 'catch them all' string/buffer type plus API. It is a good compromise for all kinds of situations, works like a memory buffer but is guaranteed 0-terminated, allows custom stack buffers with fallback to heap if to small. $ cloc buffer.c Language files blank comment code ----------------------------------------------------------------------- C 1 49 327 195 https://gitlab.com/gnuwget/wget2/blob/master/libwget/buffer.c There also is a sprintf functionality (glibc compatible) using these buffers - and the operation is well faster than glibc's sprintf-like functions for all format strings tested (tested back a few years). The code is also much smaller (380 C code lines), the return values are size_t. It doesn't support float/double. $ cloc buffer_printf.c Language files blank comment code ----------------------------------------------------------------------- C 1 74 120 380 https://gitlab.com/gnuwget/wget2/blob/master/libwget/buffer_printf.c If there is serious interest, I could prepare modules for gnulib. Regards, Tim
signature.asc
Description: OpenPGP digital signature