Mercurial > libjeffpc
view sexpr_compact.c @ 781:bc8880710efd
synch: keep track of rwlocks in the dependency graph
Signed-off-by: Josef 'Jeff' Sipek <jeffpc@josefsipek.net>
author | Josef 'Jeff' Sipek <jeffpc@josefsipek.net> |
---|---|
date | Sun, 11 Aug 2019 12:06:42 -0400 |
parents | ad9ca45cb8f1 |
children |
line wrap: on
line source
/* * Copyright (c) 2018-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 <jeffpc/sexpr.h> #include <jeffpc/nvl.h> #include <jeffpc/mem.h> #include "sexpr_impl.h" /* * Compact an array. * * Iterate over all the elements converting each. If at the end, we end up * with the same array we free the new one we made, and just return the * old. * * Note: Under no circumstance can we modify the array in-place. It may * have multiple references and we do not wish to modify all of them - only * the one that was passed in. */ static struct val *sexpr_compact_array(struct val *in) { struct val **vals; size_t nelem; bool equal; size_t i; VERIFY3U(in->type, ==, VT_ARRAY); nelem = in->array.nelem; vals = alloca(sizeof(struct val *) * nelem); equal = true; for (i = 0; i < nelem; i++) { vals[i] = sexpr_compact(val_getref(in->array.vals[i])); /* * We compare pointers on purpose. This checks whether or * not the sexpr_compact call above was a no-op. */ equal = equal && (vals[i] == in->array.vals[i]); } if (equal) { for (i = 0; i < nelem; i++) val_putref(vals[i]); return in; } else { val_putref(in); return val_alloc_array_dup(vals, nelem); } } /* * Compact an nvlist. * * Iterate over all the nv pairs converting each. If at the end, we end up * with the same nvlist we free the new one we made, and just return the * old. * * Note: Under no circumstance can we modify the nvlist in-place. It may * have multiple references and we do not wish to modify all of them - only * the one that was passed in. */ static struct val *sexpr_compact_nvl(struct val *in) { const struct nvpair *pair; struct nvlist *src, *dst; struct val *ret_ptr; bool equal; VERIFY3U(in->type, ==, VT_NVL); src = val_cast_to_nvl(in); dst = nvl_alloc(); if (IS_ERR(dst)) { ret_ptr = ERR_CAST(dst); goto err; } equal = true; nvl_for_each(pair, src) { struct nvpair tmp; int ret; tmp.name = pair->name; tmp.value = sexpr_compact(val_getref(pair->value)); if (IS_ERR(tmp.value)) { ret_ptr = tmp.value; goto err_dst; } /* * We compare pointers on purpose. This checks whether or * not the sexpr_compact call above was a no-op. */ equal = equal && (tmp.value == pair->value); /* takes a new ref on both name & val */ ret = nvl_set_pair(dst, &tmp); val_putref(tmp.value); /* release our ref */ if (ret) { ret_ptr = ERR_PTR(ret); goto err_dst; } } if (equal) { nvl_putref(dst); return in; } else { nvl_putref(src); return nvl_cast_to_val(dst); } err_dst: nvl_putref(dst); err: val_putref(in); return ret_ptr; } /* * Compact a cons list into an array. * * We assert a number of return values because the calls should never fail. * A failure means that our what-is-it logic is incorrect. In that case, it * is better to assert than to incorrectly mangle the data. */ static struct val *sexpr_compact_cons_array(struct val *in) { struct val **arr; ssize_t len; size_t i; int ret; len = sexpr_length(val_getref(in)); /* assert, to catch issues with what-is-it logic */ ASSERT3S(len, >=, 0); arr = alloca(sizeof(struct val *) * len); ret = sexpr_list_to_array(in, arr, len); /* assert, to catch issues with what-is-it logic */ ASSERT3S(ret, ==, len); /* compact each element */ for (i = 0; i < len; i++) { arr[i] = sexpr_compact(arr[i]); if (IS_ERR(arr[i])) { ret = PTR_ERR(arr[i]); goto err; } } /* make a new VT_ARRAY with the compacted values */ return val_alloc_array_dup(arr, len); err: for (i = 0; i < len; i++) { if (IS_ERR(arr[i])) continue; val_putref(arr[i]); } return ERR_PTR(ret); } /* * Compact a cons alist into an nvlist. */ static struct val *sexpr_compact_cons_nvl(struct val *in) { struct val *cur, *tmp; struct nvlist *out; int ret; out = nvl_alloc(); if (IS_ERR(out)) { val_putref(in); return nvl_cast_to_val(out); } /* * compact each element * * For example: * * ((a . b) (c . d)) * * will cause iterations (cur): * * (a . b) * (c . d) */ sexpr_for_each_noref(cur, tmp, in) { struct val *name = cur->cons.head; struct val *value = cur->cons.tail; struct nvpair new; /* NOTE: what_is_it already verified that we have a str/sym */ if (name->type == VT_STR) new.name = val_getref_str(name); else new.name = str_dup(val_cstr(name)); new.value = sexpr_compact(val_getref(value)); if (IS_ERR(new.value)) { str_putref(new.name); ret = PTR_ERR(new.value); goto err; } /* takes a new ref on both name & value */ ret = nvl_set_pair(out, &new); /* release our refs */ str_putref(new.name); val_putref(new.value); if (ret) goto err; } val_putref(in); return nvl_cast_to_val(out); err: val_putref(in); nvl_putref(out); return ERR_PTR(ret); } /* * Tries to figure out what sort of structure the passed in cons cell * represents. * * Returns: * VT_NULL - empty cons (technically convertible to either nvlist or array) * VT_NVL - an alist that's convertible to an nvlist * VT_ARRAY - a list that's convertible to an array * VT_CONS - no conversion can be made */ static enum val_type what_is_it(struct val *in) { struct val *cur; int nvl; if (sexpr_is_null(in)) return VT_NULL; nvl = true; /* * Iterate over the cons list checking each cell for whether it is a * part of a list (i.e., cdr is cons) and whether it is a part of an * alist (i.e., car is a cons with a string key) * * We don't use sexpr_for_each here because it stops iteration of * non-lists early. */ for (cur = in; !sexpr_is_null(cur); cur = cur->cons.tail) { struct val *item; if (cur->type != VT_CONS) return VT_CONS; /* a cons cell = we have a list */ /* * Don't bother checking for nvl-ness if we already found at * least one issue earlier. */ if (!nvl) continue; item = cur->cons.head; /* Is the current item a name-value cons? */ /* ...is it a cons? */ if (sexpr_is_null(item) || (item->type != VT_CONS)) { nvl = false; continue; } /* ...does it have a string/symbol name? */ if (sexpr_is_null(item->cons.head) || ((item->cons.head->type != VT_STR) && (item->cons.head->type != VT_SYM))) { nvl = false; continue; } /* looks like an nvlist pair, keep going */ } return nvl ? VT_NVL : VT_ARRAY; } static struct val *sexpr_compact_cons(struct val *in) { VERIFY3U(in->type, ==, VT_CONS); switch (what_is_it(in)) { case VT_CONS: /* no conversion possible */ return in; case VT_NULL: /* an empty cons; leave it as-is */ return in; case VT_ARRAY: return sexpr_compact_cons_array(in); case VT_NVL: return sexpr_compact_cons_nvl(in); case VT_INT: case VT_STR: case VT_SYM: case VT_BOOL: case VT_CHAR: case VT_BLOB: break; } panic("impossible cons compaction target"); } struct val *sexpr_compact(struct val *in) { switch (in->type) { case VT_NULL: case VT_INT: case VT_BOOL: case VT_STR: case VT_SYM: case VT_CHAR: case VT_BLOB: /* nothing to do */ return in; case VT_ARRAY: return sexpr_compact_array(in); case VT_NVL: return sexpr_compact_nvl(in); case VT_CONS: return sexpr_compact_cons(in); } panic("Cannot compact unknown val type (%d)", in->type); }