/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
- * Copyright 2017 Mike Becker, Olaf Wintermann All rights reserved.
+ * Copyright 2021 Mike Becker, Olaf Wintermann All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* POSSIBILITY OF SUCH DAMAGE.
*/
-#include "ucx/buffer.h"
+#include "cx/buffer.h"
+#include "cx/utils.h"
-#include <stdarg.h>
-#include <stdlib.h>
+#include <stdio.h>
#include <string.h>
-UcxBuffer *ucx_buffer_new(void *space, size_t capacity, int flags) {
- UcxBuffer *buffer = (UcxBuffer*) malloc(sizeof(UcxBuffer));
- if (buffer) {
- buffer->flags = flags;
- if (!space) {
- buffer->space = (char*)malloc(capacity);
- if (!buffer->space) {
- free(buffer);
- return NULL;
- }
- memset(buffer->space, 0, capacity);
- buffer->flags |= UCX_BUFFER_AUTOFREE;
- } else {
- buffer->space = (char*)space;
+int cxBufferInit(
+ CxBuffer *buffer,
+ void *space,
+ size_t capacity,
+ CxAllocator const *allocator,
+ int flags
+) {
+ if (allocator == NULL) allocator = cxDefaultAllocator;
+ buffer->allocator = allocator;
+ buffer->flags = flags;
+ if (!space) {
+ buffer->bytes = cxMalloc(allocator, capacity);
+ if (buffer->bytes == NULL) {
+ return 1;
}
- buffer->capacity = capacity;
- buffer->size = 0;
-
- buffer->pos = 0;
+ buffer->flags |= CX_BUFFER_FREE_CONTENTS;
+ } else {
+ buffer->bytes = space;
}
+ buffer->capacity = capacity;
+ buffer->size = 0;
+ buffer->pos = 0;
- return buffer;
-}
+ buffer->flush_func = NULL;
+ buffer->flush_target = NULL;
+ buffer->flush_blkmax = 0;
+ buffer->flush_blksize = 4096;
+ buffer->flush_threshold = SIZE_MAX;
-void ucx_buffer_free(UcxBuffer *buffer) {
- if ((buffer->flags & UCX_BUFFER_AUTOFREE) == UCX_BUFFER_AUTOFREE) {
- free(buffer->space);
- }
- free(buffer);
+ return 0;
}
-UcxBuffer* ucx_buffer_extract(
- UcxBuffer *src, size_t start, size_t length, int flags) {
- if (src->size == 0 || length == 0 ||
- ((size_t)-1) - start < length || start+length > src->capacity)
- {
- return NULL;
- }
-
- UcxBuffer *dst = (UcxBuffer*) malloc(sizeof(UcxBuffer));
- if (dst) {
- dst->space = (char*)malloc(length);
- if (!dst->space) {
- free(dst);
- return NULL;
- }
- dst->capacity = length;
- dst->size = length;
- dst->flags = flags | UCX_BUFFER_AUTOFREE;
- dst->pos = 0;
- memcpy(dst->space, src->space+start, length);
+void cxBufferDestroy(CxBuffer *buffer) {
+ if ((buffer->flags & CX_BUFFER_FREE_CONTENTS) == CX_BUFFER_FREE_CONTENTS) {
+ cxFree(buffer->allocator, buffer->bytes);
}
- return dst;
}
-int ucx_buffer_seek(UcxBuffer *buffer, off_t offset, int whence) {
+int cxBufferSeek(
+ CxBuffer *buffer,
+ off_t offset,
+ int whence
+) {
size_t npos;
switch (whence) {
- case SEEK_CUR:
- npos = buffer->pos;
- break;
- case SEEK_END:
- npos = buffer->size;
- break;
- case SEEK_SET:
- npos = 0;
- break;
- default:
- return -1;
+ case SEEK_CUR:
+ npos = buffer->pos;
+ break;
+ case SEEK_END:
+ npos = buffer->size;
+ break;
+ case SEEK_SET:
+ npos = 0;
+ break;
+ default:
+ return -1;
}
size_t opos = npos;
npos += offset;
-
+
if ((offset > 0 && npos < opos) || (offset < 0 && npos > opos)) {
return -1;
}
-
+
if (npos >= buffer->size) {
return -1;
} else {
}
-int ucx_buffer_eof(UcxBuffer *buffer) {
+void cxBufferClear(CxBuffer *buffer) {
+ memset(buffer->bytes, 0, buffer->size);
+ buffer->size = 0;
+ buffer->pos = 0;
+}
+
+int cxBufferEof(CxBuffer const *buffer) {
return buffer->pos >= buffer->size;
}
-int ucx_buffer_extend(UcxBuffer *buffer, size_t len) {
- size_t newcap = buffer->capacity;
-
- if (buffer->capacity + len < buffer->capacity) {
- return -1;
- }
-
- while (buffer->capacity + len > newcap) {
- newcap <<= 1;
- if (newcap < buffer->capacity) {
- return -1;
- }
+int cxBufferMinimumCapacity(
+ CxBuffer *buffer,
+ size_t newcap
+) {
+ if (newcap <= buffer->capacity) {
+ return 0;
}
-
- char *newspace = (char*)realloc(buffer->space, newcap);
- if (newspace) {
- memset(newspace+buffer->size, 0, newcap-buffer->size);
- buffer->space = newspace;
+
+ if (cxReallocate(buffer->allocator,
+ (void **) &buffer->bytes, newcap) == 0) {
buffer->capacity = newcap;
+ return 0;
} else {
return -1;
}
-
- return 0;
}
-size_t ucx_buffer_write(const void *ptr, size_t size, size_t nitems,
- UcxBuffer *buffer) {
+/**
+ * Helps flushing data to the flush target of a buffer.
+ *
+ * @param buffer the buffer containing the config
+ * @param space the data to flush
+ * @param size the element size
+ * @param nitems the number of items
+ * @return the number of items flushed
+ */
+static size_t cx_buffer_write_flush_helper(
+ CxBuffer *buffer,
+ unsigned char const *space,
+ size_t size,
+ size_t nitems
+) {
+ size_t pos = 0;
+ size_t remaining = nitems;
+ size_t max_items = buffer->flush_blksize / size;
+ while (remaining > 0) {
+ size_t items = remaining > max_items ? max_items : remaining;
+ size_t flushed = buffer->flush_func(
+ space + pos,
+ size, items,
+ buffer->flush_target);
+ if (flushed > 0) {
+ pos += (flushed * size);
+ remaining -= flushed;
+ } else {
+ // if no bytes can be flushed out anymore, we give up
+ break;
+ }
+ }
+ return nitems - remaining;
+}
+
+size_t cxBufferWrite(
+ void const *ptr,
+ size_t size,
+ size_t nitems,
+ CxBuffer *buffer
+) {
+ // optimize for easy case
+ if (size == 1 && (buffer->capacity - buffer->pos) >= nitems) {
+ memcpy(buffer->bytes + buffer->pos, ptr, nitems);
+ buffer->pos += nitems;
+ if (buffer->pos > buffer->size) {
+ buffer->size = buffer->pos;
+ }
+ return nitems;
+ }
+
size_t len;
- if(ucx_szmul(size, nitems, &len)) {
+ size_t nitems_out = nitems;
+ if (cx_szmul(size, nitems, &len)) {
return 0;
}
size_t required = buffer->pos + len;
if (buffer->pos > required) {
return 0;
}
-
+
+ bool perform_flush = false;
if (required > buffer->capacity) {
- if ((buffer->flags & UCX_BUFFER_AUTOEXTEND) == UCX_BUFFER_AUTOEXTEND) {
- if (ucx_buffer_extend(buffer, required - buffer->capacity)) {
- return 0;
+ if ((buffer->flags & CX_BUFFER_AUTO_EXTEND) == CX_BUFFER_AUTO_EXTEND && required) {
+ if (buffer->flush_blkmax > 0 && required > buffer->flush_threshold) {
+ perform_flush = true;
+ } else {
+ if (cxBufferMinimumCapacity(buffer, required)) {
+ return 0;
+ }
}
} else {
- len = buffer->capacity - buffer->pos;
- if (size > 1) {
- len -= len%size;
+ if (buffer->flush_blkmax > 0) {
+ perform_flush = true;
+ } else {
+ // truncate data to be written, if we can neither extend nor flush
+ len = buffer->capacity - buffer->pos;
+ if (size > 1) {
+ len -= len % size;
+ }
+ nitems_out = len / size;
}
}
}
-
+
if (len == 0) {
return len;
}
-
- memcpy(buffer->space + buffer->pos, ptr, len);
- buffer->pos += len;
- if(buffer->pos > buffer->size) {
- buffer->size = buffer->pos;
+
+ if (perform_flush) {
+ size_t flush_max;
+ if (cx_szmul(buffer->flush_blkmax, buffer->flush_blksize, &flush_max)) {
+ return 0;
+ }
+ size_t flush_pos = buffer->flush_func == NULL || buffer->flush_target == NULL
+ ? buffer->pos
+ : cx_buffer_write_flush_helper(buffer, buffer->bytes, 1, buffer->pos);
+ if (flush_pos == buffer->pos) {
+ // entire buffer has been flushed, we can reset
+ buffer->size = buffer->pos = 0;
+
+ size_t items_flush; // how many items can also be directly flushed
+ size_t items_keep; // how many items have to be written to the buffer
+
+ items_flush = flush_max >= required ? nitems : (flush_max - flush_pos) / size;
+ if (items_flush > 0) {
+ items_flush = cx_buffer_write_flush_helper(buffer, ptr, size, items_flush / size);
+ // in case we could not flush everything, keep the rest
+ }
+ items_keep = nitems - items_flush;
+ if (items_keep > 0) {
+ // try again with the remaining stuff
+ unsigned char const *new_ptr = ptr;
+ new_ptr += items_flush * size;
+ // report the directly flushed items as written plus the remaining stuff
+ return items_flush + cxBufferWrite(new_ptr, size, items_keep, buffer);
+ } else {
+ // all items have been flushed - report them as written
+ return nitems;
+ }
+ } else if (flush_pos == 0) {
+ // nothing could be flushed at all, we immediately give up without writing any data
+ return 0;
+ } else {
+ // we were partially successful, we shift left and try again
+ cxBufferShiftLeft(buffer, flush_pos);
+ return cxBufferWrite(ptr, size, nitems, buffer);
+ }
+ } else {
+ memcpy(buffer->bytes + buffer->pos, ptr, len);
+ buffer->pos += len;
+ if (buffer->pos > buffer->size) {
+ buffer->size = buffer->pos;
+ }
+ return nitems_out;
}
-
- return len / size;
+
+}
+
+int cxBufferPut(
+ CxBuffer *buffer,
+ int c
+) {
+ c &= 0xFF;
+ unsigned char const ch = c;
+ if (cxBufferWrite(&ch, 1, 1, buffer) == 1) {
+ return c;
+ } else {
+ return EOF;
+ }
+}
+
+size_t cxBufferPutString(
+ CxBuffer *buffer,
+ const char *str
+) {
+ return cxBufferWrite(str, 1, strlen(str), buffer);
}
-size_t ucx_buffer_read(void *ptr, size_t size, size_t nitems,
- UcxBuffer *buffer) {
+size_t cxBufferRead(
+ void *ptr,
+ size_t size,
+ size_t nitems,
+ CxBuffer *buffer
+) {
size_t len;
- if(ucx_szmul(size, nitems, &len)) {
+ if (cx_szmul(size, nitems, &len)) {
return 0;
}
if (buffer->pos + len > buffer->size) {
len = buffer->size - buffer->pos;
- if (size > 1) len -= len%size;
+ if (size > 1) len -= len % size;
}
-
+
if (len <= 0) {
return len;
}
-
- memcpy(ptr, buffer->space + buffer->pos, len);
+
+ memcpy(ptr, buffer->bytes + buffer->pos, len);
buffer->pos += len;
-
- return len / size;
-}
-int ucx_buffer_putc(UcxBuffer *buffer, int c) {
- if(buffer->pos >= buffer->capacity) {
- if ((buffer->flags & UCX_BUFFER_AUTOEXTEND) == UCX_BUFFER_AUTOEXTEND) {
- if(ucx_buffer_extend(buffer, 1)) {
- return EOF;
- }
- } else {
- return EOF;
- }
- }
-
- c &= 0xFF;
- buffer->space[buffer->pos] = (char) c;
- buffer->pos++;
- if(buffer->pos > buffer->size) {
- buffer->size = buffer->pos;
- }
- return c;
+ return len / size;
}
-int ucx_buffer_getc(UcxBuffer *buffer) {
- if (ucx_buffer_eof(buffer)) {
+int cxBufferGet(CxBuffer *buffer) {
+ if (cxBufferEof(buffer)) {
return EOF;
} else {
- int c = ((unsigned char*)buffer->space)[buffer->pos];
+ int c = buffer->bytes[buffer->pos];
buffer->pos++;
return c;
}
}
-size_t ucx_buffer_puts(UcxBuffer *buffer, const char *str) {
- return ucx_buffer_write((const void*)str, 1, strlen(str), buffer);
-}
-
-int ucx_buffer_shift_left(UcxBuffer* buffer, size_t shift) {
+int cxBufferShiftLeft(
+ CxBuffer *buffer,
+ size_t shift
+) {
if (shift >= buffer->size) {
buffer->pos = buffer->size = 0;
} else {
- memmove(buffer->space, buffer->space + shift, buffer->size - shift);
+ memmove(buffer->bytes, buffer->bytes + shift, buffer->size - shift);
buffer->size -= shift;
-
+
if (buffer->pos >= shift) {
buffer->pos -= shift;
} else {
return 0;
}
-int ucx_buffer_shift_right(UcxBuffer* buffer, size_t shift) {
+int cxBufferShiftRight(
+ CxBuffer *buffer,
+ size_t shift
+) {
size_t req_capacity = buffer->size + shift;
size_t movebytes;
-
+
// auto extend buffer, if required and enabled
if (buffer->capacity < req_capacity) {
- if ((buffer->flags & UCX_BUFFER_AUTOEXTEND) == UCX_BUFFER_AUTOEXTEND) {
- if (ucx_buffer_extend(buffer, req_capacity - buffer->capacity)) {
+ if ((buffer->flags & CX_BUFFER_AUTO_EXTEND) == CX_BUFFER_AUTO_EXTEND) {
+ if (cxBufferMinimumCapacity(buffer, req_capacity)) {
return 1;
}
movebytes = buffer->size;
} else {
movebytes = buffer->size;
}
-
- memmove(buffer->space + shift, buffer->space, movebytes);
- buffer->size = shift+movebytes;
-
+
+ memmove(buffer->bytes + shift, buffer->bytes, movebytes);
+ buffer->size = shift + movebytes;
+
buffer->pos += shift;
if (buffer->pos > buffer->size) {
buffer->pos = buffer->size;
}
-
+
return 0;
}
-int ucx_buffer_shift(UcxBuffer* buffer, off_t shift) {
+int cxBufferShift(
+ CxBuffer *buffer,
+ off_t shift
+) {
if (shift < 0) {
- return ucx_buffer_shift_left(buffer, (size_t) (-shift));
+ return cxBufferShiftLeft(buffer, (size_t) (-shift));
} else if (shift > 0) {
- return ucx_buffer_shift_right(buffer, (size_t) shift);
+ return cxBufferShiftRight(buffer, (size_t) shift);
} else {
return 0;
}