view val.c @ 851:f9a9816e0142

sexpr: use %define api.pure instead of %pure-parser %pure-parser got deprecated a number of years ago. Signed-off-by: Josef 'Jeff' Sipek <jeffpc@josefsipek.net>
author Josef 'Jeff' Sipek <jeffpc@josefsipek.net>
date Wed, 05 Jan 2022 23:02:19 -0500
parents 93d6c996250f
children
line wrap: on
line source

/*
 * Copyright (c) 2014-2020 Josef 'Jeff' Sipek <jeffpc@josefsipek.net>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

#include <string.h>
#include <errno.h>
#include <stddef.h>
#include <stdio.h>
#include <stdbool.h>
#include <inttypes.h>

#include <jeffpc/val.h>
#include <jeffpc/jeffpc.h>
#include <jeffpc/error.h>
#include <jeffpc/types.h>
#include <jeffpc/mem.h>

#include "val_impl.h"

#define INIT_STATIC_VAL(t, memb, val)				\
		{						\
			.type = (t),				\
			.static_struct = true,			\
			.memb = (val),				\
		}

static const struct val val_cons_empty = INIT_STATIC_VAL(VT_CONS, i, 0);
static const struct val val_true = INIT_STATIC_VAL(VT_BOOL, b, true);
static const struct val val_false = INIT_STATIC_VAL(VT_BOOL, b, false);
static const struct val val_null = INIT_STATIC_VAL(VT_NULL, i, 0);
static const struct val val_ints[10] = {
	[0] = INIT_STATIC_VAL(VT_INT, i, 0),
	[1] = INIT_STATIC_VAL(VT_INT, i, 1),
	[2] = INIT_STATIC_VAL(VT_INT, i, 2),
	[3] = INIT_STATIC_VAL(VT_INT, i, 3),
	[4] = INIT_STATIC_VAL(VT_INT, i, 4),
	[5] = INIT_STATIC_VAL(VT_INT, i, 5),
	[6] = INIT_STATIC_VAL(VT_INT, i, 6),
	[7] = INIT_STATIC_VAL(VT_INT, i, 7),
	[8] = INIT_STATIC_VAL(VT_INT, i, 8),
	[9] = INIT_STATIC_VAL(VT_INT, i, 9),
};

static struct mem_cache *val_cache;

static void __attribute__((constructor)) init_val_subsys(void)
{
	val_cache = mem_cache_create("val-cache", sizeof(struct val), 0);
	ASSERT(!IS_ERR(val_cache));
}

struct val *__val_alloc(enum val_type type)
{
	struct val *val;

	val = mem_cache_alloc(val_cache);
	if (!val)
		return ERR_PTR(-ENOMEM);

	val->type = type;
	val->static_struct = false;

	refcnt_init(&val->refcnt, 1);

	return val;
}

void val_free(struct val *val)
{
	ASSERT(val);
	ASSERT3U(refcnt_read(&val->refcnt), ==, 0);

	switch (val->type) {
		case VT_NULL:
		case VT_INT:
		case VT_BOOL:
		case VT_CHAR:
			break;
		case VT_BLOB:
			if (!val->static_alloc)
				free(val->_set_blob.ptr);
			break;
		case VT_STR:
		case VT_SYM:
			if (!val->static_alloc)
				free((char *) val->_set_str_ptr);
			break;
		case VT_CONS:
			val_putref(val->cons.head);
			val_putref(val->cons.tail);
			break;
		case VT_ARRAY: {
			size_t i;

			for (i = 0; i < val->array.nelem; i++)
				val_putref(val->_set_array.vals[i]);

			if (!val->static_alloc)
				free(val->_set_array.vals);
			break;
		}
		case VT_NVL:
			__val_free_nvl(val);
			break;
	}

	mem_cache_free(val_cache, val);
}

#define DEF_VAL_SET(fxn, vttype, valelem, ctype, putref)	\
struct val *val_alloc_##fxn(ctype v)				\
{								\
	struct val *val;					\
								\
	val = __val_alloc(vttype);				\
	if (IS_ERR(val)) {					\
		putref(v);					\
		return val;					\
	}							\
								\
	val->_set_##valelem = v;				\
								\
	return val;						\
}

static DEF_VAL_SET(int_heap, VT_INT, i, uint64_t, (void))
DEF_VAL_SET(char, VT_CHAR, i, uint64_t, (void))

struct val *val_alloc_int(uint64_t i)
{
	if (i < ARRAY_LEN(val_ints)) {
		/*
		 * Cast away the const - we define the static as const to get it
		 * into .rodata, but we have to drop the const since everything
		 * expects struct val to be writable (because refcounts modify it).
		 * In this case, we won't get any modifications because we're marked
		 * as static.
		 */
		return (struct val *) &val_ints[i];
	}

	return val_alloc_int_heap(i);
}

