#include <stdio.h>
#include "common/str_util.h"
#include "mjs_array.h"
#include "mjs_conversion.h"
#include "mjs_core.h"
#include "mjs_internal.h"
#include "mjs_object.h"
#include "mjs_primitive.h"
#include "mjs_string.h"
#include "mjs_util.h"
#define SPLICE_NEW_ITEM_IDX 2
static int v_sprintf_s(char *buf, size_t size, const char *fmt, ...) {
size_t n;
va_list ap;
va_start(ap, fmt);
n = c_vsnprintf(buf, size, fmt, ap);
if (n > size) {
return size;
}
return n;
}
mjs_val_t mjs_mk_array(struct mjs *mjs) {
mjs_val_t ret = mjs_mk_object(mjs);
ret &= ~MJS_TAG_MASK;
ret |= MJS_TAG_ARRAY;
return ret;
}
int mjs_is_array(mjs_val_t v) {
return (v & MJS_TAG_MASK) == MJS_TAG_ARRAY;
}
mjs_val_t mjs_array_get(struct mjs *mjs, mjs_val_t arr, unsigned long index) {
return mjs_array_get2(mjs, arr, index, NULL);
}
mjs_val_t mjs_array_get2(struct mjs *mjs, mjs_val_t arr, unsigned long index,
int *has) {
mjs_val_t res = MJS_UNDEFINED;
if (has != NULL) {
*has = 0;
}
if (mjs_is_object(arr)) {
struct mjs_property *p;
char buf[20];
int n = v_sprintf_s(buf, sizeof(buf), "%lu", index);
p = mjs_get_own_property(mjs, arr, buf, n);
if (p != NULL) {
if (has != NULL) {
*has = 1;
}
res = p->value;
}
}
return res;
}
unsigned long mjs_array_length(struct mjs *mjs, mjs_val_t v) {
struct mjs_property *p;
unsigned long len = 0;
if (!mjs_is_object(v)) {
len = 0;
goto clean;
}
for (p = get_object_struct(v)->properties; p != NULL; p = p->next) {
int ok = 0;
unsigned long n = 0;
str_to_ulong(mjs, p->name, &ok, &n);
if (ok && n >= len && n < 0xffffffff) {
len = n + 1;
}
}
clean:
return len;
}
mjs_err_t mjs_array_set(struct mjs *mjs, mjs_val_t arr, unsigned long index,
mjs_val_t v) {
mjs_err_t ret = MJS_OK;
if (mjs_is_object(arr)) {
char buf[20];
int n = v_sprintf_s(buf, sizeof(buf), "%lu", index);
ret = mjs_set(mjs, arr, buf, n, v);
} else {
ret = MJS_TYPE_ERROR;
}
return ret;
}
void mjs_array_del(struct mjs *mjs, mjs_val_t arr, unsigned long index) {
char buf[20];
int n = v_sprintf_s(buf, sizeof(buf), "%lu", index);
mjs_del(mjs, arr, buf, n);
}
mjs_err_t mjs_array_push(struct mjs *mjs, mjs_val_t arr, mjs_val_t v) {
return mjs_array_set(mjs, arr, mjs_array_length(mjs, arr), v);
}
MJS_PRIVATE void mjs_array_push_internal(struct mjs *mjs) {
mjs_err_t rcode = MJS_OK;
mjs_val_t ret = MJS_UNDEFINED;
int nargs = mjs_nargs(mjs);
int i;
if (!mjs_check_arg(mjs, -1 , "this", MJS_TYPE_OBJECT_ARRAY, NULL)) {
goto clean;
}
for (i = 0; i < nargs; i++) {
rcode = mjs_array_push(mjs, mjs->vals.this_obj, mjs_arg(mjs, i));
if (rcode != MJS_OK) {
mjs_prepend_errorf(mjs, rcode, "");
goto clean;
}
}
ret = mjs_mk_number(mjs, mjs_array_length(mjs, mjs->vals.this_obj));
clean:
mjs_return(mjs, ret);
return;
}
static void move_item(struct mjs *mjs, mjs_val_t arr, unsigned long from,
unsigned long to) {
mjs_val_t cur = mjs_array_get(mjs, arr, from);
mjs_array_set(mjs, arr, to, cur);
mjs_array_del(mjs, arr, from);
}
MJS_PRIVATE void mjs_array_splice(struct mjs *mjs) {
int nargs = mjs_nargs(mjs);
mjs_err_t rcode = MJS_OK;
mjs_val_t ret = mjs_mk_array(mjs);
mjs_val_t start_v = MJS_UNDEFINED;
mjs_val_t deleteCount_v = MJS_UNDEFINED;
int start = 0;
int arr_len;
int delete_cnt = 0;
int new_items_cnt = 0;
int delta = 0;
int i;
if (!mjs_check_arg(mjs, -1 , "this", MJS_TYPE_OBJECT_ARRAY, NULL)) {
goto clean;
}
arr_len = mjs_array_length(mjs, mjs->vals.this_obj);
if (!mjs_check_arg(mjs, 0, "start", MJS_TYPE_NUMBER, &start_v)) {
goto clean;
}
start = mjs_normalize_idx(mjs_get_int(mjs, start_v), arr_len);
if (nargs >= SPLICE_NEW_ITEM_IDX) {
if (!mjs_check_arg(mjs, 1, "deleteCount", MJS_TYPE_NUMBER,
&deleteCount_v)) {
goto clean;
}
delete_cnt = mjs_get_int(mjs, deleteCount_v);
new_items_cnt = nargs - SPLICE_NEW_ITEM_IDX;
} else {
delete_cnt = arr_len - start;
}
if (delete_cnt > arr_len - start) {
delete_cnt = arr_len - start;
} else if (delete_cnt < 0) {
delete_cnt = 0;
}
delta = new_items_cnt - delete_cnt;
for (i = 0; i < delete_cnt; i++) {
mjs_val_t cur = mjs_array_get(mjs, mjs->vals.this_obj, start + i);
rcode = mjs_array_push(mjs, ret, cur);
if (rcode != MJS_OK) {
mjs_prepend_errorf(mjs, rcode, "");
goto clean;
}
}
if (delta < 0) {
for (i = start; i < arr_len; i++) {
if (i >= start - delta) {
move_item(mjs, mjs->vals.this_obj, i, i + delta);
} else {
mjs_array_del(mjs, mjs->vals.this_obj, i);
}
}
} else if (delta > 0) {
for (i = arr_len - 1; i >= start; i--) {
move_item(mjs, mjs->vals.this_obj, i, i + delta);
}
}
for (i = 0; i < nargs - SPLICE_NEW_ITEM_IDX; i++) {
mjs_array_set(mjs, mjs->vals.this_obj, start + i,
mjs_arg(mjs, SPLICE_NEW_ITEM_IDX + i));
}
clean:
mjs_return(mjs, ret);
}