/* * SPL - The SPL Programming Language * Copyright (C) 2004, 2005 Clifford Wolf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * mod_array.c: Array functions (C part) */ /** * Array Module * * This module extends the built-in functionality of SPL for working with * arrays. Please also have a look at the Language Reference Manual for a * description of the built-in operators provided natively by SPL. */ #include #include #include #include #include "spl.h" #include "compat.h" static struct spl_code bytecode = #include "spl_modules/mod_array.splh" ; extern void SPL_ABI(spl_mod_array_init)(struct spl_vm *vm, struct spl_module *mod, int restore); extern void SPL_ABI(spl_mod_array_done)(struct spl_vm *vm, struct spl_module *mod); /** * This function re-indexes an array. That means, the order of the elements is * kept as they are, but the keys are newly assigned (using integer, starting * with 0). */ //builtin array_reindex(array); static struct spl_node *handler_array_reindex(struct spl_task *t, void UNUSED(*d)) { struct spl_node *node = spl_cleanup(t, spl_clib_get_node(t)); struct spl_node_sub *s = node->subs_begin; node->subs_next_idx = 0; node->subs_hash_size = 0; if (node->subs_hash) { free(node->subs_hash); node->subs_hash = 0; } while (s) { free(s->key); my_asprintf(&s->key, "%d", node->subs_next_idx++); s->hash_next = 0; s = s->next; } return 0; } /** * This function switches two elements of an array. The keys and values are * not modified by this - only the order of the elements in the array. */ //builtin array_switch(array, key1, key2); static struct spl_node *handler_array_switch(struct spl_task *t, void UNUSED(*d)) { struct spl_node *node = spl_cleanup(t, spl_clib_get_node(t)); char *aid = spl_hash_encode(spl_clib_get_string(t)); char *bid = spl_hash_encode(spl_clib_get_string(t)); struct spl_node_sub *a = spl_sub_lookup(node, aid); struct spl_node_sub *b = spl_sub_lookup(node, bid); free(aid); free(bid); if (!a || !b) return SPL_NEW_INT(0); struct spl_node_sub **a_last_next = a->last ? &a->last->next : &node->subs_begin; struct spl_node_sub **b_last_next = b->last ? &b->last->next : &node->subs_begin; struct spl_node_sub **a_next_last = a->next ? &a->next->last : &node->subs_end; struct spl_node_sub **b_next_last = b->next ? &b->next->last : &node->subs_end; struct spl_node_sub *temp; temp = *a_last_next; *a_last_next = *b_last_next; *b_last_next = temp; temp = *a_next_last; *a_next_last = *b_next_last; *b_next_last = temp; temp = a->next; a->next = b->next; b->next = temp; temp = a->last; a->last = b->last; b->last = temp; return SPL_NEW_INT(1); } static struct spl_node *handler_array_qsort(struct spl_task UNUSED(*t), void UNUSED(*d)) { #ifdef USEMACOSXAPI return SPL_NEW_INT(0); #else if (t->vm->runloop == 0) return SPL_NEW_INT(0); struct spl_node *array = spl_cleanup(t, spl_clib_get_node(t)); struct spl_node *order_function = spl_cleanup(t, spl_clib_get_node(t)); struct spl_node_sub **list = calloc(array->subs_counter, sizeof(struct spl_node_sub *)); struct spl_node_sub *s = array->subs_begin; for (int i = 0; i < array->subs_counter; i++) { assert(s); list[i] = s; s = s->next; } assert(!s); struct spl_code *code; { struct spl_asm *as = spl_asm_create(); spl_asm_add(as, SPL_OP_PUSHC, "r"); spl_asm_add(as, SPL_OP_ZERO, 0); spl_asm_add(as, SPL_OP_PUSHA, "b"); spl_asm_add(as, SPL_OP_PUSHA, "a"); spl_asm_add(as, SPL_OP_DCALL, "c"); spl_asm_add(as, SPL_OP_POPL, 0); spl_asm_add(as, SPL_OP_HALT, 0); code = spl_asm_dump(as); spl_asm_destroy(as); } struct spl_task *task = spl_clib_callback_task(t->vm); int compar(const void *a_vp, const void *b_vp) { struct spl_node_sub **a_p = (struct spl_node_sub **)a_vp; struct spl_node_sub **b_p = (struct spl_node_sub **)b_vp; char *a = (*a_p)->key; char *b = (*b_p)->key; int switched = 0; int ret = 0; if (a_vp > b_vp) { char *x = a; a = b; b = x; switched = 1; } spl_task_setcode(task, spl_code_get(code)); spl_create(task, task->ctx, "a", SPL_NEW_STRING_DUP(a), SPL_CREATE_LOCAL); spl_create(task, task->ctx, "b", SPL_NEW_STRING_DUP(b), SPL_CREATE_LOCAL); spl_clib_callback_run(task); struct spl_node *rn = spl_lookup(task, task->ctx, "r", SPL_LOOKUP_TEST); if (rn) { // the SPL callback function returns '0' when the elements are // in the right order and a different value when they are in the // wrong order. But this function must return -1, 0 or +1 for // less-then, equal or greater then. We need to convert this // information... ret = switched ? +1 : -1; if (spl_get_int(rn)) ret *= -1; } return ret; } spl_create(task, task->ctx, "c", spl_get(order_function), SPL_CREATE_LOCAL); qsort(list, array->subs_counter, sizeof(struct spl_node_sub *), compar); spl_task_destroy(task->vm, task); spl_code_put(code); for (int i = 0; i < array->subs_counter; i++) { *(i == 0 ? &array->subs_begin : &list[i-1]->next) = list[i]; *(i == array->subs_counter-1 ? &array->subs_end : &list[i+1]->last) = list[i]; } list[0]->last = 0; list[array->subs_counter-1]->next = 0; free(list); return SPL_NEW_INT(1); #endif } void SPL_ABI(spl_mod_array_init)(struct spl_vm *vm, struct spl_module *mod, int restore) { spl_clib_reg(vm, "array_reindex", handler_array_reindex, 0); spl_clib_reg(vm, "array_switch", handler_array_switch, 0); spl_clib_reg(vm, "__array_sort_backend_qsort", handler_array_qsort, 0); if ( !restore ) spl_eval_bytecode(vm, 0, strdup(mod->name), &bytecode); } void SPL_ABI(spl_mod_array_done)(struct spl_vm UNUSED(*vm), struct spl_module UNUSED(*mod)) { return; }