view sexpr.c @ 806:12a3139a2baf

sock: use atomic_cas_ptr correctly This bug was caused by incorrect documentation (already fixed). It resulted in us freeing the name on successful CAS and caching the freed pointer. Signed-off-by: Josef 'Jeff' Sipek <jeffpc@josefsipek.net>
author Josef 'Jeff' Sipek <jeffpc@josefsipek.net>
date Fri, 10 Apr 2020 12:27:10 -0400
parents 5c64f103bcef
children
line wrap: on
line source

/*
 * Copyright (c) 2015-2019 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 <stdlib.h>
#include <string.h>

#include <jeffpc/sexpr.h>
#include <jeffpc/mem.h>

#include "sexpr_impl.h"

struct val *sexpr_parse(const char *str, size_t len)
{
	struct sexpr_parser_state x;
	int ret;

	x.input = str;
	x.len   = len;
	x.pos   = 0;

	sexpr_reader_lex_init(&x.scanner);
	sexpr_reader_set_extra(&x, x.scanner);

	ret = sexpr_reader_parse(&x);

	sexpr_reader_lex_destroy(x.scanner);

	return ret ? ERR_PTR(-EINVAL) : x.output;
}

/*
 * Convert a C array of vals into a sexpr list.  E.g.,
 *
 *	vals = { A, B, C }, nvals = 3
 *
 * turns into:
 *
 *	'(A . (B . (C . ())))
 *
 * which is the same as:
 *
 *	'(A B C)
 */
struct val *sexpr_array_to_list(struct val **vals, size_t nvals)
{
	struct val *last = NULL;
	struct val *tmp;

	if (!nvals)
		return val_empty_cons();

	for (; nvals > 0; nvals--, last = tmp)
		tmp = VAL_ALLOC_CONS(vals[nvals - 1], last);

	return last;
}

/*
 * Just like sexpr_array_to_list, but obtains value from args instead of an
 * array.
 */
struct val *sexpr_args_to_list(size_t nvals, ...)
{
	const size_t n = nvals;
	struct val *arr[n];
	va_list args;
	size_t i;

	va_start(args, nvals);
	for (i = 0; i < n; i++)
		arr[i] = va_arg(args, struct val *);
	va_end(args);

	return sexpr_array_to_list(arr, n);
}

/*
 * Convert a sexpr list into a C array of vals.  E.g.,
 *
 *     '(A B C)
 *
 * turns into:
 *
 *     array = { A, B, C }, nvals = 3
 *
 * We fill in the passed in array with at most alen elements.  The number of
 * filled in elements is returned to the caller.
 */
ssize_t sexpr_list_to_array(struct val *list, struct val **array, size_t alen)
{
	struct val *tmp;
	size_t nvals = 0;
	ssize_t ret;

	for (tmp = list;
	     !sexpr_is_null(tmp) && (alen > nvals);
	     tmp = tmp->cons.tail, nvals++) {
		if (tmp->type != VT_CONS) {
			ret = -EINVAL;
			goto err;
		}

		array[nvals] = val_getref(tmp->cons.head);
	}

	if ((alen == nvals) && !sexpr_is_null(tmp)) {
		ret = -ENOMEM;
		goto err;
	}

	return nvals;

err:
	while (nvals)
		val_putref(array[--nvals]);

	return ret;
}

/*
 * Convert a sexpr list into a C array of vals.  E.g.,
 *
 *     '(A B C)
 *
 * turns into a VT_ARRAY containing:
 *
 *     { A, B, C }
 */
struct val *sexpr_list_to_val_array(struct val *list)
{
	struct val **arr;
	ssize_t len;
	int ret;

	len = sexpr_length(val_getref(list));
	if (len < 0) {
		val_putref(list);
		return ERR_PTR(-EINVAL);
	}

	arr = mem_reallocarray(NULL, len, sizeof(struct val *));
	if (!arr) {
		val_putref(list);
		return ERR_PTR(-ENOMEM);
	}

	ret = sexpr_list_to_array(list, arr, len);
	if (ret != len) {
		free(arr);
		return ERR_PTR(-EINVAL);
	}

