Reputation: 100
I am writing a method to append some data to a buffer.
The Buffer looks like:
Buffer *new_buffer(size_t reserved) {
Buffer* buffer = malloc(sizeof(Buffer));
buffer->data = (char*)calloc(reserved, sizeof(char));
buffer->length = reserved;
return buffer;
}
I would like to append data to the buffer, the method I have written is as follows:
void append_buffer(Buffer *buffer, char *data, size_t length) {
if( buffer->length <= length ) {
buffer->length = buffer->length + length;
buffer->data = realloc( buffer->data, buffer->length * 2 );
}
memcpy( &buffer->data[ strlen( buffer->data ) - 1 ], data, length );
}
Note that the size parameter is just the size of the array to append.
i.e.
append_buffer(buffer, data, len_data);
I believe there is errors with my memcpy;
The thought process is to use memcpy to place the data into memory for the last character in the buffer; so that we can avoid the terminating character.
Running this just doesn't append any data and I'm not really sure why!
Upvotes: 0
Views: 1156
Reputation: 154995
When using buffers in C that contain arbitrary binary data you need to store two length fields: the capacity of the buffer as-allocated, and the amount of data currently stored in the buffer.
(For comparison to C#/.NET and Java, this is like how List<T>
(ArrayList
in Java) has both an internal T[].Length
and a separate Count
value).
I've rewritten your code to show how I would do it. Note that:
Buffer
struct stores both capacity
and length
.assert()
to validate parameters and verify that allocations succeeded. This is only for demonstration purposes because assert
is removed from the program if NDEBUG
is defiend, whereas memory allocations should always be verified.append_buffer
) from the logic for reallocating the buffer (expand_buffer
).Like so:
#undef NDEBUG // So `assert` is always compiled.
struct Buffer {
char* data;
size_t capacity; // Size of the actual allocated space
size_t length; // Amount of space currently used
};
struct Buffer* new_buffer( size_t initialCapacity ) {
struct Buffer* info = malloc( sizeof(Buffer) ); // Allocate space for the metadata
assert( info ); // Always ensure allocation succeeded!
info->data = calloc( initialCapacity, sizeof(char) );
assert( info->data );
info->capacity = initialCapacity;
info->length = 0;
return info;
}
void append_buffer( struct Buffer* buffer, char* data, size_t dataLength ) {
assert( buffer ); // Parameter argument validation
assert( data );
if( dataLength == 0 ) return;
size_t remainingCapacity = buffer->capacity - buffer->length;
if( remainingCapacity < dataLength ) {
size_t desiredCapacity = buffer->capacity + dataLength; // You might want to add a growth factor here
expand_buffer( buffer, desiredCapacity );
}
char* start = buffer->data + buffer->length;
memcpy( start, data, dataLength );
buffer->length += dataLength;
}
void expand_buffer( struct Buffer* buffer, size_t desiredCapacity ) {
assert( buffer );
assert( buffer->data );
char* newData = realloc( buffer->data, desiredCapacity );
assert( newData );
buffer->data = newData;
buffer->capacity = desiredCapacity;
// buffer->length remains unchanged
}
void destroy_buffer( struct Buffer* buffer ) {
assert( buffer );
if( buffer-> data ) {
free( buffer->data );
buffer->data = NULL;
}
free( buffer );
}
Upvotes: 1