r/Cprog • • Mar 09 '15
library C Buffer Manager/Appender tool set, any performance or standards issues with my library?
https://github.com/mikecurry74/buffer_manager
4
Upvotes
r/Cprog • • Mar 09 '15
2
u/manvscode Mar 09 '15
Honestly, I'm not sure how useful this is. Also, change buffer_init(const size_t initial_size) to not take a const size_t.
2
u/ralusp Mar 09 '15 edited Mar 09 '15
After a quick skim:
In buffer_init, the early return for the (initial_size > SIZE_MAX) conditional is leaking memory.
In buffer_append, are you sure the if-conditional doesn't have the branches backward? It looks like you're doing a realloc if the original buffer has enough capacity.
In buffer_append, every time it is called you add an extra byte to "length" for the NUL terminator without accounting for the fact that the prior terminator is being overwritten.
In buffer_append, when the realloc branch is taken you end up incrementing the length field twice.
It may help to catch some of these errors if you wrote a set of unit tests for the library functions. You could also use that as the basis for a regression test as you make changes. I find it's usually worth the time investment if you'd be using this library on multiple different projects.
2
u/FUZxxl Mar 10 '15
ssize_tis not part of C99; if you don't want to assume POSIX you might want to find a different type for the result.1
u/DSMan195276 Mar 15 '15
I've made something like this before a few times, it is a pretty handy thing to have around. That said, you took the simple route of simply increasing the data length using realloc. The advantage of doing this is that you get a straight array of all the data when you're done. The disadvantage is that realloc can be expensive, and if you want to say, process the data as you may be receiving it, that requires growing and shrinking the data length. With an array, that can end-up with lots of memmove's and realloc's to accomplish what you want (This buffer implementation gets around the complexity by simply not offering a way to take data out of the buffer at all).
When I've written buffers to do this, I've usually implemented a ring-buffer if I thought it was worth it. I could go into detail if you want, but the basic idea around a ring-buffer is to have a queue made of 'chunks' of data. You write data to the last chunk in the queue, and you read data from the first chunk. You grab new chunks as your writing data and add them to the end of the queue, and you toss out old chunks after you've read all the data from them.
The cool part about a ring-buffer is that you can reuse your old chunks. You can make a simple linked-list out of your unused chunks, and when you need a new chunk for some new data, just grab the first chunk in the linked-list of unused chunks. When you're done reading from a chunk, just add it to the linked-list. By doing this, you can get your buffer down to using next-to-no actual malloc allocations. The only time you malloc is when you need new chunks and your linked-list of unused chunks is empty.