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};