[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: string types
From: |
Tim Rühsen |
Subject: |
Re: string types |
Date: |
Mon, 6 Jan 2020 11:34:35 +0100 |
User-agent: |
Mozilla/5.0 (X11; Linux x86_64; rv:68.0) Gecko/20100101 Thunderbird/68.3.1 |
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
- Re: string types,
Tim Rühsen <=