mirror of
https://github.com/ccxvii/mujs.git
synced 2026-09-21 14:43:34 +08:00
Simplify Array.prototype.sort by sorting in place using quicksort.
Don't use libc qsort (which uses malloc on gnu libc) which can leak memory if the callback throws an exception.
This commit is contained in:
@@ -233,49 +233,154 @@ static void Ap_slice(js_State *J)
|
||||
js_setindex(J, -2, n);
|
||||
}
|
||||
|
||||
struct sortslot {
|
||||
js_Value v;
|
||||
js_State *J;
|
||||
};
|
||||
|
||||
static int sortcmp(const void *avoid, const void *bvoid)
|
||||
static int Ap_sort_cmp(js_State *J, int idx_a, int idx_b)
|
||||
{
|
||||
const struct sortslot *aslot = avoid, *bslot = bvoid;
|
||||
const js_Value *a = &aslot->v, *b = &bslot->v;
|
||||
js_State *J = aslot->J;
|
||||
const char *sx, *sy;
|
||||
double v;
|
||||
int c;
|
||||
|
||||
int unx = (a->t.type == JS_TUNDEFINED);
|
||||
int uny = (b->t.type == JS_TUNDEFINED);
|
||||
if (unx) return !uny;
|
||||
if (uny) return -1;
|
||||
|
||||
if (js_iscallable(J, 1)) {
|
||||
js_copy(J, 1); /* copy function */
|
||||
js_pushundefined(J);
|
||||
js_pushvalue(J, *a);
|
||||
js_pushvalue(J, *b);
|
||||
js_call(J, 2);
|
||||
v = js_tonumber(J, -1);
|
||||
c = (v == 0) ? 0 : (v < 0) ? -1 : 1;
|
||||
js_pop(J, 1);
|
||||
js_Object *obj = js_tovalue(J, 0)->u.object;
|
||||
if (obj->u.a.simple) {
|
||||
js_Value *val_a = &obj->u.a.array[idx_a];
|
||||
js_Value *val_b = &obj->u.a.array[idx_b];
|
||||
int und_a = val_a->t.type == JS_TUNDEFINED;
|
||||
int und_b = val_b->t.type == JS_TUNDEFINED;
|
||||
if (und_a) return und_b;
|
||||
if (und_b) return -1;
|
||||
if (js_iscallable(J, 1)) {
|
||||
double v;
|
||||
js_copy(J, 1); /* copy function */
|
||||
js_pushundefined(J); /* no 'this' binding */
|
||||
js_pushvalue(J, *val_a);
|
||||
js_pushvalue(J, *val_b);
|
||||
js_call(J, 2);
|
||||
v = js_tonumber(J, -1);
|
||||
js_pop(J, 1);
|
||||
if (isnan(v))
|
||||
return 0;
|
||||
if (v == 0)
|
||||
return 0;
|
||||
return v < 0 ? -1 : 1;
|
||||
} else {
|
||||
const char *str_a, *str_b;
|
||||
int c;
|
||||
js_pushvalue(J, *val_a);
|
||||
js_pushvalue(J, *val_b);
|
||||
str_a = js_tostring(J, -2);
|
||||
str_b = js_tostring(J, -1);
|
||||
c = strcmp(str_a, str_b);
|
||||
js_pop(J, 2);
|
||||
return c;
|
||||
}
|
||||
} else {
|
||||
js_pushvalue(J, *a);
|
||||
js_pushvalue(J, *b);
|
||||
sx = js_tostring(J, -2);
|
||||
sy = js_tostring(J, -1);
|
||||
c = strcmp(sx, sy);
|
||||
js_pop(J, 2);
|
||||
int und_a, und_b;
|
||||
int has_a = js_hasindex(J, 0, idx_a);
|
||||
int has_b = js_hasindex(J, 0, idx_b);
|
||||
if (!has_a && !has_b) {
|
||||
return 0;
|
||||
}
|
||||
if (has_a && !has_b) {
|
||||
js_pop(J, 1);
|
||||
return -1;
|
||||
}
|
||||
if (!has_a && has_b) {
|
||||
js_pop(J, 1);
|
||||
return 1;
|
||||
}
|
||||
|
||||
und_a = js_isundefined(J, -2);
|
||||
und_b = js_isundefined(J, -1);
|
||||
if (und_a) {
|
||||
js_pop(J, 2);
|
||||
return und_b;
|
||||
}
|
||||
if (und_b) {
|
||||
js_pop(J, 2);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (js_iscallable(J, 1)) {
|
||||
double v;
|
||||
js_copy(J, 1); /* copy function */
|
||||
js_pushundefined(J); /* no 'this' binding */
|
||||
js_copy(J, -4);
|
||||
js_copy(J, -4);
|
||||
js_call(J, 2);
|
||||
v = js_tonumber(J, -1);
|
||||
js_pop(J, 3);
|
||||
if (isnan(v))
|
||||
return 0;
|
||||
if (v == 0)
|
||||
return 0;
|
||||
return v < 0 ? -1 : 1;
|
||||
} else {
|
||||
const char *str_a = js_tostring(J, -2);
|
||||
const char *str_b = js_tostring(J, -1);
|
||||
int c = strcmp(str_a, str_b);
|
||||
js_pop(J, 2);
|
||||
return c;
|
||||
}
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
static void Ap_sort_swap(js_State *J, int idx_a, int idx_b)
|
||||
{
|
||||
js_Object *obj = js_tovalue(J, 0)->u.object;
|
||||
if (obj->u.a.simple) {
|
||||
js_Value tmp = obj->u.a.array[idx_a];
|
||||
obj->u.a.array[idx_a] = obj->u.a.array[idx_b];
|
||||
obj->u.a.array[idx_b] = tmp;
|
||||
} else {
|
||||
int has_a = js_hasindex(J, 0, idx_a);
|
||||
int has_b = js_hasindex(J, 0, idx_b);
|
||||
if (has_a && has_b) {
|
||||
js_setindex(J, 0, idx_a);
|
||||
js_setindex(J, 0, idx_b);
|
||||
} else if (has_a && !has_b) {
|
||||
js_delindex(J, 0, idx_a);
|
||||
js_setindex(J, 0, idx_b);
|
||||
} else if (!has_a && has_b) {
|
||||
js_delindex(J, 0, idx_b);
|
||||
js_setindex(J, 0, idx_a);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int Ap_sort_quicksort_partition(js_State *J, int a, int b)
|
||||
{
|
||||
int pivot = (a + b) >> 1;
|
||||
while (a <= b) {
|
||||
while (Ap_sort_cmp(J, a, pivot) < 0)
|
||||
++a;
|
||||
while (Ap_sort_cmp(J, b, pivot) > 0)
|
||||
--b;
|
||||
if (a <= b) {
|
||||
Ap_sort_swap(J, a, b);
|
||||
++a;
|
||||
--b;
|
||||
}
|
||||
}
|
||||
return a;
|
||||
}
|
||||
|
||||
static void Ap_sort_quicksort(js_State *J, int a, int b)
|
||||
{
|
||||
int i, j, m;
|
||||
|
||||
/* insertion sort small fragments */
|
||||
if (b - a < 10) {
|
||||
for (i = a+1; i < b; ++i)
|
||||
for (j = i; j > a && Ap_sort_cmp(J, j-1, j) > 0; --j)
|
||||
Ap_sort_swap(J, j-1, j);
|
||||
return;
|
||||
}
|
||||
|
||||
m = Ap_sort_quicksort_partition(J, a, b);
|
||||
if (a < m - 1)
|
||||
Ap_sort_quicksort(J, a, m - 1);
|
||||
if (m < b)
|
||||
Ap_sort_quicksort(J, m, b);
|
||||
}
|
||||
|
||||
static void Ap_sort(js_State *J)
|
||||
{
|
||||
struct sortslot * volatile array = NULL;
|
||||
int i, n, len;
|
||||
int len;
|
||||
|
||||
len = js_getlength(J, 0);
|
||||
if (len <= 0) {
|
||||
@@ -283,47 +388,18 @@ static void Ap_sort(js_State *J)
|
||||
return;
|
||||
}
|
||||
|
||||
if (len >= INT_MAX / (int)sizeof(*array))
|
||||
if (!js_iscallable(J, 1) && !js_isundefined(J, 1))
|
||||
js_typeerror(J, "comparison function must be a function or undefined");
|
||||
|
||||
if (len >= INT_MAX)
|
||||
js_rangeerror(J, "array is too large to sort");
|
||||
|
||||
/* Holding objects where the GC cannot see them is illegal, but if we
|
||||
* don't allow the GC to run we can use qsort() on a temporary array of
|
||||
* js_Values for fast sorting.
|
||||
*/
|
||||
++J->gcpause;
|
||||
|
||||
if (js_try(J)) {
|
||||
--J->gcpause;
|
||||
js_free(J, array);
|
||||
js_throw(J);
|
||||
if (len <= 1) {
|
||||
js_copy(J, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
array = js_malloc(J, len * sizeof *array);
|
||||
|
||||
n = 0;
|
||||
for (i = 0; i < len; ++i) {
|
||||
if (js_hasindex(J, 0, i)) {
|
||||
array[n].v = *js_tovalue(J, -1);
|
||||
array[n].J = J;
|
||||
js_pop(J, 1);
|
||||
++n;
|
||||
}
|
||||
}
|
||||
|
||||
qsort(array, n, sizeof *array, sortcmp);
|
||||
|
||||
for (i = 0; i < n; ++i) {
|
||||
js_pushvalue(J, array[i].v);
|
||||
js_setindex(J, 0, i);
|
||||
}
|
||||
for (i = len-i; i >= n; --i) {
|
||||
js_delindex(J, 0, i);
|
||||
}
|
||||
|
||||
--J->gcpause;
|
||||
|
||||
js_endtry(J);
|
||||
js_free(J, array);
|
||||
Ap_sort_quicksort(J, 0, len - 1);
|
||||
|
||||
js_copy(J, 0);
|
||||
}
|
||||
|
||||
@@ -147,12 +147,6 @@ void js_gc(js_State *J, int report)
|
||||
int mark;
|
||||
int i;
|
||||
|
||||
if (J->gcpause) {
|
||||
if (report)
|
||||
js_report(J, "garbage collector is paused");
|
||||
return;
|
||||
}
|
||||
|
||||
mark = J->gcmark = J->gcmark == 1 ? 2 : 1;
|
||||
|
||||
/* Add initial roots. */
|
||||
|
||||
Reference in New Issue
Block a user