diff options
author | Yorhel <git@yorhel.nl> | 2013-01-13 12:02:18 +0100 |
---|---|---|
committer | Yorhel <git@yorhel.nl> | 2013-01-13 12:02:18 +0100 |
commit | 085ef9f205f671c9ae40e78b913cca7e524dc5b9 (patch) | |
tree | b9a33bf43a15d6c21df957e1b0b3946657366aaa | |
parent | 6c90c8d748deed256b25d5644799d1d93c16b02d (diff) |
ecbuf: Fix write-after-buffer bug in _push()
And added a minor optimization to avoid a realloc() when the write
pointer is allowed to wrap to the 0 index. (b would be set to cn-1, but
in that case we might as well just increase cn and not set b)
Unfortunately, this makes the _push() function a bit larger than I'd
like it to be. :-(
-rw-r--r-- | ecbuf.h | 23 |
1 files changed, 18 insertions, 5 deletions
@@ -99,16 +99,29 @@ typedef struct { static inline void *ecbuf__push(ecbuf_vars_t *v, void **a, size_t alen) { - int i; + /* The algortihm is something like: + * 1. If the buffer is full, "grow" it + * 2. Calculate next write position + * 3. If write position is outside of buffer (can happen), grow it + * It may be possible to combine some of these steps and shorten the + * function, but that doesn't look very easy. :-( + */ + int i, obn = v->bn; + /* 1 */ if(v->l == v->bn) { - if(v->cn == v->bn) - v->b = (v->o - 1 + v->cn) & (v->cn-1); v->bn <<= 1; - *a = realloc(*a, v->bn*alen); + if(v->cn == obn) { + if(v->o) v->b = (v->o - 1 + v->cn) & (v->cn-1); + else v->cn = v->bn; + } } + /* 2 */ i = v->l + v->o - v->b - 1; - if(v->bn == v->cn) i &= v->cn-1; + if(v->bn == v->cn) i &= v->cn-1; else if(v->o <= v->b) i += v->cn; + /* 3 */ + if(i >= v->bn) v->bn <<= 1; + if(v->bn != obn) *a = realloc(*a, v->bn*alen); v->l++; return ((char *)*a)+alen*i; } |