}
typedef enum JSIteratorHelperKindEnum {
+ JS_ITERATOR_HELPER_KIND_CHUNKS,
JS_ITERATOR_HELPER_KIND_DROP,
JS_ITERATOR_HELPER_KIND_EVERY,
JS_ITERATOR_HELPER_KIND_FILTER,
JS_ITERATOR_HELPER_KIND_MAP,
JS_ITERATOR_HELPER_KIND_SOME,
JS_ITERATOR_HELPER_KIND_TAKE,
+ JS_ITERATOR_HELPER_KIND_WINDOWS,
} JSIteratorHelperKindEnum;
typedef struct JSIteratorHelperData {
JSValue func; // predicate (filter) or mapper (flatMap, map)
JSValue inner; // innerValue (flatMap)
JSValue inner_next; // innerValue next method (flatMap)
+ JSValue *buf; // buffered values (chunks, windows)
int64_t count; // limit (drop, take) or counter (filter, map, flatMap)
+ int buf_count; // number of values in |buf|
+ int buf_size; // allocated size of |buf|
JSIteratorHelperKindEnum kind : 8;
uint8_t executing : 1;
uint8_t done : 1;
+ uint8_t allow_partial : 1; // yield a partial final window (windows)
} JSIteratorHelperData;
static JSValue js_create_iterator_helper(JSContext *ctx, JSValueConst this_val,
JSValue obj, method;
int64_t count;
JSIteratorHelperData *it;
+ bool allow_partial;
if (!JS_IsObject(this_val))
return JS_ThrowTypeErrorNotAnObject(ctx);
func = JS_UNDEFINED;
count = 0;
+ allow_partial = FALSE;
switch(magic) {
+ case JS_ITERATOR_HELPER_KIND_CHUNKS:
+ case JS_ITERATOR_HELPER_KIND_WINDOWS:
+ {
+ const char *name;
+ double d;
+ BOOL ok;
+
+ name = magic == JS_ITERATOR_HELPER_KIND_CHUNKS ? "chunkSize"
+ : "windowSize";
+ // note: no coercion, the argument must be an integral number
+ d = NAN;
+ if (JS_IsNumber(argv[0]) && JS_ToFloat64(ctx, &d, argv[0]))
+ goto fail;
+ if (!isfinite(d) || trunc(d) != d) {
+ JS_ThrowTypeError(ctx, "%s is not an integral number", name);
+ goto fail;
+ }
+ if (d < 1 || d > UINT32_MAX) {
+ JS_ThrowRangeError(ctx, "%s out of range", name);
+ goto fail;
+ }
+ count = (int64_t)d;
+ if (magic == JS_ITERATOR_HELPER_KIND_WINDOWS && argc > 1 &&
+ !JS_IsUndefined(argv[1])) {
+ ok = FALSE;
+ if (JS_IsString(argv[1])) {
+ const char *str = JS_ToCString(ctx, argv[1]);
+ if (!str)
+ goto fail;
+ allow_partial = !strcmp(str, "allow-partial");
+ ok = allow_partial || !strcmp(str, "only-full");
+ JS_FreeCString(ctx, str);
+ }
+ if (!ok) {
+ JS_ThrowTypeError(ctx, "bad undersized option");
+ goto fail;
+ }
+ }
+ }
+ break;
case JS_ITERATOR_HELPER_KIND_DROP:
case JS_ITERATOR_HELPER_KIND_TAKE:
{
it->next = method;
it->inner = JS_UNDEFINED;
it->inner_next = JS_UNDEFINED;
+ it->buf = NULL;
it->count = count;
+ it->buf_count = 0;
+ it->buf_size = 0;
it->executing = 0;
it->done = 0;
+ it->allow_partial = allow_partial;
JS_SetOpaque(obj, it);
return obj;
range_error:
JS_FreeValueRT(rt, it->next);
JS_FreeValueRT(rt, it->inner);
JS_FreeValueRT(rt, it->inner_next);
+ while (it->buf_count > 0)
+ JS_FreeValueRT(rt, it->buf[--it->buf_count]);
+ js_free_rt(rt, it->buf);
js_free_rt(rt, it);
}
}
JS_MarkValue(rt, it->next, mark_func);
JS_MarkValue(rt, it->inner, mark_func);
JS_MarkValue(rt, it->inner_next, mark_func);
+ for (int i = 0; i < it->buf_count; i++)
+ JS_MarkValue(rt, it->buf[i], mark_func);
}
}
}
if (JS_IteratorClose(ctx, it->obj, JS_IsException(ret)))
ret = JS_EXCEPTION;
+ it->done = TRUE;
goto done;
}
switch (it->kind) {
+ case JS_ITERATOR_HELPER_KIND_CHUNKS:
+ case JS_ITERATOR_HELPER_KIND_WINDOWS:
+ {
+ BOOL windows = it->kind == JS_ITERATOR_HELPER_KIND_WINDOWS;
+ JSValue item;
+
+ for (;;) {
+ item = JS_IteratorNext(ctx, it->obj, it->next, pdone);
+ if (JS_IsException(item))
+ goto fail_no_close;
+ if (*pdone) {
+ JS_FreeValue(ctx, item);
+ /* the underlying iterator is done: yield what's left in
+ the buffer, if anything, then stop */
+ it->done = TRUE;
+ ret = JS_UNDEFINED;
+ if (it->buf_count < 1)
+ break;
+ if (windows) {
+ /* a full window was already yielded */
+ if (!it->allow_partial || it->buf_count >= it->count)
+ break;
+ ret = js_create_array(ctx, it->buf_count, (JSValueConst *)it->buf);
+ } else {
+ ret = js_create_array_free(ctx, it->buf_count, it->buf);
+ it->buf_count = 0; // ownership transferred
+ }
+ if (JS_IsException(ret))
+ goto fail_no_close;
+ *pdone = FALSE;
+ goto done;
+ }
+ if (windows && it->buf_count == it->count) {
+ /* slide the window */
+ JS_FreeValue(ctx, it->buf[0]);
+ it->buf_count--;
+ memmove(it->buf, &it->buf[1],
+ it->buf_count * sizeof(*it->buf));
+ }
+ if (js_resize_array(ctx, (void **)&it->buf, sizeof(*it->buf),
+ &it->buf_size, it->buf_count + 1)) {
+ JS_FreeValue(ctx, item);
+ goto fail_no_close;
+ }
+ it->buf[it->buf_count++] = item;
+ if (it->buf_count == it->count) {
+ if (windows) {
+ ret = js_create_array(ctx, it->buf_count, (JSValueConst *)it->buf);
+ } else {
+ ret = js_create_array_free(ctx, it->buf_count, it->buf);
+ it->buf_count = 0; // ownership transferred
+ }
+ if (JS_IsException(ret))
+ goto fail_no_close;
+ break;
+ }
+ }
+ }
+ break;
case JS_ITERATOR_HELPER_KIND_DROP:
{
JSValue item;
JS_FreeValue(ctx, item);
if (*pdone) {
ret = JS_UNDEFINED;
- goto done;
+ goto done_drop;
}
}
if (JS_IsException(item))
goto fail_no_close;
ret = item;
+ done_drop: ;
}
break;
case JS_ITERATOR_HELPER_KIND_FILTER:
goto fail_no_close;
if (*pdone) {
ret = item;
- goto done;
+ break;
}
index_val = JS_NewInt64(ctx, it->count++);
args[0] = item;
abort();
}
- done:
it->done = *pdone;
+ done:
it->executing = 0;
+ if (it->done && it->buf) {
+ while (it->buf_count > 0)
+ JS_FreeValue(ctx, it->buf[--it->buf_count]);
+ js_free(ctx, it->buf);
+ it->buf = NULL;
+ it->buf_size = 0;
+ }
return ret;
fail:
/* close the iterator object, preserving pending exception */
JS_IteratorClose(ctx, it->obj, TRUE);
fail_no_close:
ret = JS_EXCEPTION;
+ it->done = TRUE;
goto done;
}
};
static const JSCFunctionListEntry js_iterator_proto_funcs[] = {
+ JS_CFUNC_MAGIC_DEF("chunks", 1, js_create_iterator_helper, JS_ITERATOR_HELPER_KIND_CHUNKS ),
JS_CFUNC_MAGIC_DEF("drop", 1, js_create_iterator_helper, JS_ITERATOR_HELPER_KIND_DROP ),
JS_CFUNC_MAGIC_DEF("filter", 1, js_create_iterator_helper, JS_ITERATOR_HELPER_KIND_FILTER ),
JS_CFUNC_MAGIC_DEF("flatMap", 1, js_create_iterator_helper, JS_ITERATOR_HELPER_KIND_FLAT_MAP ),
JS_CFUNC_MAGIC_DEF("map", 1, js_create_iterator_helper, JS_ITERATOR_HELPER_KIND_MAP ),
JS_CFUNC_MAGIC_DEF("take", 1, js_create_iterator_helper, JS_ITERATOR_HELPER_KIND_TAKE ),
+ JS_CFUNC_MAGIC_DEF("windows", 1, js_create_iterator_helper, JS_ITERATOR_HELPER_KIND_WINDOWS ),
JS_CFUNC_MAGIC_DEF("every", 1, js_iterator_proto_func, JS_ITERATOR_HELPER_KIND_EVERY ),
JS_CFUNC_MAGIC_DEF("find", 1, js_iterator_proto_func, JS_ITERATOR_HELPER_KIND_FIND),
JS_CFUNC_MAGIC_DEF("forEach", 1, js_iterator_proto_func, JS_ITERATOR_HELPER_KIND_FOR_EACH ),