A
Andrew Poelstra
I hacked this together this morning so that I could shift my out-of-
space code away from the rest of my logic. I wanted to allow array
syntax on my dynamic buffers, so I manually created a struct with
malloc() and judicious use of void* pointers.
I decided to post it because stuff like this is breeding ground for
UB, and this group is good at finding it. (I had a dentist analogy
for that, but it didn't work out.)
Here's my code:
/* Start. */
#include <stdlib.h>
#include <string.h>
void *create_buffer (size_t size, size_t max)
{
/* We need to allocate three size_t before the actual buffer to hold
* the size of each item, the maximum number of items, and the actual
* number of items. We do it this way instead of using a struct so that
* array syntax can still be used on the returned structure. */
size_t *tmp = malloc ( (3 * sizeof *tmp) + (size * max) );
if (tmp != NULL)
{
tmp[0] = size; /* Size of each item */
tmp[1] = max; /* Maximum # of items */
tmp[2] = 0; /* Actual # of items */
}
return (tmp != NULL) ? tmp + 3 : NULL;
}
/* This function will return three possible things:
* 1. On ordinary usage, it will return the new maximum size of the buffer.
* 2. If it runs out of memory, it will return 0.
* 3. If passed NULL, it will do nothing but return maximum size of buf.
* The third case is used after the second case to recover lost size info.
* If buf is NULL, Bad Things will happen. */
size_t append_item (void *buf, void *itm)
{
size_t *tmp = (size_t *) buf - 3;
size_t size = tmp[0];
size_t max = tmp[1];
size_t num = ++tmp[2];
/* Early return on special case. */
if (itm == NULL)
return num;
/* If we don't have enough space, try and get some more. */
if (num == max) {
size_t *rtmp;
max *= 2;
while (!(rtmp = realloc (tmp, max)) && (max > tmp[1]))
--max;
}
/* If we now have enough space, we're good. Otherwise, we're cooked. */
if (max > num) {
memmove (buf, itm, size);
return num;
} else return 0;
}
/* End. */
It compiles in gcc with all warnings set.
space code away from the rest of my logic. I wanted to allow array
syntax on my dynamic buffers, so I manually created a struct with
malloc() and judicious use of void* pointers.
I decided to post it because stuff like this is breeding ground for
UB, and this group is good at finding it. (I had a dentist analogy
for that, but it didn't work out.)
Here's my code:
/* Start. */
#include <stdlib.h>
#include <string.h>
void *create_buffer (size_t size, size_t max)
{
/* We need to allocate three size_t before the actual buffer to hold
* the size of each item, the maximum number of items, and the actual
* number of items. We do it this way instead of using a struct so that
* array syntax can still be used on the returned structure. */
size_t *tmp = malloc ( (3 * sizeof *tmp) + (size * max) );
if (tmp != NULL)
{
tmp[0] = size; /* Size of each item */
tmp[1] = max; /* Maximum # of items */
tmp[2] = 0; /* Actual # of items */
}
return (tmp != NULL) ? tmp + 3 : NULL;
}
/* This function will return three possible things:
* 1. On ordinary usage, it will return the new maximum size of the buffer.
* 2. If it runs out of memory, it will return 0.
* 3. If passed NULL, it will do nothing but return maximum size of buf.
* The third case is used after the second case to recover lost size info.
* If buf is NULL, Bad Things will happen. */
size_t append_item (void *buf, void *itm)
{
size_t *tmp = (size_t *) buf - 3;
size_t size = tmp[0];
size_t max = tmp[1];
size_t num = ++tmp[2];
/* Early return on special case. */
if (itm == NULL)
return num;
/* If we don't have enough space, try and get some more. */
if (num == max) {
size_t *rtmp;
max *= 2;
while (!(rtmp = realloc (tmp, max)) && (max > tmp[1]))
--max;
}
/* If we now have enough space, we're good. Otherwise, we're cooked. */
if (max > num) {
memmove (buf, itm, size);
return num;
} else return 0;
}
/* End. */
It compiles in gcc with all warnings set.