#include #include "flatcc_rtconfig.h" #include "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; }