198 lines
4.4 KiB
C
198 lines
4.4 KiB
C
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <stdio.h>
|
|
|
|
#include <assert.h>
|
|
|
|
#include "cmsg_private.h"
|
|
#include "ref.h"
|
|
#include "sexp.h"
|
|
|
|
static sexp_t *freelist = NULL;
|
|
|
|
void release_sexp_cache(void) {
|
|
int count = 0;
|
|
while (freelist != NULL) {
|
|
sexp_t *x = freelist;
|
|
freelist = x->data.pair.tail;
|
|
free(x);
|
|
count++;
|
|
}
|
|
info("Released %d cached sexp shells.\n", count);
|
|
}
|
|
|
|
static inline sexp_t *alloc_shell(sexp_type_t kind) {
|
|
sexp_t *x = freelist;
|
|
if (x == NULL) {
|
|
x = malloc(sizeof(*x));
|
|
} else {
|
|
freelist = x->data.pair.tail;
|
|
}
|
|
x->refcount = ZERO_REFCOUNT();
|
|
x->kind = kind;
|
|
return x;
|
|
}
|
|
|
|
static inline void release_shell(sexp_t *x) {
|
|
x->data.pair.tail = freelist;
|
|
freelist = x;
|
|
}
|
|
|
|
void sexp_data_destructor(sexp_data_t *data) {
|
|
cmsg_bytes_free(data->data);
|
|
free(data);
|
|
}
|
|
|
|
void sexp_destructor(sexp_t *x) {
|
|
tail_recursion:
|
|
switch (x->kind) {
|
|
case SEXP_BYTES:
|
|
cmsg_bytes_free(x->data.bytes);
|
|
break;
|
|
case SEXP_SLICE:
|
|
DECREF(x->data.slice.data, sexp_data_destructor);
|
|
break;
|
|
case SEXP_DISPLAY_HINT:
|
|
case SEXP_PAIR: {
|
|
sexp_t *next = x->data.pair.tail;
|
|
DECREF(x->data.pair.head, sexp_destructor);
|
|
if (next != NULL) {
|
|
if (next->refcount.count == 1) {
|
|
release_shell(x);
|
|
x = next;
|
|
goto tail_recursion;
|
|
} else {
|
|
DECREF(next, sexp_destructor);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
die("Unknown sexp kind %d in dtor\n", x->kind);
|
|
}
|
|
release_shell(x);
|
|
}
|
|
|
|
sexp_data_t *sexp_data_copy(cmsg_bytes_t body, size_t offset, size_t length) {
|
|
assert(offset + length <= body.len);
|
|
return sexp_data_alias(cmsg_bytes_malloc_dup(CMSG_BYTES(length, body.bytes + offset)));
|
|
}
|
|
|
|
sexp_data_t *sexp_data_alias(cmsg_bytes_t body) {
|
|
sexp_data_t *data = malloc(sizeof(*data));
|
|
data->refcount = ZERO_REFCOUNT();
|
|
data->data = body;
|
|
return data;
|
|
}
|
|
|
|
sexp_t *sexp_bytes(cmsg_bytes_t bytes) {
|
|
sexp_t *x = alloc_shell(SEXP_BYTES);
|
|
x->data.bytes = cmsg_bytes_malloc_dup(bytes);
|
|
return x;
|
|
}
|
|
|
|
sexp_t *sexp_slice(sexp_data_t *data, size_t offset, size_t length) {
|
|
sexp_t *x = alloc_shell(SEXP_SLICE);
|
|
x->data.slice.data = INCREF(data);
|
|
x->data.slice.offset = offset;
|
|
x->data.slice.length = length;
|
|
return x;
|
|
}
|
|
|
|
sexp_t *sexp_display_hint(sexp_t *hint, sexp_t *body) {
|
|
sexp_t *x = alloc_shell(SEXP_DISPLAY_HINT);
|
|
assert(sexp_simple_stringp(hint));
|
|
assert(sexp_simple_stringp(body));
|
|
x->data.pair.head = INCREF(hint);
|
|
x->data.pair.tail = INCREF(body);
|
|
return x;
|
|
}
|
|
|
|
sexp_t *sexp_cons(sexp_t *head, sexp_t *tail) {
|
|
sexp_t *x = alloc_shell(SEXP_PAIR);
|
|
x->data.pair.head = INCREF(head);
|
|
x->data.pair.tail = INCREF(tail);
|
|
return x;
|
|
}
|
|
|
|
cmsg_bytes_t sexp_data(sexp_t *x) {
|
|
restart:
|
|
switch (x->kind) {
|
|
case SEXP_BYTES:
|
|
return x->data.bytes;
|
|
case SEXP_SLICE:
|
|
return CMSG_BYTES(x->data.slice.length,
|
|
x->data.slice.data->data.bytes + x->data.slice.offset);
|
|
case SEXP_DISPLAY_HINT:
|
|
x = x->data.pair.tail;
|
|
goto restart;
|
|
default:
|
|
die("Unknown sexp kind %d in data accessor\n", x->kind);
|
|
}
|
|
}
|
|
|
|
sexp_t *sexp_assoc(sexp_t *list, cmsg_bytes_t key) {
|
|
while (list != NULL) {
|
|
sexp_t *candidate = sexp_head(list);
|
|
if (sexp_stringp(candidate)) {
|
|
cmsg_bytes_t candidate_data = sexp_data(candidate);
|
|
if ((candidate_data.len == key.len)
|
|
&& (!memcmp(candidate_data.bytes, key.bytes, key.len))) {
|
|
return candidate;
|
|
}
|
|
}
|
|
list = sexp_tail(list);
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
size_t sexp_length(sexp_t *list) {
|
|
size_t result = 0;
|
|
while (sexp_pairp(list)) {
|
|
result++;
|
|
list = sexp_tail(list);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
sexp_t *sexp_new_queue(void) {
|
|
return sexp_cons(NULL, NULL);
|
|
}
|
|
|
|
int sexp_queue_emptyp(sexp_t *q) {
|
|
return sexp_head(q) == NULL;
|
|
}
|
|
|
|
void sexp_queue_pushback(sexp_t *q, sexp_t *x) {
|
|
sexp_t *cell = sexp_cons(x, sexp_head(q));
|
|
if (sexp_head(q) == NULL) {
|
|
sexp_settail(q, cell);
|
|
}
|
|
sexp_sethead(q, cell);
|
|
}
|
|
|
|
void sexp_enqueue(sexp_t *q, sexp_t *x) {
|
|
sexp_t *cell = sexp_cons(x, NULL);
|
|
if (sexp_head(q) == NULL) {
|
|
sexp_sethead(q, cell);
|
|
} else {
|
|
sexp_settail(sexp_tail(q), cell);
|
|
}
|
|
sexp_settail(q, cell);
|
|
}
|
|
|
|
sexp_t *sexp_dequeue(sexp_t *q) {
|
|
if (sexp_head(q) == NULL) {
|
|
return NULL;
|
|
} else {
|
|
sexp_t *x = INCREF(sexp_head(sexp_head(q)));
|
|
sexp_t *cell = sexp_tail(sexp_head(q));
|
|
sexp_sethead(q, cell);
|
|
if (cell == NULL) {
|
|
sexp_settail(q, NULL);
|
|
}
|
|
UNGRAB(x);
|
|
return x;
|
|
}
|
|
}
|