123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532 |
- /* Definitions for the array type.
- This file is part of khipu.
- khipu is free software: you can redistribute it and/or modify
- it under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 3 of the License, or
- (at your option) any later version.
- This program 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 Lesser General Public License for more details.
- You should have received a copy of the GNU Lesser General Public License
- along with this program. If not, see <https://www.gnu.org/licenses/>. */
- #include <cstdio>
- #include "khipu.hpp"
- #include "utils/raw_acc.hpp"
- KP_DECLS_BEGIN
- static array empty_array;
- array* array::alloc_raw (uint32_t n)
- {
- array *ret = (array *)alloch (sizeof (*ret) +
- n * sizeof (object), typecode::ARRAY);
- ret->data = (object *)&ret[1];
- ret->len = n;
- return (ret);
- }
- result<object> alloc_array (interpreter *interp, uint32_t nelems, object fill)
- {
- if (!nelems)
- kp_return (empty_array.as_obj ());
- auto eg = KP_TRY (evh_guard::make (interp));
- array *ret = array::alloc_raw (nelems);
- for (uint32_t i = 0; i < nelems; ++i)
- ret->data[i] = fill;
- interp->alval = ret->as_obj ();
- gc_register (interp, ret, sizeof (*ret) + nelems * sizeof (object));
- return (interp->alval);
- }
- result<object> add_aa (interpreter *interp, object a1, object a2)
- {
- array *p1 = as_array (a1), *p2 = as_array (a2);
- if (!p1->len && !p2->len)
- kp_return (a1);
- object ret = KP_TRY (alloc_array (interp, p1->len + p2->len));
- copy_objs (copy_objs (as_array(ret)->data,
- p1->data, p1->len),
- p2->data, p2->len);
- kp_return (ret);
- }
- result<object> concat_a (interpreter *interp, object *argv, int argc)
- {
- if (argc == 1)
- kp_return (*argv);
- uint32_t nlen = 0;
- for (int i = 0; i < argc; ++i)
- {
- if (!array_p (argv[i]))
- return (interp->raise ("type-error", "arguments must be arrays"));
- nlen += len_a (argv[i]);
- }
- object obj = KP_TRY (alloc_array (interp, nlen));
- object *dstp = as_array(obj)->data;
- for (int i = 0; i < argc; ++i)
- dstp = copy_objs (dstp, &xaref(argv[i], 0), len_a (argv[i]));
- kp_return (interp->alval);
- }
- result<object> mul_ia (interpreter *interp, object iv, object ax)
- {
- int ival = as_int (iv);
- const array *ap = as_array (ax);
- if (ival <= 0)
- kp_return (empty_array.as_obj ());
- object ret = KP_TRY (alloc_array (interp, ap->len * ival));
- object *ptr = as_array(ret)->data;
- for (int i = 0; i < ival; ++i)
- ptr = copy_objs (ptr, ap->data, ap->len);
- kp_return (ret);
- }
- static inline intptr_t
- get_idx (const array *ap, intptr_t ix)
- {
- return (ix < 0 ? ap->len + ix : ix);
- }
- static inline result<object*>
- array_ref (interpreter *interp, object ax, object iv)
- {
- int idx;
- if (!as<int> (iv, idx))
- return (interp->raise ("type-error", "index is not an integer"));
- array *ap = as_array (ax);
- idx = get_idx (ap, idx);
- if (kp_unlikely (idx < 0 || (uint32_t)idx >= ap->len))
- return (interp->raise_oob (idx, ap->len));
- return (&ap->data[idx]);
- }
- result<object> get_a (interpreter *interp,
- object ax, object iv, object dfl)
- {
- if (kp_unlikely (dfl != UNBOUND))
- return (interp->raise_nargs (2, 2, 3));
- object *p = KP_TRY (array_ref (interp, ax, iv));
- kp_return (*p);
- }
- result<object> nput_a (interpreter *interp,
- object ax, object iv, object val)
- {
- if (kp_unlikely (as_varobj(ax)->flagged_p (FLAGS_CONST)))
- return (interp->raise_const ());
- object *p = KP_TRY (array_ref (interp, ax, iv));
- deref (gc_wbarrier (interp, ax, *p = val));
- kp_return (val);
- }
- result<object> subseq_a (interpreter *interp,
- object ax, object ix1, object ix2)
- {
- const array *ap = as_array (ax);
- bool got = true;
- int i1, i2;
- if (ix2 == UNBOUND)
- i2 = (int)ap->len;
- else
- got = as<int> (ix2, i2);
- if (!got || !as<int> (ix1, i1))
- return (interp->raise ("type-error", "indices must be integers"));
- i1 = get_idx (ap, i1), i2 = get_idx (ap, i2);
- if ((i1 | i2) < 0 || i1 > i2 || (uint32_t)i2 > ap->len)
- return (interp->raise ("index-error", "indices out of bounds"));
- object ret = KP_TRY (alloc_array (interp, i2 - i1));
- copy_objs (as_array(ret)->data, ap->data + i1, i2 - i1);
- kp_return (ret);
- }
- result<object> copy_a (interpreter *interp, object obj, bool deep)
- {
- const array *ap = as_array (obj);
- if (!deep)
- {
- object ret = KP_TRY (alloc_array (interp, ap->len));
- copy_objs (&xaref(ret, 0), ap->data, ap->len);
- kp_return (ret);
- }
- raw_acc<array> rx (ap->len);
- array *ret = rx.get ();
- valref obr (interp, ret->as_obj ()), tmp (interp);
- for (ret->len = 0; ret->len < ap->len; ++ret->len)
- {
- *tmp = ap->data[ret->len];
- ret->data[ret->len] = KP_TRY (copy (interp, *tmp, true));
- }
- rx.release ();
- interp->retval = *obr;
- gc_register (interp, ret, sizeof (*ret) + ret->len * sizeof (object));
- return (interp->retval);
- }
- static const uint32_t ARRAY_HASH_SEED = 1634890337;
- result<uint32_t> hash_a (interpreter *interp, object obj)
- {
- uint32_t ret = ARRAY_HASH_SEED;
- for (array::iterator it { interp, obj }; it.valid (); ++it)
- {
- uint32_t tmp = KP_TRY (xhash (interp, *it));
- ret = mix_hash (ret, tmp);
- }
- return (ret);
- }
- result<object> nreverse_a (interpreter *interp, object obj)
- {
- array *ap = as_array (obj);
- if (kp_unlikely (ap->flagged_p (FLAGS_CONST)))
- return (interp->raise_const ());
- else if (ap->len == 0)
- kp_return (obj);
- for (uint32_t i = 0, j = ap->len - 1; i < j; ++i, --j)
- swap (ap->data[i], ap->data[j]);
- kp_return (obj);
- }
- result<object> reverse_a (interpreter *interp, object obj)
- {
- const array *ap = as_array (obj);
- if (ap->len == 0)
- kp_return (obj);
- object ret = KP_TRY (alloc_array (interp, ap->len));
- for (uint32_t i = 0, j = ap->len - 1 ; ; ++i, --j)
- {
- xaref(ret, i) = ap->data[j];
- if (i >= j)
- break;
- }
- kp_return (ret);
- }
- result<object> nsort_a (interpreter *interp, object obj, comparator& cmp)
- {
- array *ap = as_array (obj);
-
- if (kp_unlikely (ap->flagged_p (FLAGS_CONST)))
- return (interp->raise_const ());
- else if (ap->len <= 1)
- kp_return (obj);
- valref v1 (interp), v2 (interp);
- object *wp = ap->data - 1;
- for (uint32_t r = ap->len / 2; r > 0; --r)
- for (uint32_t i = r ; ; )
- {
- uint32_t j = i + i;
- if (j > ap->len)
- break;
- else if (j != ap->len)
- {
- bool rv = KP_TRY (cmp (*v1 = wp[j], *v2 = wp[j + 1]));
- if (rv)
- ++j;
- }
- bool rv = KP_TRY (cmp (*v1 = wp[i], *v2 = wp[j]));
- if (rv)
- swap (wp[i], wp[j]);
- i = j;
- }
- for (uint32_t s = ap->len - 1; s > 0; --s)
- {
- swap (*ap->data, ap->data[s]);
- for (uint32_t i = 1 ; ; )
- {
- uint32_t j = i + i;
- if (j > s)
- break;
- else if (j != s)
- {
- bool rv = KP_TRY (cmp (*v1 = wp[j], *v2 = wp[j + 1]));
- if (rv)
- ++j;
- }
- bool rv = KP_TRY (cmp (*v1 = wp[i], *v2 = wp[j]));
- if (rv)
- swap (wp[i], wp[j]);
- i = j;
- }
- }
- kp_return (obj);
- }
- result<bool> eq_aa (interpreter *interp, object a1, object a2)
- {
- if (len_a (a1) != len_a (a2))
- return (false);
- for (array::iterator i1 { interp, a1 }, i2 { interp, a2 };
- i1.valid (); ++i1, ++i2)
- {
- bool rv = KP_TRY (equal (interp, *i1, *i2));
- if (!rv)
- return (false);
- }
- return (true);
- }
- result<int> cmp_aa (interpreter *interp, object a1, object a2)
- {
- const array *ap1 = as_array (a1), *ap2 = as_array (a2);
- uint32_t rl = min (ap1->len, ap2->len);
- valref x (interp), y (interp);
- for (uint32_t i = 0; i < rl; ++i)
- {
- int c = KP_TRY (xcmp (interp, *x = ap1->data[i], *y = ap2->data[i]));
- if (c)
- return (c);
- }
- return (ap1->len < ap2->len ? -1 : ap2->len != ap1->len);
- }
- result<object> iter_a (interpreter *interp, object obj, object token, bool adv)
- {
- if (token == UNBOUND)
- kp_return (len_a (obj) == 0 ? NIL : fixint (0));
- else if (!adv)
- return (get_a (interp, obj, token, UNBOUND));
- else if (!fixint_p (token))
- return (interp->raise ("type-error", "token must be an int"));
- int ix = as_int (token) + 1;
- kp_return ((uint32_t)ix >= len_a (obj) ? NIL : fixint (ix));
- }
- result<object> nzap_a (interpreter *interp, object obj, object key,
- uint32_t flags, object fn, object *argv, int argc)
- {
- int idx;
- if (kp_unlikely (!as<int> (key, idx)))
- return (interp->raise ("type-error", "index is not an integer"));
- else if (kp_unlikely (flags & NZAP_DFL))
- return (interp->raise ("arg-error", "default argument not supported"));
- array *ap = as_array (obj);
- if (kp_unlikely (ap->flagged_p (FLAGS_CONST)))
- return (interp->raise_const ());
- idx = get_idx (ap, idx);
- if (kp_unlikely (idx < 0 || (uint32_t)idx >= ap->len))
- return (interp->raise_oob (idx, ap->len));
- sp_guard spg { interp };
- KP_VTRY (interp->growstk (argc + 1));
- *interp->stkend++ = fn;
- *interp->stkend++ = fixint (0);
- int stack_idx = interp->stklen () - 1;
- for (int i = 0; i < argc; ++i)
- *interp->stkend++ = argv[i];
- valref prev (interp);
- if (flags & NZAP_NOMT)
- {
- interp->stack[stack_idx] = *prev = ap->data[idx];
- ap->data[idx] = KP_TRY (call_n (interp, argc + 1));
- }
- else
- while (true)
- {
- *prev = ap->data[idx];
- interp->stack[stack_idx] = *prev;
- KP_VTRY (call_n (interp, argc + 1));
- if (atomic_cas_bool ((atomic_t *)&ap->data[idx],
- (atomic_t)*prev, (atomic_t)interp->retval))
- break;
- atomic_spin_nop ();
- }
- deref (gc_wbarrier (interp, obj, interp->retval));
- if (flags & NZAP_PREV)
- interp->retval = *prev;
-
- return (interp->retval);
- }
- result<object> last_a (interpreter *interp, object obj)
- {
- const array *ap = as_array (obj);
- if (!ap->len)
- return (interp->raise_oob (0, 0));
- kp_return (ap->data[ap->len - 1]);
- }
- result<object> find_a (interpreter *interp, object obj,
- object key, object start, object end, object test)
- {
- const array *ap = as_array (obj);
- int istart = start == UNBOUND ? 0 : get_idx (ap, as_int (start));
- int iend = end == UNBOUND ? ap->len : get_idx (ap, as_int (end));
- if (istart > iend)
- kp_return (NIL);
- else if ((istart | iend) < 0 || (uint32_t)iend > ap->len)
- return (interp->raise ("index-error", "indices out of bounds"));
- if (test == UNBOUND)
- {
- valref tmp (interp);
- for (; istart < iend; ++istart)
- {
- bool rv = KP_TRY (equal (interp, key, *tmp = ap->data[istart]));
- if (rv)
- kp_return (fixint (istart));
- }
- }
- else
- {
- KP_VTRY (interp->growstk (3));
- for (; istart < iend; ++istart)
- {
- *interp->stkend++ = test;
- *interp->stkend++ = key;
- *interp->stkend++ = ap->data[istart];
- KP_VTRY (call_n (interp, 2));
- if (interp->retval != NIL)
- kp_return (fixint (istart));
- }
- }
- kp_return (NIL);
- }
- result<int64_t> write_a (interpreter *interp, stream *strm,
- object obj, io_info& info)
- {
- int64_t ret = 0;
- ret += KP_TRY (strm->putb (interp, '['));
- array::iterator it { interp, obj };
- if (it.valid ())
- while (true)
- {
- ret += KP_TRY (xwrite (interp, strm, *it, info));
- if (!(++it).valid ())
- break;
- ret += KP_TRY (strm->putb (interp, ' '));
- }
- ret += KP_TRY (strm->putb (interp, ']'));
- return (ret);
- }
- result<int64_t> pack_a (interpreter *interp, stream *strm,
- object obj, pack_info& info)
- {
- const array *ap = as_array (obj);
- int64_t ret = 0;
- if (kp_likely (ap->len <= 0xff))
- { ret += KP_TRY (strm->putb (interp, (unsigned char)ap->len)); }
- else
- {
- ret += KP_TRY (strm->putb (interp, 0));
- ret += KP_TRY (strm->write (interp, &ap->len));
- }
- pack_info::eviction_guard eg { info, !ap->flagged_p (array::nonref_flag) };
- for (array::iterator it { interp, obj }; it.valid (); ++it)
- { ret += KP_TRY (xpack (interp, strm, *it, info)); }
- return (ret);
- }
- result<object> unpack_a (interpreter *interp, stream *strm,
- pack_info& info, bool save)
- {
- uint32_t len = KP_TRY (strm->getb (interp));
- if ((int)len < 0)
- return (info.error ("invalid array length"));
- else if (len == 0)
- {
- bool rv = KP_TRY (strm->sread (interp, &len));
- if (!rv)
- return (info.error ("invalid array length"));
- }
- raw_acc<array> ax (len);
- array *ret = ax.get ();
- valref obr (interp, ret->as_obj ());
- if (save)
- KP_VTRY (info.add_mapping (interp, *info.offset, *obr));
- for (ret->len = 0; ret->len < len; ++ret->len)
- { ret->data[ret->len] = KP_TRY (xunpack (interp, strm, info)); }
- ax.release ();
- interp->retval = *obr;
- gc_register (interp, ret, sizeof (*ret) + ret->len * sizeof (object));
- return (interp->retval);
- }
- static int
- do_init_array (interpreter *)
- {
- empty_array.vo_full = FLAGS_CONST;
- empty_array.vo_type = typecode::ARRAY;
- return (init_op::result_ok);
- }
- init_op init_array (do_init_array, "array");
- KP_DECLS_END
|