mirror of
git://jb55.com/damus
synced 2024-09-30 00:40:45 +00:00
270 lines
6.9 KiB
C
270 lines
6.9 KiB
C
#include <stdlib.h>
|
|
|
|
#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;
|
|
}
|