summaryrefslogtreecommitdiff
path: root/src/buffer.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/buffer.c')
-rw-r--r--src/buffer.c154
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]))