Milán Major

netscape-revival

1 branch
Code

mocha/src/mo_array.c

/*
** Mocha array class.
**
** Brendan Eich, 11/15/95
*/
#include <stdlib.h>
#include <string.h>
#include "prprf.h"
#include "mo_cntxt.h"
#include "mo_scope.h"
#include "mocha.h"
#include "mochaapi.h"
#include "mochalib.h"

enum array_slot {
    ARRAY_LENGTH = -1
};

static MochaPropertySpec array_props[] = {
    {"length",	ARRAY_LENGTH},
    {0}
};

static MochaBoolean
array_get_property(MochaContext *mc, MochaObject *obj, MochaSlot slot,
		   MochaDatum *dp)
{
    switch (slot) {
      case ARRAY_LENGTH:
	MOCHA_INIT_DATUM(mc, dp, MOCHA_NUMBER, u.fval, obj->scope->freeslot);
	break;
      default:;
    }
    return MOCHA_TRUE;
}

static MochaBoolean
array_set_property(MochaContext *mc, MochaObject *obj, MochaSlot slot,
		   MochaDatum *dp)
{
    MochaSlot newlen, oldlen;

    switch (slot) {
      case ARRAY_LENGTH:
	if (dp->tag != MOCHA_NUMBER &&
	    !MOCHA_ConvertDatum(mc, *dp, MOCHA_NUMBER, dp)) {
	    return MOCHA_FALSE;
	}
	newlen = (MochaSlot)dp->u.fval;
	if (dp->u.fval != (MochaFloat)newlen || newlen < 0) {
	    MOCHA_ReportError(mc, "illegal array length %g", dp->u.fval);
	    return MOCHA_FALSE;
	}
	oldlen = obj->scope->freeslot;
	for (slot = newlen; slot < oldlen; slot++)
	    MOCHA_RemoveSlot(mc, obj, slot);
	obj->scope->freeslot = newlen;
	break;

      default:;
    }
    return MOCHA_TRUE;
}

static MochaBoolean
array_convert(MochaContext *mc, MochaObject *obj, MochaTag tag, MochaDatum *dp)
{
    switch (tag) {
      case MOCHA_NUMBER:
	MOCHA_INIT_DATUM(mc, dp, MOCHA_NUMBER, u.fval, obj->scope->freeslot);
	return MOCHA_TRUE;
      case MOCHA_BOOLEAN:
	MOCHA_INIT_DATUM(mc, dp, MOCHA_BOOLEAN,
			 u.bval, obj->scope->freeslot != 0);
	return MOCHA_TRUE;
      default:
	return MOCHA_TRUE;
    }
}

static MochaClass array_class = {
    "Array",
    array_get_property, array_set_property, MOCHA_ListPropStub,
    MOCHA_ResolveStub, array_convert, MOCHA_FinalizeStub
};

static MochaBoolean
array_join_str(MochaContext *mc, MochaObject *obj, const char *separator,
	       MochaDatum *rval)
{
    char *last;
    MochaSlot slot;
    MochaDatum d;
    uint16 taint;
    MochaAtom *atom;

    last = 0;
    taint = MOCHA_TAINT_IDENTITY;
    for (slot = 0; slot < obj->scope->freeslot; slot++) {
	if (!MOCHA_GetSlot(mc, obj, slot, &d))
	    return MOCHA_FALSE;
	if (MOCHA_DATUM_IS_NULL(d)) {
	    atom = mocha_HoldAtom(mc, MOCHA_empty.u.atom);
	} else {
	    if (!mocha_RawDatumToString(mc, d, &atom))
		return MOCHA_FALSE;
	}
	last = PR_sprintf_append(last, "%s%s",
				 (slot == 0) ? "" : separator,
				 atom_name(atom));
	mocha_DropAtom(mc, atom);
	if (!last) {
	    MOCHA_ReportOutOfMemory(mc);
	    return MOCHA_FALSE;
	}
	MOCHA_MIX_TAINT(mc, taint, d.taint);
    }
    if (!last) {
	*rval = MOCHA_empty;
	return MOCHA_TRUE;
    }
    atom = mocha_Atomize(mc, last, ATOM_STRING);
    free(last);
    if (!atom)
	return MOCHA_FALSE;
    MOCHA_INIT_FULL_DATUM(mc, rval, MOCHA_STRING, 0, taint, u.atom, atom);
    return MOCHA_TRUE;
}

static MochaBoolean
array_to_string(MochaContext *mc, MochaObject *obj,
		unsigned argc, MochaDatum *argv, MochaDatum *rval)
{
    return array_join_str(mc, obj, ",", rval);
}

