cfad47cfa3/tads3/vmvec.cpp
Commiter: Nikos Chantziaras
Author: Nikos Chantziaras
Revision: cfad47cfa3
File Size: 80.2 KB
(June 01, 2009 20:54 UTC) Almost 3 years ago
Initial commit.
Showing without highlighting since it looks like a big file and may slow your browser - show with highlighting
Show/hide line numbers#ifdef RCSID
static char RCSid[] =
"$Header$";
#endif
/*
* Copyright (c) 2000, 2002 Michael J. Roberts. All Rights Reserved.
*
* Please see the accompanying license file, LICENSE.TXT, for information
* on using and copying this software.
*/
/*
Name
vmvec.cpp - T3 VM Vector metaclass
Function
Notes
Modified
05/14/00 MJRoberts - Creation
*/
#include <stdlib.h>
#include "t3std.h"
#include "vmtype.h"
#include "vmmcreg.h"
#include "vmmeta.h"
#include "vmglob.h"
#include "vmobj.h"
#include "vmvec.h"
#include "vmlst.h"
#include "vmerr.h"
#include "vmerrnum.h"
#include "vmfile.h"
#include "vmstack.h"
#include "vmbif.h"
#include "vmundo.h"
#include "vmrun.h"
#include "vmiter.h"
#include "vmsort.h"
/* ------------------------------------------------------------------------ */
/*
* statics
*/
/* metaclass registration object */
static CVmMetaclassVector metaclass_reg_obj;
CVmMetaclass *CVmObjVector::metaclass_reg_ = &metaclass_reg_obj;
/* function table */
int (CVmObjVector::
*CVmObjVector::func_table_[])(VMG_ vm_obj_id_t self,
vm_val_t *retval, uint *argc) =
{
&CVmObjVector::getp_undef,
&CVmObjVector::getp_to_list,
&CVmObjVector::getp_get_size,
&CVmObjVector::getp_copy_from,
&CVmObjVector::getp_fill_val,
&CVmObjVector::getp_subset,
&CVmObjVector::getp_apply_all,
&CVmObjVector::getp_index_which,
&CVmObjVector::getp_for_each,
&CVmObjVector::getp_for_each_assoc,
&CVmObjVector::getp_map_all,
&CVmObjVector::getp_index_of,
&CVmObjVector::getp_val_which,
&CVmObjVector::getp_last_index_of,
&CVmObjVector::getp_last_index_which,
&CVmObjVector::getp_last_val_which,
&CVmObjVector::getp_count_of,
&CVmObjVector::getp_count_which,
&CVmObjVector::getp_get_unique,
&CVmObjVector::getp_append_unique,
&CVmObjVector::getp_sort,
&CVmObjVector::getp_set_length,
&CVmObjVector::getp_insert_at,
&CVmObjVector::getp_remove_element_at,
&CVmObjVector::getp_remove_range,
&CVmObjVector::getp_append,
&CVmObjVector::getp_prepend,
&CVmObjVector::getp_append_all,
&CVmObjVector::getp_remove_element
};
/* ------------------------------------------------------------------------ */
/*
* create with a given number of elements
*/
CVmObjVector::CVmObjVector(VMG_ size_t element_count)
{
/* allocate space */
alloc_vector(vmg_ element_count);
/*
* a vector intially has no elements in use; the element_count
* merely gives the number of slots to be allocated initially (this
* won't affect the allocation count, which is stored separately)
*/
set_element_count(0);
}
/* ------------------------------------------------------------------------ */
/*
* allocate space
*/
void CVmObjVector::alloc_vector(VMG_ size_t cnt)
{
/* allocate space for the given number of elements */
ext_ = (char *)G_mem->get_var_heap()->alloc_mem(calc_alloc(cnt), this);
/* set the element count and allocated count */
set_allocated_count(cnt);
set_element_count(cnt);
/* clear the undo bits */
clear_undo_bits();
}
/* ------------------------------------------------------------------------ */
/*
* notify of deletion
*/
void CVmObjVector::notify_delete(VMG_ int)
{
/* free our extension */
if (ext_ != 0)
G_mem->get_var_heap()->free_mem(ext_);
}
/* ------------------------------------------------------------------------ */
/*
* create dynamically using stack arguments
*/
vm_obj_id_t CVmObjVector::create_from_stack(VMG_ const uchar **pc_ptr,
uint argc)
{
vm_val_t *arg1;
vm_obj_id_t id;
CVmObjVector *vec;
size_t cnt = 0;
size_t copy_cnt;
const char *src_lst;
CVmObjVector *src_vec;
size_t i;
/* check arguments */
if (argc < 1 || argc > 2)
err_throw(VMERR_WRONG_NUM_OF_ARGS);
/* get the first argument */
arg1 = G_stk->get(0);
/* presume we will have no source argument */
copy_cnt = 0;
src_lst = 0;
src_vec = 0;
/* check what kind of argument we have */
if (arg1->typ == VM_INT)
{
/* get the number of elements to allocate */
cnt = (size_t)arg1->val.intval;
}
else
{
/* anything else is invalid */
err_throw(VMERR_BAD_TYPE_BIF);
}
/* if there's a second argument, it's a source list/vector */
if (argc >= 2)
{
vm_val_t *arg2;
/* get the second argument */
arg2 = G_stk->get(1);
/* see what we have */
if ((src_lst = arg2->get_as_list(vmg0_)) != 0)
{
/* it's a list - copy its elements */
copy_cnt = vmb_get_len(src_lst);
}
else if (arg2->typ == VM_OBJ
&& (vm_objp(vmg_ arg2->val.obj)
->is_of_metaclass(metaclass_reg_)))
{
/* it's a vector */
src_vec = (CVmObjVector *)vm_objp(vmg_ arg2->val.obj);
copy_cnt = src_vec->get_element_count();
}
else if (arg2->typ == VM_INT)
{
/* they want an explicit initial size */
copy_cnt = (size_t)arg2->val.intval;
}
else
{
/* invalid source type */
err_throw(VMERR_BAD_TYPE_BIF);
}
/*
* make sure we allocate enough initial space to store the source
* object we're copying
*/
if (copy_cnt > cnt)
cnt = copy_cnt;
}
/* create the vector with the given number of elements */
id = vm_new_id(vmg_ FALSE, TRUE, FALSE);
vec = new (vmg_ id) CVmObjVector(vmg_ cnt);
/* copy or initialize the elements */
for (i = 0 ; i < copy_cnt ; ++i)
{
vm_val_t ele;
/*
* get my element at this index: if we have a source list or
* vector, take the current element from that list or vector;
* otherwise, initialize the element to nil
*/
if (src_lst != 0)
CVmObjList::index_list(vmg_ &ele, src_lst, i + 1);
else if (src_vec != 0)
src_vec->get_element(i, &ele);
else
ele.set_nil();
/*
* set my element at this index - this is construction, so there's
* no need to save undo information
*/
vec->set_element(i, &ele);
}
/* set the initial size */
vec->set_element_count(copy_cnt);
/* discard arguments */
G_stk->discard(argc);
/* return the new object */
return id;
}
/* ------------------------------------------------------------------------ */
/*
* save to a file
*/
void CVmObjVector::save_to_file(VMG_ class CVmFile *fp)
{
size_t ele_cnt;
size_t alloc_cnt;
/* get our element count and full allocation count */
alloc_cnt = get_allocated_count();
ele_cnt = get_element_count();
/* write the counts and the elements */
fp->write_int2(alloc_cnt);
fp->write_int2(ele_cnt);
fp->write_bytes(get_element_ptr(0), calc_alloc_ele(ele_cnt));
}
/*
* restore from a file
*/
void CVmObjVector::restore_from_file(VMG_ vm_obj_id_t self,
CVmFile *fp, CVmObjFixup *fixups)
{
size_t ele_cnt;
size_t alloc_cnt;
/* read the element count and the full allocation count */
alloc_cnt = fp->read_uint2();
ele_cnt = fp->read_uint2();
/* free any existing extension */
if (ext_ != 0)
{
G_mem->get_var_heap()->free_mem(ext_);
ext_ = 0;
}
/* allocate the space at the allocation count */
alloc_vector(vmg_ alloc_cnt);
/* set the actual in-use element count */
set_element_count(ele_cnt);
/* read the contents */
fp->read_bytes(get_element_ptr(0), calc_alloc_ele(ele_cnt));
/* fix up object references in the values */
fixups->fix_dh_array(vmg_ get_element_ptr(0), ele_cnt);
}
/* ------------------------------------------------------------------------ */
/*
* create with no initial contents
*/
vm_obj_id_t CVmObjVector::create(VMG_ int in_root_set)
{
vm_obj_id_t id = vm_new_id(vmg_ in_root_set, TRUE, FALSE);
new (vmg_ id) CVmObjVector();
return id;
}
/*
* create with a given number of elements
*/
vm_obj_id_t CVmObjVector::create(VMG_ int in_root_set, size_t element_count)
{
vm_obj_id_t id = vm_new_id(vmg_ in_root_set, TRUE, FALSE);
new (vmg_ id) CVmObjVector(vmg_ element_count);
return id;
}
/* ------------------------------------------------------------------------ */
/*
* Create a copy of this object
*/
vm_obj_id_t CVmObjVector::create_copy(VMG_ const vm_val_t *self_val)
{
vm_obj_id_t new_id;
CVmObjVector *new_vec;
/* save the original object on the stack for gc protection */
G_stk->push(self_val);
/* create a new vector with the same parameters as this one */
new_id = vm_new_id(vmg_ FALSE, TRUE, FALSE);
new_vec = new (vmg_ new_id) CVmObjVector(vmg_ get_allocated_count());
new_vec->set_element_count(get_element_count());
/* copy the elements from the old vector to the new vector */
memcpy(new_vec->get_element_ptr(0), get_element_ptr(0),
calc_alloc_ele(get_element_count()));
/* done with the gc protection - discard it */
G_stk->discard(1);
/* return the new vector's object ID */
return new_id;
}
/* ------------------------------------------------------------------------ */
/*
* Set the allocation size - this is the number of elements we've allocated
* space for, which might exceed the number of elements we're actually
* storing currently.
*/
void CVmObjVector::set_allocated_count(size_t cnt)
{
/* if the new size exceeds the maximum allowed size, throw an error */
if (cnt > 65535)
err_throw(VMERR_OUT_OF_RANGE);
/* set the new allocated size */
vmb_put_len(cons_get_vector_ext_ptr(), cnt);
}
/* ------------------------------------------------------------------------ */
/*
* Create an iterator
*/
void CVmObjVector::new_iterator(VMG_ vm_val_t *retval,
const vm_val_t *self_val)
{
vm_val_t copy_val;
/*
* Create a copy of the vector - since a vector can change, we must
* always create an iterator based on a copy, so that the iterator has
* a consistent view of the original as of the time the iterator was
* created.
*/
copy_val.set_obj(create_copy(vmg_ self_val));
/* push the copy for gc protection */
G_stk->push(©_val);
/*
* Set up a new indexed iterator object. The first valid index for a
* vector is always 1, and the last valid index is the same as the
* number of elements.
*/
retval->set_obj(CVmObjIterIdx::create_for_coll(vmg_ ©_val,
1, get_element_count()));
/* discard gc protection */
G_stk->discard();
}
/*
* Create a live iterator
*/
void CVmObjVector::new_live_iterator(VMG_ vm_val_t *retval,
const vm_val_t *self_val)
{
/*
* Set up a new indexed iterator object. The first valid index for a
* vector is always 1, and the last valid index is the same as the
* number of elements. Since we want a "live" iterator, create the
* iterator with a direct reference to the vector.
*/
retval->set_obj(CVmObjIterIdx::create_for_coll(vmg_ self_val,
1, get_element_count()));
}
/* ------------------------------------------------------------------------ */
/*
* set a property
*/
void CVmObjVector::set_prop(VMG_ class CVmUndo *,
vm_obj_id_t, vm_prop_id_t,
const vm_val_t *)
{
err_throw(VMERR_INVALID_SETPROP);
}
/* ------------------------------------------------------------------------ */
/*
* get a property
*/
int CVmObjVector::get_prop(VMG_ vm_prop_id_t prop, vm_val_t *retval,
vm_obj_id_t self, vm_obj_id_t *source_obj,
uint *argc)
{
uint func_idx;
/* translate the property index to an index into our function table */
func_idx = G_meta_table
->prop_to_vector_idx(metaclass_reg_->get_reg_idx(), prop);
/* call the appropriate function */
if ((this->*func_table_[func_idx])(vmg_ self, retval, argc))
{
*source_obj = metaclass_reg_->get_class_obj(vmg0_);
return TRUE;
}
/* inherit our base class handling */
return CVmObjCollection::
get_prop(vmg_ prop, retval, self, source_obj, argc);
}
/* ------------------------------------------------------------------------ */
/*
* Expand the vector to accommodate the given number of new elements.
* We'll increase the element count, and we'll save undo for the change.
* If necessary, we'll re-allocate our memory extension block at a
* larger size.
*/
void CVmObjVector::expand_by(VMG_ vm_obj_id_t self, size_t added_elements)
{
size_t new_ele_cnt;
/* calculate the element count */
new_ele_cnt = get_element_count() + added_elements;
/* remember the new size, saving undo */
set_element_count_undo(vmg_ self, new_ele_cnt);
}
/* ------------------------------------------------------------------------ */
/*
* Set the element count, keeping undo for the change
*/
void CVmObjVector::set_element_count_undo(VMG_ vm_obj_id_t self,
size_t new_ele_cnt)
{
/* add undo if necessary */
if (G_undo != 0)
{
size_t old_ele_cnt;
vm_val_t old_val;
size_t idx;
vm_val_t nil_val;
/* get the old size */
old_ele_cnt = get_element_count();
/*
* If we're shrinking the vector, save undo for each element we're
* dropping off the end; to do this, simply set each element we're
* losing to nil, since this will add an undo record with the
* original value of the element.
*
* Note that we must save undo for dropped elements BEFORE we set
* the vector's new sizd. We must perform the steps in this order
* because undo is applied in reverse order: when we read back the
* undo, we'll first change the vector size to its old size (since
* that's the last saved undo instruction), then we'll set the
* elements to their old values.
*/
nil_val.set_nil();
for (idx = new_ele_cnt ; idx < old_ele_cnt ; ++idx)
set_element_undo(vmg_ self, idx, &nil_val);
/* set up an integer with the pre-modification element count */
old_val.set_int(old_ele_cnt);
/*
* add the undo record - use the special index value 0xffffffff
* as the key; this will never be a valid index for a real
* element, because we could never address an element with an
* index this high in a 32-bit address space
*/
G_undo->add_new_record_int_key(vmg_ self, 0xffffffffU, &old_val);
}
/*
* if the new size is larger than our current allocation size,
* increase the allocated size
*/
if (new_ele_cnt > get_allocated_count())
{
size_t new_alloc_cnt;
size_t new_mem_size;
/* bump up the allocated size */
new_alloc_cnt = get_allocated_count() + get_alloc_count_increment();
/* if that's still not big enough, go up to the requested size */
if (new_alloc_cnt < new_ele_cnt)
new_alloc_cnt = new_ele_cnt;
/* get the new size we need */
new_mem_size = calc_alloc(new_alloc_cnt);
/* reallocate our memory at the new, larger size */
ext_ = (char *)G_mem->get_var_heap()
->realloc_mem(new_mem_size, ext_, this);
/* set the new allocation size */
set_allocated_count(new_alloc_cnt);
/*
* clear the undo bits (it's easier than moving them, and the only
* cost is that we might generate some redundant undo records)
*/
clear_undo_bits();
}
/*
* if we're expanding the in-use size of the vector, set each
* newly-added element to nil
*/
if (new_ele_cnt > get_element_count())
{
size_t i;
vm_val_t nil_val;
/*
* set the old value for each new element to nil (we don't have to
* save undo for this, since the undo operation for the change in
* size will simply discard all of the new elements)
*/
nil_val.set_nil();
for (i = get_element_count() ; i < new_ele_cnt ; ++i)
set_element(i, &nil_val);
}
/* set the new element count */
set_element_count(new_ele_cnt);
}
/* ------------------------------------------------------------------------ */
/*
* notify of new undo savepoint
*/
void CVmObjVector::notify_new_savept()
{
/*
* clear the undo bits - we obviously have no undo for the new
* savepoint yet
*/
clear_undo_bits();
}
/* ------------------------------------------------------------------------ */
/*
* apply undo
*/
void CVmObjVector::apply_undo(VMG_ struct CVmUndoRecord *rec)
{
/*
* if the record refers to index 0xffffffff, this is a size-change
* record; otherwise, it's an ordinary element change record
*/
if ((size_t)rec->id.intval == 0xffffffffU)
{
/*
* it's a size change - apply the new size, which is stored as
* an integer in the old value record
*/
set_element_count((size_t)rec->oldval.val.intval);
}
else
{
/* it's an ordinary index record - set the element from undo data */
set_element((size_t)rec->id.intval, &rec->oldval);
}
}
/* ------------------------------------------------------------------------ */
/*
* mark references from an undo record
*/
void CVmObjVector::mark_undo_ref(VMG_ CVmUndoRecord *rec)
{
/* if the undo record refers to an object, mark the object */
if (rec->oldval.typ == VM_OBJ)
G_obj_table->mark_all_refs(rec->oldval.val.obj, VMOBJ_REACHABLE);
}
/* ------------------------------------------------------------------------ */
/*
* mark references
*/
void CVmObjVector::mark_refs(VMG_ uint state)
{
size_t cnt;
char *p;
/* get my element count */
cnt = get_element_count();
/* mark as referenced each object in the vector */
for (p = get_element_ptr(0) ; cnt != 0 ; --cnt, inc_element_ptr(&p))
{
/*
* if this is an object, mark it as referenced, and mark its
* references as referenced
*/
if (vmb_get_dh_type(p) == VM_OBJ)
G_obj_table->mark_all_refs(vmb_get_dh_obj(p), state);
}
}
/* ------------------------------------------------------------------------ */
/*
* load from an image file
*/
void CVmObjVector::load_from_image(VMG_ vm_obj_id_t self,
const char *ptr, size_t siz)
{
/* load from the image data */
load_image_data(vmg_ ptr, siz);
/*
* save our image data pointer in the object table, so that we can
* access it (without storing it ourselves) during a reload
*/
G_obj_table->save_image_pointer(self, ptr, siz);
}
/*
* reload the object from image data
*/
void CVmObjVector::reload_from_image(VMG_ vm_obj_id_t,
const char *ptr, size_t siz)
{
/* load the image data */
load_image_data(vmg_ ptr, siz);
}
/*
* load the data from an image file
*/
void CVmObjVector::load_image_data(VMG_ const char *ptr, size_t siz)
{
size_t alloc_cnt;
size_t ele_cnt;
/* if we already have memory allocated, free it */
if (ext_ != 0)
{
G_mem->get_var_heap()->free_mem(ext_);
ext_ = 0;
}
/* get the allocation size and the actual in-use size */
alloc_cnt = vmb_get_len(ptr);
ele_cnt = vmb_get_len(ptr + VMB_LEN);
/* make sure we allocate at least as much as is actually in use */
if (alloc_cnt < ele_cnt)
alloc_cnt = ele_cnt;
/* make sure the size isn't larger than we'd expect */
if (siz > VMB_LEN*2 + (VMB_DATAHOLDER * ele_cnt))
siz = VMB_LEN*2 + (VMB_DATAHOLDER * ele_cnt);
/*
* allocate memory at the new allocation size as indicated in the
* image data
*/
alloc_vector(vmg_ alloc_cnt);
/* set the in-use element count */
set_element_count(ele_cnt);
/*
* if the size is smaller than we'd expect from the initialized
* element count, set extra elements to nil
*/
if (siz < VMB_LEN*2 + (VMB_DATAHOLDER * ele_cnt))
{
size_t i;
vm_val_t nil_val;
/* set all elements to nil */
nil_val.set_nil();
for (i = 0 ; i < ele_cnt ; ++i)
set_element(i, &nil_val);
}
/* copy the initialized elements from the image data */
memcpy(get_element_ptr(0), ptr + VMB_LEN*2, siz - VMB_LEN*2);
/* we're resetting to initial state, so forget all undo */
clear_undo_bits();
}
/* ------------------------------------------------------------------------ */
/*
* index the vector
*/
void CVmObjVector::index_val(VMG_ vm_val_t *result, vm_obj_id_t,
const vm_val_t *index_val)
{
uint32 idx;
/* get the index value as an integer */
idx = index_val->num_to_int();
/* make sure it's in range - 1 to our element count, inclusive */
if (idx < 1 || idx > get_element_count())
err_throw(VMERR_INDEX_OUT_OF_RANGE);
/*
* get the indexed element and store it in the result, adjusting the
* index to the C-style 0-based range
*/
get_element(idx - 1, result);
}
/* ------------------------------------------------------------------------ */
/*
* set an indexed element of the vector
*/
void CVmObjVector::set_index_val(VMG_ vm_val_t *new_container,
vm_obj_id_t self,
const vm_val_t *index_val,
const vm_val_t *new_val)
{
uint32 idx;
/* get the index value as an integer */
idx = index_val->num_to_int();
/* make sure it's at least 1 */
if (idx < 1)
err_throw(VMERR_INDEX_OUT_OF_RANGE);
/*
* if it's higher than the current length, extend the vector with nil
* entries to the requested size
*/
if (idx > get_element_count())
{
size_t i;
vm_val_t nil_val;
/* note the first new element index */
i = get_element_count();
/* extend the vector */
set_element_count_undo(vmg_ self, idx);
/*
* Fill in entries between the old length and the new length with
* nil. Note that we don't have to fill in the very last element,
* since we'll explicitly set it to the new value momentarily
* anyway. Note also that we don't need to keep undo for the
* initializations, since on undo we'll truncate the vector to
* remove the newly-added elements and thus won't need to restore
* any values for the slots.
*/
for (nil_val.set_nil() ; i < (size_t)idx - 1 ; ++i)
set_element(i, &nil_val);
/*
* set the new value - this doesn't require undo since we had to
* expand the vector to make room for it
*/
set_element(idx - 1, new_val);
}
else
{
/* set the element and record undo, using a zero-based index */
set_element_undo(vmg_ self, (size_t)idx - 1, new_val);
}
/* the result is the original vector value */
new_container->set_obj(self);
}
/* ------------------------------------------------------------------------ */
/*
* Set an element and record undo for the change - takes a C-style 0-based
* index.
*/
void CVmObjVector::set_element_undo(VMG_ vm_obj_id_t self,
size_t idx, const vm_val_t *new_val)
{
/* if we don't have undo for this element already, save undo now */
if (G_undo != 0 && !get_undo_bit(idx))
{
vm_val_t old_val;
/* get the pre-modification value of this element */
get_element(idx, &old_val);
/* add the undo record */
G_undo->add_new_record_int_key(vmg_ self, idx, &old_val);
/*
* set the undo bit to indicate that we now have undo for this
* element
*/
set_undo_bit(idx, TRUE);
}
/* get the indexed element and store it in the result */
set_element(idx, new_val);
}
/* ------------------------------------------------------------------------ */
/*
* Check a value for equality. We will match any list or vector with the
* same number of elements and the same value for each element.
*/
int CVmObjVector::equals(VMG_ vm_obj_id_t self, const vm_val_t *val,
int depth) const
{
const char *p;
int p_is_list;
CVmObjVector *vec2;
size_t cnt;
size_t cnt2;
size_t idx;
/* if the recursion depth is excessive, throw an error */
if (depth > VM_MAX_TREE_DEPTH_EQ)
err_throw(VMERR_TREE_TOO_DEEP_EQ);
/* if the other value is a reference to myself, we certainly match */
if (val->typ == VM_OBJ && val->val.obj == self)
{
/* no need to look at the contents if this is a reference to me */
return TRUE;
}
/* try it as a list and as a vector */
if ((p = val->get_as_list(vmg0_)) != 0)
{
/* it's a list */
p_is_list = TRUE;
/* get the number of elements in the list */
cnt2 = vmb_get_len(p);
/* the second element isn't a vector */
vec2 = 0;
}
else
{
/* it's not a list */
p_is_list = FALSE;
/* check to see if it's a vector */
if (val->typ == VM_OBJ
&& (vm_objp(vmg_ val->val.obj)->is_of_metaclass(metaclass_reg_)))
{
/* it's a vector - cast it accordingly */
vec2 = (CVmObjVector *)vm_objp(vmg_ val->val.obj);
/* get the number of elements */
cnt2 = vec2->get_element_count();
}
else
{
/* it's some other type, so it can't be equal */
return FALSE;
}
}
/* if the sizes don't match, the values are not equal */
cnt = get_element_count();
if (cnt != cnt2)
return FALSE;
/* compare element by element */
for (idx = 0 ; idx < cnt ; ++idx)
{
vm_val_t val1;
vm_val_t val2;
/* get this element of self */
get_element(idx, &val1);
/* get this element of the other value */
if (p_is_list)
CVmObjList::index_list(vmg_ &val2, p, idx + 1);
else
vec2->get_element(idx, &val2);
/* if these elements aren't equal, our values aren't equal */
if (!val1.equals(vmg_ &val2, depth + 1))
return FALSE;
/*
* if the other value is a list, re-translate it in case we did
* any constant data swapping
*/
if (p_is_list)
p = val->get_as_list(vmg0_);
}
/* we didn't find any differences, so the values are equal */
return TRUE;
}
/* ------------------------------------------------------------------------ */
/*
* Hash value calculation
*/
uint CVmObjVector::calc_hash(VMG_ vm_obj_id_t self, int depth) const
{
uint hash;
uint i;
/* if the recursion depth is excessive, throw an error */
if (depth > VM_MAX_TREE_DEPTH_EQ)
err_throw(VMERR_TREE_TOO_DEEP_EQ);
/* combine the hash values of the members of the vector */
for (hash = 0, i = 0 ; i < get_element_count() ; ++i)
{
vm_val_t ele;
/* get this element */
get_element(i, &ele);
/*
* Calculate its hash value and add it into the combined hash.
*
* Note that we have to increase the recursion depth, because we're
* recursively calculating the hash value of our contents, and our
* contents can refer (directly or indirectly) back to this object.
* In other words, we can have cycles in the reference tree from
* this object back to itself, so we need to keep track of the
* recursion depth to ensure we don't loop forever.
*/
hash += ele.calc_hash(vmg_ depth + 1);
}
/* return the combined hash */
return hash;
}
/* ------------------------------------------------------------------------ */
/*
* add a value to the vector
*/
void CVmObjVector::add_val(VMG_ vm_val_t *result,
vm_obj_id_t self, const vm_val_t *val)
{
size_t idx;
size_t rhs_cnt;
size_t i;
CVmObjVector *new_vec;
/* push the value to append, for gc protection */
G_stk->push(val);
/*
* remember the index of the first new element - this is simply one
* higher than the last valid current index
*/
idx = get_element_count();
/* get the number of elements to add */
rhs_cnt = val->get_coll_addsub_rhs_ele_cnt(vmg0_);
/*
* allocate a copy of the vector for the result - make it big enough
* for my elements plus the elements to be appended
*/
result->set_obj(create(vmg_ FALSE, idx + rhs_cnt));
/* get the return value as a vector */
new_vec = (CVmObjVector *)vm_objp(vmg_ result->val.obj);
/*
* set the element count in the new vector (we're creating it anew, so
* there's no need to save undo)
*/
new_vec->set_element_count(idx + rhs_cnt);
/* push the result for gc protection */
G_stk->push(result);
/* copy my elements into the result */
memcpy(new_vec->get_element_ptr(0), get_element_ptr(0),
calc_alloc_ele(idx));
/* add the new elements */
for (i = 0 ; i < rhs_cnt ; ++i, ++idx)
{
vm_val_t ele;
/* get this element from the right-hand side */
val->get_coll_addsub_rhs_ele(vmg_ i + 1, &ele);
/* store the element in the result */
new_vec->set_element(idx, &ele);
}
/* discard the gc protect */
G_stk->discard(2);
}
/* ------------------------------------------------------------------------ */
/*
* subtract a value from the vector
*/
void CVmObjVector::sub_val(VMG_ vm_val_t *result,
vm_obj_id_t self, const vm_val_t *val)
{
size_t ele_cnt;
size_t rhs_cnt;
size_t i;
CVmObjVector *new_vec;
size_t new_cnt;
/* push the value to append, for gc protection */
G_stk->push(val);
/* note the initial element count */
ele_cnt = get_element_count();
/* get the number of elements to remove */
rhs_cnt = val->get_coll_addsub_rhs_ele_cnt(vmg0_);
/*
* allocate a new vector for the result - the new object will be no
* larger than the current object, so allocate at the same size
*/
result->set_obj(create(vmg_ FALSE, ele_cnt));
/* push the result for gc protection */
G_stk->push(result);
/* get the return value as a vector */
new_vec = (CVmObjVector *)vm_objp(vmg_ result->val.obj);
/*
* copy each element from me to the new vector, but don't copy
* elements found in the right-hand side
*/
for (i = 0, new_cnt = 0 ; i < ele_cnt ; ++i)
{
vm_val_t ele;
size_t j;
int found;
/* get this element of self */
get_element(i, &ele);
/* scan the right side for the element */
for (found = FALSE, j = 0 ; j < rhs_cnt ; ++j)
{
vm_val_t rh_ele;
/* get this right-hand element */
val->get_coll_addsub_rhs_ele(vmg_ j + 1, &rh_ele);
/*
* if this right-hand element matches this left-hand element,
* note that we have a match
*/
if (rh_ele.equals(vmg_ &ele))
{
/* note that we found it */
found = TRUE;
/* no need to look any further - one match is enough */
break;
}
}
/*
* If we didn't find the value, include it in the result vector.
* Note that we don't have to save undo, since we're still
* constructing the new vector.
*/
if (!found)
{
/* store the element in the result vector */
new_vec->set_element(new_cnt, &ele);
/* count it */
++new_cnt;
}
}
/*
* update the new vector's size - we don't have to save undo for this
* change, because we're still constructing the new vector
*/
new_vec->set_element_count(new_cnt);
/* discard the gc protection */
G_stk->discard(2);
}
/* ------------------------------------------------------------------------ */
/*
* property evaluator - convert to a list
*/
int CVmObjVector::getp_to_list(VMG_ vm_obj_id_t self, vm_val_t *retval,
uint *argc)
{
size_t src_cnt;
size_t dst_cnt;
size_t start_idx;
CVmObjList *lst;
size_t idx;
uint orig_argc = (argc != 0 ? *argc : 0);
static CVmNativeCodeDesc desc(0, 2);
/* check arguments */
if (get_prop_check_argc(retval, argc, &desc))
return TRUE;
/* note our size */
src_cnt = get_element_count();
/*
* if there's a starting index, retrieve it; otherwise, start at our
* first element
*/
if (orig_argc >= 1)
start_idx = (size_t)CVmBif::pop_int_val(vmg0_);
else
start_idx = 1;
/* if the starting index is below 1, force it to 1 */
if (start_idx < 1)
start_idx = 1;
/* adjust the starting index to a 0-based index */
--start_idx;
/*
* if there's a size argument, retrieve it; if it's not specified,
* use our actual size for the output size
*/
if (orig_argc >= 2)
dst_cnt = (size_t)CVmBif::pop_int_val(vmg0_);
else
dst_cnt = src_cnt;
/*
* in no case will the result list have more elements than we can
* actually supply
*/
if (start_idx >= src_cnt)
{
/* we're starting past our last element - we can't supply anything */
dst_cnt = 0;
}
else if (src_cnt - start_idx < dst_cnt)
{
/* we can't supply as many values as requested - lower the size */
dst_cnt = src_cnt - start_idx;
}
/* push a self-reference for garbage collection protection */
G_stk->push()->set_obj(self);
/* create the new list */
retval->set_obj(CVmObjList::create(vmg_ FALSE, dst_cnt));
lst = (CVmObjList *)vm_objp(vmg_ retval->val.obj);
/* set the list elements */
for (idx = 0 ; idx < dst_cnt ; ++idx)
{
vm_val_t val;
/* get my element at this index */
get_element(idx + start_idx, &val);
/* store the element in the list */
lst->cons_set_element(idx, &val);
}
/* discard the self-reference */
G_stk->discard();
/* handled */
return TRUE;
}
/* ------------------------------------------------------------------------ */
/*
* property evaluator - copy from another vector or list
*/
int CVmObjVector::getp_copy_from(VMG_ vm_obj_id_t self, vm_val_t *retval,
uint *argc)
{
vm_val_t src_val;
size_t src_cnt = 0;
size_t dst_cnt;
size_t src_start;
size_t dst_start;
size_t copy_cnt;
const char *src_lst;
int src_is_list = FALSE;
CVmObjVector *src_vec = 0;
size_t i;
static CVmNativeCodeDesc desc(4);
/* check arguments */
if (get_prop_check_argc(retval, argc, &desc))
return TRUE;
/* retrieve the source value */
G_stk->pop(&src_val);
/* get the source starting index */
src_start = (size_t)CVmBif::pop_int_val(vmg0_);
/* get the destination starting index */
dst_start = (size_t)CVmBif::pop_int_val(vmg0_);
/* get the number of elements to copy */
copy_cnt = (size_t)CVmBif::pop_int_val(vmg0_);
/* if either starting index is below 1, force it to 1 */
if (src_start < 1)
src_start = 1;
if (dst_start < 1)
dst_start = 1;
/* adjust the starting indices to 0-based values */
--src_start;
--dst_start;
/* note the destination length (i.e., my element count) */
dst_cnt = get_element_count();
/* get the source value */
if ((src_lst = src_val.get_as_list(vmg0_)) != 0)
{
/* note that it's a list */
src_is_list = TRUE;
/* note the source size */
src_cnt = vmb_get_len(src_lst);
}
else if (src_val.typ == VM_OBJ
&& (vm_objp(vmg_ src_val.val.obj)->get_metaclass_reg()
== metaclass_reg_))
{
/* it's a vector */
src_vec = (CVmObjVector *)vm_objp(vmg_ src_val.val.obj);
/* note the soure size */
src_cnt = src_vec->get_element_count();
}
else
err_throw(VMERR_BAD_TYPE_BIF);
/* limit our copying to the remaining elements of the source */
if (src_start >= src_cnt)
copy_cnt = 0;
else if (src_start + copy_cnt >= src_cnt)
copy_cnt = src_cnt - src_start;
/* expand the vector if necessary to make room for added elements */
if (dst_start + copy_cnt > dst_cnt)
set_element_count_undo(vmg_ self, dst_start + copy_cnt);
/* set the list elements */
for (i = 0 ; i < copy_cnt ; ++i)
{
vm_val_t val;
/* get my element at this index */
if (src_is_list)
CVmObjList::index_list(vmg_ &val, src_lst, i + src_start + 1);
else
src_vec->get_element(i + src_start, &val);
/* set my element at this index, recording undo for the change */
set_element_undo(vmg_ self, i + dst_start, &val);
}
/* the return value is 'self' */
retval->set_obj(self);
/* handled */
return TRUE;
}
/* ------------------------------------------------------------------------ */
/*
* property evaluator - fill with a value
*/
int CVmObjVector::getp_fill_val(VMG_ vm_obj_id_t self, vm_val_t *retval,
uint *argc)
{
vm_val_t fill_val;
size_t cnt;
size_t start_idx;
size_t end_idx;
size_t idx;
uint orig_argc = (argc != 0 ? *argc : 0);
static CVmNativeCodeDesc desc(1, 2);
/* check arguments */
if (get_prop_check_argc(retval, argc, &desc))
return TRUE;
/* note our size */
cnt = get_element_count();
/* the return value is 'self' */
retval->set_obj(self);
/* get the fill value */
G_stk->pop(&fill_val);
/*
* if there's a starting index, retrieve it; otherwise, start at our
* first element
*/
if (orig_argc >= 2)
start_idx = (size_t)CVmBif::pop_int_val(vmg0_);
else
start_idx = 1;
/* if the starting index is below 1, force it to 1 */
if (start_idx < 1)
start_idx = 1;
/* adjust the starting index to a 0-based index */
--start_idx;
/*
* if there's a count argument, retrieve it; if it's not specified,
* use our actual size for the count
*/
if (orig_argc >= 3)
end_idx = start_idx + (size_t)CVmBif::pop_int_val(vmg0_);
else
end_idx = cnt;
/* push self and the fill value for gc protection */
G_stk->push(retval);
G_stk->push(&fill_val);
/* expand the vector to the requested size */
if (end_idx > cnt)
set_element_count_undo(vmg_ self, end_idx);
/* set the elements to the fill value */
for (idx = start_idx ; idx < end_idx ; ++idx)
{
/* set the element to the fill value, recording undo for the change */
set_element_undo(vmg_ self, idx, &fill_val);
}
/* discard GC protection */
G_stk->discard(2);
/* handled */
return TRUE;
}
/* ------------------------------------------------------------------------ */
/*
* property evaluator - get the number of elements
*/
int CVmObjVector::getp_get_size(VMG_ vm_obj_id_t, vm_val_t *retval,
uint *argc)
{
static CVmNativeCodeDesc desc(0);
/* check arguments */
if (get_prop_check_argc(retval, argc, &desc))
return TRUE;
/* return my element count */
retval->set_int(get_element_count());
/* handled */
return TRUE;
}
/* ------------------------------------------------------------------------ */
/*
* property evaluator - select a subset; returns a new vector consisting
* of the subset of the original vector
*/
int CVmObjVector::getp_subset(VMG_ vm_obj_id_t self, vm_val_t *retval,
uint *argc)
{
const vm_val_t *func_val;
size_t src;
size_t dst;
size_t cnt;
CVmObjVector *new_vec;
static CVmNativeCodeDesc desc(1);
/* check arguments */
if (get_prop_check_argc(retval, argc, &desc))
return TRUE;
/* get the function pointer argument, but leave it on the stack */
func_val = G_stk->get(0);
/* push a self-reference while allocating to protect from gc */
G_interpreter->push_obj(vmg_ self);
/*
* allocate the new vector that we'll return - all we know at this
* point is that the new vector won't be larger than the current
* vector, so allocate at the current size
*/
cnt = get_element_count();
retval->set_obj(create(vmg_ FALSE, cnt));
/* get the return value as an vector */
new_vec = (CVmObjVector *)vm_objp(vmg_ retval->val.obj);
/*
* push a reference to the new list to protect it from the garbage
* collector, which could be invoked in the course of executing the
* user callback
*/
G_stk->push(retval);
/*
* Go through each element of our list, and invoke the callback on
* each element. If the element passes, write it to the current
* output location in the list; otherwise, just skip it.
*
* Note that we're using the same list as source and destination,
* which is easy because the list will either shrink or stay the
* same - we'll never need to insert new elements.
*/
for (src = dst = 0 ; src < cnt ; ++src)
{
vm_val_t ele;
const vm_val_t *val;
/* get this element from the source vector */
get_element(src, &ele);
/* push the element as the callback's argument */
G_stk->push(&ele);
/* invoke the callback */
G_interpreter->call_func_ptr(vmg_ func_val, 1, "Vector.subset", 0);
/* get the result from R0 */
val = G_interpreter->get_r0();
/*
* if the callback returned non-nil and non-zero, include this
* element in the result
*/
if (val->typ == VM_NIL
|| (val->typ == VM_INT && val->val.intval == 0))
{
/* it's nil or zero - don't include it in the result */
}
else
{
/*
* include this element in the result (there's no need to
* save undo, since the whole vector is new)
*/
new_vec->set_element(dst, &ele);
/* advance the output index */
++dst;
}
}
/* set the actual number of elements in the result */
new_vec->set_element_count(dst);
/* discard our gc protection (self, return value) and our arguments */
G_stk->discard(3);
/* handled */
return TRUE;
}
/* ------------------------------------------------------------------------ */
/*
* property evaluator - apply a callback to each element
*/
int CVmObjVector::getp_apply_all(VMG_ vm_obj_id_t self, vm_val_t *retval,
uint *argc)
{
const vm_val_t *func_val;
size_t idx;
static CVmNativeCodeDesc desc(1);
/* check arguments */
if (get_prop_check_argc(retval, argc, &desc))
return TRUE;
/* get the function pointer argument, but leave it on the stack */
func_val = G_stk->get(0);
/* push a self-reference while working to protect from gc */
G_interpreter->push_obj(vmg_ self);
/*
* we're going to return the 'self' object, since we update the vector
* in-place
*/
retval->set_obj(self);
/*
* Go through each element of our vector, invoking the callback on
* each element. Replace each element with the result of the
* callback. Note that we intentionally re-check the element count on
* each iteration, in case the callback changes the number of
* elements.
*/
for (idx = 0 ; idx < get_element_count() ; ++idx)
{
/* push this element as the callback's argument */
push_element(vmg_ idx);
/* invoke the callback */
G_interpreter->call_func_ptr(vmg_ func_val, 1, "Vector.applyAll", 0);
/* replace this element with the result */
set_element_undo(vmg_ self, idx, G_interpreter->get_r0());
}
/* discard our gc protection (self) and our arguments */
G_stk->discard(2);
/* handled */
return TRUE;
}
/* ------------------------------------------------------------------------ */
/*
* property evaluator - find the first element matching a condition
*/
int CVmObjVector::getp_index_which(VMG_ vm_obj_id_t self, vm_val_t *retval,
uint *argc)
{
/* use our general handler, working forwards */
return gen_index_which(vmg_ self, retval, argc, TRUE, FALSE);
}
/*
* property evaluator - lastIndexWhich
*/
int CVmObjVector::getp_last_index_which(VMG_ vm_obj_id_t self,
vm_val_t *retval, uint *argc)
{
/* use our general handler, working backwards */
return gen_index_which(vmg_ self, retval, argc, FALSE, FALSE);
}
/*
* general handler for indexWhich and lastIndexWhich
*/
int CVmObjVector::gen_index_which(VMG_ vm_obj_id_t self, vm_val_t *retval,
uint *argc, int forward, int count_only)
{
const vm_val_t *func_val;
size_t cnt;
size_t idx;
int val_cnt;
static CVmNativeCodeDesc desc(1);
/* check arguments */
if (get_prop_check_argc(retval, argc, &desc))
return TRUE;
/* get the function pointer argument, but leave it on the stack */
func_val = G_stk->get(0);
/* push a self-reference while working to protect from gc */
G_interpreter->push_obj(vmg_ self);
/* presume we won't find a matching element */
retval->set_nil();
val_cnt = 0;
/* get the number of elements to visit */
cnt = get_element_count();
/* start at the first or last element, depending on direction */
idx = (forward ? 0 : cnt);
/*
* Go through each element of our vector, and invoke the callback on
* each element, looking for the first one that returns true.
*/
for (;;)
{
/* if we've reached the last element, we can stop looking */
if (forward ? idx >= cnt : idx == 0)
break;
/* if we're going backwards, decrement the index */
if (!forward)
--idx;
/* push the element as the callback's argument */
push_element(vmg_ idx);
/* invoke the callback */
G_interpreter->call_func_ptr(vmg_ func_val, 1,
"Vector.indexWhich", 0);
/*
* if the callback returned true, we've found the element we're
* looking for
*/
if (G_interpreter->get_r0()->typ == VM_NIL
|| (G_interpreter->get_r0()->typ == VM_INT
&& G_interpreter->get_r0()->val.intval == 0))
{
/* nil or zero - this one failed the test, so keep looking */
}
else
{
/* it passed the test - check what we're doing */
if (count_only)
{
/* we only want the count */
++val_cnt;
}
else
{
/* we want the (1-based) index - return it */
retval->set_int(idx + 1);
/* found it - no need to keep looking */
break;
}
}
/* if we're going forwards, increment the index */
if (forward)
++idx;
}
/* discard our gc protection (self) and our arguments */
G_stk->discard(2);
/* return the count if desired */
if (count_only)
retval->set_int(val_cnt);
/* handled */
return TRUE;
}
/* ------------------------------------------------------------------------ */
/*
* property evaluator - lastValWhich
*/
int CVmObjVector::getp_last_val_which(VMG_ vm_obj_id_t self,
vm_val_t *retval, uint *argc)
{
/* get the index of the value using lastIndexWhich */
getp_last_index_which(vmg_ self, retval, argc);
/* if the return value is a valid index, get the value at the index */
if (retval->typ == VM_INT)
{
int idx;
/* get the element as the return value */
idx = (int)retval->val.intval;
get_element(idx - 1, retval);
}
/* handled */
return TRUE;
}
/* ------------------------------------------------------------------------ */
/*
* property evaluator - valWhich
*/
int CVmObjVector::getp_val_which(VMG_ vm_obj_id_t self, vm_val_t *retval,
uint *argc)
{
/* get the index of the value using indexWhich */
getp_index_which(vmg_ self, retval, argc);
/* if the return value is a valid index, get the value at the index */
if (retval->typ == VM_INT)
{
int idx;
/* get the element as the return value */
idx = (int)retval->val.intval;
get_element(idx - 1, retval);
}
/* handled */
return TRUE;
}
/* ------------------------------------------------------------------------ */
/*
* property evaluator - call a callback for each element
*/
int CVmObjVector::getp_for_each(VMG_ vm_obj_id_t self, vm_val_t *retval,
uint *argc)
{
/* use the general forEach processor */
return for_each_gen(vmg_ self, retval, argc, FALSE);
}
/*
* property evaluator - call a callback for each element
*/
int CVmObjVector::getp_for_each_assoc(VMG_ vm_obj_id_t self,
vm_val_t *retval, uint *argc)
{
/* use the general forEach processor */
return for_each_gen(vmg_ self, retval, argc, TRUE);
}
/*
* General forEach/forEachAssoc processor
*/
int CVmObjVector::for_each_gen(VMG_ vm_obj_id_t self,
vm_val_t *retval, uint *argc,
int pass_key_to_cb)
{
const vm_val_t *func_val;
size_t idx;
static CVmNativeCodeDesc desc(1);
/* check arguments */
if (get_prop_check_argc(retval, argc, &desc))
return TRUE;
/* get the function pointer argument, but leave it on the stack */
func_val = G_stk->get(0);
/* push a self-reference while working to protect from gc */
G_interpreter->push_obj(vmg_ self);
/* no return value */
retval->set_nil();
/*
* Invoke the callback on each element. Note that we intentionally do
* not cache the element count, since it is possible that a program
* could change the vector size in the course of an iteration; if we
* cached the size, and the actual size were reduced during the
* iteration, we would visit invalid elements past the new end of the
* vector. To avoid this possibility, we re-check the current element
* count on each iteration to make sure we haven't run off the end of
* the vector.
*/
for (idx = 0 ; idx < get_element_count() ; ++idx)
{
/* push the element as the callback's argument */
push_element(vmg_ idx);
/* push the index argument if desired */
if (pass_key_to_cb)
G_stk->push()->set_int(idx + 1);
/* invoke the callback */
G_interpreter->call_func_ptr(vmg_ func_val, pass_key_to_cb ? 2 : 1,
"Vector.forEach", 0);
}
/* discard our gc protection (self) and our arguments */
G_stk->discard(2);
/* handled */
return TRUE;
}
/* ------------------------------------------------------------------------ */
/*
* property evaluator - mapAll
*/
int CVmObjVector::getp_map_all(VMG_ vm_obj_id_t self, vm_val_t *retval,
uint *argc)
{
const vm_val_t *func_val;
size_t idx;
CVmObjVector *new_vec;
static CVmNativeCodeDesc desc(1);
/* check arguments */
if (get_prop_check_argc(retval, argc, &desc))
return TRUE;
/* get the function pointer argument, but leave it on the stack */
func_val = G_stk->get(0);
/* push a self-reference while working to protect from gc */
G_interpreter->push_obj(vmg_ self);
/*
* allocate a new vector for the return value - the new vector will
* have the same size as the original, since we're mapping each
* element of the old vector to the corresponding element of the new
* vector
*/
retval->set_obj(create(vmg_ FALSE, get_allocated_count()));
/* get the return value as an vector */
new_vec = (CVmObjVector *)vm_objp(vmg_ retval->val.obj);
/* set the result element count the same as my own */
new_vec->set_element_count(get_element_count());
/*
* push a reference to the new list to protect it from the garbage
* collector, which could be invoked in the course of executing the
* user callback
*/
G_stk->push(retval);
/*
* Go through each element of our vector, and invoke the callback on
* each element, storing the result in the corresponding element of
* the new vector. Note that we re-check the element count on each
* iteration, in case the callback changes it on us.
*/
for (idx = 0 ; idx < get_element_count() ; ++idx)
{
/* push the element as the callback's argument */
push_element(vmg_ idx);
/* invoke the callback */
G_interpreter->call_func_ptr(vmg_ func_val, 1, "Vector.mapAll", 0);
/*
* replace this element with the result (there's no need to save
* undo, since the whole vector is new)
*/
new_vec->set_element(idx, G_interpreter->get_r0());
}
/* discard our gc protection (self, new vector) and our arguments */
G_stk->discard(3);
/* handled */
return TRUE;
}
/* ------------------------------------------------------------------------ */
/*
* property evaluator - indexOf
*/
int CVmObjVector::getp_index_of(VMG_ vm_obj_id_t self, vm_val_t *retval,
uint *argc)
{
/* use our general handler, going forwards */
return gen_index_of(vmg_ self, retval, argc, TRUE, FALSE);
}
/*
* property evaluator - lastIndexOf
*/
int CVmObjVector::getp_last_index_of(VMG_ vm_obj_id_t self, vm_val_t *retval,
uint *argc)
{
/* use our general handler, going backwards */
return gen_index_of(vmg_ self, retval, argc, FALSE, FALSE);
}
/*
* general handler for indexOf and lastIndexOf - searches the vector
* either forwards of backwards for a given value
*/
int CVmObjVector::gen_index_of(VMG_ vm_obj_id_t self, vm_val_t *retval,
uint *argc, int forward, int count_only)
{
const vm_val_t *val;
size_t cnt;
size_t idx;
int val_cnt;
static CVmNativeCodeDesc desc(1);
/* check arguments */
if (get_prop_check_argc(retval, argc, &desc))
return TRUE;
/* get the value, but leave it on the stack */
val = G_stk->get(0);
/* push a self-reference while working to protect from gc */
G_interpreter->push_obj(vmg_ self);
/* presume we won't find a matching element */
retval->set_nil();
val_cnt = 0;
/* get the number of elements to visit */
cnt = get_element_count();
/* start at the first or last element, depending on the direction */
idx = (forward ? 0 : cnt);
/* scan the list, looking for the element */
for (;;)
{
vm_val_t ele;
/* if we've reached the last element, stop looking */
if (forward ? idx >= cnt : idx == 0)
break;
/* if we're going backwards, move to the next element position */
if (!forward)
--idx;
/* get this element */
get_element(idx, &ele);
/* if the element matches the search value, return its index */
if (ele.equals(vmg_ val))
{
/* it matches - see what we're doing */
if (count_only)
{
/* they only want the count */
++val_cnt;
}
else
{
/* this is the one - return its 1-based index */
retval->set_int(idx + 1);
/* foind it - no need to keep searching */
break;
}
}
/* if we're going forwards, move to the next element */
if (forward)
++idx;
}
/* discard our gc protection (self) and our arguments */
G_stk->discard(2);
/* return the count if desired */
if (count_only)
retval->set_int(val_cnt);
/* handled */
return TRUE;
}
/* ------------------------------------------------------------------------ */
/*
* property evaluator - countOf
*/
int CVmObjVector::getp_count_of(VMG_ vm_obj_id_t self, vm_val_t *retval,
uint *argc)
{
/* use our general handler to obtain the count */
return gen_index_of(vmg_ self, retval, argc, TRUE, TRUE);
}
/* ------------------------------------------------------------------------ */
/*
* property evaluator - countWhich
*/
int CVmObjVector::getp_count_which(VMG_ vm_obj_id_t self, vm_val_t *retval,
uint *argc)
{
/* use our general handler to obtain the count */
return gen_index_which(vmg_ self, retval, argc, TRUE, TRUE);
}
/* ------------------------------------------------------------------------ */
/*
* property evaluator - getUnique; returns a new vector consisting of the
* unique elements of the original vector
*/
int CVmObjVector::getp_get_unique(VMG_ vm_obj_id_t self, vm_val_t *retval,
uint *argc)
{
CVmObjVector *new_vec;
size_t cnt;
static CVmNativeCodeDesc desc(0);
/* check arguments */
if (get_prop_check_argc(retval, argc, &desc))
return TRUE;
/* put myself on the stack for GC protection */
G_interpreter->push_obj(vmg_ self);
/*
* allocate a new vector for the return value - the new vector will
* never be any larger than the original
*/
cnt = get_element_count();
retval->set_obj(create(vmg_ FALSE, cnt));
/* push a reference to the new list for gc protection */
G_stk->push(retval);
/* get the return value as a vector */
new_vec = (CVmObjVector *)vm_objp(vmg_ retval->val.obj);
/* start out with the result element count the same as my own */
new_vec->set_element_count(cnt);
/* copy my elements to the new vector */
memcpy(new_vec->get_element_ptr(0), get_element_ptr(0),
calc_alloc_ele(cnt));
/* uniquify the result */
new_vec->cons_uniquify(vmg0_);
/* discard the gc protection */
G_stk->discard(2);
/* handled */
return TRUE;
}
/*
* For construction, eliminate repeated elements of the vector, leaving
* only the unique elements. Reduces the size of the vector to the size
* required to accommodate the unique elements.
*/
void CVmObjVector::cons_uniquify(VMG0_)
{
size_t cnt;
size_t src, dst;
/* get the number of elements */
cnt = get_element_count();
/* loop through the list and look for repeated values */
for (src = dst = 0 ; src < cnt ; ++src)
{
size_t idx;
vm_val_t src_val;
int found;
/*
* look for a copy of this source value already in the output list
*/
get_element(src, &src_val);
for (idx = 0, found = FALSE ; idx < dst ; ++idx)
{
vm_val_t idx_val;
/* get this value */
get_element(idx, &idx_val);
/* if it's equal to the current source value, note it */
if (src_val.equals(vmg_ &idx_val))
{
/* note that we found it */
found = TRUE;
/* no need to look any further */
break;
}
}
/* if we didn't find the value, copy it to the output list */
if (!found)
{
/*
* add it to the output vector - since this is a
* construction-only method, there's no need to save undo (if
* we apply undo, we'll undo the entire construction of the
* vector, hence there's no need to track changes made since
* creation)
*/
set_element(dst, &src_val);
/* count it in the output */
++dst;
}
}
/* adjust the size of the result list */
set_element_count(dst);
}
/* ------------------------------------------------------------------------ */
/*
* property evaluator - appendUnique
*/
int CVmObjVector::getp_append_unique(VMG_ vm_obj_id_t self, vm_val_t *retval,
uint *argc)
{
vm_val_t *val2;
size_t cnt2;
size_t cnt;
static CVmNativeCodeDesc desc(1);
size_t i;
/* check arguments */
if (get_prop_check_argc(retval, argc, &desc))
return TRUE;
/* get the other value, but leave it on the stack */
val2 = G_stk->get(0);
/* the return value is 'self' */
retval->set_obj(self);
/* put myself on the stack for GC protection */
G_stk->push(retval);
/* get the number of elements in the original */
cnt = get_element_count();
/* get the number of elements to append */
cnt2 = val2->get_coll_addsub_rhs_ele_cnt(vmg0_);
/* expand myself to make room for the new elements */
expand_by(vmg_ self, cnt2);
/* append each element of the right-hand side */
for (i = 0 ; i < cnt2 ; ++i)
{
vm_val_t ele;
/* get this element */
val2->get_coll_addsub_rhs_ele(vmg_ i + 1, &ele);
/* store it */
set_element_undo(vmg_ self, cnt + i, &ele);
}
/* uniquify the result */
uniquify_for_append(vmg_ self);
/* discard the gc protection and arguments */
G_stk->discard(2);
/* handled */
return TRUE;
}
/*
* uniquify after an appendUnique operation
*/
void CVmObjVector::uniquify_for_append(VMG_ vm_obj_id_t self)
{
size_t cnt;
size_t src, dst;
/* get the number of elements */
cnt = get_element_count();
/* loop through the list and look for repeated values */
for (src = dst = 0 ; src < cnt ; ++src)
{
size_t idx;
vm_val_t src_val;
int found;
/* look for a copy of this value elsewhere in the vector */
get_element(src, &src_val);
for (idx = 0, found = FALSE ; idx < dst ; ++idx)
{
vm_val_t idx_val;
/* get this value */
get_element(idx, &idx_val);
/* if it's equal to the current source value, note it */
if (src_val.equals(vmg_ &idx_val))
{
/* note that we found it */
found = TRUE;
/* no need to look any further */
break;
}
}
/* if we didn't find the value, copy it to the output list */
if (!found)
{
/*
* Add it to the output vector if we're actually changing this
* element (i.e., the destination and source indices are
* unequal).
*/
if (dst != src)
set_element_undo(vmg_ self, dst, &src_val);
/* count it in the output */
++dst;
}
}
/* adjust the size of the result list */
set_element_count_undo(vmg_ self, dst);
}
/* ------------------------------------------------------------------------ */
/*
* sorter for vector
*/
class CVmQSortVector: public CVmQSortVal
{
public:
CVmQSortVector()
{
vec_ = 0;
self_ = VM_INVALID_OBJ;
}
/* get an element from the vector */
void get_ele(VMG_ size_t idx, vm_val_t *val)
{ vec_->get_element(idx, val); }
/* store an element */
void set_ele(VMG_ size_t idx, const vm_val_t *val)
{ vec_->set_element_undo(vmg_ self_, idx, val); }
/* our vector object */
CVmObjVector *vec_;
vm_obj_id_t self_;
};
/*
* property evaluator - sort
*/
int CVmObjVector::getp_sort(VMG_ vm_obj_id_t self, vm_val_t *retval,
uint *in_argc)
{
size_t len;
uint argc = (in_argc == 0 ? 0 : *in_argc);
CVmQSortVector sorter;
static CVmNativeCodeDesc desc(0, 2);
/* check arguments */
if (get_prop_check_argc(retval, in_argc, &desc))
return TRUE;
/* remember the length of my list */
len = get_element_count();
/* set the vector object in the sorter */
sorter.vec_ = this;
sorter.self_ = self;
/* if we have an 'descending' flag, note it */
if (argc >= 1)
sorter.descending_ = (G_stk->get(0)->typ != VM_NIL);
/*
* if we have a comparison function, note it, but leave it on the
* stack for gc protection
*/
if (argc >= 2)
sorter.compare_fn_ = *G_stk->get(1);
/* put myself on the stack for GC protection */
G_interpreter->push_obj(vmg_ self);
/* sort the vector, if we have any elements */
if (len != 0)
sorter.sort(vmg_ 0, len - 1);
/* discard the gc protection and arguments */
G_stk->discard(1 + argc);
/* return myself */
retval->set_obj(self);
/* handled */
return TRUE;
}
/* ------------------------------------------------------------------------ */
/*
* property evaluator - set the length
*/
int CVmObjVector::getp_set_length(VMG_ vm_obj_id_t self, vm_val_t *retval,
uint *argc)
{
size_t old_len;
size_t new_len;
size_t idx;
vm_val_t nil_val;
static CVmNativeCodeDesc desc(1);
/* check arguments */
if (get_prop_check_argc(retval, argc, &desc))
return TRUE;
/* the return value is 'self' */
retval->set_obj(self);
/* note the old length */
old_len = get_element_count();
/* get the new length */
new_len = (size_t)CVmBif::pop_int_val(vmg0_);
/* can't go less than zero */
if (new_len < 0)
err_throw(VMERR_BAD_VAL_BIF);
/* set the vector to its new size */
set_element_count_undo(vmg_ self, new_len);
/*
* Set each newly added element to nil. Note that we don't bother
* saving undo for these changes: to undo this change, the only thing
* we'll have to do is reduce the vector size to its previous size,
* and we've already saved undo for the size change.
*/
nil_val.set_nil();
for (idx = old_len ; idx < new_len ; ++idx)
set_element(idx, &nil_val);
/* handled */
return TRUE;
}
/* ------------------------------------------------------------------------ */
/*
* property evaluator - insert one or more elements at a given index
*/
int CVmObjVector::getp_insert_at(VMG_ vm_obj_id_t self, vm_val_t *retval,
uint *in_argc)
{
size_t idx;
size_t ins_idx;
size_t old_cnt;
size_t add_cnt;
size_t new_cnt;
uint argc = (in_argc != 0 ? *in_argc : 0);
static CVmNativeCodeDesc desc(2, 0, TRUE);
/* we must have at least two arguments */
if (get_prop_check_argc(retval, in_argc, &desc))
return TRUE;
/* get the starting insertion index */
ins_idx = (size_t)CVmBif::pop_int_val(vmg0_);
/* the return value is 'self' */
retval->set_obj(self);
/* note the original size of the vector */
old_cnt = get_element_count();
/*
* note the number of items we're adding - we're adding each
* argument, other than the first
*/
add_cnt = argc - 1;
/*
* note the new size - we're inserting one element for each argument
* past the first one
*/
new_cnt = old_cnt + add_cnt;
/*
* if the starting index is out of range, it's an error - it can
* range from 1 to one higher than the current element count (the
* top end of the range allows us to insert elements after all of
* the current elements)
*/
if (ins_idx < 1 || ins_idx > get_element_count() + 1)
err_throw(VMERR_INDEX_OUT_OF_RANGE);
/* adjust the starting index to a zero-based value */
--ins_idx;
/* create the new elements */
set_element_count_undo(vmg_ self, new_cnt);
/*
* Move the existing elements to their new slots. Start with the
* starting index, and move it to its new position, since the starting
* index is the first element that needs to be moved. Note that we
* start at the top and work down, to avoid overwriting any
* overlapping part of the new position block and the old position
* block.
*
* If we're inserting after the last element, there's no moving that
* needs to be done.
*/
if (ins_idx < old_cnt)
{
idx = old_cnt;
for (;;)
{
size_t new_idx;
vm_val_t ele;
/* move to the previous element */
--idx;
/* get the value at this slot */
get_element(idx, &ele);
/* calculate the new slot */
new_idx = idx + add_cnt;
/*
* if the new index is within the old size range, keep undo
* for the change; otherwise, no undo is necessary, since when
* we undo we'll be completely dropping this new slot
*/
if (new_idx < old_cnt)
set_element_undo(vmg_ self, new_idx, &ele);
else
set_element(new_idx, &ele);
/* if this was the last element, we're done */
if (idx == ins_idx)
break;
}
}
/* add the new items */
for (idx = ins_idx ; add_cnt != 0 ; ++idx, --add_cnt)
{
vm_val_t val;
/* pop the next argument value */
G_stk->pop(&val);
/* store the item, keeping undo only if it's in the old size range */
if (idx < old_cnt)
set_element_undo(vmg_ self, idx, &val);
else
set_element(idx, &val);
}
/* handled */
return TRUE;
}
/* ------------------------------------------------------------------------ */
/*
* property evaluator - append an element
*/
int CVmObjVector::getp_prepend(VMG_ vm_obj_id_t self, vm_val_t *retval,
uint *argc)
{
vm_val_t val;
size_t cnt;
size_t i;
static CVmNativeCodeDesc desc(1);
/* check arguments */
if (get_prop_check_argc(retval, argc, &desc))
return TRUE;
/* get the old size */
cnt = get_element_count();
/* expand the vector by one element */
set_element_count_undo(vmg_ self, cnt + 1);
/* move each existing element up one slot to make room for the new one */
for (i = cnt ; i != 0 ; --i)
{
/* get this element */
get_element(i - 1, &val);
/*
* bump it up to the next slot - keep undo except for the last
* slot, which is newly added and thus doesn't have an old value
* that we need to keep
*/
if (i == cnt)
set_element(i, &val);
else
set_element_undo(vmg_ self, i, &val);
}
/* retrieve the value for the new element */
G_stk->pop(&val);
/*
* set the first element - note that there's no need to save undo,
* since undoing this operation will simply discard this element (in
* other words, there's no previous value for this element since it's
* being created anew here)
*/
set_element(0, &val);
/* the return value is 'self' */
retval->set_obj(self);
/* handled */
return TRUE;
}
/* ------------------------------------------------------------------------ */
/*
* property evaluator - remove an element at the given position
*/
int CVmObjVector::getp_remove_element_at(VMG_ vm_obj_id_t self,
vm_val_t *retval, uint *argc)
{
size_t start_idx;
static CVmNativeCodeDesc desc(1);
/* check arguments */
if (get_prop_check_argc(retval, argc, &desc))
return TRUE;
/* get the deletion index */
start_idx = (size_t)CVmBif::pop_int_val(vmg0_);
/* make sure the index is in range */
if (start_idx < 1 || start_idx > get_element_count())
err_throw(VMERR_INDEX_OUT_OF_RANGE);
/* adjust to a zero-based index */
--start_idx;
/* the return value is 'self' */
retval->set_obj(self);
/* delete one element */
remove_elements_undo(vmg_ self, start_idx, 1);
/* handled */
return TRUE;
}
/* ------------------------------------------------------------------------ */
/*
* property evaluator - remove element(s) matching a given value
*/
int CVmObjVector::getp_remove_element(VMG_ vm_obj_id_t self,
vm_val_t *retval, uint *argc)
{
vm_val_t *del_val;
static CVmNativeCodeDesc desc(1);
size_t src;
size_t dst;
size_t old_cnt;
/* check arguments */
if (get_prop_check_argc(retval, argc, &desc))
return TRUE;
/* get the value to be removed */
del_val = G_stk->get(0);
/* the return value is 'self'; leave it on the stack */
retval->set_obj(self);
G_stk->push(retval);
/* get the original number of elements */
old_cnt = get_element_count();
/* delete each element matching the given value */
for (src = dst = 0 ; src < old_cnt ; ++src)
{
vm_val_t ele_val;
/* get this element */
get_element(src, &ele_val);
/*
* if this element doesn't match the value to be deleted, add it
* to the result vector
*/
if (!ele_val.equals(vmg_ del_val))
{
/*
* This element is a keeper - if we're moving it to a new
* position (i.e., we've deleted any elements before this
* one), store it at its new position, keeping undo
* information. If we're storing it at its same position,
* there's no need to re-store the value or keep any undo,
* since no change is involved.
*/
if (dst != src)
set_element_undo(vmg_ self, dst, &ele_val);
/* increment the write index past this element */
++dst;
}
}
/* if the element count changed, set the new element count */
if (dst != old_cnt)
set_element_count_undo(vmg_ self, dst);
/* discard gc protection and arguments */
G_stk->discard(2);
/* handled */
return TRUE;
}
/* ------------------------------------------------------------------------ */
/*
* property evaluator - remove a range of elements
*/
int CVmObjVector::getp_remove_range(VMG_ vm_obj_id_t self,
vm_val_t *retval, uint *argc)
{
size_t start_idx;
size_t end_idx;
static CVmNativeCodeDesc desc(2);
/* check arguments */
if (get_prop_check_argc(retval, argc, &desc))
return TRUE;
/* get the starting index */
start_idx = (size_t)CVmBif::pop_int_val(vmg0_);
/*
* make sure the index is in range - it must refer to an existing
* element
*/
if (start_idx < 1 || start_idx > get_element_count())
err_throw(VMERR_INDEX_OUT_OF_RANGE);
/* get the ending index */
end_idx = (size_t)CVmBif::pop_int_val(vmg0_);
/*
* make sure it's in range - it must refer to an existing element,
* and it must be greater than or equal to the starting index
*/
if (end_idx < start_idx || end_idx > get_element_count())
err_throw(VMERR_INDEX_OUT_OF_RANGE);
/* adjust to zero-based indices */
--start_idx;
--end_idx;
/* the return value is 'self' */
retval->set_obj(self);
/*
* delete the elements - the number of elements we're deleting is
* one higher than the difference of the starting and ending indices
* (if the two indices are the same, we're deleting just the one
* element)
*/
remove_elements_undo(vmg_ self, start_idx, end_idx - start_idx + 1);
/* handled */
return TRUE;
}
/* ------------------------------------------------------------------------ */
/*
* property evaluator - append an element
*/
int CVmObjVector::getp_append(VMG_ vm_obj_id_t self, vm_val_t *retval,
uint *argc)
{
vm_val_t *valp;
size_t cnt;
static CVmNativeCodeDesc desc(1);
/* check arguments */
if (get_prop_check_argc(retval, argc, &desc))
return TRUE;
/* get the value to append, but leave it on the stack */
valp = G_stk->get(0);
/* get the old size */
cnt = get_element_count();
/* the result object is 'self' */
retval->set_obj(self);
/* push a self-reference for gc protection */
G_stk->push(retval);
/* expand myself by one element to make room for the addition */
expand_by(vmg_ self, 1);
/* add the new element, saving undo */
set_element_undo(vmg_ self, cnt, valp);
/* discard the argument and gc protection */
G_stk->discard(2);
/* handled */
return TRUE;
}
/* ------------------------------------------------------------------------ */
/*
* property evaluator - append elements from a collection, or append a
* single non-collection element
*/
int CVmObjVector::getp_append_all(VMG_ vm_obj_id_t self, vm_val_t *retval,
uint *argc)
{
vm_val_t *valp;
size_t cnt;
static CVmNativeCodeDesc desc(1);
size_t add_cnt;
size_t i;
/* check arguments */
if (get_prop_check_argc(retval, argc, &desc))
return TRUE;
/* get the value to append, but leave it on the stack */
valp = G_stk->get(0);
/* the result object is 'self' */
retval->set_obj(self);
/* push a self-reference for gc protection */
G_stk->push(retval);
/* get the old size */
cnt = get_element_count();
/* get the number of items to add */
add_cnt = valp->get_coll_addsub_rhs_ele_cnt(vmg0_);
/* expand myself to make room for the new elements */
expand_by(vmg_ self, add_cnt);
/* add the new elements */
for (i = 0 ; i < add_cnt ; ++i)
{
vm_val_t ele;
/* get the next new element */
valp->get_coll_addsub_rhs_ele(vmg_ i + 1, &ele);
/*
* add the new element - note that we don't need to keep undo
* because we created this new element in this same operation, and
* hence undoing the operation will truncate the vector to exclude
* the element, and hence we don't need an old value for the
* element
*/
set_element(cnt + i, &ele);
}
/* discard the argument and gc protection */
G_stk->discard(2);
/* handled */
return TRUE;
}
/* ------------------------------------------------------------------------ */
/*
* Delete a range of elements
*/
void CVmObjVector::remove_elements_undo(VMG_ vm_obj_id_t self,
size_t start_idx, size_t del_cnt)
{
size_t idx;
size_t cnt;
/* note the original size of the vector */
cnt = get_element_count();
/* move all of the elements to their new slots, keeping undo */
for (idx = start_idx ; idx < cnt ; ++idx)
{
vm_val_t val;
size_t move_idx;
/*
* calculate the index of the element to replace this one - it's
* past us by the number of items being deleted
*/
move_idx = idx + del_cnt;
/*
* if we're moving from a valid index in the old vector, get the
* element; otherwise, replace the value with nil (but still
* replace the value, since we want to keep undo for its
* original value)
*/
if (move_idx < cnt)
get_element(move_idx, &val);
else
val.set_nil();
/* store the element at its new position */
set_element_undo(vmg_ self, idx, &val);
}
/*
* Reduce the size, keeping undo. Note that it's important we do
* this last: undo is applied in reverse order, so when applying
* undo, we first want to increase the vector's size to its original
* size, then we want to apply the element value restorations.
*/
set_element_count_undo(vmg_ self, cnt - del_cnt);
}
|