hop-2012/experiments/cmsg/sexp.c

256 lines
5.9 KiB
C

/* Copyright 2010, 2011, 2012 Tony Garnock-Jones <tonygarnockjones@gmail.com>.
*
* This file is part of Hop.
*
* Hop is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Hop is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
* License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Hop. If not, see <http://www.gnu.org/licenses/>.
*/
#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;
sexp_t *sexp_empty_bytes = NULL;
void init_sexp(void) {
sexp_empty_bytes = INCREF(sexp_cstring(""));
}
void done_sexp(void) {
int count = 0;
DECREF(sexp_empty_bytes, sexp_destructor);
sexp_empty_bytes = NULL;
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;
}
sexp_t *sexp_incref(sexp_t *x) {
return INCREF(x);
}
sexp_t *sexp_decref(sexp_t *x) {
return DECREF(x, sexp_destructor);
}
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_cstring(char const *str) {
return sexp_bytes(cmsg_cstring_bytes(str));
}
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);
}
}
int sexp_cmp(sexp_t *a, sexp_t *b) {
tail:
if (a == b) return 0;
if (sexp_stringp(a) && sexp_stringp(b)) {
return cmsg_bytes_cmp(sexp_data(a), sexp_data(b));
}
if (sexp_pairp(a) && sexp_pairp(b)) {
int result = sexp_cmp(sexp_head(a), sexp_head(b));
if (result) return result;
a = sexp_tail(a);
b = sexp_tail(b);
goto tail;
}
if (a == NULL) return -1;
if (b == NULL) return 1;
if (a->kind < b->kind) return -1;
return 1;
}
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;
}
}