static MochaBoolean
array_join(MochaContext *mc, MochaObject *obj,
	   unsigned argc, MochaDatum *argv, MochaDatum *rval)
{
    MochaAtom *atom;
    MochaBoolean ok;

    if (argc == 0)
	return array_to_string(mc, obj, argc, argv, rval);
    if (!mocha_DatumToString(mc, argv[0], &atom))
	return MOCHA_FALSE;
    ok = array_join_str(mc, obj, atom_name(atom), rval);
    mocha_DropAtom(mc, atom);
    return ok;
}

static MochaBoolean
InitArrayObject(MochaContext *mc, MochaObject *obj, unsigned length,
		MochaDatum *base)
{
    MochaSlot slot;
    MochaDatum d;

    for (slot = 0; (unsigned)slot < length; slot++) {
	d = base ? base[slot] : MOCHA_null;
	d.flags |= MDF_ENUMERATE;
	if (!MOCHA_SetSlot(mc, obj, slot, d))
	    return MOCHA_FALSE;
    }
    return MOCHA_TRUE;
}

static MochaBoolean
array_reverse(MochaContext *mc, MochaObject *obj,
	      unsigned argc, MochaDatum *argv, MochaDatum *rval)
{
    size_t len, i;
    MochaDatum *vec, *dp;
    MochaProperty *prop;

    len = (size_t)obj->scope->freeslot;
    vec = MOCHA_malloc(mc, len * sizeof *vec);
    if (!vec)
	return MOCHA_FALSE;
    memset(vec, 0, len * sizeof *vec);
    for (prop = obj->scope->props; prop; prop = prop->next) {
	if (prop->slot >= 0) {
	    dp = &vec[len - prop->slot - 1];
	    *dp = prop->datum;
	    mocha_HoldRef(mc, dp);
	}
    }
    InitArrayObject(mc, obj, len, vec);
    for (i = 0; i < len; i++)
	mocha_DropRef(mc, &vec[i]);
    MOCHA_free(mc, vec);
    MOCHA_INIT_DATUM(mc, rval, MOCHA_OBJECT, u.obj, obj);
    return MOCHA_TRUE;
}

/* XXX begin move me to prqsort.h */
typedef int (*PRComparator)(const void *a, const void *b, void *arg);

extern PRBool
PR_qsort(void *vec, size_t nel, size_t elsize, PRComparator cmp, void *arg);
/* XXX end move me to prqsort.h */

/* XXX begin move me to prqsort.c */
typedef struct QSortArgs {
    void         *vec;
    size_t       elsize;
    void         *pivot;
    PRComparator cmp;
    void         *arg;
} QSortArgs;

static void
pr_qsort_r(QSortArgs *qa, int lo, int hi)
{
    void *pivot, *a, *b;
    int i, j;

    pivot = qa->pivot;
    while (lo < hi) {
	i = lo;
	j = hi;
	a = (char *)qa->vec + i * qa->elsize;
	memmove(pivot, a, qa->elsize);
	while (i < j) {
	    for (;;) {
		b = (char *)qa->vec + j * qa->elsize;
		if ((*qa->cmp)(b, pivot, qa->arg) <= 0)
		    break;
		j--;
	    }
	    memmove(a, b, qa->elsize);
	    while (i < j && (*qa->cmp)(a, pivot, qa->arg) <= 0) {
		i++;
		a = (char *)qa->vec + i * qa->elsize;
	    }
	    memmove(b, a, qa->elsize);
	}
	memmove(a, pivot, qa->elsize);
	if (i - lo < hi - i) {
	    pr_qsort_r(qa, lo, i - 1);
	    lo = i + 1;
	} else {
	    pr_qsort_r(qa, i + 1, hi);
	    hi = i - 1;
	}
    }
}

PRBool
PR_qsort(void *vec, size_t nel, size_t elsize, PRComparator cmp, void *arg)
{
    void *pivot;
    QSortArgs qa;

    pivot = malloc(elsize);
    if (!pivot)
	return PR_FALSE;
    qa.vec = vec;
    qa.elsize = elsize;
    qa.pivot = pivot;
    qa.cmp = cmp;
    qa.arg = arg;
    pr_qsort_r(&qa, 0, (int)(nel - 1));
    free(pivot);
    return PR_TRUE;
}
/* XXX end move me to prqsort.c */

typedef struct CompareArgs {
    MochaContext  *context;
    MochaFunction *fun;
    MochaBoolean  status;
} CompareArgs;

