aboutsummaryrefslogtreecommitdiff
path: root/flatcc/src/runtime/emitter.c
diff options
context:
space:
mode:
Diffstat (limited to 'flatcc/src/runtime/emitter.c')
-rw-r--r--flatcc/src/runtime/emitter.c269
1 files changed, 269 insertions, 0 deletions
diff --git a/flatcc/src/runtime/emitter.c b/flatcc/src/runtime/emitter.c
new file mode 100644
index 0000000..089ea00
--- /dev/null
+++ b/flatcc/src/runtime/emitter.c
@@ -0,0 +1,269 @@
+#include <stdlib.h>
+
+#include "flatcc/flatcc_rtconfig.h"
+#include "flatcc/flatcc_emitter.h"
+
+static int advance_front(flatcc_emitter_t *E)
+{
+ flatcc_emitter_page_t *p = 0;
+
+ if (E->front && E->front->prev != E->back) {
+ E->front->prev->page_offset = E->front->page_offset - FLATCC_EMITTER_PAGE_SIZE;
+ E->front = E->front->prev;
+ goto done;
+ }
+ if (!(p = FLATCC_EMITTER_ALLOC(sizeof(flatcc_emitter_page_t)))) {
+ return -1;
+ }
+ E->capacity += FLATCC_EMITTER_PAGE_SIZE;
+ if (E->front) {
+ p->prev = E->back;
+ p->next = E->front;
+ E->front->prev = p;
+ E->back->next = p;
+ E->front = p;
+ goto done;
+ }
+ /*
+ * The first page is shared between front and back to avoid
+ * double unecessary extra allocation.
+ */
+ E->front = p;
+ E->back = p;
+ p->next = p;
+ p->prev = p;
+ E->front_cursor = E->front->page + FLATCC_EMITTER_PAGE_SIZE / 2;
+ E->back_cursor = E->front_cursor;
+ E->front_left = FLATCC_EMITTER_PAGE_SIZE / 2;
+ E->back_left = FLATCC_EMITTER_PAGE_SIZE - E->front_left;
+ p->page_offset = -(flatbuffers_soffset_t)E->front_left;
+ return 0;
+done:
+ E->front_cursor = E->front->page + FLATCC_EMITTER_PAGE_SIZE;
+ E->front_left = FLATCC_EMITTER_PAGE_SIZE;
+ E->front->page_offset = E->front->next->page_offset - FLATCC_EMITTER_PAGE_SIZE;
+ return 0;
+}
+
+static int advance_back(flatcc_emitter_t *E)
+{
+ flatcc_emitter_page_t *p = 0;
+
+ if (E->back && E->back->next != E->front) {
+ E->back = E->back->next;
+ goto done;
+ }
+ if (!(p = FLATCC_EMITTER_ALLOC(sizeof(flatcc_emitter_page_t)))) {
+ return -1;
+ }
+ E->capacity += FLATCC_EMITTER_PAGE_SIZE;
+ if (E->back) {
+ p->prev = E->back;
+ p->next = E->front;
+ E->front->prev = p;
+ E->back->next = p;
+ E->back = p;
+ goto done;
+ }
+ /*
+ * The first page is shared between front and back to avoid
+ * double unecessary extra allocation.
+ */
+ E->front = p;
+ E->back = p;
+ p->next = p;
+ p->prev = p;
+ E->front_cursor = E->front->page + FLATCC_EMITTER_PAGE_SIZE / 2;
+ E->back_cursor = E->front_cursor;
+ E->front_left = FLATCC_EMITTER_PAGE_SIZE / 2;
+ E->back_left = FLATCC_EMITTER_PAGE_SIZE - E->front_left;
+ p->page_offset = -(flatbuffers_soffset_t)E->front_left;
+ return 0;
+done:
+ E->back_cursor = E->back->page;
+ E->back_left = FLATCC_EMITTER_PAGE_SIZE;
+ E->back->page_offset = E->back->prev->page_offset + FLATCC_EMITTER_PAGE_SIZE;
+ return 0;
+}
+
+static int copy_front(flatcc_emitter_t *E, uint8_t *data, size_t size)
+{
+ size_t k;
+
+ data += size;
+ while (size) {
+ k = size;
+ if (k > E->front_left) {
+ k = E->front_left;
+ if (k == 0) {
+ if (advance_front(E)) {
+ return -1;
+ }
+ continue;
+ }
+ }
+ E->front_cursor -= k;
+ E->front_left -= k;
+ data -= k;
+ size -= k;
+ memcpy(E->front_cursor, data, k);
+ };
+ return 0;
+}
+
+static int copy_back(flatcc_emitter_t *E, uint8_t *data, size_t size)
+{
+ size_t k;
+
+ while (size) {
+ k = size;
+ if (k > E->back_left) {
+ k = E->back_left;
+ if (k == 0) {
+ if (advance_back(E)) {
+ return -1;
+ }
+ continue;
+ }
+ }
+ memcpy(E->back_cursor, data, k);
+ size -= k;
+ data += k;
+ E->back_cursor += k;
+ E->back_left -= k;
+ }
+ return 0;
+}
+
+int flatcc_emitter_recycle_page(flatcc_emitter_t *E, flatcc_emitter_page_t *p)
+{
+ if (p == E->front || p == E->back) {
+ return -1;
+ }
+ p->next->prev = p->prev;
+ p->prev->next = p->next;
+ p->prev = E->front->prev;
+ p->next = E->front;
+ p->prev->next = p;
+ p->next->prev = p;
+ return 0;
+}
+
+void flatcc_emitter_reset(flatcc_emitter_t *E)
+{
+ flatcc_emitter_page_t *p = E->front;
+
+ if (!E->front) {
+ return;
+ }
+ E->back = E->front;
+ E->front_cursor = E->front->page + FLATCC_EMITTER_PAGE_SIZE / 2;
+ E->back_cursor = E->front_cursor;
+ E->front_left = FLATCC_EMITTER_PAGE_SIZE / 2;
+ E->back_left = FLATCC_EMITTER_PAGE_SIZE - FLATCC_EMITTER_PAGE_SIZE / 2;
+ E->front->page_offset = -(flatbuffers_soffset_t)E->front_left;
+ /* Heuristic to reduce peak allocation over time. */
+ if (E->used_average == 0) {
+ E->used_average = E->used;
+ }
+ E->used_average = E->used_average * 3 / 4 + E->used / 4;
+ E->used = 0;
+ while (E->used_average * 2 < E->capacity && E->back->next != E->front) {
+ /* We deallocate the page after back since it is less likely to be hot in cache. */
+ p = E->back->next;
+ E->back->next = p->next;
+ p->next->prev = E->back;
+ FLATCC_EMITTER_FREE(p);
+ E->capacity -= FLATCC_EMITTER_PAGE_SIZE;
+ }
+}
+
+void flatcc_emitter_clear(flatcc_emitter_t *E)
+{
+ flatcc_emitter_page_t *p = E->front;
+
+ if (!p) {
+ return;
+ }
+ p->prev->next = 0;
+ while (p->next) {
+ p = p->next;
+ FLATCC_EMITTER_FREE(p->prev);
+ }
+ FLATCC_EMITTER_FREE(p);
+ memset(E, 0, sizeof(*E));
+}
+
+int flatcc_emitter(void *emit_context,
+ const flatcc_iovec_t *iov, int iov_count,
+ flatbuffers_soffset_t offset, size_t len)
+{
+ flatcc_emitter_t *E = emit_context;
+ uint8_t *p;
+
+ E->used += len;
+ if (offset < 0) {
+ if (len <= E->front_left) {
+ E->front_cursor -= len;
+ E->front_left -= len;
+ p = E->front_cursor;
+ goto copy;
+ }
+ iov += iov_count;
+ while (iov_count--) {
+ --iov;
+ if (copy_front(E, iov->iov_base, iov->iov_len)) {
+ return -1;
+ }
+ }
+ } else {
+ if (len <= E->back_left) {
+ p = E->back_cursor;
+ E->back_cursor += len;
+ E->back_left -= len;
+ goto copy;
+ }
+ while (iov_count--) {
+ if (copy_back(E, iov->iov_base, iov->iov_len)) {
+ return -1;
+ }
+ ++iov;
+ }
+ }
+ return 0;
+copy:
+ while (iov_count--) {
+ memcpy(p, iov->iov_base, iov->iov_len);
+ p += iov->iov_len;
+ ++iov;
+ }
+ return 0;
+}
+
+void *flatcc_emitter_copy_buffer(flatcc_emitter_t *E, void *buf, size_t size)
+{
+ flatcc_emitter_page_t *p;
+ size_t len;
+
+ if (size < E->used) {
+ return 0;
+ }
+ if (!E->front) {
+ return 0;
+ }
+ if (E->front == E->back) {
+ memcpy(buf, E->front_cursor, E->used);
+ return buf;
+ }
+ len = FLATCC_EMITTER_PAGE_SIZE - E->front_left;
+ memcpy(buf, E->front_cursor, len);
+ buf = (uint8_t *)buf + len;
+ p = E->front->next;
+ while (p != E->back) {
+ memcpy(buf, p->page, FLATCC_EMITTER_PAGE_SIZE);
+ buf = (uint8_t *)buf + FLATCC_EMITTER_PAGE_SIZE;
+ p = p->next;
+ }
+ memcpy(buf, p->page, FLATCC_EMITTER_PAGE_SIZE - E->back_left);
+ return buf;
+}