/* Copyright (C) 2010, 2011 Tony Garnock-Jones. All rights reserved. */ #include #include #include #include #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; } }