1
0
mirror of git://jb55.com/damus synced 2024-09-16 02:03:45 +00:00
damus/nostrdb/flatcc/emitter.c
2023-08-25 19:05:34 -07:00

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;
}