diff options
Diffstat (limited to 'src/buffer.c')
-rw-r--r-- | src/buffer.c | 154 |
1 files changed, 89 insertions, 65 deletions
diff --git a/src/buffer.c b/src/buffer.c index e2ebc02..7d16af8 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -4,6 +4,7 @@ #include <string.h> #include <stdio.h> #include <stdlib.h> +#include <stdint.h> #include "config.h" #include "cmark_ctype.h" @@ -14,48 +15,75 @@ */ unsigned char cmark_strbuf__initbuf[1]; -#define ENSURE_SIZE(b, d) \ - if ((d) > b->asize) \ - cmark_strbuf_grow(b, (d)); \ - #ifndef MIN #define MIN(x,y) ((x<y) ? x : y) #endif -void cmark_strbuf_init(cmark_strbuf *buf, int initial_size) +void cmark_strbuf_init(cmark_strbuf *buf, bufsize_t initial_size) { buf->asize = 0; buf->size = 0; buf->ptr = cmark_strbuf__initbuf; - if (initial_size) + if (initial_size > 0) cmark_strbuf_grow(buf, initial_size); } -void cmark_strbuf_grow(cmark_strbuf *buf, int target_size) +void cmark_strbuf_overflow_err() { + fprintf(stderr, "String buffer overflow"); + abort(); +} + +static inline void +S_strbuf_grow_by(cmark_strbuf *buf, size_t add) { + size_t target_size = (size_t)buf->size + add; + + if (target_size < add /* Integer overflow. */ + || target_size > BUFSIZE_MAX /* Truncation overflow. */ + ) { + cmark_strbuf_overflow_err(); + return; /* unreachable */ + } + + if ((bufsize_t)target_size >= buf->asize) + cmark_strbuf_grow(buf, (bufsize_t)target_size); +} + +void cmark_strbuf_grow(cmark_strbuf *buf, bufsize_t target_size) { unsigned char *new_ptr; - int new_size; - if (target_size <= buf->asize) + if (target_size < buf->asize) return; if (buf->asize == 0) { - new_size = target_size; new_ptr = NULL; } else { - new_size = buf->asize; new_ptr = buf->ptr; } - /* grow the buffer size by 1.5, until it's big enough - * to fit our target size */ - while (new_size < target_size) - new_size = (new_size << 1) - (new_size >> 1); + /* Oversize the buffer by 50% to guarantee amortized linear time + * complexity on append operations. */ + size_t new_size = (size_t)target_size + (size_t)target_size / 2; + + /* Account for terminating null byte. */ + new_size += 1; /* round allocation up to multiple of 8 */ new_size = (new_size + 7) & ~7; + if (new_size < (size_t)target_size /* Integer overflow. */ + || new_size > BUFSIZE_MAX /* Truncation overflow. */ + ) { + if (target_size >= BUFSIZE_MAX) { + /* No space for terminating null byte. */ + cmark_strbuf_overflow_err(); + return; /* unreachable */ + } + /* Oversize by the maximum possible amount. */ + new_size = BUFSIZE_MAX; + } + new_ptr = (unsigned char *)realloc(new_ptr, new_size); if (!new_ptr) { @@ -63,16 +91,11 @@ void cmark_strbuf_grow(cmark_strbuf *buf, int target_size) abort(); } - buf->asize = new_size; + buf->asize = (bufsize_t)new_size; buf->ptr = new_ptr; - - /* truncate the existing buffer size if necessary */ - if (buf->size >= buf->asize) - buf->size = buf->asize - 1; - buf->ptr[buf->size] = '\0'; } -size_t cmark_strbuf_len(const cmark_strbuf *buf) +bufsize_t cmark_strbuf_len(const cmark_strbuf *buf) { return buf->size; } @@ -95,13 +118,14 @@ void cmark_strbuf_clear(cmark_strbuf *buf) buf->ptr[0] = '\0'; } -void cmark_strbuf_set(cmark_strbuf *buf, const unsigned char *data, int len) +void cmark_strbuf_set(cmark_strbuf *buf, const unsigned char *data, bufsize_t len) { if (len <= 0 || data == NULL) { cmark_strbuf_clear(buf); } else { if (data != buf->ptr) { - ENSURE_SIZE(buf, len + 1); + if (len >= buf->asize) + cmark_strbuf_grow(buf, len); memmove(buf->ptr, data, len); } buf->size = len; @@ -112,22 +136,22 @@ void cmark_strbuf_set(cmark_strbuf *buf, const unsigned char *data, int len) void cmark_strbuf_sets(cmark_strbuf *buf, const char *string) { cmark_strbuf_set(buf, (const unsigned char *)string, - string ? strlen(string) : 0); + string ? cmark_strbuf_safe_strlen(string) : 0); } void cmark_strbuf_putc(cmark_strbuf *buf, int c) { - ENSURE_SIZE(buf, buf->size + 2); + S_strbuf_grow_by(buf, 1); buf->ptr[buf->size++] = c; buf->ptr[buf->size] = '\0'; } -void cmark_strbuf_put(cmark_strbuf *buf, const unsigned char *data, int len) +void cmark_strbuf_put(cmark_strbuf *buf, const unsigned char *data, bufsize_t len) { if (len <= 0) return; - ENSURE_SIZE(buf, buf->size + len + 1); + S_strbuf_grow_by(buf, len); memmove(buf->ptr + buf->size, data, len); buf->size += len; buf->ptr[buf->size] = '\0'; @@ -135,21 +159,22 @@ void cmark_strbuf_put(cmark_strbuf *buf, const unsigned char *data, int len) void cmark_strbuf_puts(cmark_strbuf *buf, const char *string) { - cmark_strbuf_put(buf, (const unsigned char *)string, strlen(string)); + cmark_strbuf_put(buf, (const unsigned char *)string, + cmark_strbuf_safe_strlen(string)); } void cmark_strbuf_vprintf(cmark_strbuf *buf, const char *format, va_list ap) { - const int expected_size = buf->size + (strlen(format) * 2); - int len; - - ENSURE_SIZE(buf, expected_size); + size_t expected_size = strlen(format); + if (expected_size <= SIZE_MAX / 2) + expected_size *= 2; + S_strbuf_grow_by(buf, expected_size); while (1) { va_list args; va_copy(args, ap); - len = vsnprintf( + int len = vsnprintf( (char *)buf->ptr + buf->size, buf->asize - buf->size, format, args @@ -168,12 +193,12 @@ void cmark_strbuf_vprintf(cmark_strbuf *buf, const char *format, va_list ap) abort(); } - if (len + 1 <= buf->asize - buf->size) { + if ((size_t)len < (size_t)(buf->asize - buf->size)) { buf->size += len; break; } - ENSURE_SIZE(buf, buf->size + len + 1); + S_strbuf_grow_by(buf, len); } } @@ -186,11 +211,13 @@ void cmark_strbuf_printf(cmark_strbuf *buf, const char *format, ...) va_end(ap); } -void cmark_strbuf_copy_cstr(char *data, int datasize, const cmark_strbuf *buf) +void cmark_strbuf_copy_cstr(char *data, bufsize_t datasize, const cmark_strbuf *buf) { - int copylen; + bufsize_t copylen; - assert(data && datasize && buf); + assert(buf); + if (!data || datasize <= 0) + return; data[0] = '\0'; @@ -224,22 +251,6 @@ unsigned char *cmark_strbuf_detach(cmark_strbuf *buf) return data; } -void cmark_strbuf_attach(cmark_strbuf *buf, unsigned char *ptr, int asize) -{ - cmark_strbuf_free(buf); - - if (ptr) { - buf->ptr = ptr; - buf->size = strlen((char *)ptr); - if (asize) - buf->asize = (asize < buf->size) ? buf->size + 1 : asize; - else /* pass 0 to fall back on strlen + 1 */ - buf->asize = buf->size + 1; - } else { - cmark_strbuf_grow(buf, asize); - } -} - int cmark_strbuf_cmp(const cmark_strbuf *a, const cmark_strbuf *b) { int result = memcmp(a->ptr, b->ptr, MIN(a->size, b->size)); @@ -247,20 +258,28 @@ int cmark_strbuf_cmp(const cmark_strbuf *a, const cmark_strbuf *b) (a->size < b->size) ? -1 : (a->size > b->size) ? 1 : 0; } -int cmark_strbuf_strchr(const cmark_strbuf *buf, int c, int pos) +bufsize_t cmark_strbuf_strchr(const cmark_strbuf *buf, int c, bufsize_t pos) { + if (pos >= buf->size) + return -1; + if (pos < 0) + pos = 0; + const unsigned char *p = (unsigned char *)memchr(buf->ptr + pos, c, buf->size - pos); if (!p) return -1; - return (int)(p - (const unsigned char *)buf->ptr); + return (bufsize_t)(p - (const unsigned char *)buf->ptr); } -int cmark_strbuf_strrchr(const cmark_strbuf *buf, int c, int pos) +bufsize_t cmark_strbuf_strrchr(const cmark_strbuf *buf, int c, bufsize_t pos) { - int i; + if (pos < 0 || buf->size == 0) + return -1; + if (pos >= buf->size) + pos = buf->size - 1; - for (i = pos; i >= 0; i--) { + for (bufsize_t i = pos; i >= 0; i--) { if (buf->ptr[i] == (unsigned char) c) return i; } @@ -268,17 +287,22 @@ int cmark_strbuf_strrchr(const cmark_strbuf *buf, int c, int pos) return -1; } -void cmark_strbuf_truncate(cmark_strbuf *buf, int len) +void cmark_strbuf_truncate(cmark_strbuf *buf, bufsize_t len) { + if (len < 0) + len = 0; + if (len < buf->size) { buf->size = len; buf->ptr[buf->size] = '\0'; } } -void cmark_strbuf_drop(cmark_strbuf *buf, int n) +void cmark_strbuf_drop(cmark_strbuf *buf, bufsize_t n) { if (n > 0) { + if (n > buf->size) + n = buf->size; buf->size = buf->size - n; if (buf->size) memmove(buf->ptr, buf->ptr + n, buf->size); @@ -304,7 +328,7 @@ void cmark_strbuf_rtrim(cmark_strbuf *buf) void cmark_strbuf_trim(cmark_strbuf *buf) { - int i = 0; + bufsize_t i = 0; if (!buf->size) return; @@ -322,7 +346,7 @@ void cmark_strbuf_trim(cmark_strbuf *buf) void cmark_strbuf_normalize_whitespace(cmark_strbuf *s) { bool last_char_was_space = false; - int r, w; + bufsize_t r, w; for (r = 0, w = 0; r < s->size; ++r) { switch (s->ptr[r]) { @@ -347,7 +371,7 @@ void cmark_strbuf_normalize_whitespace(cmark_strbuf *s) // Destructively unescape a string: remove backslashes before punctuation chars. extern void cmark_strbuf_unescape(cmark_strbuf *buf) { - int r, w; + bufsize_t r, w; for (r = 0, w = 0; r < buf->size; ++r) { if (buf->ptr[r] == '\\' && cmark_ispunct(buf->ptr[r + 1])) |