hop-2012/sexp.c

122 lines
2.8 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;
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);
}
}