	return val_alloc_array(arr, len);
}

struct val *sexpr_car(struct val *lv)
{
	struct val *ret;

	if (!lv)
		return NULL;

	if (lv->type == VT_CONS)
		ret = val_getref(lv->cons.head);
	else
		ret = NULL;

	val_putref(lv);

	return ret;
}

struct val *sexpr_cdr(struct val *lv)
{
	struct val *ret;

	if (!lv)
		return NULL;

	if (lv->type == VT_CONS)
		ret = val_getref(lv->cons.tail);
	else
		ret =  NULL;

	val_putref(lv);

	return ret;
}

ssize_t sexpr_length(struct val *lv)
{
	ssize_t len;

	len = 0;

	while (!sexpr_is_null(lv)) {
		if (lv->type != VT_CONS) {
			/* not a list */
			val_putref(lv);
			return -1;
		}

		len++;
		lv = sexpr_cdr(lv);
	}

	val_putref(lv);

	return len;
}

struct val *sexpr_nth(struct val *lv, uint64_t n)
{
	while (n-- && lv) {
		struct val *tmp;

		if (lv->type == VT_CONS) {
			/*
			 * If this is not the one we want, follow the tail.
			 * Otherwise, grab the head.
			 */
			if (n)
				tmp = val_getref(lv->cons.tail);
			else
				tmp = val_getref(lv->cons.head);
		} else {
			tmp = NULL;
		}

		val_putref(lv);

		lv = tmp;
	}

	return lv;
}

/*
 * Given a list, lookup a certain name.
 *
 * The input list looks like:
 *   '((a . b) (c . d))
 * which really is:
 *   '((a . b) . ((c . d) . ()))
 *
 * So, to check it, we examine the car of the list, if that's not the right
 * key, we recurse on cdr of the list.
 */
struct val *sexpr_assoc(struct val *lv, const char *name)
{
	struct val *head;
	struct val *tail;

	/* empty list */
	if (!lv)
		return NULL;

	/* not a list */
	if (lv->type != VT_CONS)
		return NULL;

	head = lv->cons.head;
	tail = lv->cons.tail;

	/*
	 * check the head of current cons cell: '(head . tail)
	 *   (1) must be non-null
	 *   (2) must be a cons cell, i.e.,  head == '(a . b)
	 *   (3) (car head) must be a string or symbol
	 *   (4) (car head) must be equal to the value passed in
	 */
	if (head && (head->type == VT_CONS) &&
	    head->cons.head &&
	    ((head->cons.head->type == VT_STR) ||
	     (head->cons.head->type == VT_SYM)) &&
	    !strcmp(val_cstr(head->cons.head), name))
		return val_getref(head);

	return sexpr_assoc(tail, name);
}

