Afficher un message
Vieux 30/05/2008, 15h44   #3
James Harris
Aucun Avatar
 
Messages: n/a
Hébergeur:
Par défaut Re: Expanding buffer - response to "Determine the size of malloc"query

On 30 May, 14:12, James Harris <james.harri...@googlemail.com> wrote:
> Initial issue: read in an arbitrary-length piece of text.
> Perceived issue: handle variable-length data
>
> The code below is a suggestion for implementing a variable length
> buffer that could be used to read text or handle arrays of arbitrary
> length. I don't have the expertise in C of many folks here so I feel
> like I'm offering a small furry animal for sacrifice to a big armour
> plated one... but will offer it anyway. Please do suggest improvements
> or challenge the premise. It would be great if it could be improved to
> become a generally useful piece of code.
>
> Well, here goes. This should be fun. :-?
>
> -
>
> The following utility code is passed a buffer (allocated by the
> caller) and maintains it at an appropriate size. The main function
> increases the allocation (when necessary) by factors - rather than
> fixed amounts - for speed. There is a secondary function to trim a
> buffer back to a specific size. An extra byte (one more than is
> requested) is always left at the end.
>
> /*
> * Expanding buffer
> */
>
> #define EBUF_SIZE_INIT 128
> #define EBUF_SIZE_MIN 128
> #define EBUF_INCREASE 1.5 /* Factor to increase space by each time */
>
> #include <stdio.h>
> #include <stdlib.h>
> #include <malloc.h>
>
> int ebuf_full(char **buf, size_t *buf_size, size_t offset) {
> size_t new_size;
> char *new_buf;
>
> if (*buf_size < offset + 2) { /* NB last pos left empty */
> new_size = *buf_size * EBUF_INCREASE + 1;
> if (new_size < offset + 2) new_size = offset + 2;
> if (new_size < EBUF_SIZE_MIN) new_size = EBUF_SIZE_MIN;
> if ((new_buf = realloc(*buf, new_size)) == NULL) {
> return 1; /* Failed to realloc buffer */
> }
> *buf = new_buf;
> *buf_size = new_size;
> }
> return 0; /* Reallocated successfuly */
>
> }
>
> int ebuf_trim(char **buf, size_t *buf_size, size_t offset) {
> int new_size = offset + 2; /* Includes empty char */
> char *new_buf;
>
> if (new_size < EBUF_SIZE_MIN) new_size = EBUF_SIZE_MIN;
> if (new_size != *buf_size) {
> if ((new_buf = realloc(*buf, new_size)) == NULL) {
> return 1; /* Reallocation failed (unlikely) */
> }
> *buf = new_buf;
> *buf_size = new_size;
> }
> return 0; /* Reallocation succeeded */
>
> }


Here's another piece of example code to use the proposed functions.
This one is to read an arbitrary-length line. Hopefully when compared
with a custom line-reading function the code below keeps a far simpler
interface while allowing any necessary options. It should also be fast
in that, again, the function only gets called if there is a need for
more space. Since the function allocates memory in ever-increasing
chunks for most iterations the function will not be called.


#define ENDCHAR '\n'

FILE *infile = stdin;
char *buffer;
size_t bufsize = 100; /* Initial size only */
size_t offset;

... (allocate buffer)

/* Read to 'endchar' */
for (offset = 0; (ch = getc(infile)) != EOF; ) {
if (offset + 2 > bufsize &&
ebuf_full(&buffer, &bufsize, offset) {
fprintf(stderr, "Line too long for memory");
exit(1);
}
buffer[offset++] = ch;
if (ch == ENDCHAR) break;
}

... (free buffer)

Notably since we invoke getc() we could easily have more than one
termination character such as

if (ch == '\n' || ch == '\0' || ch == ',')

etc. which is intended to be a big advantage over calling a line
reader function.

--
James
  Réponse avec citation
 
Page generated in 0,07372 seconds with 9 queries