struct val *val_alloc_bool(bool b)
{
	const struct val *ret;

	ret = b ? &val_true : &val_false;

	/*
	 * Cast away the const - we define the static as const to get it
	 * into .rodata, but we have to drop the const since everything
	 * expects struct val to be writable (because refcounts modify it).
	 * In this case, we won't get any modifications because we're marked
	 * as static.
	 */
	return (struct val *) ret;
}

struct val *val_alloc_null(void)
{
	/*
	 * Cast away the const - we define the static as const to get it
	 * into .rodata, but we have to drop the const since everything
	 * expects struct val to be writable (because refcounts modify it).
	 * In this case, we won't get any modifications because we're marked
	 * as static.
	 */
	return (struct val *) &val_null;
}

static struct val *__val_alloc_blob(void *ptr, size_t size, bool heap)
{
	struct val *val;

	val = __val_alloc(VT_BLOB);
	if (IS_ERR(val)) {
		if (heap)
			free(ptr);

		return val;
	}

	val->_set_blob.ptr = ptr;
	val->_set_blob.size = size;
	val->static_alloc = !heap;

	return val;
}

struct val *val_alloc_blob(void *ptr, size_t size)
{
	return __val_alloc_blob(ptr, size, true);
}

struct val *val_alloc_blob_dup(const void *ptr, size_t size)
{
	void *tmp;

	tmp = malloc(size);
	if (!tmp)
		return ERR_PTR(-ENOMEM);

	memcpy(tmp, ptr, size);

	return __val_alloc_blob(tmp, size, true);
}

struct val *val_alloc_blob_static(const void *ptr, size_t size)
{
	return __val_alloc_blob((void *) ptr, size, false);
}

struct val *val_empty_cons(void)
{
	/*
	 * Cast away the const - we define the static as const to get it
	 * into .rodata, but we have to drop the const since everything
	 * expects struct val to be writable (because refcounts modify it).
	 * In this case, we won't get any modifications because we're marked
	 * as static.
	 */
	return (struct val *) &val_cons_empty;
}

struct val *val_alloc_cons(struct val *head, struct val *tail)
{
	struct val *val;

	if (!head && !tail)
		return val_empty_cons();

	val = __val_alloc(VT_CONS);
	if (IS_ERR(val)) {
		val_putref(head);
		val_putref(tail);
		return val;
	}

	val->_set_cons.head = head;
	val->_set_cons.tail = tail;

	return val;
}

int val_cmp(const struct val *a, const struct val *b)
{
	if (a == b)
		return 0;

	if (a->type != b->type)
		return (a->type < b->type) ? -1 : +1;

	switch (a->type) {
		case VT_STR:
		case VT_SYM:
			return strcmp(val_cstr(a), val_cstr(b));
		case VT_INT:
		case VT_CHAR:
			if (a->i < b->i)
				return -1;
			if (a->i > b->i)
				return +1;
			return 0;
		case VT_BOOL:
			if (!a->b && b->b)
				return -1;
			if (a->b && !b->b)
				return +1;
			return 0;
		case VT_NULL:
			return 0;
		case VT_BLOB: {
			int ret;

			ret = memcmp(a->blob.ptr, b->blob.ptr,
				     MIN(a->blob.size, b->blob.size));
			if (ret)
				return ret;

			if (a->blob.size < b->blob.size)
				return -1;
			if (a->blob.size > b->blob.size)
				return +1;
			return 0;
		}
		case VT_CONS:
		case VT_ARRAY:
		case VT_NVL:
			panic("not yet implemented");
	}

	panic("unknown val type %d (%s)", a->type, val_typename(a->type));
}