bool sexpr_equal(struct val *lhs, struct val *rhs)
{
	bool ret;

	/* if they are the same object, they are equal - even if NULL */
	if (lhs == rhs) {
		ret = true;
		goto out;
	}

	/* if one is NULL, they are unequal */
	if (!lhs || !rhs) {
		/* ... unless we're comparing a NULL with a '() */
		if ((!lhs && ((rhs->type != VT_CONS) ||
			      rhs->cons.head ||
			      rhs->cons.tail)) ||
		    (!rhs && ((lhs->type != VT_CONS) ||
			      lhs->cons.head ||
			      lhs->cons.tail))) {
			ret = false;
			goto out;
		}

		ret = true;
		goto out;
	}

	/*
	 * At this point, we have two non-NULL values.
	 */

	/* different type -> unequal */
	if (lhs->type != rhs->type) {
		ret = false;
		goto out;
	}

	switch (lhs->type) {
		case VT_NULL:
			ret = true;
			goto out;
		case VT_INT:
		case VT_CHAR:
			ret = (lhs->i == rhs->i);
			goto out;
		case VT_STR:
		case VT_SYM:
			ret = str_cmp(val_cast_to_str(lhs),
				      val_cast_to_str(rhs)) == 0;
			goto out;
		case VT_BOOL:
			ret = (lhs->b == rhs->b);
			goto out;
		case VT_BLOB:
			ret = (lhs->blob.size == rhs->blob.size);
			if (!ret)
				goto out;

			ret = (lhs->blob.ptr == rhs->blob.ptr);
			if (ret)
				goto out;

			ret = (memcmp(lhs->blob.ptr, rhs->blob.ptr,
				      lhs->blob.size) == 0);
			goto out;
		case VT_CONS:
			ret = sexpr_equal(val_getref(lhs->cons.head),
					  val_getref(rhs->cons.head)) &&
			      sexpr_equal(val_getref(lhs->cons.tail),
					  val_getref(rhs->cons.tail));
			goto out;
		case VT_ARRAY: {
			size_t i;

			ret = (lhs->array.nelem == rhs->array.nelem);
			if (!ret)
				goto out;

			for (i = 0; i < lhs->array.nelem; i++) {
				ret = sexpr_equal(val_getref(lhs->_set_array.vals[i]),
						  val_getref(rhs->_set_array.vals[i]));
				if (!ret)
					break;
			}

			goto out;
		}
		case VT_NVL: {
			struct rb_tree *ltree = &lhs->_set_nvl.values;
			struct rb_tree *rtree = &rhs->_set_nvl.values;
			struct nvpair *lcur;
			struct nvpair *rcur;

			lcur = rb_first(ltree);
			rcur = rb_first(rtree);

			while (lcur && rcur) {
				ret = (str_cmp(lcur->name, rcur->name) == 0);
				if (!ret)
					goto out;

				ret = sexpr_equal(val_getref(lcur->value),
						  val_getref(rcur->value));
				if (!ret)
					goto out;

				lcur = rb_next(ltree, lcur);
				rcur = rb_next(rtree, rcur);
			}

			/* if both sides reached the end, then they are equal */
			ret = !lcur && !rcur;
			goto out;
		}
	}

	panic("unknown struct val type %u", lhs->type);

out:
	val_putref(lhs);
	val_putref(rhs);
	return ret;
}

struct val *sexpr_alist_lookup_val(struct val *lv, const char *name)
{
	if (!lv || !name)
		return NULL;

	return sexpr_cdr(sexpr_assoc(lv, name));
}

struct str *sexpr_alist_lookup_str(struct val *lv, const char *name)
{
	struct str *ret;
	struct val *v;

	if (!lv || !name)
		return NULL;

	v = sexpr_cdr(sexpr_assoc(lv, name));
	if (!v || (v->type != VT_STR))
		ret = NULL;
	else
		ret = val_cast_to_str(val_getref(v));

	val_putref(v);

	return ret;
}

uint64_t sexpr_alist_lookup_int(struct val *lv, const char *name, bool *found)
{
	struct val *v;
	uint64_t ret;
	bool ok;

	if (!lv || !name) {
		if (found)
			*found = false;
		return 0;
	}

	v = sexpr_cdr(sexpr_assoc(lv, name));
	ok = v && (v->type == VT_INT);

	if (!ok)
		ret = 0;
	else
		ret = v->i;

	val_putref(v);

	if (found)
		*found = ok;

	return ret;
}

bool sexpr_alist_lookup_bool(struct val *lv, const char *name, bool def,
			     bool *found)
{
	struct val *v;
	bool ret;
	bool ok;

	if (!lv || !name) {
		if (found)
			*found = false;
		return def;
	}

	v = sexpr_cdr(sexpr_assoc(lv, name));
	ok = v && (v->type == VT_BOOL);

	if (!ok)
		ret = def;
	else
		ret = v->b;

	val_putref(v);

	if (found)
		*found = ok;

	return ret;
}

struct val *sexpr_alist_lookup_list(struct val *lv, const char *name)
{
	struct val *ret;
	struct val *v;

	if (!lv || !name)
		return NULL;

	v = sexpr_cdr(sexpr_assoc(lv, name));
	if (!v || (v->type != VT_CONS))
		ret = NULL;
	else
		ret = val_getref(v);

	val_putref(v);

	return ret;
}