From c5fb4cfed70d970c5daa61425fd2b6ee4584b20e Mon Sep 17 00:00:00 2001 From: GrieferAtWork Date: Fri, 20 Dec 2024 14:28:32 +0100 Subject: [PATCH] Implement iterator-based `Sequence.cached` --- .vs/deemon-v141.sln | 1 + .vs/deemon-v141.vcxproj | 2 + .vs/deemon-v141.vcxproj.filters | 6 + .vs/deemon-v142.sln | 1 + .vs/deemon-v142.vcxproj | 2 + .vs/deemon-v142.vcxproj.filters | 6 + src/deemon/objects/dict.c | 1 + src/deemon/objects/hashset.c | 1 + src/deemon/objects/list.c | 1 + src/deemon/objects/rodict.c | 1 + src/deemon/objects/roset.c | 1 + src/deemon/objects/seq.c | 86 +- src/deemon/objects/seq/cached-seq.c | 769 ++++++++++++++++++ src/deemon/objects/seq/cached-seq.h | 119 +++ .../default-api-methods-attrproxy-impl.c.inl | 7 + .../seq/default-api-methods-last.c.inl | 44 + src/deemon/objects/seq/default-api-methods.c | 23 + .../seq/default-api-require-method-impl.c.inl | 34 +- src/deemon/objects/seq/default-api.c | 4 + src/deemon/objects/seq/default-api.h | 21 + src/deemon/objects/seq/flat.c | 14 +- src/deemon/objects/tuple.c | 1 + src/deemon/runtime/strings.h | 4 + src/dex/collections/bitset.c | 3 + src/dex/collections/deque.c | 1 + src/dex/collections/fixedlist.c | 1 + src/dex/collections/rbtree.c | 25 +- src/dex/collections/udict.c | 2 + src/dex/collections/uset.c | 6 +- util/test/deemon-sequence-cached.dee | 68 ++ 30 files changed, 1201 insertions(+), 54 deletions(-) create mode 100644 src/deemon/objects/seq/cached-seq.c create mode 100644 src/deemon/objects/seq/cached-seq.h create mode 100644 util/test/deemon-sequence-cached.dee diff --git a/.vs/deemon-v141.sln b/.vs/deemon-v141.sln index 0f4989826..a0dbdf39d 100644 --- a/.vs/deemon-v141.sln +++ b/.vs/deemon-v141.sln @@ -396,6 +396,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{2360741E-F ..\util\test\deemon-sequence-each-contains.dee = ..\util\test\deemon-sequence-each-contains.dee ..\util\test\deemon-sequence-each.dee = ..\util\test\deemon-sequence-each.dee ..\util\test\deemon-sequence-enumerate.dee = ..\util\test\deemon-sequence-enumerate.dee + ..\util\test\deemon-sequence-flatten.dee = ..\util\test\deemon-sequence-flatten.dee ..\util\test\deemon-sequence-locate.dee = ..\util\test\deemon-sequence-locate.dee ..\util\test\deemon-sequence-math.dee = ..\util\test\deemon-sequence-math.dee ..\util\test\deemon-sequence-reversed.dee = ..\util\test\deemon-sequence-reversed.dee diff --git a/.vs/deemon-v141.vcxproj b/.vs/deemon-v141.vcxproj index 72ca8dac6..c43cab3f4 100644 --- a/.vs/deemon-v141.vcxproj +++ b/.vs/deemon-v141.vcxproj @@ -245,6 +245,7 @@ + @@ -443,6 +444,7 @@ + diff --git a/.vs/deemon-v141.vcxproj.filters b/.vs/deemon-v141.vcxproj.filters index 3ec72f7f8..e2e448ffa 100644 --- a/.vs/deemon-v141.vcxproj.filters +++ b/.vs/deemon-v141.vcxproj.filters @@ -708,6 +708,9 @@ src\objects\seq + + src\objects\seq + src\objects\seq @@ -1298,6 +1301,9 @@ src\objects\seq + + src\objects\seq + src\objects\seq diff --git a/.vs/deemon-v142.sln b/.vs/deemon-v142.sln index 7d3b80356..4565eb487 100644 --- a/.vs/deemon-v142.sln +++ b/.vs/deemon-v142.sln @@ -396,6 +396,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{2360741E-F ..\util\test\deemon-sequence-each-contains.dee = ..\util\test\deemon-sequence-each-contains.dee ..\util\test\deemon-sequence-each.dee = ..\util\test\deemon-sequence-each.dee ..\util\test\deemon-sequence-enumerate.dee = ..\util\test\deemon-sequence-enumerate.dee + ..\util\test\deemon-sequence-flatten.dee = ..\util\test\deemon-sequence-flatten.dee ..\util\test\deemon-sequence-locate.dee = ..\util\test\deemon-sequence-locate.dee ..\util\test\deemon-sequence-math.dee = ..\util\test\deemon-sequence-math.dee ..\util\test\deemon-sequence-reversed.dee = ..\util\test\deemon-sequence-reversed.dee diff --git a/.vs/deemon-v142.vcxproj b/.vs/deemon-v142.vcxproj index a16e30cad..50fb30e5e 100644 --- a/.vs/deemon-v142.vcxproj +++ b/.vs/deemon-v142.vcxproj @@ -245,6 +245,7 @@ + @@ -443,6 +444,7 @@ + diff --git a/.vs/deemon-v142.vcxproj.filters b/.vs/deemon-v142.vcxproj.filters index 3ec72f7f8..e2e448ffa 100644 --- a/.vs/deemon-v142.vcxproj.filters +++ b/.vs/deemon-v142.vcxproj.filters @@ -708,6 +708,9 @@ src\objects\seq + + src\objects\seq + src\objects\seq @@ -1298,6 +1301,9 @@ src\objects\seq + + src\objects\seq + src\objects\seq diff --git a/src/deemon/objects/dict.c b/src/deemon/objects/dict.c index 4e4d0b1ea..a39eaf2fb 100644 --- a/src/deemon/objects/dict.c +++ b/src/deemon/objects/dict.c @@ -2906,6 +2906,7 @@ INTERN_TPCONST struct type_getset tpconst dict_getsets[] = { TYPE_GETTER_F(STR_frozen, &DeeRoDict_FromSequence, METHOD_FNOREFESCAPE, "->?Ert:RoDict\n" "Returns a read-only (frozen) copy of @this ?."), + TYPE_GETTER(STR_cached, &DeeObject_NewRef, "->?."), #ifndef CONFIG_NO_DEEMON_100_COMPAT TYPE_GETSET_F("max_load_factor", &deprecated_d100_get_maxloadfactor, diff --git a/src/deemon/objects/hashset.c b/src/deemon/objects/hashset.c index 76cc252dd..3d2acfc45 100644 --- a/src/deemon/objects/hashset.c +++ b/src/deemon/objects/hashset.c @@ -2048,6 +2048,7 @@ INTERN_TPCONST struct type_getset tpconst hashset_getsets[] = { TYPE_GETTER_F(STR_frozen, &DeeRoSet_FromSequence, METHOD_FNOREFESCAPE, "->?Ert:RoSet\n" "Returns a read-only (frozen) copy of @this HashSet"), + TYPE_GETTER(STR_cached, &DeeObject_NewRef, "->?."), #ifndef CONFIG_NO_DEEMON_100_COMPAT TYPE_GETSET_F("max_load_factor", &deprecated_d100_get_maxloadfactor, diff --git a/src/deemon/objects/list.c b/src/deemon/objects/list.c index 4e3a2c288..c8ae2fde8 100644 --- a/src/deemon/objects/list.c +++ b/src/deemon/objects/list.c @@ -3608,6 +3608,7 @@ PRIVATE struct type_getset tpconst list_getsets[] = { TYPE_GETTER_F(STR_frozen, &list_get_frozen, METHOD_FNOREFESCAPE, "->?DTuple\n" "Return a copy of the contents of @this List as an immutable sequence"), + TYPE_GETTER(STR_cached, &DeeObject_NewRef, "->?."), TYPE_GETTER_F("__sizeof__", &list_sizeof, METHOD_FNOREFESCAPE, "->?Dint"), TYPE_GETSET_END }; diff --git a/src/deemon/objects/rodict.c b/src/deemon/objects/rodict.c index a003ec650..63562d180 100644 --- a/src/deemon/objects/rodict.c +++ b/src/deemon/objects/rodict.c @@ -1082,6 +1082,7 @@ PRIVATE struct type_method tpconst rodict_methods[] = { PRIVATE struct type_getset tpconst rodict_getsets[] = { TYPE_GETTER(STR_frozen, &DeeObject_NewRef, "->?."), + TYPE_GETTER(STR_cached, &DeeObject_NewRef, "->?."), TYPE_GETTER_F("__sizeof__", &rodict_sizeof, METHOD_FNOREFESCAPE, "->?Dint"), TYPE_GETSET_END }; diff --git a/src/deemon/objects/roset.c b/src/deemon/objects/roset.c index 393cba32d..7ee7b5802 100644 --- a/src/deemon/objects/roset.c +++ b/src/deemon/objects/roset.c @@ -585,6 +585,7 @@ PRIVATE struct type_method tpconst roset_methods[] = { PRIVATE struct type_getset tpconst roset_getsets[] = { TYPE_GETTER(STR_frozen, &DeeObject_NewRef, "->?."), + TYPE_GETTER(STR_cached, &DeeObject_NewRef, "->?."), TYPE_GETTER_F("__sizeof__", &roset_sizeof, METHOD_FNOREFESCAPE, "->?Dint"), TYPE_GETSET_END }; diff --git a/src/deemon/objects/seq.c b/src/deemon/objects/seq.c index 541e630e9..a8960162f 100644 --- a/src/deemon/objects/seq.c +++ b/src/deemon/objects/seq.c @@ -2179,35 +2179,59 @@ PRIVATE struct type_getset tpconst seq_getsets[] = { /**/ "print repr x.flatten; /* { 0, 1, 2, 5, 7, 9, 1, 10, 1 } */" "}"), - /* TODO: Implement this one similar to how "first" and "last" are implement (iow: using `struct Dee_type_seq_cache') */ /* TODO: Variants of this need to be added for `Set' and `Mapping' */ -//TODO: TYPE_GETTER("cached", &DeeSeq_InvokeCached, -//TODO: "->?.\n" -//TODO: "Returns a sequence that is a lazily-populated, read-only proxy of @this ?{.}.\n" -//TODO: "As such, ?#cached behaves similar to ?#frozen, except that the rather than" -//TODO: /**/ "evaluating the elements of the sequence immediately, said evaluation will" -//TODO: /**/ "happen the first time elements are accessed. Additionally, there is no" -//TODO: /**/ "requirement that changes to the original sequence won't propagate into the" -//TODO: /**/ "\"cached\" sequence (even after the cache may have been populated).\n" -//TODO: "The only difference between some $seq and ${seq.cached} is that the later" -//TODO: /**/ "will allow for #B{O(1)} access to elements after an initial access, and" -//TODO: /**/ "that any side-effects of accessing some element will happen exactly once.\n" -//TODO: "In practice, this means that ?Acached?DTuple and ?Acached?DList will just" -//TODO: /**/ "re-return the tuple/list, but for most other sequences, a proxy is returned" -//TODO: /**/ "that will do one of the following:" -//TODO: "#L-{" -//TODO: /**/ "If @this's ?A__seqclass__?DType is ?. and implement ${operator []} and ${operator size}," -//TODO: /**/ /**/ "the cache lazily computes its size the first time it is accessed, and elements" -//TODO: /**/ /**/ "are saved into an internal buffer which is re-used during repeated calls|" -//TODO: /**/ "If @this's ?A__seqclass__?DType is ?. and only implement ${operator []}, the same thing" -//TODO: /**/ /**/ "happens, except that the size is determined by accessing+caching elements" -//TODO: /**/ /**/ "until the underlying sequence's ${operator []} returns :IndexError|" -//TODO: /**/ "If @this implements ${operator iter}, that operator is invoked the first time" -//TODO: /**/ /**/ "the size- or an element of the cache is accessed, then proceeds to yield from" -//TODO: /**/ /**/ "that iterator and cache its results until the requested index is reached. (In" -//TODO: /**/ /**/ "the case of the returned cache's size being accessed, the cache is populated" -//TODO: /**/ /**/ "fully)" -//TODO: "}"), + TYPE_GETTER(STR_cached, &default_seq_cached, + "->?.\n" + "Returns a sequence that is a lazily-populated, read-only proxy of @this ?..\n" + "The returned proxy will ensure that any indirect side-effects¹ of accessing elements, " + /**/ "or iterating the sequence will only happen once upon initial access/iteration. " + /**/ "E.g. a yield-function is only executed one time, and only up the point that " + /**/ "elements have been requested for.\n" + "Sequence types where element access/iteration has no indirect side-effects¹, meaning " + /**/ "essentially every materialized ?. type (e.g. ?DList, ?DTuple, ?DDict, ...), can " + /**/ "implement this property as a no-op: ${property cached = { . -\\> this; }}\n" + "As such, ?#cached behaves similar to ?#frozen, except that the rather than " + /**/ "evaluating the elements of the sequence immediately, said evaluation will " + /**/ "happen the first time elements are accessed. Additionally, there is no " + /**/ "requirement that changes to the original sequence won't propagate into the " + /**/ "\"cached\" sequence (even after the cache may have been populated).\n" + "The difference between some $seq and ${seq.cached} is that the later " + /**/ "will allow for #B{O(1)} access to elements after an initial access, and " + /**/ "that any indirect side-effects¹ of accessing some element will happen " + /**/ "exactly once.\n" + "${" + /**/ "/* \"lines\" will lazily populate itself from the contents of \"data.txt\" */\n" + /**/ "local lines = Sequence.cached(File.open(\"data.txt\", \"rb\"));\n" + /**/ "print lines.locate(line -\\> !line.startswith(\"##\"));\n" + /**/ "\\\n" + /**/ "/* This will print the first couple of lines from the cache, up until (and\n" + /**/ " * including) the already-accessed line, then continue reading from \"data.txt\" */\n" + /**/ "for (local line: lines)\n" + /**/ " print line;" + "}\n" + "${" + /**/ "function getItems() {\n" + /**/ " print \"Enter\";\n" + /**/ " yield 10;\n" + /**/ " print \"Middle\";\n" + /**/ " yield 20;\n" + /**/ " print \"Leave\";\n" + /**/ "}\n" + /**/ "\\\n" + /**/ "local items = getItems().cached;\n" + /**/ "print items[0]; /* Enter 10 */\n" + /**/ "print items[0]; /* 10 */\n" + /**/ "print items[1]; /* Middle 20 */\n" + /**/ "print items[0]; /* 10 */\n" + /**/ "print items[1]; /* 20 */\n" + /**/ "print ##items; /* Leave 3 */\n" + /**/ "print repr items; /* { 10, 20, 30 } */" + "}\n" + "#L{" + /**/ "{¹}An indirect side-effect is a change to the program's global state, usually caused " + /**/ /**/ "by the invocation of user-code. Examples include: changes to global variables, " + /**/ /**/ "output being written to a file, or text being printed to stdout." + "}"), /* TODO: itemtype->?DType * Check if the type of @this overrides the ?#ItemType class attribute. @@ -2233,9 +2257,9 @@ PRIVATE struct type_getset tpconst seq_getsets[] = { "Returns a copy of @this Sequence, with all of its current elements, as well as " /**/ "their current order frozen in place, constructing a snapshot of the Sequence's " /**/ "current elements. - The actual type of Sequence returned is implementation- " - /**/ "and type- specific, but a guaranty is made that nothing no non-implementation-" - /**/ "specific functions/attributes (i.e. anything that doesn't match #C{__*__}) will " - /**/ "be able to change the elements of the returned sequence.\n" + /**/ "and type- specific, but a guaranty is made that no non-implementation-specific " + /**/ "functions/attributes (i.e. anything that doesn't match #C{__*__}) will be able " + /**/ "to change the elements of the returned sequence.\n" "By default, this attribute simply casts @this Sequence into a ?DTuple, but sequence " /**/ "types that are known to already be immutable can override this attribute such " /**/ "that they simple re-return themselves.\n" diff --git a/src/deemon/objects/seq/cached-seq.c b/src/deemon/objects/seq/cached-seq.c new file mode 100644 index 000000000..d31340d16 --- /dev/null +++ b/src/deemon/objects/seq/cached-seq.c @@ -0,0 +1,769 @@ +/* Copyright (c) 2018-2024 Griefer@Work * + * * + * This software is provided 'as-is', without any express or implied * + * warranty. In no event will the authors be held liable for any damages * + * arising from the use of this software. * + * * + * Permission is granted to anyone to use this software for any purpose, * + * including commercial applications, and to alter it and redistribute it * + * freely, subject to the following restrictions: * + * * + * 1. The origin of this software must not be misrepresented; you must not * + * claim that you wrote the original software. If you use this software * + * in a product, an acknowledgement (see the following) in the product * + * documentation is required: * + * Portions Copyright (c) 2018-2024 Griefer@Work * + * 2. Altered source versions must be plainly marked as such, and must not be * + * misrepresented as being the original software. * + * 3. This notice may not be removed or altered from any source distribution. * + */ +#ifndef GUARD_DEEMON_OBJECTS_CACHED_SEQ_C +#define GUARD_DEEMON_OBJECTS_CACHED_SEQ_C 1 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/**/ +#include "../../runtime/runtime_error.h" +#include "../../runtime/strings.h" +#include "../generic-proxy.h" + +/**/ +#include "cached-seq.h" + +DECL_BEGIN + +PRIVATE WUNUSED NONNULL((1)) int DCALL +cswi_ctor(CachedSeq_WithIter *__restrict self) { + Dee_Incref(Dee_EmptyIterator); + self->cswi_iter = Dee_EmptyIterator; + Dee_atomic_lock_init(&self->cswi_lock); + objectlist_init(&self->cswi_cache); + CachedSeq_WithIter_SetFinished(self); + return 0; +} + +PRIVATE WUNUSED NONNULL((1, 2)) int DCALL +cswi_copycache(CachedSeq_WithIter *__restrict self, + CachedSeq_WithIter *__restrict other) { + DREF DeeObject **cache_copy; + size_t cache_size; +again: + CachedSeq_WithIter_LockAcquire(other); + cache_size = other->cswi_cache.ol_elemc; + cache_copy = Dee_objectlist_elemv_trymalloc(cache_size); + if unlikely(!cache_copy) { + CachedSeq_WithIter_LockRelease(other); + cache_copy = Dee_objectlist_elemv_malloc(cache_size); + if unlikely(!cache_copy) + goto err; + CachedSeq_WithIter_LockAcquire(other); + if unlikely(cache_size != other->cswi_cache.ol_elemc) { + CachedSeq_WithIter_LockRelease(other); + Dee_objectlist_elemv_free(cache_copy); + goto again; + } + } + cache_copy = Dee_Movrefv(cache_copy, other->cswi_cache.ol_elemv, cache_size); + _Dee_objectlist_setalloc(&self->cswi_cache, cache_size); +#ifdef CachedSeq_WithIter_HAVE_cswi_finished + self->cswi_finished = other->cswi_finished; +#else /* CachedSeq_WithIter_HAVE_cswi_finished */ + if (CachedSeq_WithIter_GetFinished(other)) + CachedSeq_WithIter_SetFinished(self); +#endif /* !CachedSeq_WithIter_HAVE_cswi_finished */ + CachedSeq_WithIter_LockRelease(other); + self->cswi_cache.ol_elemc = cache_size; + self->cswi_cache.ol_elemv = cache_copy; + return 0; +err: + return -1; +} + +PRIVATE WUNUSED NONNULL((1, 2)) int DCALL +cswi_copy(CachedSeq_WithIter *__restrict self, + CachedSeq_WithIter *__restrict other) { + if unlikely(cswi_copycache(self, other)) + goto err; + if (CachedSeq_WithIter_GetFinished(self)) { + Dee_Incref(Dee_EmptyIterator); + self->cswi_iter = Dee_EmptyIterator; + } else { + self->cswi_iter = DeeObject_Copy(other->cswi_iter); + if unlikely(!self->cswi_iter) + goto err_cache; + } + return 0; +err_cache: + objectlist_fini(&self->cswi_cache); +err: + return -1; +} + +PRIVATE WUNUSED NONNULL((1, 2)) int DCALL +cswi_deep(CachedSeq_WithIter *__restrict self, + CachedSeq_WithIter *__restrict other) { + if unlikely(cswi_copycache(self, other)) + goto err; + if (CachedSeq_WithIter_GetFinished(self)) { + Dee_Incref(Dee_EmptyIterator); + self->cswi_iter = Dee_EmptyIterator; + } else { + self->cswi_iter = DeeObject_DeepCopy(other->cswi_iter); + if unlikely(!self->cswi_iter) + goto err_cache; + } + return 0; +err_cache: + objectlist_fini(&self->cswi_cache); +err: + return -1; +} + +PRIVATE WUNUSED NONNULL((1)) int DCALL +cswi_deepload(CachedSeq_WithIter *__restrict self) { + size_t i; + for (i = 0;; ++i) { + DREF DeeObject *oldval, *newval; +again_copy_at_i: + CachedSeq_WithIter_LockAcquire(self); + if (i >= self->cswi_cache.ol_elemc) { + CachedSeq_WithIter_LockRelease(self); + break; + } + oldval = self->cswi_cache.ol_elemv[i]; + Dee_Incref(oldval); + CachedSeq_WithIter_LockRelease(self); + newval = DeeObject_DeepCopy(oldval); + Dee_Decref_unlikely(oldval); + if unlikely(!newval) + goto err; + CachedSeq_WithIter_LockAcquire(self); + if unlikely(i >= self->cswi_cache.ol_elemc) { + CachedSeq_WithIter_LockRelease(self); + Dee_Decref(newval); + break; + } + if unlikely(self->cswi_cache.ol_elemv[i] != oldval) { + CachedSeq_WithIter_LockRelease(self); + Dee_Decref(newval); + goto again_copy_at_i; + } + self->cswi_cache.ol_elemv[i] = newval; /* Inherit reference (x2) */ + CachedSeq_WithIter_LockRelease(self); + Dee_Decref(oldval); + } + return 0; +err: + return -1; +} + +PRIVATE WUNUSED NONNULL((1)) int DCALL +cswi_init(CachedSeq_WithIter *__restrict self, + size_t argc, DeeObject *const *argv) { + if (DeeArg_Unpack(argc, argv, "o:_CachedSeqWithIter", &self->cswi_iter)) + goto err; + Dee_Incref(self->cswi_iter); + Dee_atomic_lock_init(&self->cswi_lock); + objectlist_init(&self->cswi_cache); + CachedSeq_WithIter_InitFinished(self); + return 0; +err: + return -1; +} + +PRIVATE NONNULL((1)) void DCALL +cswi_fini(CachedSeq_WithIter *__restrict self) { + objectlist_fini(&self->cswi_cache); + Dee_Decref(self->cswi_iter); +} + +PRIVATE NONNULL((1, 2)) void DCALL +cswi_visit(CachedSeq_WithIter *__restrict self, dvisit_t proc, void *arg) { + CachedSeq_WithIter_LockAcquire(self); + Dee_Visitv(self->cswi_cache.ol_elemv, + self->cswi_cache.ol_elemc); + CachedSeq_WithIter_LockRelease(self); + Dee_Visit(self->cswi_iter); +} + +PRIVATE NONNULL((1)) void DCALL +cswi_clear(CachedSeq_WithIter *__restrict self) { + size_t old_elemc; + DREF DeeObject **old_elemv; + CachedSeq_WithIter_LockAcquire(self); + old_elemc = self->cswi_cache.ol_elemc; + old_elemv = self->cswi_cache.ol_elemv; + self->cswi_cache.ol_elemc = 0; + self->cswi_cache.ol_elemv = NULL; + _Dee_objectlist_setalloc(&self->cswi_cache, 0); + CachedSeq_WithIter_LockRelease(self); + Dee_Decrefv(old_elemv, old_elemc); + Dee_objectlist_elemv_free(old_elemv); +} + +PRIVATE struct type_gc cswi_gc = { + /* .tp_clear = */ (void (DCALL *)(DeeObject *__restrict))&cswi_clear +}; + +/* @return: 1 : Failure (`index' is out-of-bounds, but the cache is now fully loaded) + * @return: 0 : Success (`index' is now loaded within the cache) + * @return: -1: Error * */ +PRIVATE WUNUSED NONNULL((1)) int DCALL +cswi_ensure_loaded(CachedSeq_WithIter *__restrict self, size_t index) { + DREF DeeObject *nextitem; + CachedSeq_WithIter_LockAcquire(self); + while (index >= self->cswi_cache.ol_elemc) { + size_t alloc; + if (CachedSeq_WithIter_GetFinished(self)) { + CachedSeq_WithIter_LockRelease(self); + return 1; /* Out-of-bounds */ + } + CachedSeq_WithIter_LockRelease(self); + + /* Load 1 additional element. */ + nextitem = DeeObject_IterNext(self->cswi_iter); + if (!ITER_ISOK(nextitem)) { + if unlikely(!nextitem) + goto err; + CachedSeq_WithIter_LockAcquire(self); + CachedSeq_WithIter_SetFinished(self); + CachedSeq_WithIter_LockRelease(self); + return 1; + } + + /* Store "nextitem" within the cache. */ +lock_and_append_to_cache: + CachedSeq_WithIter_LockAcquire(self); + alloc = Dee_objectlist_getalloc(&self->cswi_cache); + ASSERT(alloc >= self->cswi_cache.ol_elemc); + if unlikely(alloc <= self->cswi_cache.ol_elemc) { + DREF DeeObject **new_elemv; + size_t new_alloc = DEE_OBJECTLIST_MOREALLOC(alloc); + if (new_alloc < DEE_OBJECTLIST_MINALLOC) + new_alloc = DEE_OBJECTLIST_MINALLOC; + new_elemv = Dee_objectlist_elemv_tryrealloc_safe(self->cswi_cache.ol_elemv, new_alloc); + if unlikely(!new_elemv) { + size_t min_alloc = self->cswi_cache.ol_elemc; + new_alloc = min_alloc + 1; + new_elemv = Dee_objectlist_elemv_tryrealloc_safe(self->cswi_cache.ol_elemv, new_alloc); + if unlikely(!new_elemv) { + /* Cannot realloc -> try again while not holding any locks. */ + CachedSeq_WithIter_LockRelease(self); + new_alloc = DEE_OBJECTLIST_MOREALLOC(alloc); + if (new_alloc < DEE_OBJECTLIST_MINALLOC) + new_alloc = DEE_OBJECTLIST_MINALLOC; + new_elemv = Dee_objectlist_elemv_trymalloc_safe(new_alloc); + if unlikely(!new_elemv) { + new_alloc = min_alloc + 1; + new_elemv = Dee_objectlist_elemv_malloc_safe(new_alloc); + if unlikely(!new_elemv) + goto err_nextitem; + } + CachedSeq_WithIter_LockAcquire(self); + if unlikely(self->cswi_cache.ol_elemc > min_alloc) { + CachedSeq_WithIter_LockRelease(self); + Dee_objectlist_elemv_free(new_elemv); + goto lock_and_append_to_cache; + } + /* Copy into the new cache-buffer. */ + new_elemv = (DREF DeeObject **)memcpyc(new_elemv, self->cswi_cache.ol_elemv, + self->cswi_cache.ol_elemc, + sizeof(DREF DeeObject *)); + Dee_objectlist_elemv_free(self->cswi_cache.ol_elemv); + self->cswi_cache.ol_elemv = new_elemv; + _Dee_objectlist_setalloc(&self->cswi_cache, new_alloc); + } + } + self->cswi_cache.ol_elemv = new_elemv; + _Dee_objectlist_setalloc(&self->cswi_cache, new_alloc); + } + self->cswi_cache.ol_elemv[self->cswi_cache.ol_elemc] = nextitem; /* Inherit reference */ + ++self->cswi_cache.ol_elemc; + } + CachedSeq_WithIter_LockRelease(self); + return 0; /* Already in cache! */ +err_nextitem: + Dee_Decref(nextitem); +err: + return -1; +} + +PRIVATE WUNUSED NONNULL((1)) DREF DeeObject *DCALL +cswi_trygetitem_index(CachedSeq_WithIter *__restrict self, size_t index) { + DREF DeeObject *result; + int status; +#ifndef CONFIG_NO_THREADS +again: +#endif /* !CONFIG_NO_THREADS */ + status = cswi_ensure_loaded(self, index); + if unlikely(status < 0) + goto err; + if (status > 0) + return ITER_DONE; + CachedSeq_WithIter_LockAcquire(self); +#ifndef CONFIG_NO_THREADS + if unlikely(index >= self->cswi_cache.ol_elemc) { + CachedSeq_WithIter_LockRelease(self); + goto again; + } +#endif /* !CONFIG_NO_THREADS */ + ASSERT(index < self->cswi_cache.ol_elemc); + result = self->cswi_cache.ol_elemv[index]; + Dee_Incref(result); + CachedSeq_WithIter_LockRelease(self); + return result; +err: + return NULL; +} + +PRIVATE WUNUSED NONNULL((1)) DREF DeeObject *DCALL +cswi_getitem_index(CachedSeq_WithIter *__restrict self, size_t index) { + DREF DeeObject *result = cswi_trygetitem_index(self, index); + if unlikely(result == ITER_DONE) + goto err_oob; + return result; + { + size_t size; +err_oob: + CachedSeq_WithIter_LockAcquire(self); + size = self->cswi_cache.ol_elemc; + CachedSeq_WithIter_LockRelease(self); + err_index_out_of_bounds((DeeObject *)self, index, size); + } +/*err:*/ + return NULL; +} + +PRIVATE WUNUSED NONNULL((1)) int DCALL +cswi_bounditem_index(CachedSeq_WithIter *__restrict self, size_t index) { + int result = cswi_ensure_loaded(self, index); + if likely(result >= 0) + result = result ? -2 : 1; + return result; +} + +PRIVATE WUNUSED NONNULL((1)) int DCALL +cswi_hasitem_index(CachedSeq_WithIter *__restrict self, size_t index) { + int result = cswi_ensure_loaded(self, index); + if likely(result >= 0) + result = !result; + return result; +} + +PRIVATE WUNUSED NONNULL((1)) size_t DCALL +cswi_size(CachedSeq_WithIter *__restrict self) { + size_t result; + if unlikely(cswi_ensure_loaded(self, (size_t)-1) < 0) + goto err; + CachedSeq_WithIter_LockAcquire(self); + result = self->cswi_cache.ol_elemc; + CachedSeq_WithIter_LockRelease(self); + return result; +err: + return (size_t)-1; +} + +PRIVATE WUNUSED NONNULL((1)) size_t DCALL +cswi_size_fast(CachedSeq_WithIter *__restrict self) { + size_t result; + CachedSeq_WithIter_LockAcquire(self); + result = self->cswi_cache.ol_elemc; + if (!CachedSeq_WithIter_GetFinished(self)) + result = (size_t)-1; + CachedSeq_WithIter_LockRelease(self); + return result; +} + +PRIVATE WUNUSED NONNULL((1)) DREF CachedSeq_WithIter_Iterator *DCALL +cswi_iter(CachedSeq_WithIter *__restrict self) { + DREF CachedSeq_WithIter_Iterator *result; + result = DeeObject_MALLOC(CachedSeq_WithIter_Iterator); + if unlikely(!result) + goto err; + Dee_Incref(self); + result->cswii_cache = self; + result->cswii_index = 0; + DeeObject_Init(result, &CachedSeq_WithIter_Iterator_Type); + return result; +err: + return NULL; +} + +PRIVATE WUNUSED NONNULL((1)) Dee_ssize_t DCALL +cswi_foreach(CachedSeq_WithIter *__restrict self, Dee_foreach_t cb, void *arg) { + size_t i; + Dee_ssize_t temp, result = 0; + for (i = 0;; ++i) { + DREF DeeObject *item; + item = cswi_trygetitem_index(self, i); + if unlikely(!ITER_ISOK(item)) { + if unlikely(!item) + goto err; + break; + } + temp = (*cb)(arg, item); + Dee_Decref(item); + if unlikely(temp < 0) + goto err_temp; + result += temp; + } + return result; +err_temp: + return temp; +err: + return -1; +} + +PRIVATE WUNUSED NONNULL((1)) size_t DCALL +cswi_asvector(CachedSeq_WithIter *self, size_t dst_length, /*out*/ DREF DeeObject **dst) { + size_t result; + if unlikely(cswi_ensure_loaded(self, (size_t)-1) < 0) + goto err; + CachedSeq_WithIter_LockAcquire(self); + result = self->cswi_cache.ol_elemc; + if likely(dst_length >= result) + Dee_Movrefv(dst, self->cswi_cache.ol_elemv, result); + CachedSeq_WithIter_LockRelease(self); + return result; +err: + return (size_t)-1; +} + + + +PRIVATE struct type_seq cswi_seq = { + /* .tp_iter = */ (DREF DeeObject *(DCALL *)(DeeObject *__restrict))&cswi_iter, + /* .tp_sizeob = */ NULL, + /* .tp_contains = */ NULL, + /* .tp_getitem = */ NULL, + /* .tp_delitem = */ NULL, + /* .tp_setitem = */ NULL, + /* .tp_getrange = */ NULL, + /* .tp_delrange = */ NULL, + /* .tp_setrange = */ NULL, + /* .tp_nsi = */ NULL, + /* .tp_foreach = */ (Dee_ssize_t (DCALL *)(DeeObject *__restrict, Dee_foreach_t, void *))&cswi_foreach, + /* .tp_foreach_pair = */ NULL, + /* .tp_enumerate = */ NULL, + /* .tp_enumerate_index = */ NULL, + /* .tp_iterkeys = */ NULL, + /* .tp_bounditem = */ NULL, + /* .tp_hasitem = */ NULL, + /* .tp_size = */ (size_t (DCALL *)(DeeObject *__restrict))&cswi_size, + /* .tp_size_fast = */ (size_t (DCALL *)(DeeObject *__restrict))&cswi_size_fast, + /* .tp_getitem_index = */ (DREF DeeObject *(DCALL *)(DeeObject *, size_t))&cswi_getitem_index, + /* .tp_getitem_index_fast = */ NULL, + /* .tp_delitem_index = */ NULL, + /* .tp_setitem_index = */ NULL, + /* .tp_bounditem_index = */ (int (DCALL *)(DeeObject *, size_t))&cswi_bounditem_index, + /* .tp_hasitem_index = */ (int (DCALL *)(DeeObject *, size_t))&cswi_hasitem_index, + /* .tp_getrange_index = */ NULL, + /* .tp_delrange_index = */ NULL, + /* .tp_setrange_index = */ NULL, + /* .tp_getrange_index_n = */ NULL, + /* .tp_delrange_index_n = */ NULL, + /* .tp_setrange_index_n = */ NULL, + /* .tp_trygetitem = */ NULL, + /* .tp_trygetitem_index = */ (DREF DeeObject *(DCALL *)(DeeObject *, size_t))&cswi_trygetitem_index, + /* .tp_trygetitem_string_hash = */ NULL, + /* .tp_getitem_string_hash = */ NULL, + /* .tp_delitem_string_hash = */ NULL, + /* .tp_setitem_string_hash = */ NULL, + /* .tp_bounditem_string_hash = */ NULL, + /* .tp_hasitem_string_hash = */ NULL, + /* .tp_trygetitem_string_len_hash = */ NULL, + /* .tp_getitem_string_len_hash = */ NULL, + /* .tp_delitem_string_len_hash = */ NULL, + /* .tp_setitem_string_len_hash = */ NULL, + /* .tp_bounditem_string_len_hash = */ NULL, + /* .tp_hasitem_string_len_hash = */ NULL, + /* NOTE: "tp_asvector" explicitly states that "repeated" invocations mustn't produce any + * (additional) side-effects. As such, it *does* allow side-effects on the first + * invocation, meaning we're perfectly fine to implement it! */ + /* .tp_asvector = */ (size_t (DCALL *)(DeeObject *, size_t, DREF DeeObject **))&cswi_asvector, +}; + +PRIVATE WUNUSED NONNULL((1)) int DCALL +cswi_bool(CachedSeq_WithIter *__restrict self) { + return cswi_hasitem_index(self, 0); +} + +PRIVATE WUNUSED NONNULL((1)) DREF CachedSeq_WithIter *DCALL +cswi_getfrozen(CachedSeq_WithIter *__restrict self) { + /* Once fully loaded, a cache can be considered as frozen! */ + if unlikely(cswi_ensure_loaded(self, (size_t)-1) < 0) + goto err; + return_reference_(self); +err: + return NULL; +} + +PRIVATE WUNUSED NONNULL((1)) DREF DeeObject *DCALL +cswi_getseq(CachedSeq_WithIter *__restrict self) { + return DeeObject_GetAttr(self->cswi_iter, (DeeObject *)&str_seq); +} + +PRIVATE WUNUSED NONNULL((1)) DREF DeeObject *DCALL +cswi_populate(CachedSeq_WithIter *__restrict self, + size_t argc, DeeObject *const *argv) { + size_t count = (size_t)-1; + if (DeeArg_Unpack(argc, argv, ":populate", &count)) + goto err; + if likely(count) { + --count; + if unlikely(cswi_ensure_loaded(self, count) < 0) + goto err; + } + return_none; +err: + return NULL; +} + + +PRIVATE struct type_method tpconst cswi_methods[] = { + TYPE_METHOD("populate", &cswi_populate, + "(count:?Dint=!ASIZE_MAX?Dint)\n" + "Ensure that the first @count elements of ?#__seq__ have been cached"), + TYPE_METHOD_END +}; + +PRIVATE struct type_getset tpconst cswi_getsets[] = { + TYPE_GETTER(STR_cached, &DeeObject_NewRef, "->?."), + TYPE_GETTER(STR_frozen, &cswi_getfrozen, "->?.\nFully populate the cache, then re-return it"), + TYPE_GETTER("__seq__", &cswi_getseq, "->?DSequence\nAlias for ${this.__iter__.seq}"), + TYPE_GETSET_END +}; + +PRIVATE struct type_member tpconst cswi_members[] = { + TYPE_MEMBER_FIELD_DOC("__iter__", STRUCT_OBJECT, offsetof(CachedSeq_WithIter, cswi_iter), "->?DIterator"), + TYPE_MEMBER_FIELD("__cache_size__", STRUCT_SIZE_T | STRUCT_CONST | STRUCT_ATOMIC, + offsetof(CachedSeq_WithIter, cswi_cache.ol_elemc)), + TYPE_MEMBER_END +}; + +PRIVATE struct type_member tpconst cswi_class_members[] = { + TYPE_MEMBER_CONST(STR_Iterator, &CachedSeq_WithIter_Iterator_Type), + TYPE_MEMBER_END +}; + + + +/* Uses an auto-growing vector for elements, that is fed by an iterator. */ +INTERN DeeTypeObject CachedSeq_WithIter_Type = { + OBJECT_HEAD_INIT(&DeeType_Type), + /* .tp_name = */ "_CachedSeqWithIter", + /* .tp_doc = */ DOC("()\n" + "(iter:?DIterator)"), + /* .tp_flags = */ TP_FNORMAL | TP_FFINAL | TP_FGC, + /* .tp_weakrefs = */ 0, + /* .tp_features = */ TF_NONE, + /* .tp_base = */ &DeeSeq_Type, + /* .tp_init = */ { + { + /* .tp_alloc = */ { + /* .tp_ctor = */ (dfunptr_t)&cswi_ctor, + /* .tp_copy_ctor = */ (dfunptr_t)&cswi_copy, + /* .tp_deep_ctor = */ (dfunptr_t)&cswi_deep, + /* .tp_any_ctor = */ (dfunptr_t)&cswi_init, + TYPE_FIXED_ALLOCATOR_GC(CachedSeq_WithIter) + } + }, + /* .tp_dtor = */ (void (DCALL *)(DeeObject *__restrict))&cswi_fini, + /* .tp_assign = */ NULL, + /* .tp_move_assign = */ NULL, + /* .tp_deepload = */ (int (DCALL *)(DeeObject *__restrict))&cswi_deepload, + }, + /* .tp_cast = */ { + /* .tp_str = */ NULL, + /* .tp_repr = */ NULL, + /* .tp_bool = */ (int (DCALL *)(DeeObject *__restrict))&cswi_bool + }, + /* .tp_call = */ NULL, + /* .tp_visit = */ (void (DCALL *)(DeeObject *__restrict, dvisit_t, void *))&cswi_visit, + /* .tp_gc = */ &cswi_gc, + /* .tp_math = */ NULL, + /* .tp_cmp = */ NULL, + /* .tp_seq = */ &cswi_seq, + /* .tp_iter_next = */ NULL, + /* .tp_iterator = */ NULL, + /* .tp_attr = */ NULL, + /* .tp_with = */ NULL, + /* .tp_buffer = */ NULL, + /* .tp_methods = */ cswi_methods, + /* .tp_getsets = */ cswi_getsets, + /* .tp_members = */ cswi_members, + /* .tp_class_methods = */ NULL, + /* .tp_class_getsets = */ NULL, + /* .tp_class_members = */ cswi_class_members +}; + + +STATIC_ASSERT(offsetof(CachedSeq_WithIter_Iterator, cswii_cache) == offsetof(ProxyObject, po_obj)); +#define cswiiter_fini generic_proxy_fini +#define cswiiter_visit generic_proxy_visit + +PRIVATE WUNUSED NONNULL((1)) int DCALL +cswiiter_ctor(CachedSeq_WithIter_Iterator *__restrict self) { + self->cswii_cache = (DREF CachedSeq_WithIter *)DeeObject_NewDefault(&CachedSeq_WithIter_Type); + if unlikely(!self->cswii_cache) + goto err; + self->cswii_index = 0; + return 0; +err: + return -1; +} + +PRIVATE WUNUSED NONNULL((1, 2)) int DCALL +cswiiter_copy(CachedSeq_WithIter_Iterator *__restrict self, + CachedSeq_WithIter_Iterator *__restrict other) { + self->cswii_index = atomic_read(&other->cswii_index); + return generic_proxy_copy_alias((ProxyObject *)self, (ProxyObject *)other); +} + +PRIVATE WUNUSED NONNULL((1, 2)) int DCALL +cswiiter_deep(CachedSeq_WithIter_Iterator *__restrict self, + CachedSeq_WithIter_Iterator *__restrict other) { + self->cswii_index = atomic_read(&other->cswii_index); + return generic_proxy_deepcopy((ProxyObject *)self, (ProxyObject *)other); +} + +PRIVATE WUNUSED NONNULL((1)) int DCALL +cswiiter_init(CachedSeq_WithIter_Iterator *__restrict self, + size_t argc, DeeObject *const *argv) { + self->cswii_index = 0; + if (DeeArg_Unpack(argc, argv, "o|" UNPuSIZ ":_CachedSeqWithIterIterator", + &self->cswii_cache, &self->cswii_index)) + goto err; + if (DeeObject_AssertTypeExact(self->cswii_cache, &CachedSeq_WithIter_Type)) + goto err; + Dee_Incref(self->cswii_cache); + return 0; +err: + return -1; +} + +PRIVATE WUNUSED NONNULL((1)) int DCALL +cswiiter_bool(CachedSeq_WithIter_Iterator *__restrict self) { + size_t index = atomic_read(&self->cswii_index); + return cswi_hasitem_index(self->cswii_cache, index); +} + +PRIVATE WUNUSED NONNULL((1)) DREF DeeObject *DCALL +cswiiter_next(CachedSeq_WithIter_Iterator *__restrict self) { + DREF DeeObject *result; + for (;;) { + size_t index; + index = atomic_read(&self->cswii_index); + result = cswi_trygetitem_index(self->cswii_cache, index); + if unlikely(!ITER_ISOK(result)) + break; + if likely(atomic_cmpxch_or_write(&self->cswii_index, index, index + 1)) + break; + Dee_Decref(result); + } + return result; +} + +PRIVATE WUNUSED NONNULL((1)) size_t DCALL +cswiiter_advance(CachedSeq_WithIter_Iterator *__restrict self, size_t step) { + size_t old_index, new_index; + do { + old_index = atomic_read(&self->cswii_index); + if (OVERFLOW_UADD(old_index, step, &new_index)) + new_index = (size_t)-1; + if likely(new_index > 0) { + int load_status; + load_status = cswi_ensure_loaded(self->cswii_cache, new_index - 1); + if unlikely(load_status < 0) + goto err; + if (load_status > 0) { + /* Clap "new_index" to the max possible value */ + CachedSeq_WithIter_LockAcquire(self->cswii_cache); + new_index = self->cswii_cache->cswi_cache.ol_elemc; + CachedSeq_WithIter_LockRelease(self->cswii_cache); + } + } + } while (!atomic_cmpxch_or_write(&self->cswii_index, old_index, new_index)); + return new_index - old_index; +err: + return (size_t)-1; +} + +PRIVATE struct type_iterator cswiiter_iterator = { + /* .tp_nextpair = */ NULL, + /* .tp_nextkey = */ NULL, + /* .tp_nextvalue = */ NULL, + /* .tp_advance = */ (size_t (DCALL *)(DeeObject *__restrict, size_t))&cswiiter_advance, +}; + +PRIVATE struct type_member tpconst cswiiter_members[] = { + TYPE_MEMBER_FIELD_DOC(STR_seq, STRUCT_OBJECT, + offsetof(CachedSeq_WithIter_Iterator, cswii_cache), + "->?Ert:CachedSeqWithIter"), + TYPE_MEMBER_FIELD("__index__", STRUCT_SIZE_T | STRUCT_ATOMIC, + offsetof(CachedSeq_WithIter_Iterator, cswii_index)), + TYPE_MEMBER_END +}; + +INTERN DeeTypeObject CachedSeq_WithIter_Iterator_Type = { + OBJECT_HEAD_INIT(&DeeType_Type), + /* .tp_name = */ "_CachedSeqWithIterIterator", + /* .tp_doc = */ DOC("()\n" + "(base:?Ert:CachedSeqWithIter,index=!0)"), + /* .tp_flags = */ TP_FNORMAL | TP_FFINAL, + /* .tp_weakrefs = */ 0, + /* .tp_features = */ TF_NONE, + /* .tp_base = */ &DeeIterator_Type, + /* .tp_init = */ { + { + /* .tp_alloc = */ { + /* .tp_ctor = */ (dfunptr_t)&cswiiter_ctor, + /* .tp_copy_ctor = */ (dfunptr_t)&cswiiter_copy, + /* .tp_deep_ctor = */ (dfunptr_t)&cswiiter_deep, + /* .tp_any_ctor = */ (dfunptr_t)&cswiiter_init, + TYPE_FIXED_ALLOCATOR(CachedSeq_WithIter_Iterator) + } + }, + /* .tp_dtor = */ (void (DCALL *)(DeeObject *__restrict))&cswiiter_fini, + /* .tp_assign = */ NULL, + /* .tp_move_assign = */ NULL + }, + /* .tp_cast = */ { + /* .tp_str = */ NULL, + /* .tp_repr = */ NULL, + /* .tp_bool = */ (int (DCALL *)(DeeObject *__restrict))&cswiiter_bool + }, + /* .tp_call = */ NULL, + /* .tp_visit = */ (void (DCALL *)(DeeObject *__restrict, dvisit_t, void *))&cswiiter_visit, + /* .tp_gc = */ NULL, + /* .tp_math = */ NULL, + /* .tp_cmp = */ NULL, + /* .tp_seq = */ NULL, + /* .tp_iter_next = */ (DREF DeeObject *(DCALL *)(DeeObject *__restrict))&cswiiter_next, + /* .tp_iterator = */ &cswiiter_iterator, + /* .tp_attr = */ NULL, + /* .tp_with = */ NULL, + /* .tp_buffer = */ NULL, + /* .tp_methods = */ NULL, + /* .tp_getsets = */ NULL, + /* .tp_members = */ cswiiter_members, + /* .tp_class_methods = */ NULL, + /* .tp_class_getsets = */ NULL, + /* .tp_class_members = */ NULL +}; + +DECL_END + +#endif /* !GUARD_DEEMON_OBJECTS_CACHED_SEQ_C */ diff --git a/src/deemon/objects/seq/cached-seq.h b/src/deemon/objects/seq/cached-seq.h new file mode 100644 index 000000000..58036db26 --- /dev/null +++ b/src/deemon/objects/seq/cached-seq.h @@ -0,0 +1,119 @@ +/* Copyright (c) 2018-2024 Griefer@Work * + * * + * This software is provided 'as-is', without any express or implied * + * warranty. In no event will the authors be held liable for any damages * + * arising from the use of this software. * + * * + * Permission is granted to anyone to use this software for any purpose, * + * including commercial applications, and to alter it and redistribute it * + * freely, subject to the following restrictions: * + * * + * 1. The origin of this software must not be misrepresented; you must not * + * claim that you wrote the original software. If you use this software * + * in a product, an acknowledgement (see the following) in the product * + * documentation is required: * + * Portions Copyright (c) 2018-2024 Griefer@Work * + * 2. Altered source versions must be plainly marked as such, and must not be * + * misrepresented as being the original software. * + * 3. This notice may not be removed or altered from any source distribution. * + */ +#ifndef GUARD_DEEMON_OBJECTS_CACHED_SEQ_H +#define GUARD_DEEMON_OBJECTS_CACHED_SEQ_H 1 + +#include +#include +#include +#include +#include +/**/ + +#include "../generic-proxy.h" + +DECL_BEGIN + +/************************************************************************/ +/* ITERATOR-BASED CACHE */ +/************************************************************************/ +typedef struct { + PROXY_OBJECT_HEAD(cswi_iter) /* [1..1][const] The iterator whose results are being cached. */ +#ifndef CONFIG_NO_THREADS + Dee_atomic_lock_t cswi_lock; /* The lock used to synchronize the cache below. */ +#endif /* !CONFIG_NO_THREADS */ + struct objectlist cswi_cache; /* Cache of results returned by `cswi_iter' */ +#ifndef DEE_OBJECTLIST_HAVE_ELEMA +#define CachedSeq_WithIter_HAVE_cswi_finished + bool cswi_finished; /* Set to true once `cswi_iter' has been exhausted */ +#endif /* !DEE_OBJECTLIST_HAVE_ELEMA */ +} CachedSeq_WithIter; + +/* Uses an auto-growing vector for elements, that is fed by an iterator. */ +INTDEF DeeTypeObject CachedSeq_WithIter_Type; + +#ifdef CachedSeq_WithIter_HAVE_cswi_finished +#define CachedSeq_WithIter_InitFinished(self) (void)((self)->cswi_finished = false) +#define CachedSeq_WithIter_GetFinished(self) ((self)->cswi_finished) +#define CachedSeq_WithIter_SetFinished(self) (void)((self)->cswi_finished = true) +#else /* CachedSeq_WithIter_HAVE_cswi_finished */ +#define CachedSeq_WithIter_InitFinished(self) (void)0 +#define CachedSeq_WithIter_GetFinished(self) ((self)->cswi_cache.ol_elema == (size_t)-1) +#define CachedSeq_WithIter_SetFinished(self) (void)((self)->cswi_cache.ol_elema = (size_t)-1) +#endif /* !CachedSeq_WithIter_HAVE_cswi_finished */ + +#define CachedSeq_WithIter_LockAvailable(self) Dee_atomic_lock_available(&(self)->cswi_lock) +#define CachedSeq_WithIter_LockAcquired(self) Dee_atomic_lock_acquired(&(self)->cswi_lock) +#define CachedSeq_WithIter_LockTryAcquire(self) Dee_atomic_lock_tryacquire(&(self)->cswi_lock) +#define CachedSeq_WithIter_LockAcquire(self) Dee_atomic_lock_acquire(&(self)->cswi_lock) +#define CachedSeq_WithIter_LockWaitFor(self) Dee_atomic_lock_waitfor(&(self)->cswi_lock) +#define CachedSeq_WithIter_LockRelease(self) Dee_atomic_lock_release(&(self)->cswi_lock) + + + +typedef struct { + PROXY_OBJECT_HEAD_EX(CachedSeq_WithIter, cswii_cache) /* [1..1][const] The cache that is being iterated. */ + size_t cswii_index; /* [lock(ATOMIC)] index of next element to yield. */ +} CachedSeq_WithIter_Iterator; + +INTDEF DeeTypeObject CachedSeq_WithIter_Iterator_Type; + +LOCAL WUNUSED NONNULL((1)) DREF CachedSeq_WithIter *DCALL +CachedSeq_WithIter_New(/*inherit(always)*/ DREF DeeObject *iter) { + DREF CachedSeq_WithIter *result; + result = DeeGCObject_MALLOC(CachedSeq_WithIter); + if unlikely(!result) + goto err; + result->cswi_iter = iter; /* Inherit reference */ + Dee_atomic_lock_init(&result->cswi_lock); + objectlist_init(&result->cswi_cache); + CachedSeq_WithIter_InitFinished(result); + DeeObject_Init(result, &CachedSeq_WithIter_Type); + return (DREF CachedSeq_WithIter *)DeeGC_Track((DREF DeeObject *)result); +err: + Dee_Decref_likely(iter); + return NULL; +} + + + + + + + +/************************************************************************/ +/* INDEX-BASED CACHE */ +/************************************************************************/ +typedef struct { + PROXY_OBJECT_HEAD(cswgi_seq) /* [1..1][const] The sequence being cached. */ + /* TODO */ +} CachedSeq_WithGetItem; + +INTDEF DeeTypeObject CachedSeq_WithGetItem_Type; /* Uses a lazily-allocated vector for small integers, and a dict for large ones, or non-integer keys */ +INTDEF DeeTypeObject CachedSeq_WithSizeObAndGetItem_Type; /* Like `CachedSeq_WithGetItem_Type', but also uses+caches `DeeSeq_OperatorSizeOb' */ +INTDEF DeeTypeObject CachedSeq_WithSizeAndGetItemIndex_Type; /* Like `CachedSeq_WithSizeObAndGetItem_Type', but uses+caches `DeeSeq_OperatorSize'. */ + +INTDEF DeeTypeObject CachedSeq_WithGetItem_Iterator_Type; +INTDEF DeeTypeObject CachedSeq_WithSizeObAndGetItem_Iterator_Type; +INTDEF DeeTypeObject CachedSeq_WithSizeAndGetItemIndex_Iterator_Type; + +DECL_END + +#endif /* !GUARD_DEEMON_OBJECTS_CACHED_SEQ_H */ diff --git a/src/deemon/objects/seq/default-api-methods-attrproxy-impl.c.inl b/src/deemon/objects/seq/default-api-methods-attrproxy-impl.c.inl index 6507f070c..15f8097e1 100644 --- a/src/deemon/objects/seq/default-api-methods-attrproxy-impl.c.inl +++ b/src/deemon/objects/seq/default-api-methods-attrproxy-impl.c.inl @@ -65,6 +65,7 @@ DECL_BEGIN #define LOCAL_DeeSeq_DefaultBoundLastWithCallAttrGetLast LOCAL_DeeSeq_DefaultFooWithCallAttrBar(BoundLast, GetLast) #define LOCAL_DeeSeq_DefaultDelLastWithCallAttrDelLast LOCAL_DeeSeq_DefaultFooWithCallAttrBar(DelLast, DelLast) #define LOCAL_DeeSeq_DefaultSetLastWithCallAttrSetLast LOCAL_DeeSeq_DefaultFooWithCallAttrBar(SetLast, SetLast) +#define LOCAL_DeeSeq_DefaultCachedWithCallAttrCached LOCAL_DeeSeq_DefaultFooWithCallAttrBar(Cached, Cached) #define LOCAL_DeeSeq_DefaultAnyWithCallAttrAny LOCAL_DeeSeq_DefaultFooWithCallAttrBar(Any, Any) #define LOCAL_DeeSeq_DefaultAnyWithKeyWithCallAttrAnyForSeq LOCAL_DeeSeq_DefaultFooWithCallAttrBar_(AnyWithKey, Any, ForSeq) #define LOCAL_DeeSeq_DefaultAnyWithKeyWithCallAttrAnyForSetOrMap LOCAL_DeeSeq_DefaultFooWithCallAttrBar_(AnyWithKey, Any, ForSetOrMap) @@ -1560,6 +1561,11 @@ LOCAL_DeeSeq_DefaultSetLastWithCallAttrSetLast(DeeObject *self, DeeObject *value return LOCAL_DeeObject_SetAttr(self, tsc_seq_dellast_data, &str_last, value); } +INTERN WUNUSED NONNULL((1)) DREF DeeObject *DCALL +LOCAL_DeeSeq_DefaultCachedWithCallAttrCached(DeeObject *__restrict self) { + return LOCAL_DeeObject_GetAttr(self, tsc_seq_cached_data, &str_cached); +} + INTERN WUNUSED NONNULL((1)) DREF DeeObject *DCALL LOCAL_DeeMap_DefaultKeysWithCallAttrKeys(DeeObject *self) { return LOCAL_DeeObject_GetAttr(self, tsc_map_keys_data, &str_keys); @@ -1596,6 +1602,7 @@ LOCAL_DeeMap_DefaultIterValuesWithCallAttrIterValues(DeeObject *self) { #undef LOCAL_DeeSeq_DefaultBoundLastWithCallAttrBoundLast #undef LOCAL_DeeSeq_DefaultDelLastWithCallAttrDelLast #undef LOCAL_DeeSeq_DefaultSetLastWithCallAttrSetLast +#undef LOCAL_DeeSeq_DefaultCachedWithCallAttrCached #undef LOCAL_DeeSeq_DefaultAnyWithCallAttrAny #undef LOCAL_DeeSeq_DefaultAnyWithKeyWithCallAttrAnyForSeq #undef LOCAL_DeeSeq_DefaultAnyWithKeyWithCallAttrAnyForSetOrMap diff --git a/src/deemon/objects/seq/default-api-methods-last.c.inl b/src/deemon/objects/seq/default-api-methods-last.c.inl index 6e96a3314..fde765554 100644 --- a/src/deemon/objects/seq/default-api-methods-last.c.inl +++ b/src/deemon/objects/seq/default-api-methods-last.c.inl @@ -386,4 +386,48 @@ DeeSeq_DefaultSetLastWithError(DeeObject *self, DeeObject *value) { return err_seq_unsupportedf(self, "last = %r", value); } + +INTERN WUNUSED NONNULL((1)) DREF DeeObject *DCALL +DeeSeq_DefaultCachedWithSeqIter(DeeObject *__restrict self) { + DREF CachedSeq_WithIter *result; + DREF DeeObject *iter; + iter = DeeSeq_OperatorIter(self); + if unlikely(!iter) + goto err; + result = CachedSeq_WithIter_New(iter); + return (DREF DeeObject *)result; +err: + return NULL; +} + +INTERN WUNUSED NONNULL((1)) DREF DeeObject *DCALL +DeeSeq_DefaultCachedWithSeqGetItem(DeeObject *__restrict self) { + /* TODO */ + (void)self; + DeeError_NOTIMPLEMENTED(); + return NULL; +} + +INTERN WUNUSED NONNULL((1)) DREF DeeObject *DCALL +DeeSeq_DefaultCachedWithSeqSizeObAndSeqGetItem(DeeObject *__restrict self) { + /* TODO */ + (void)self; + DeeError_NOTIMPLEMENTED(); + return NULL; +} + +INTERN WUNUSED NONNULL((1)) DREF DeeObject *DCALL +DeeSeq_DefaultCachedWithSeqSizeAndSeqGetItemIndex(DeeObject *__restrict self) { + /* TODO */ + (void)self; + DeeError_NOTIMPLEMENTED(); + return NULL; +} + +INTERN WUNUSED NONNULL((1)) DREF DeeObject *DCALL +DeeSeq_DefaultCachedWithError(DeeObject *__restrict self) { + err_seq_unsupportedf(self, "cached"); + return NULL; +} + DECL_END diff --git a/src/deemon/objects/seq/default-api-methods.c b/src/deemon/objects/seq/default-api-methods.c index d4b856753..169dec969 100644 --- a/src/deemon/objects/seq/default-api-methods.c +++ b/src/deemon/objects/seq/default-api-methods.c @@ -47,6 +47,7 @@ #include /**/ +#include "cached-seq.h" #include "default-iterators.h" #include "default-map-proxy.h" #include "default-reversed.h" @@ -6206,6 +6207,7 @@ print define_Dee_HashStr("end"); * to inject the most optimized version of getsets into top-level objects: * - Sequence.first * - Sequence.last + * - Sequence.cached * - Mapping.keys * - Mapping.values * - Mapping.iterkeys @@ -6214,6 +6216,7 @@ print define_Dee_HashStr("end"); #ifdef __OPTIMIZE_SIZE__ #define maybe_cache_optimized_seq_first_in_membercache(self) (void)0 #define maybe_cache_optimized_seq_last_in_membercache(self) (void)0 +#define maybe_cache_optimized_seq_cached_in_membercache(self) (void)0 #define maybe_cache_optimized_map_keys_in_membercache(self) (void)0 #define maybe_cache_optimized_map_values_in_membercache(self) (void)0 #define maybe_cache_optimized_map_iterkeys_in_membercache(self) (void)0 @@ -6249,6 +6252,20 @@ maybe_cache_optimized_seq_last_in_membercache(DeeTypeObject *__restrict self) { &new_getset, &gs_default_seq_last); } +PRIVATE struct type_getset tpconst gs_default_seq_cached = +TYPE_GETTER_NODOC(NULL, &default_seq_cached); +PRIVATE NONNULL((1)) void DCALL +maybe_cache_optimized_seq_cached_in_membercache(DeeTypeObject *__restrict self) { + struct type_getset new_getset; + new_getset.gs_name = STR_cached; + new_getset.gs_get = DeeType_RequireSeqCached(self); + new_getset.gs_del = NULL; + new_getset.gs_set = NULL; + new_getset.gs_bound = NULL; + DeeTypeMRO_PatchGetSet(self, &DeeSeq_Type, Dee_HashStr__cached, + &new_getset, &gs_default_seq_cached); +} + PRIVATE struct type_getset tpconst gs_default_map_keys = TYPE_GETTER_NODOC(NULL, &default_map_keys); PRIVATE NONNULL((1)) void DCALL @@ -6354,6 +6371,12 @@ default_seq_setlast(DeeObject *self, DeeObject *value) { return DeeSeq_InvokeSetLast(self, value); } +INTERN WUNUSED NONNULL((1)) DREF DeeObject * +(DCALL default_seq_cached)(DeeObject *__restrict self) { + maybe_cache_optimized_seq_cached_in_membercache(Dee_TYPE(self)); + return DeeSeq_InvokeCached(self); +} + PRIVATE ATTR_NOINLINE WUNUSED NONNULL((1)) DREF DeeObject *DCALL do_seq_enumerate_with_kw(DeeObject *self, size_t argc, DeeObject *const *argv, DeeObject *kw) { diff --git a/src/deemon/objects/seq/default-api-require-method-impl.c.inl b/src/deemon/objects/seq/default-api-require-method-impl.c.inl index 1a33ebd31..6f2260d02 100644 --- a/src/deemon/objects/seq/default-api-require-method-impl.c.inl +++ b/src/deemon/objects/seq/default-api-require-method-impl.c.inl @@ -29,6 +29,7 @@ //#define DEFINE_DeeType_RequireSeqBoundLast //#define DEFINE_DeeType_RequireSeqDelLast //#define DEFINE_DeeType_RequireSetLast +#define DEFINE_DeeType_RequireCached //#define DEFINE_DeeType_RequireSeqAny //#define DEFINE_DeeType_RequireSeqAnyWithKey //#define DEFINE_DeeType_RequireSeqAnyWithRange @@ -84,7 +85,7 @@ //#define DEFINE_DeeType_RequireSeqPushFront //#define DEFINE_DeeType_RequireSeqAppend //#define DEFINE_DeeType_RequireSeqExtend -#define DEFINE_DeeType_RequireSeqXchItemIndex +//#define DEFINE_DeeType_RequireSeqXchItemIndex //#define DEFINE_DeeType_RequireSeqClear //#define DEFINE_DeeType_RequireSeqPop //#define DEFINE_DeeType_RequireSeqRemove @@ -142,6 +143,7 @@ defined(DEFINE_DeeType_RequireSeqBoundLast) + \ defined(DEFINE_DeeType_RequireSeqDelLast) + \ defined(DEFINE_DeeType_RequireSetLast) + \ + defined(DEFINE_DeeType_RequireCached) + \ defined(DEFINE_DeeType_RequireSeqAny) + \ defined(DEFINE_DeeType_RequireSeqAnyWithKey) + \ defined(DEFINE_DeeType_RequireSeqAnyWithRange) + \ @@ -320,6 +322,13 @@ DECL_BEGIN #define LOCAL_IS_GETSET_SET #define LOCAL_default_seq_foo default_seq_setlast #define LOCAL_NO_TMH +#elif defined(DEFINE_DeeType_RequireCached) +#define LOCAL_foo cached +#define LOCAL_Foo Cached +#define LOCAL_IS_GETSET_GET +#define LOCAL_default_seq_foo default_seq_cached +#define LOCAL_NO_TMH +#define LOCAL_ATTR_REQUIRED_SEQCLASS Dee_SEQCLASS_SEQ #elif defined(DEFINE_DeeType_RequireSeqAny) #define LOCAL_foo any #define LOCAL_Foo Any @@ -1337,6 +1346,28 @@ LOCAL_DeeType_RequireSeqFoo_private_uncached(DeeTypeObject *orig_type, DeeTypeOb } } } +#elif defined(DEFINE_DeeType_RequireCached) + if (seqclass == Dee_SEQCLASS_SEQ) { + if (DeeType_HasPrivateOperator_in(orig_type, self, OPERATOR_GETITEM)) { + ASSERT(self->tp_seq); + ASSERT(self->tp_seq->tp_getitem); + ASSERT(self->tp_seq->tp_getitem_index); + if (self->tp_seq->tp_size && !DeeType_IsDefaultSize(self->tp_seq->tp_size)) + return &DeeSeq_DefaultCachedWithSeqSizeAndSeqGetItemIndex; + if (self->tp_seq->tp_sizeob && !DeeType_IsDefaultSizeOb(self->tp_seq->tp_sizeob)) + return &DeeSeq_DefaultCachedWithSeqSizeObAndSeqGetItem; + if ((self->tp_seq->tp_getitem && !DeeType_IsDefaultGetItem(self->tp_seq->tp_getitem)) || + (self->tp_seq->tp_getitem_index && !DeeType_IsDefaultGetItemIndex(self->tp_seq->tp_getitem_index))) + return &DeeSeq_DefaultCachedWithSeqGetItem; /* custom getitem. */ + if (DeeType_HasPrivateOperator_in(orig_type, self, OPERATOR_ITER)) + return &DeeSeq_DefaultCachedWithSeqIter; /* custom iter/foreach/etc. */ + if (DeeType_HasPrivateOperator_in(orig_type, self, OPERATOR_SIZE)) + return &DeeSeq_DefaultCachedWithSeqSizeObAndSeqGetItem; + return &DeeSeq_DefaultCachedWithSeqGetItem; + } + } + if (DeeType_HasPrivateOperator_in(orig_type, self, OPERATOR_ITER)) + return &DeeSeq_DefaultCachedWithSeqIter; #elif defined(DEFINE_DeeType_RequireSeqAny) /* ... */ #elif defined(DEFINE_DeeType_RequireSeqAnyWithKey) @@ -2508,6 +2539,7 @@ DECL_END #undef DEFINE_DeeType_RequireSeqBoundLast #undef DEFINE_DeeType_RequireSeqDelLast #undef DEFINE_DeeType_RequireSetLast +#undef DEFINE_DeeType_RequireCached #undef DEFINE_DeeType_RequireSeqAny #undef DEFINE_DeeType_RequireSeqAnyWithKey #undef DEFINE_DeeType_RequireSeqAnyWithRange diff --git a/src/deemon/objects/seq/default-api.c b/src/deemon/objects/seq/default-api.c index 29a5f470d..41b9a2180 100644 --- a/src/deemon/objects/seq/default-api.c +++ b/src/deemon/objects/seq/default-api.c @@ -394,6 +394,8 @@ Dee_type_seq_cache_destroy(struct Dee_type_seq_cache *__restrict self) { Dee_tsc_uslot_fini_function(&self->tsc_seq_dellast_data); if (self->tsc_seq_setlast == &DeeSeq_DefaultSetLastWithCallSetLastDataFunction) Dee_tsc_uslot_fini_function(&self->tsc_seq_setlast_data); + if (self->tsc_seq_cached == &DeeSeq_DefaultCachedWithCallCachedDataFunction) + Dee_tsc_uslot_fini_function(&self->tsc_seq_cached_data); if (self->tsc_seq_any == &DeeSeq_DefaultAnyWithCallAnyDataFunction || self->tsc_seq_any_with_key == &DeeSeq_DefaultAnyWithKeyWithCallAnyDataFunctionForSeq || self->tsc_seq_any_with_key == &DeeSeq_DefaultAnyWithKeyWithCallAnyDataFunctionForSetOrMap || @@ -1117,6 +1119,8 @@ DECL_END #include "default-api-require-method-impl.c.inl" #define DEFINE_DeeType_RequireSetLast #include "default-api-require-method-impl.c.inl" +#define DEFINE_DeeType_RequireCached +#include "default-api-require-method-impl.c.inl" #define DEFINE_DeeType_RequireSeqAny #include "default-api-require-method-impl.c.inl" #define DEFINE_DeeType_RequireSeqAnyWithKey diff --git a/src/deemon/objects/seq/default-api.h b/src/deemon/objects/seq/default-api.h index caa9bdbe2..6fdd60385 100644 --- a/src/deemon/objects/seq/default-api.h +++ b/src/deemon/objects/seq/default-api.h @@ -68,6 +68,9 @@ typedef WUNUSED_T NONNULL_T((1)) int (DCALL *Dee_mh_seq_boundlast_t)(DeeObject * typedef WUNUSED_T NONNULL_T((1)) int (DCALL *Dee_mh_seq_dellast_t)(DeeObject *__restrict self); typedef WUNUSED_T NONNULL_T((1, 2)) int (DCALL *Dee_mh_seq_setlast_t)(DeeObject *self, DeeObject *value); +/* Returns an auto-caching variant of the sequence (s.a. "cached-seq.h") */ +typedef WUNUSED_T NONNULL_T((1)) DREF DeeObject *(DCALL *Dee_mh_seq_cached_t)(DeeObject *__restrict self); + typedef WUNUSED_T NONNULL_T((1)) DREF DeeObject *(DCALL *Dee_mh_map_keys_t)(DeeObject *__restrict self); typedef WUNUSED_T NONNULL_T((1)) DREF DeeObject *(DCALL *Dee_mh_map_values_t)(DeeObject *__restrict self); typedef WUNUSED_T NONNULL_T((1)) DREF DeeObject *(DCALL *Dee_mh_map_iterkeys_t)(DeeObject *__restrict self); @@ -96,6 +99,10 @@ struct Dee_type_seq_cache { Dee_mh_seq_dellast_t tsc_seq_dellast; Dee_mh_seq_setlast_t tsc_seq_setlast; + /* Extra callbacks for Sequence.cached */ + Dee_mh_seq_cached_t tsc_seq_cached; + + /* Extra callbacks for Mapping.keys/values/iterkeys/itervalues */ Dee_mh_map_keys_t tsc_map_keys; Dee_mh_map_values_t tsc_map_values; Dee_mh_map_iterkeys_t tsc_map_iterkeys; @@ -107,6 +114,7 @@ struct Dee_type_seq_cache { union Dee_tsc_uslot tsc_seq_getlast_data; union Dee_tsc_uslot tsc_seq_dellast_data; union Dee_tsc_uslot tsc_seq_setlast_data; + union Dee_tsc_uslot tsc_seq_cached_data; union Dee_tsc_uslot tsc_seq_any_data; union Dee_tsc_uslot tsc_seq_all_data; union Dee_tsc_uslot tsc_seq_parity_data; @@ -188,6 +196,7 @@ INTDEF ATTR_PURE ATTR_RETNONNULL WUNUSED NONNULL((1)) Dee_mh_seq_getlast_t DCALL INTDEF ATTR_PURE ATTR_RETNONNULL WUNUSED NONNULL((1)) Dee_mh_seq_boundlast_t DCALL DeeType_RequireSeqBoundLast(DeeTypeObject *__restrict self); INTDEF ATTR_PURE ATTR_RETNONNULL WUNUSED NONNULL((1)) Dee_mh_seq_dellast_t DCALL DeeType_RequireSeqDelLast(DeeTypeObject *__restrict self); INTDEF ATTR_PURE ATTR_RETNONNULL WUNUSED NONNULL((1)) Dee_mh_seq_setlast_t DCALL DeeType_RequireSeqSetLast(DeeTypeObject *__restrict self); +INTDEF ATTR_PURE ATTR_RETNONNULL WUNUSED NONNULL((1)) Dee_mh_seq_cached_t DCALL DeeType_RequireSeqCached(DeeTypeObject *__restrict self); INTDEF ATTR_PURE ATTR_RETNONNULL WUNUSED NONNULL((1)) Dee_mh_map_keys_t DCALL DeeType_RequireMapKeys(DeeTypeObject *__restrict self); INTDEF ATTR_PURE ATTR_RETNONNULL WUNUSED NONNULL((1)) Dee_mh_map_values_t DCALL DeeType_RequireMapValues(DeeTypeObject *__restrict self); @@ -687,6 +696,7 @@ INTDEF struct type_math DeeMap_OperatorMath; #define DeeSeq_InvokeBoundLast(self) (*DeeType_RequireSeqBoundLast(Dee_TYPE(self)))(self) #define DeeSeq_InvokeDelLast(self) (*DeeType_RequireSeqDelLast(Dee_TYPE(self)))(self) #define DeeSeq_InvokeSetLast(self, v) (*DeeType_RequireSeqSetLast(Dee_TYPE(self)))(self, v) +#define DeeSeq_InvokeCached(self) (*DeeType_RequireSeqCached(Dee_TYPE(self)))(self) #define DeeSeq_InvokeAny(self) (*DeeType_RequireSeqAny(Dee_TYPE(self)))(self) #define DeeSeq_InvokeAnyWithKey(self, key) (*DeeType_RequireSeqAnyWithKey(Dee_TYPE(self)))(self, key) #define DeeSeq_InvokeAnyWithRange(self, start, end) (*DeeType_RequireSeqAnyWithRange(Dee_TYPE(self)))(self, start, end) @@ -1299,6 +1309,15 @@ INTDEF WUNUSED NONNULL((1, 2)) int DCALL DeeSeq_DefaultSetLastWithSizeAndSetItem INTDEF WUNUSED NONNULL((1, 2)) int DCALL DeeSeq_DefaultSetLastWithSizeAndSetItem(DeeObject *self, DeeObject *value); INTDEF WUNUSED NONNULL((1, 2)) int DCALL DeeSeq_DefaultSetLastWithError(DeeObject *self, DeeObject *value); +/* Implementations for `Sequence.cached' */ +INTDEF WUNUSED NONNULL((1)) DREF DeeObject *DCALL DeeSeq_DefaultCachedWithCallAttrCached(DeeObject *__restrict self); +INTDEF WUNUSED NONNULL((1)) DREF DeeObject *DCALL DeeSeq_DefaultCachedWithCallCachedDataFunction(DeeObject *__restrict self); +INTDEF WUNUSED NONNULL((1)) DREF DeeObject *DCALL DeeSeq_DefaultCachedWithSeqIter(DeeObject *__restrict self); +INTDEF WUNUSED NONNULL((1)) DREF DeeObject *DCALL DeeSeq_DefaultCachedWithSeqGetItem(DeeObject *__restrict self); +INTDEF WUNUSED NONNULL((1)) DREF DeeObject *DCALL DeeSeq_DefaultCachedWithSeqSizeObAndSeqGetItem(DeeObject *__restrict self); +INTDEF WUNUSED NONNULL((1)) DREF DeeObject *DCALL DeeSeq_DefaultCachedWithSeqSizeAndSeqGetItemIndex(DeeObject *__restrict self); +INTDEF WUNUSED NONNULL((1)) DREF DeeObject *DCALL DeeSeq_DefaultCachedWithError(DeeObject *__restrict self); + /* Functions that need additional variants for sequence sub-types that don't have indices (sets, maps) */ INTDEF WUNUSED NONNULL((1)) int DCALL DeeSeq_DefaultAnyWithCallAttrAny(DeeObject *self); @@ -2081,10 +2100,12 @@ INTDEF WUNUSED NONNULL((1)) DREF DeeObject *DCALL default_seq_getlast(DeeObject INTDEF WUNUSED NONNULL((1)) int DCALL default_seq_boundlast(DeeObject *__restrict self); INTDEF WUNUSED NONNULL((1)) int DCALL default_seq_dellast(DeeObject *__restrict self); INTDEF WUNUSED NONNULL((1, 2)) int DCALL default_seq_setlast(DeeObject *self, DeeObject *value); +INTDEF WUNUSED NONNULL((1)) DREF DeeObject *(DCALL default_seq_cached)(DeeObject *__restrict self); INTDEF WUNUSED NONNULL((1)) DREF DeeObject *(DCALL default_map_keys)(DeeObject *self); INTDEF WUNUSED NONNULL((1)) DREF DeeObject *(DCALL default_map_values)(DeeObject *self); INTDEF WUNUSED NONNULL((1)) DREF DeeObject *(DCALL default_map_iterkeys)(DeeObject *self); INTDEF WUNUSED NONNULL((1)) DREF DeeObject *(DCALL default_map_itervalues)(DeeObject *self); +#define default_seq_cached(self) DeeSeq_InvokeCached(self) #define default_map_keys(self) DeeMap_InvokeKeys(self) #define default_map_values(self) DeeMap_InvokeValues(self) #define default_map_iterkeys(self) DeeMap_InvokeIterKeys(self) diff --git a/src/deemon/objects/seq/flat.c b/src/deemon/objects/seq/flat.c index e7462d8f8..fdbf8a9a5 100644 --- a/src/deemon/objects/seq/flat.c +++ b/src/deemon/objects/seq/flat.c @@ -154,9 +154,9 @@ PRIVATE WUNUSED NONNULL((2)) Dee_ssize_t DCALL sf_bool_foreach_cb(void *UNUSED(arg), DeeObject *item) { int result = DeeSeq_OperatorBool(item); if (result > 0) - result = SF_BOOL_FOREACH_YES; - ASSERT(result == 0 || result == -1 || result == SF_BOOL_FOREACH_YES); - return result; + result = (int)SF_BOOL_FOREACH_YES; + ASSERT(result == 0 || result == -1 || result == (int)SF_BOOL_FOREACH_YES); + return (Dee_ssize_t)result; } PRIVATE WUNUSED NONNULL((1)) int DCALL @@ -226,9 +226,9 @@ sf_contains_foreach_cb(void *arg, DeeObject *item) { goto err; result = DeeObject_BoolInherited(contains_ob); if (result > 0) - result = SF_CONTAINS_FOREACH_YES; - ASSERT(result == 0 || result == -1 || result == SF_CONTAINS_FOREACH_YES); - return result; + result = (int)SF_CONTAINS_FOREACH_YES; + ASSERT(result == 0 || result == -1 || result == (int)SF_CONTAINS_FOREACH_YES); + return (Dee_ssize_t)result; err: return -1; } @@ -840,7 +840,7 @@ INTERN DeeTypeObject SeqFlat_Type = { OBJECT_HEAD_INIT(&DeeType_Type), /* .tp_name = */ "_SeqFlat", /* .tp_doc = */ DOC("()\n" - "(seq:??S?S?O)"), + "(seq:?S?S?O)"), /* .tp_flags = */ TP_FNORMAL | TP_FFINAL, /* .tp_weakrefs = */ 0, /* .tp_features = */ TF_NONE, diff --git a/src/deemon/objects/tuple.c b/src/deemon/objects/tuple.c index fcc61573d..ebab6522e 100644 --- a/src/deemon/objects/tuple.c +++ b/src/deemon/objects/tuple.c @@ -1855,6 +1855,7 @@ PRIVATE struct type_getset tpconst tuple_getsets[] = { TYPE_GETTER_F_NODOC(STR_last, &tuple_last, METHOD_FNOREFESCAPE | METHOD_FCONSTCALL), #define nullable_tuple_getsets (tuple_getsets + 2) TYPE_GETTER_F(STR_frozen, &DeeObject_NewRef, METHOD_FCONSTCALL, "->?."), + TYPE_GETTER_F(STR_cached, &DeeObject_NewRef, METHOD_FCONSTCALL, "->?."), TYPE_GETTER_F("__sizeof__", &tuple_sizeof, METHOD_FNOREFESCAPE, "->?Dint"), TYPE_GETSET_END }; diff --git a/src/deemon/runtime/strings.h b/src/deemon/runtime/strings.h index e66d7f21b..ca4f82433 100644 --- a/src/deemon/runtime/strings.h +++ b/src/deemon/runtime/strings.h @@ -188,6 +188,7 @@ local STRINGS = { "__module__", "first", "last", + "cached", "size", "filename", @@ -685,6 +686,9 @@ DEF_STRING(str_first, "first", 0xa9f0e818, 0x9d12a485470a29a7) #define Dee_HashStr__last _Dee_HashSelectC(0x185a4f9a, 0x760894ca6d41e4dc) DEF_STRING(str_last, "last", 0x185a4f9a, 0x760894ca6d41e4dc) #define STR_last DeeString_STR(&str_last) +#define Dee_HashStr__cached _Dee_HashSelectC(0x915e175e, 0xddfd408a14eae4b4) +DEF_STRING(str_cached, "cached", 0x915e175e, 0xddfd408a14eae4b4) +#define STR_cached DeeString_STR(&str_cached) #define Dee_HashStr__size _Dee_HashSelectC(0xed8917fa, 0x3fe8023bdf261c0f) DEF_STRING(str_size, "size", 0xed8917fa, 0x3fe8023bdf261c0f) #define STR_size DeeString_STR(&str_size) diff --git a/src/dex/collections/bitset.c b/src/dex/collections/bitset.c index 1d7e25ab0..789262ee0 100644 --- a/src/dex/collections/bitset.c +++ b/src/dex/collections/bitset.c @@ -1789,6 +1789,7 @@ PRIVATE struct type_method tpconst bs_methods[] = { PRIVATE struct type_getset tpconst bs_getsets[] = { TYPE_GETTER_F("frozen", &bs_frozen, METHOD_FNOREFESCAPE, "->?#Frozen"), + TYPE_GETTER("cached", &DeeObject_NewRef, "->?."), TYPE_GETTER_F("__sizeof__", &bs_sizeof, METHOD_FCONSTCALL | METHOD_FNOREFESCAPE, "->?Dint"), TYPE_GETSET_END }; @@ -2358,6 +2359,7 @@ PRIVATE struct type_method tpconst robs_methods[] = { PRIVATE struct type_getset tpconst robs_getsets[] = { TYPE_GETTER_F("frozen", &DeeObject_NewRef, METHOD_FCONSTCALL, "->?Dint"), + TYPE_GETTER("cached", &DeeObject_NewRef, "->?."), TYPE_GETTER_F("__sizeof__", &robs_sizeof, METHOD_FCONSTCALL | METHOD_FNOREFESCAPE, "->?Dint"), TYPE_GETSET_END }; @@ -4070,6 +4072,7 @@ PRIVATE struct type_getset tpconst bsv_getsets[] = { /**/ "read-only, and the underlying object is also read-only, or a @this view " /**/ "wrapped as a ?AFrozen?GBitset when the underlying bits may be modified " /**/ "by something"), + TYPE_GETTER("cached", &DeeObject_NewRef, "->?."), TYPE_GETTER_F("nbits", &bsv_nbits, METHOD_FNOREFESCAPE | METHOD_FCONSTCALL, "->?Dint\n" "The # of bits stored in this bitset. Attempting to alter the state of " diff --git a/src/dex/collections/deque.c b/src/dex/collections/deque.c index 3a22de36c..fff967123 100644 --- a/src/dex/collections/deque.c +++ b/src/dex/collections/deque.c @@ -1355,6 +1355,7 @@ PRIVATE struct type_method_hint tpconst deq_method_hints[] = { PRIVATE struct type_getset tpconst deq_getsets[] = { TYPE_GETTER_F("__sizeof__", &deq_sizeof, METHOD_FNOREFESCAPE, "->?Dint"), + TYPE_GETTER("cached", &DeeObject_NewRef, "->?."), TYPE_GETSET_END }; diff --git a/src/dex/collections/fixedlist.c b/src/dex/collections/fixedlist.c index c38c9df9f..a9a7e1c8a 100644 --- a/src/dex/collections/fixedlist.c +++ b/src/dex/collections/fixedlist.c @@ -1357,6 +1357,7 @@ fl_sizeof(FixedList *self) { PRIVATE struct type_getset tpconst fl_getsets[] = { TYPE_GETTER_F("__sizeof__", &fl_sizeof, METHOD_FNOREFESCAPE, "->?Dint"), + TYPE_GETTER("cached", &DeeObject_NewRef, "->?."), TYPE_GETSET_END }; diff --git a/src/dex/collections/rbtree.c b/src/dex/collections/rbtree.c index 9d247c05c..56703fee3 100644 --- a/src/dex/collections/rbtree.c +++ b/src/dex/collections/rbtree.c @@ -3620,22 +3620,23 @@ PRIVATE struct type_method tpconst rbtree_methods[] = { PRIVATE struct type_getset tpconst rbtree_getsets[] = { TYPE_GETTER_F("first", &rbtree_get_first, METHOD_FNOREFESCAPE, - "->?T3?O?O?O\n" - DOC_ERROR_ValueError_EMPTY_SEQUENCE - "Return the triple #C{minkey,maxkey,value} for the lowest range in @this ?."), + "->?T3?O?O?O\n" + DOC_ERROR_ValueError_EMPTY_SEQUENCE + "Return the triple #C{minkey,maxkey,value} for the lowest range in @this ?."), TYPE_GETTER_F("last", &rbtree_get_last, METHOD_FNOREFESCAPE, - "->?T3?O?O?O\n" - DOC_ERROR_ValueError_EMPTY_SEQUENCE - "Return the triple #C{minkey,maxkey,value} for the greatest range in @this ?."), + "->?T3?O?O?O\n" + DOC_ERROR_ValueError_EMPTY_SEQUENCE + "Return the triple #C{minkey,maxkey,value} for the greatest range in @this ?."), TYPE_GETTER_F("__root__", &rbtree_get_itroot, METHOD_FNOREFESCAPE, - "->?#Iterator\n" - DOC_ERROR_ValueError_EMPTY_SEQUENCE - "Return an ?#Iterator for the root node of the tree"), + "->?#Iterator\n" + DOC_ERROR_ValueError_EMPTY_SEQUENCE + "Return an ?#Iterator for the root node of the tree"), TYPE_GETTER_F("__depth__", &rbtree_get_depth, METHOD_FNOREFESCAPE, - "->?Dint\n" - "Depth of the left-most tree node (since the tree is balanced, " - /**/ "this is either the tree's max-depth, or one less than that)"), + "->?Dint\n" + "Depth of the left-most tree node (since the tree is balanced, " + /**/ "this is either the tree's max-depth, or one less than that)"), TYPE_GETTER_F("__sizeof__", &rbtree_sizeof, METHOD_FNOREFESCAPE, "->?Dint"), + TYPE_GETTER("cached", &DeeObject_NewRef, "->?."), TYPE_GETSET_END }; diff --git a/src/dex/collections/udict.c b/src/dex/collections/udict.c index cd20e8df6..a91ad1565 100644 --- a/src/dex/collections/udict.c +++ b/src/dex/collections/udict.c @@ -1282,6 +1282,7 @@ PRIVATE struct type_getset tpconst udict_getsets[] = { TYPE_GETTER_F("frozen", &URoDict_FromUDict, METHOD_FNOREFESCAPE, "->?AFrozen?.\n" "Returns a read-only (frozen) copy of @this dict"), + TYPE_GETTER("cached", &DeeObject_NewRef, "->?."), TYPE_GETTER_F("__sizeof__", &udict_sizeof, METHOD_FNOREFESCAPE, "->?Dint"), TYPE_GETSET_END }; @@ -2084,6 +2085,7 @@ urodict_sizeof(URoDict *self) { PRIVATE struct type_getset tpconst urodict_getsets[] = { TYPE_GETTER("frozen", &DeeObject_NewRef, "->?."), + TYPE_GETTER("cached", &DeeObject_NewRef, "->?."), TYPE_GETTER_F("__sizeof__", &urodict_sizeof, METHOD_FNOREFESCAPE, "->?Dint"), TYPE_GETSET_END }; diff --git a/src/dex/collections/uset.c b/src/dex/collections/uset.c index d30d9633e..792c2f084 100644 --- a/src/dex/collections/uset.c +++ b/src/dex/collections/uset.c @@ -706,6 +706,7 @@ PRIVATE struct type_getset tpconst uset_getsets[] = { TYPE_GETTER_F("frozen", &URoSet_FromUSet, METHOD_FNOREFESCAPE, "->?AFrozen?.\n" "Returns a read-only (frozen) copy of @this set"), + TYPE_GETTER("cached", &DeeObject_NewRef, "->?."), TYPE_GETTER_F("__sizeof__", &uset_sizeof, METHOD_FNOREFESCAPE, "->?Dint"), TYPE_GETSET_END }; @@ -1983,9 +1984,8 @@ uroset_sizeof(URoSet *self) { } PRIVATE struct type_getset tpconst uroset_getsets[] = { - TYPE_GETTER("frozen", &DeeObject_NewRef, - "->?AFrozen?.\n" - "Simply re-return @this object"), + TYPE_GETTER("frozen", &DeeObject_NewRef, "->?."), + TYPE_GETTER("cached", &DeeObject_NewRef, "->?."), TYPE_GETTER_F("__sizeof__", &uroset_sizeof, METHOD_FNOREFESCAPE, "->?Dint"), TYPE_GETSET_END }; diff --git a/util/test/deemon-sequence-cached.dee b/util/test/deemon-sequence-cached.dee new file mode 100644 index 000000000..5eb47ff27 --- /dev/null +++ b/util/test/deemon-sequence-cached.dee @@ -0,0 +1,68 @@ +#!/usr/bin/deemon +/* Copyright (c) 2018-2024 Griefer@Work * + * * + * This software is provided 'as-is', without any express or implied * + * warranty. In no event will the authors be held liable for any damages * + * arising from the use of this software. * + * * + * Permission is granted to anyone to use this software for any purpose, * + * including commercial applications, and to alter it and redistribute it * + * freely, subject to the following restrictions: * + * * + * 1. The origin of this software must not be misrepresented; you must not * + * claim that you wrote the original software. If you use this software * + * in a product, an acknowledgement (see the following) in the product * + * documentation is required: * + * Portions Copyright (c) 2018-2024 Griefer@Work * + * 2. Altered source versions must be plainly marked as such, and must not be * + * misrepresented as being the original software. * + * 3. This notice may not be removed or altered from any source distribution. * + */ + +import * from deemon; +import * from errors; + +global hits = []; + +function producer() { + hits.append(1); + yield 10; + hits.append(2); + yield 20; + hits.append(3); + yield 30; + hits.append(4); + yield 40; + hits.append(5); +} + +local items = producer().cached; +assert hits == {}; + +assert items[0] == 10; +assert items[0] == 10; +assert hits == {1}; + +assert items[2] == 30; +assert items[2] == 30; +assert hits == {1,2,3}; + +assert items[1] == 20; +assert items[1] == 20; +assert hits == {1,2,3}; + +assert items[3] == 40; +assert items[3] == 40; +assert hits == {1,2,3,4}; + +assert #items == 4; +assert #items == 4; +assert hits == {1,2,3,4,5}; + +assert (try items[4] catch (e...) e) is IndexError; +assert (try items[4] catch (e...) e) is IndexError; +assert hits == {1,2,3,4,5}; + +assert items == {10,20,30,40}; +assert items == {10,20,30,40}; +assert hits == {1,2,3,4,5};