summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYorhel <git@yorhel.nl>2013-01-13 12:02:18 +0100
committerYorhel <git@yorhel.nl>2013-01-13 12:02:18 +0100
commit085ef9f205f671c9ae40e78b913cca7e524dc5b9 (patch)
treeb9a33bf43a15d6c21df957e1b0b3946657366aaa
parent6c90c8d748deed256b25d5644799d1d93c16b02d (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.h23
1 files changed, 18 insertions, 5 deletions
diff --git a/ecbuf.h b/ecbuf.h
index 658a784..deb4521 100644
--- a/ecbuf.h
+++ b/ecbuf.h
@@ -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;
}