static int
sort_compare(const void *a, const void *b, void *arg)
{
    const MochaDatum *adp = a, *bdp = b;
    CompareArgs *ca = arg;
    MochaContext *mc = ca->context;
    MochaFloat fval = -1;
    MochaDatum fd, argv[2], rval;

    if (!ca->fun) {
	MochaAtom *aatom = 0, *batom = 0;

	if (mocha_RawDatumToString(mc, *adp, &aatom) &&
	    mocha_RawDatumToString(mc, *bdp, &batom)) {
	    fval = strcoll(atom_name(aatom), atom_name(batom));
	}
	if (aatom) mocha_DropAtom(mc, aatom);
	if (batom) mocha_DropAtom(mc, batom);
    } else {
	MOCHA_INIT_FULL_DATUM(mc, &fd, MOCHA_FUNCTION, 0,
			      MOCHA_TAINT_IDENTITY, u.fun, ca->fun);
	argv[0] = *adp;
	argv[1] = *bdp;
	ca->status = mocha_Call(ca->context, fd, 2, argv, &rval);
	if (ca->status) {
	    ca->status = mocha_DatumToNumber(mc, rval, &fval);
	    mocha_DropRef(mc, &rval);
	}
    }
    return (int)fval;
}

static MochaBoolean
array_sort(MochaContext *mc, MochaObject *obj,
	   unsigned argc, MochaDatum *argv, MochaDatum *rval)
{
    MochaFunction *fun;
    size_t len, i;
    MochaDatum *vec;
    MochaProperty *prop;
    CompareArgs ca;

    fun = 0;
    if (argc > 0 && !MOCHA_DatumToFunction(mc, argv[0], &fun))
	return MOCHA_FALSE;

    len = (size_t)obj->scope->freeslot;
    vec = MOCHA_malloc(mc, len * sizeof *vec);
    if (!vec) {
	MOCHA_DropObject(mc, &fun->object);
	return MOCHA_FALSE;
    }
    memset(vec, 0, len * sizeof *vec);
    for (prop = obj->scope->props; prop; prop = prop->next) {
	if (prop->slot >= 0) {
	    vec[prop->slot] = prop->datum;
	    mocha_HoldRef(mc, &vec[prop->slot]);
	}
    }

    ca.context = mc;
    ca.fun = fun;
    ca.status = MOCHA_TRUE;
    if (!PR_qsort(vec, len, sizeof *vec, sort_compare, &ca))
	ca.status = MOCHA_FALSE;
    MOCHA_DropObject(mc, &fun->object);
    if (ca.status)
	InitArrayObject(mc, obj, len, vec);
    for (i = 0; i < len; i++)
	mocha_DropRef(mc, &vec[i]);
    MOCHA_free(mc, vec);
    MOCHA_INIT_DATUM(mc, rval, MOCHA_OBJECT, u.obj, obj);
    return ca.status;
}

static MochaFunctionSpec array_methods[] = {
    {mocha_toStringStr,         array_to_string,        0},
    {"join",                    array_join,             1},
    {"reverse",                 array_reverse,          0},
    {"sort",                    array_sort,             1},
    {0}
};

static MochaBoolean
Array(MochaContext *mc, MochaObject *obj,
      unsigned argc, MochaDatum *argv, MochaDatum *rval)
{
    unsigned length;
    MochaDatum *base;

    if (argc == 0) {
	length = 0;
	base = 0;
    } else if (argc == 1 && argv[0].tag == MOCHA_NUMBER) {
	length = (unsigned) argv[0].u.fval;
	base = 0;
    } else {
	length = argc;
	base = argv;
    }
    if (!InitArrayObject(mc, obj, length, base))
	return MOCHA_FALSE;
    MOCHA_INIT_DATUM(mc, rval, MOCHA_OBJECT, u.obj, obj);
    return MOCHA_TRUE;
}

MochaObject *
mocha_InitArrayClass(MochaContext *mc, MochaObject *obj)
{
    return MOCHA_InitClass(mc, obj, &array_class, 0, Array, 1,
			   array_props, array_methods, 0, 0);
}

MochaObject *
mocha_NewArrayObject(MochaContext *mc, unsigned length, MochaDatum *base)
{
    MochaObject *obj;

    obj = mocha_NewObjectByClass(mc, &array_class);
    if (!obj)
	return 0;
    if (!InitArrayObject(mc, obj, length, base)) {
	mocha_DestroyObject(mc, obj);
	return 0;
    }
    return obj;
}