1 #ifdef RCSID
2 static char RCSid[] =
3 "$Header$";
4 #endif
5
6 /*
7 * Copyright (c) 2000, 2002 Michael J. Roberts. All Rights Reserved.
8 *
9 * Please see the accompanying license file, LICENSE.TXT, for information
10 * on using and copying this software.
11 */
12 /*
13 Name
14 vmvec.cpp - T3 VM Vector metaclass
15 Function
16
17 Notes
18
19 Modified
20 05/14/00 MJRoberts - Creation
21 */
22
23 #include <stdlib.h>
24
25 #include "t3std.h"
26 #include "vmtype.h"
27 #include "vmmcreg.h"
28 #include "vmmeta.h"
29 #include "vmglob.h"
30 #include "vmobj.h"
31 #include "vmvec.h"
32 #include "vmlst.h"
33 #include "vmerr.h"
34 #include "vmerrnum.h"
35 #include "vmfile.h"
36 #include "vmstack.h"
37 #include "vmbif.h"
38 #include "vmundo.h"
39 #include "vmrun.h"
40 #include "vmiter.h"
41 #include "vmsort.h"
42
43
44 /* ------------------------------------------------------------------------ */
45 /*
46 * statics
47 */
48
49 /* metaclass registration object */
50 static CVmMetaclassVector metaclass_reg_obj;
51 CVmMetaclass *CVmObjVector::metaclass_reg_ = &metaclass_reg_obj;
52
53 /* function table */
54 int (CVmObjVector::
55 *CVmObjVector::func_table_[])(VMG_ vm_obj_id_t self,
56 vm_val_t *retval, uint *argc) =
57 {
58 &CVmObjVector::getp_undef,
59 &CVmObjVector::getp_to_list,
60 &CVmObjVector::getp_get_size,
61 &CVmObjVector::getp_copy_from,
62 &CVmObjVector::getp_fill_val,
63 &CVmObjVector::getp_subset,
64 &CVmObjVector::getp_apply_all,
65 &CVmObjVector::getp_index_which,
66 &CVmObjVector::getp_for_each,
67 &CVmObjVector::getp_for_each_assoc,
68 &CVmObjVector::getp_map_all,
69 &CVmObjVector::getp_index_of,
70 &CVmObjVector::getp_val_which,
71 &CVmObjVector::getp_last_index_of,
72 &CVmObjVector::getp_last_index_which,
73 &CVmObjVector::getp_last_val_which,
74 &CVmObjVector::getp_count_of,
75 &CVmObjVector::getp_count_which,
76 &CVmObjVector::getp_get_unique,
77 &CVmObjVector::getp_append_unique,
78 &CVmObjVector::getp_sort,
79 &CVmObjVector::getp_set_length,
80 &CVmObjVector::getp_insert_at,
81 &CVmObjVector::getp_remove_element_at,
82 &CVmObjVector::getp_remove_range,
83 &CVmObjVector::getp_append,
84 &CVmObjVector::getp_prepend,
85 &CVmObjVector::getp_append_all,
86 &CVmObjVector::getp_remove_element
87 };
88
89 /* ------------------------------------------------------------------------ */
90 /*
91 * create with a given number of elements
92 */
CVmObjVector(VMG_ size_t element_count)93 CVmObjVector::CVmObjVector(VMG_ size_t element_count)
94 {
95 /* allocate space */
96 alloc_vector(vmg_ element_count);
97
98 /*
99 * a vector intially has no elements in use; the element_count
100 * merely gives the number of slots to be allocated initially (this
101 * won't affect the allocation count, which is stored separately)
102 */
103 set_element_count(0);
104 }
105
106 /* ------------------------------------------------------------------------ */
107 /*
108 * allocate space
109 */
alloc_vector(VMG_ size_t cnt)110 void CVmObjVector::alloc_vector(VMG_ size_t cnt)
111 {
112 /* allocate space for the given number of elements */
113 ext_ = (char *)G_mem->get_var_heap()->alloc_mem(calc_alloc(cnt), this);
114
115 /* set the element count and allocated count */
116 set_allocated_count(cnt);
117 set_element_count(cnt);
118
119 /* clear the undo bits */
120 clear_undo_bits();
121 }
122
123 /* ------------------------------------------------------------------------ */
124 /*
125 * notify of deletion
126 */
notify_delete(VMG_ int)127 void CVmObjVector::notify_delete(VMG_ int)
128 {
129 /* free our extension */
130 if (ext_ != 0)
131 G_mem->get_var_heap()->free_mem(ext_);
132 }
133
134 /* ------------------------------------------------------------------------ */
135 /*
136 * create dynamically using stack arguments
137 */
create_from_stack(VMG_ const uchar ** pc_ptr,uint argc)138 vm_obj_id_t CVmObjVector::create_from_stack(VMG_ const uchar **pc_ptr,
139 uint argc)
140 {
141 vm_val_t *arg1;
142 vm_obj_id_t id;
143 CVmObjVector *vec;
144 size_t cnt;
145 size_t copy_cnt;
146 const char *src_lst;
147 CVmObjVector *src_vec;
148 size_t i;
149
150 /* check arguments */
151 if (argc < 1 || argc > 2)
152 err_throw(VMERR_WRONG_NUM_OF_ARGS);
153
154 /* get the first argument */
155 arg1 = G_stk->get(0);
156
157 /* presume we will have no source argumnet */
158 copy_cnt = 0;
159 src_lst = 0;
160 src_vec = 0;
161
162 /* check what kind of argument we have */
163 if (arg1->typ == VM_INT)
164 {
165 /* get the number of elements to allocate */
166 cnt = (size_t)arg1->val.intval;
167 }
168 else
169 {
170 /* anything else is invalid */
171 err_throw(VMERR_BAD_TYPE_BIF);
172 }
173
174 /* if there's a second argument, it's a source list/vector */
175 if (argc >= 2)
176 {
177 vm_val_t *arg2;
178
179 /* get the second argument */
180 arg2 = G_stk->get(1);
181
182 /* see what we have */
183 if ((src_lst = arg2->get_as_list(vmg0_)) != 0)
184 {
185 /* it's a list - copy its elements */
186 copy_cnt = vmb_get_len(src_lst);
187 }
188 else if (arg2->typ == VM_OBJ
189 && (vm_objp(vmg_ arg2->val.obj)
190 ->is_of_metaclass(metaclass_reg_)))
191 {
192 /* it's a vector */
193 src_vec = (CVmObjVector *)vm_objp(vmg_ arg2->val.obj);
194 copy_cnt = src_vec->get_element_count();
195 }
196 else if (arg2->typ == VM_INT)
197 {
198 /* they want an explicit initial size */
199 copy_cnt = (size_t)arg2->val.intval;
200 }
201 else
202 {
203 /* invalid source type */
204 err_throw(VMERR_BAD_TYPE_BIF);
205 }
206
207 /*
208 * make sure we allocate enough initial space to store the source
209 * object we're copying
210 */
211 if (copy_cnt > cnt)
212 cnt = copy_cnt;
213 }
214
215 /* create the vector with the given number of elements */
216 id = vm_new_id(vmg_ FALSE, TRUE, FALSE);
217 vec = new (vmg_ id) CVmObjVector(vmg_ cnt);
218
219 /* copy or initialize the elements */
220 for (i = 0 ; i < copy_cnt ; ++i)
221 {
222 vm_val_t ele;
223
224 /*
225 * get my element at this index: if we have a source list or
226 * vector, take the current element from that list or vector;
227 * otherwise, initialize the element to nil
228 */
229 if (src_lst != 0)
230 CVmObjList::index_list(vmg_ &ele, src_lst, i + 1);
231 else if (src_vec != 0)
232 src_vec->get_element(i, &ele);
233 else
234 ele.set_nil();
235
236 /*
237 * set my element at this index - this is construction, so there's
238 * no need to save undo information
239 */
240 vec->set_element(i, &ele);
241 }
242
243 /* set the initial size */
244 vec->set_element_count(copy_cnt);
245
246 /* discard arguments */
247 G_stk->discard(argc);
248
249 /* return the new object */
250 return id;
251 }
252
253 /* ------------------------------------------------------------------------ */
254 /*
255 * save to a file
256 */
save_to_file(VMG_ class CVmFile * fp)257 void CVmObjVector::save_to_file(VMG_ class CVmFile *fp)
258 {
259 size_t ele_cnt;
260 size_t alloc_cnt;
261
262 /* get our element count and full allocation count */
263 alloc_cnt = get_allocated_count();
264 ele_cnt = get_element_count();
265
266 /* write the counts and the elements */
267 fp->write_int2(alloc_cnt);
268 fp->write_int2(ele_cnt);
269 fp->write_bytes(get_element_ptr(0), calc_alloc_ele(ele_cnt));
270 }
271
272 /*
273 * restore from a file
274 */
restore_from_file(VMG_ vm_obj_id_t self,CVmFile * fp,CVmObjFixup * fixups)275 void CVmObjVector::restore_from_file(VMG_ vm_obj_id_t self,
276 CVmFile *fp, CVmObjFixup *fixups)
277 {
278 size_t ele_cnt;
279 size_t alloc_cnt;
280
281 /* read the element count and the full allocation count */
282 alloc_cnt = fp->read_uint2();
283 ele_cnt = fp->read_uint2();
284
285 /* free any existing extension */
286 if (ext_ != 0)
287 {
288 G_mem->get_var_heap()->free_mem(ext_);
289 ext_ = 0;
290 }
291
292 /* allocate the space at the allocation count */
293 alloc_vector(vmg_ alloc_cnt);
294
295 /* set the actual in-use element count */
296 set_element_count(ele_cnt);
297
298 /* read the contents */
299 fp->read_bytes(get_element_ptr(0), calc_alloc_ele(ele_cnt));
300
301 /* fix up object references in the values */
302 fixups->fix_dh_array(vmg_ get_element_ptr(0), ele_cnt);
303 }
304
305 /* ------------------------------------------------------------------------ */
306 /*
307 * create with no initial contents
308 */
create(VMG_ int in_root_set)309 vm_obj_id_t CVmObjVector::create(VMG_ int in_root_set)
310 {
311 vm_obj_id_t id = vm_new_id(vmg_ in_root_set, TRUE, FALSE);
312 new (vmg_ id) CVmObjVector();
313 return id;
314 }
315
316 /*
317 * create with a given number of elements
318 */
create(VMG_ int in_root_set,size_t element_count)319 vm_obj_id_t CVmObjVector::create(VMG_ int in_root_set, size_t element_count)
320 {
321 vm_obj_id_t id = vm_new_id(vmg_ in_root_set, TRUE, FALSE);
322 new (vmg_ id) CVmObjVector(vmg_ element_count);
323 return id;
324 }
325
326
327 /* ------------------------------------------------------------------------ */
328 /*
329 * Create a copy of this object
330 */
create_copy(VMG_ const vm_val_t * self_val)331 vm_obj_id_t CVmObjVector::create_copy(VMG_ const vm_val_t *self_val)
332 {
333 vm_obj_id_t new_id;
334 CVmObjVector *new_vec;
335
336 /* save the original object on the stack for gc protection */
337 G_stk->push(self_val);
338
339 /* create a new vector with the same parameters as this one */
340 new_id = vm_new_id(vmg_ FALSE, TRUE, FALSE);
341 new_vec = new (vmg_ new_id) CVmObjVector(vmg_ get_allocated_count());
342 new_vec->set_element_count(get_element_count());
343
344 /* copy the elements from the old vector to the new vector */
345 memcpy(new_vec->get_element_ptr(0), get_element_ptr(0),
346 calc_alloc_ele(get_element_count()));
347
348 /* done with the gc protection - discard it */
349 G_stk->discard(1);
350
351 /* return the new vector's object ID */
352 return new_id;
353 }
354
355
356 /* ------------------------------------------------------------------------ */
357 /*
358 * Create an iterator
359 */
new_iterator(VMG_ vm_val_t * retval,const vm_val_t * self_val)360 void CVmObjVector::new_iterator(VMG_ vm_val_t *retval,
361 const vm_val_t *self_val)
362 {
363 vm_val_t copy_val;
364
365 /*
366 * Create a copy of the vector - since a vector can change, we must
367 * always create an iterator based on a copy, so that the iterator has
368 * a consistent view of the original as of the time the iterator was
369 * created.
370 */
371 copy_val.set_obj(create_copy(vmg_ self_val));
372
373 /*
374 * Set up a new indexed iterator object. The first valid index for a
375 * vector is always 1, and the last valid index is the same as the
376 * number of elements.
377 */
378 retval->set_obj(CVmObjIterIdx::create_for_coll(vmg_ ©_val,
379 1, get_element_count()));
380 }
381
382 /*
383 * Create a live iterator
384 */
new_live_iterator(VMG_ vm_val_t * retval,const vm_val_t * self_val)385 void CVmObjVector::new_live_iterator(VMG_ vm_val_t *retval,
386 const vm_val_t *self_val)
387 {
388 /*
389 * Set up a new indexed iterator object. The first valid index for a
390 * vector is always 1, and the last valid index is the same as the
391 * number of elements. Since we want a "live" iterator, create the
392 * iterator with a direct reference to the vector.
393 */
394 retval->set_obj(CVmObjIterIdx::create_for_coll(vmg_ self_val,
395 1, get_element_count()));
396 }
397
398 /* ------------------------------------------------------------------------ */
399 /*
400 * set a property
401 */
set_prop(VMG_ class CVmUndo *,vm_obj_id_t,vm_prop_id_t,const vm_val_t *)402 void CVmObjVector::set_prop(VMG_ class CVmUndo *,
403 vm_obj_id_t, vm_prop_id_t,
404 const vm_val_t *)
405 {
406 err_throw(VMERR_INVALID_SETPROP);
407 }
408
409 /* ------------------------------------------------------------------------ */
410 /*
411 * get a property
412 */
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)413 int CVmObjVector::get_prop(VMG_ vm_prop_id_t prop, vm_val_t *retval,
414 vm_obj_id_t self, vm_obj_id_t *source_obj,
415 uint *argc)
416 {
417 ushort func_idx;
418
419 /* translate the property index to an index into our function table */
420 func_idx = G_meta_table
421 ->prop_to_vector_idx(metaclass_reg_->get_reg_idx(), prop);
422
423 /* call the appropriate function */
424 if ((this->*func_table_[func_idx])(vmg_ self, retval, argc))
425 {
426 *source_obj = metaclass_reg_->get_class_obj(vmg0_);
427 return TRUE;
428 }
429
430 /* inherit our base class handling */
431 return CVmObjCollection::
432 get_prop(vmg_ prop, retval, self, source_obj, argc);
433 }
434
435 /* ------------------------------------------------------------------------ */
436 /*
437 * Expand the vector to accommodate the given number of new elements.
438 * We'll increase the element count, and we'll save undo for the change.
439 * If necessary, we'll re-allocate our memory extension block at a
440 * larger size.
441 */
expand_by(VMG_ vm_obj_id_t self,size_t added_elements)442 void CVmObjVector::expand_by(VMG_ vm_obj_id_t self, size_t added_elements)
443 {
444 size_t new_ele_cnt;
445
446 /* calculate the element count */
447 new_ele_cnt = get_element_count() + added_elements;
448
449 /* remember the new size, saving undo */
450 set_element_count_undo(vmg_ self, new_ele_cnt);
451 }
452
453
454 /* ------------------------------------------------------------------------ */
455 /*
456 * Set the element count, keeping undo for the change
457 */
set_element_count_undo(VMG_ vm_obj_id_t self,size_t new_ele_cnt)458 void CVmObjVector::set_element_count_undo(VMG_ vm_obj_id_t self,
459 size_t new_ele_cnt)
460 {
461 /* add undo if necessary */
462 if (G_undo != 0)
463 {
464 size_t old_ele_cnt;
465 vm_val_t old_val;
466 size_t idx;
467 vm_val_t nil_val;
468
469 /* get the old size */
470 old_ele_cnt = get_element_count();
471
472 /*
473 * If we're shrinking the vector, save undo for each element we're
474 * dropping off the end; to do this, simply set each element we're
475 * losing to nil, since this will add an undo record with the
476 * original value of the element.
477 *
478 * Note that we must save undo for dropped elements BEFORE we set
479 * the vector's new sizd. We must perform the steps in this order
480 * because undo is applied in reverse order: when we read back the
481 * undo, we'll first change the vector size to its old size (since
482 * that's the last saved undo instruction), then we'll set the
483 * elements to their old values.
484 */
485 nil_val.set_nil();
486 for (idx = new_ele_cnt ; idx < old_ele_cnt ; ++idx)
487 set_element_undo(vmg_ self, idx, &nil_val);
488
489 /* set up an integer with the pre-modification element count */
490 old_val.set_int(old_ele_cnt);
491
492 /*
493 * add the undo record - use the special index value 0xffffffff
494 * as the key; this will never be a valid index for a real
495 * element, because we could never address an element with an
496 * index this high in a 32-bit address space
497 */
498 G_undo->add_new_record_int_key(vmg_ self, 0xffffffffU, &old_val);
499 }
500
501 /*
502 * if the new size is larger than our current allocation size,
503 * increase the allocated size
504 */
505 if (new_ele_cnt > get_allocated_count())
506 {
507 size_t new_alloc_cnt;
508 size_t new_mem_size;
509
510 /* bump up the allocated size */
511 new_alloc_cnt = get_allocated_count() + get_alloc_count_increment();
512
513 /* if that's still not big enough, go up to the requested size */
514 if (new_alloc_cnt < new_ele_cnt)
515 new_alloc_cnt = new_ele_cnt;
516
517 /* get the new size we need */
518 new_mem_size = calc_alloc(new_alloc_cnt);
519
520 /* reallocate our memory at the new, larger size */
521 ext_ = (char *)G_mem->get_var_heap()
522 ->realloc_mem(new_mem_size, ext_, this);
523
524 /* set the new allocation size */
525 set_allocated_count(new_alloc_cnt);
526
527 /*
528 * clear the undo bits (it's easier than moving them, and the only
529 * cost is that we might generate some redundant undo records)
530 */
531 clear_undo_bits();
532 }
533
534 /*
535 * if we're expanding the in-use size of the vector, set each
536 * newly-added element to nil
537 */
538 if (new_ele_cnt > get_element_count())
539 {
540 size_t i;
541 vm_val_t nil_val;
542
543 /*
544 * set the old value for each new element to nil (we don't have to
545 * save undo for this, since the undo operation for the change in
546 * size will simply discard all of the new elements)
547 */
548 nil_val.set_nil();
549 for (i = get_element_count() ; i < new_ele_cnt ; ++i)
550 set_element(i, &nil_val);
551 }
552
553 /* set the new element count */
554 set_element_count(new_ele_cnt);
555 }
556
557 /* ------------------------------------------------------------------------ */
558 /*
559 * notify of new undo savepoint
560 */
notify_new_savept()561 void CVmObjVector::notify_new_savept()
562 {
563 /*
564 * clear the undo bits - we obviously have no undo for the new
565 * savepoint yet
566 */
567 clear_undo_bits();
568 }
569
570 /* ------------------------------------------------------------------------ */
571 /*
572 * apply undo
573 */
apply_undo(VMG_ struct CVmUndoRecord * rec)574 void CVmObjVector::apply_undo(VMG_ struct CVmUndoRecord *rec)
575 {
576 /*
577 * if the record refers to index 0xffffffff, this is a size-change
578 * record; otherwise, it's an ordinary element change record
579 */
580 if ((size_t)rec->id.intval == 0xffffffffU)
581 {
582 /*
583 * it's a size change - apply the new size, which is stored as
584 * an integer in the old value record
585 */
586 set_element_count((size_t)rec->oldval.val.intval);
587 }
588 else
589 {
590 /* it's an ordinary index record - set the element from undo data */
591 set_element((size_t)rec->id.intval, &rec->oldval);
592 }
593 }
594
595 /* ------------------------------------------------------------------------ */
596 /*
597 * mark references from an undo record
598 */
mark_undo_ref(VMG_ CVmUndoRecord * rec)599 void CVmObjVector::mark_undo_ref(VMG_ CVmUndoRecord *rec)
600 {
601 /* if the undo record refers to an object, mark the object */
602 if (rec->oldval.typ == VM_OBJ)
603 G_obj_table->mark_all_refs(rec->oldval.val.obj, VMOBJ_REACHABLE);
604 }
605
606 /* ------------------------------------------------------------------------ */
607 /*
608 * mark references
609 */
mark_refs(VMG_ uint state)610 void CVmObjVector::mark_refs(VMG_ uint state)
611 {
612 size_t cnt;
613 char *p;
614
615 /* get my element count */
616 cnt = get_element_count();
617
618 /* mark as referenced each object in the vector */
619 for (p = get_element_ptr(0) ; cnt != 0 ; --cnt, inc_element_ptr(&p))
620 {
621 /*
622 * if this is an object, mark it as referenced, and mark its
623 * references as referenced
624 */
625 if (vmb_get_dh_type(p) == VM_OBJ)
626 G_obj_table->mark_all_refs(vmb_get_dh_obj(p), state);
627 }
628 }
629
630 /* ------------------------------------------------------------------------ */
631 /*
632 * load from an image file
633 */
load_from_image(VMG_ vm_obj_id_t self,const char * ptr,size_t siz)634 void CVmObjVector::load_from_image(VMG_ vm_obj_id_t self,
635 const char *ptr, size_t siz)
636 {
637 /* load from the image data */
638 load_image_data(vmg_ ptr, siz);
639
640 /*
641 * save our image data pointer in the object table, so that we can
642 * access it (without storing it ourselves) during a reload
643 */
644 G_obj_table->save_image_pointer(self, ptr, siz);
645 }
646
647 /*
648 * reload the object from image data
649 */
reload_from_image(VMG_ vm_obj_id_t,const char * ptr,size_t siz)650 void CVmObjVector::reload_from_image(VMG_ vm_obj_id_t,
651 const char *ptr, size_t siz)
652 {
653 /* load the image data */
654 load_image_data(vmg_ ptr, siz);
655 }
656
657 /*
658 * load the data from an image file
659 */
load_image_data(VMG_ const char * ptr,size_t siz)660 void CVmObjVector::load_image_data(VMG_ const char *ptr, size_t siz)
661 {
662 size_t alloc_cnt;
663 size_t ele_cnt;
664
665 /* if we already have memory allocated, free it */
666 if (ext_ != 0)
667 {
668 G_mem->get_var_heap()->free_mem(ext_);
669 ext_ = 0;
670 }
671
672 /* get the allocation size and the actual in-use size */
673 alloc_cnt = vmb_get_len(ptr);
674 ele_cnt = vmb_get_len(ptr + VMB_LEN);
675
676 /* make sure we allocate at least as much as is actually in use */
677 if (alloc_cnt < ele_cnt)
678 alloc_cnt = ele_cnt;
679
680 /* make sure the size isn't larger than we'd expect */
681 if (siz > VMB_LEN*2 + (VMB_DATAHOLDER * ele_cnt))
682 siz = VMB_LEN*2 + (VMB_DATAHOLDER * ele_cnt);
683
684 /*
685 * allocate memory at the new allocation size as indicated in the
686 * image data
687 */
688 alloc_vector(vmg_ alloc_cnt);
689
690 /* set the in-use element count */
691 set_element_count(ele_cnt);
692
693 /*
694 * if the size is smaller than we'd expect from the initialized
695 * element count, set extra elements to nil
696 */
697 if (siz < VMB_LEN*2 + (VMB_DATAHOLDER * ele_cnt))
698 {
699 size_t i;
700 vm_val_t nil_val;
701
702 /* set all elements to nil */
703 nil_val.set_nil();
704 for (i = 0 ; i < ele_cnt ; ++i)
705 set_element(i, &nil_val);
706 }
707
708 /* copy the initialized elements from the image data */
709 memcpy(get_element_ptr(0), ptr + VMB_LEN*2, siz - VMB_LEN*2);
710
711 /* we're resetting to initial state, so forget all undo */
712 clear_undo_bits();
713 }
714
715 /* ------------------------------------------------------------------------ */
716 /*
717 * index the vector
718 */
index_val(VMG_ vm_val_t * result,vm_obj_id_t,const vm_val_t * index_val)719 void CVmObjVector::index_val(VMG_ vm_val_t *result, vm_obj_id_t,
720 const vm_val_t *index_val)
721 {
722 uint32 idx;
723
724 /* get the index value as an integer */
725 idx = index_val->num_to_int();
726
727 /* make sure it's in range - 1 to our element count, inclusive */
728 if (idx < 1 || idx > get_element_count())
729 err_throw(VMERR_INDEX_OUT_OF_RANGE);
730
731 /*
732 * get the indexed element and store it in the result, adjusting the
733 * index to the C-style 0-based range
734 */
735 get_element(idx - 1, result);
736 }
737
738 /* ------------------------------------------------------------------------ */
739 /*
740 * set an indexed element of the vector
741 */
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)742 void CVmObjVector::set_index_val(VMG_ vm_val_t *new_container,
743 vm_obj_id_t self,
744 const vm_val_t *index_val,
745 const vm_val_t *new_val)
746 {
747 uint32 idx;
748
749 /* get the index value as an integer */
750 idx = index_val->num_to_int();
751
752 /* make sure it's at least 1 */
753 if (idx < 1)
754 err_throw(VMERR_INDEX_OUT_OF_RANGE);
755
756 /*
757 * if it's higher than the current length, extend the vector with nil
758 * entries to the requested size
759 */
760 if (idx > get_element_count())
761 {
762 size_t i;
763 vm_val_t nil_val;
764
765 /* note the first new element index */
766 i = get_element_count();
767
768 /* extend the vector */
769 set_element_count_undo(vmg_ self, idx);
770
771 /*
772 * Fill in entries between the old length and the new length with
773 * nil. Note that we don't have to fill in the very last element,
774 * since we'll explicitly set it to the new value momentarily
775 * anyway. Note also that we don't need to keep undo for the
776 * initializations, since on undo we'll truncate the vector to
777 * remove the newly-added elements and thus won't need to restore
778 * any values for the slots.
779 */
780 for (nil_val.set_nil() ; i < (size_t)idx - 1 ; ++i)
781 set_element(i, &nil_val);
782
783 /*
784 * set the new value - this doesn't require undo since we had to
785 * expand the vector to make room for it
786 */
787 set_element(idx - 1, new_val);
788 }
789 else
790 {
791 /* set the element and record undo, using a zero-based index */
792 set_element_undo(vmg_ self, (size_t)idx - 1, new_val);
793 }
794
795 /* the result is the original vector value */
796 new_container->set_obj(self);
797 }
798
799 /* ------------------------------------------------------------------------ */
800 /*
801 * Set an element and record undo for the change - takes a C-style 0-based
802 * index.
803 */
set_element_undo(VMG_ vm_obj_id_t self,size_t idx,const vm_val_t * new_val)804 void CVmObjVector::set_element_undo(VMG_ vm_obj_id_t self,
805 size_t idx, const vm_val_t *new_val)
806 {
807 /* if we don't have undo for this element already, save undo now */
808 if (G_undo != 0 && !get_undo_bit(idx))
809 {
810 vm_val_t old_val;
811
812 /* get the pre-modification value of this element */
813 get_element(idx, &old_val);
814
815 /* add the undo record */
816 G_undo->add_new_record_int_key(vmg_ self, idx, &old_val);
817
818 /*
819 * set the undo bit to indicate that we now have undo for this
820 * element
821 */
822 set_undo_bit(idx, TRUE);
823 }
824
825 /* get the indexed element and store it in the result */
826 set_element(idx, new_val);
827 }
828
829 /* ------------------------------------------------------------------------ */
830 /*
831 * Check a value for equality. We will match any list or vector with the
832 * same number of elements and the same value for each element.
833 */
equals(VMG_ vm_obj_id_t self,const vm_val_t * val,int depth) const834 int CVmObjVector::equals(VMG_ vm_obj_id_t self, const vm_val_t *val,
835 int depth) const
836 {
837 const char *p;
838 int p_is_list;
839 CVmObjVector *vec2;
840 size_t cnt;
841 size_t cnt2;
842 size_t idx;
843
844 /* if the recursion depth is excessive, throw an error */
845 if (depth > VM_MAX_TREE_DEPTH_EQ)
846 err_throw(VMERR_TREE_TOO_DEEP_EQ);
847
848 /* if the other value is a reference to myself, we certainly match */
849 if (val->typ == VM_OBJ && val->val.obj == self)
850 {
851 /* no need to look at the contents if this is a reference to me */
852 return TRUE;
853 }
854
855 /* try it as a list and as a vector */
856 if ((p = val->get_as_list(vmg0_)) != 0)
857 {
858 /* it's a list */
859 p_is_list = TRUE;
860
861 /* get the number of elements in the list */
862 cnt2 = vmb_get_len(p);
863 }
864 else
865 {
866 /* it's not a list */
867 p_is_list = FALSE;
868
869 /* check to see if it's a vector */
870 if (val->typ == VM_OBJ
871 && (vm_objp(vmg_ val->val.obj)->is_of_metaclass(metaclass_reg_)))
872 {
873 /* it's a vector - cast it accordingly */
874 vec2 = (CVmObjVector *)vm_objp(vmg_ val->val.obj);
875
876 /* get the number of elements */
877 cnt2 = vec2->get_element_count();
878 }
879 else
880 {
881 /* it's some other type, so it can't be equal */
882 return FALSE;
883 }
884 }
885
886 /* if the sizes don't match, the values are not equal */
887 cnt = get_element_count();
888 if (cnt != cnt2)
889 return FALSE;
890
891 /* compare element by element */
892 for (idx = 0 ; idx < cnt ; ++idx)
893 {
894 vm_val_t val1;
895 vm_val_t val2;
896
897 /* get this element of self */
898 get_element(idx, &val1);
899
900 /* get this element of the other value */
901 if (p_is_list)
902 CVmObjList::index_list(vmg_ &val2, p, idx + 1);
903 else
904 vec2->get_element(idx, &val2);
905
906 /* if these elements aren't equal, our values aren't equal */
907 if (!val1.equals(vmg_ &val2, depth + 1))
908 return FALSE;
909
910 /*
911 * if the other value is a list, re-translate it in case we did
912 * any constant data swapping
913 */
914 if (p_is_list)
915 p = val->get_as_list(vmg0_);
916 }
917
918 /* we didn't find any differences, so the values are equal */
919 return TRUE;
920 }
921
922 /* ------------------------------------------------------------------------ */
923 /*
924 * Hash value calculation
925 */
calc_hash(VMG_ vm_obj_id_t self,int depth) const926 uint CVmObjVector::calc_hash(VMG_ vm_obj_id_t self, int depth) const
927 {
928 uint hash;
929 uint i;
930
931 /* if the recursion depth is excessive, throw an error */
932 if (depth > VM_MAX_TREE_DEPTH_EQ)
933 err_throw(VMERR_TREE_TOO_DEEP_EQ);
934
935 /* combine the hash values of the members of the vector */
936 for (hash = 0, i = 0 ; i < get_element_count() ; ++i)
937 {
938 vm_val_t ele;
939
940 /* get this element */
941 get_element(i, &ele);
942
943 /*
944 * Calculate its hash value and add it into the combined hash.
945 *
946 * Note that we have to increase the recursion depth, because we're
947 * recursively calculating the hash value of our contents, and our
948 * contents can refer (directly or indirectly) back to this object.
949 * In other words, we can have cycles in the reference tree from
950 * this object back to itself, so we need to keep track of the
951 * recursion depth to ensure we don't loop forever.
952 */
953 hash += ele.calc_hash(vmg_ depth + 1);
954 }
955
956 /* return the combined hash */
957 return hash;
958 }
959
960 /* ------------------------------------------------------------------------ */
961 /*
962 * add a value to the vector
963 */
add_val(VMG_ vm_val_t * result,vm_obj_id_t self,const vm_val_t * val)964 void CVmObjVector::add_val(VMG_ vm_val_t *result,
965 vm_obj_id_t self, const vm_val_t *val)
966 {
967 size_t idx;
968 size_t rhs_cnt;
969 size_t i;
970 CVmObjVector *new_vec;
971
972 /* push the value to append, for gc protection */
973 G_stk->push(val);
974
975 /*
976 * remember the index of the first new element - this is simply one
977 * higher than the last valid current index
978 */
979 idx = get_element_count();
980
981 /* get the number of elements to add */
982 rhs_cnt = val->get_coll_addsub_rhs_ele_cnt(vmg0_);
983
984 /*
985 * allocate a copy of the vector for the result - make it big enough
986 * for my elements plus the elements to be appended
987 */
988 result->set_obj(create(vmg_ FALSE, idx + rhs_cnt));
989
990 /* get the return value as a vector */
991 new_vec = (CVmObjVector *)vm_objp(vmg_ result->val.obj);
992
993 /*
994 * set the element count in the new vector (we're creating it anew, so
995 * there's no need to save undo)
996 */
997 new_vec->set_element_count(idx + rhs_cnt);
998
999 /* push the result for gc protection */
1000 G_stk->push(result);
1001
1002 /* copy my elements into the result */
1003 memcpy(new_vec->get_element_ptr(0), get_element_ptr(0),
1004 calc_alloc_ele(idx));
1005
1006 /* add the new elements */
1007 for (i = 0 ; i < rhs_cnt ; ++i, ++idx)
1008 {
1009 vm_val_t ele;
1010
1011 /* get this element from the right-hand side */
1012 val->get_coll_addsub_rhs_ele(vmg_ i + 1, &ele);
1013
1014 /* store the element in the result */
1015 new_vec->set_element(idx, &ele);
1016 }
1017
1018 /* discard the gc protect */
1019 G_stk->discard(2);
1020 }
1021
1022 /* ------------------------------------------------------------------------ */
1023 /*
1024 * subtract a value from the vector
1025 */
sub_val(VMG_ vm_val_t * result,vm_obj_id_t self,const vm_val_t * val)1026 void CVmObjVector::sub_val(VMG_ vm_val_t *result,
1027 vm_obj_id_t self, const vm_val_t *val)
1028 {
1029 size_t ele_cnt;
1030 size_t rhs_cnt;
1031 size_t i;
1032 CVmObjVector *new_vec;
1033 size_t new_cnt;
1034
1035 /* push the value to append, for gc protection */
1036 G_stk->push(val);
1037
1038 /* note the initial element count */
1039 ele_cnt = get_element_count();
1040
1041 /* get the number of elements to remove */
1042 rhs_cnt = val->get_coll_addsub_rhs_ele_cnt(vmg0_);
1043
1044 /*
1045 * allocate a new vector for the result - the new object will be no
1046 * larger than the current object, so allocate at the same size
1047 */
1048 result->set_obj(create(vmg_ FALSE, ele_cnt));
1049
1050 /* push the result for gc protection */
1051 G_stk->push(result);
1052
1053 /* get the return value as a vector */
1054 new_vec = (CVmObjVector *)vm_objp(vmg_ result->val.obj);
1055
1056 /*
1057 * copy each element from me to the new vector, but don't copy
1058 * elements found in the right-hand side
1059 */
1060 for (i = 0, new_cnt = 0 ; i < ele_cnt ; ++i)
1061 {
1062 vm_val_t ele;
1063 size_t j;
1064 int found;
1065
1066 /* get this element of self */
1067 get_element(i, &ele);
1068
1069 /* scan the right side for the element */
1070 for (found = FALSE, j = 0 ; j < rhs_cnt ; ++j)
1071 {
1072 vm_val_t rh_ele;
1073
1074 /* get this right-hand element */
1075 val->get_coll_addsub_rhs_ele(vmg_ j + 1, &rh_ele);
1076
1077 /*
1078 * if this right-hand element matches this left-hand element,
1079 * note that we have a match
1080 */
1081 if (rh_ele.equals(vmg_ &ele))
1082 {
1083 /* note that we found it */
1084 found = TRUE;
1085
1086 /* no need to look any further - one match is enough */
1087 break;
1088 }
1089 }
1090
1091 /*
1092 * If we didn't find the value, include it in the result vector.
1093 * Note that we don't have to save undo, since we're still
1094 * constructing the new vector.
1095 */
1096 if (!found)
1097 {
1098 /* store the element in the result vector */
1099 new_vec->set_element(new_cnt, &ele);
1100
1101 /* count it */
1102 ++new_cnt;
1103 }
1104 }
1105
1106 /*
1107 * update the new vector's size - we don't have to save undo for this
1108 * change, because we're still constructing the new vector
1109 */
1110 new_vec->set_element_count(new_cnt);
1111
1112 /* discard the gc protection */
1113 G_stk->discard(2);
1114 }
1115
1116 /* ------------------------------------------------------------------------ */
1117 /*
1118 * property evaluator - convert to a list
1119 */
getp_to_list(VMG_ vm_obj_id_t self,vm_val_t * retval,uint * argc)1120 int CVmObjVector::getp_to_list(VMG_ vm_obj_id_t self, vm_val_t *retval,
1121 uint *argc)
1122 {
1123 size_t src_cnt;
1124 size_t dst_cnt;
1125 size_t start_idx;
1126 CVmObjList *lst;
1127 size_t idx;
1128 uint orig_argc = (argc != 0 ? *argc : 0);
1129 static CVmNativeCodeDesc desc(0, 2);
1130
1131 /* check arguments */
1132 if (get_prop_check_argc(retval, argc, &desc))
1133 return TRUE;
1134
1135 /* note our size */
1136 src_cnt = get_element_count();
1137
1138 /*
1139 * if there's a starting index, retrieve it; otherwise, start at our
1140 * first element
1141 */
1142 if (orig_argc >= 1)
1143 start_idx = (size_t)CVmBif::pop_int_val(vmg0_);
1144 else
1145 start_idx = 1;
1146
1147 /* if the starting index is below 1, force it to 1 */
1148 if (start_idx < 1)
1149 start_idx = 1;
1150
1151 /* adjust the starting index to a 0-based index */
1152 --start_idx;
1153
1154 /*
1155 * if there's a size argument, retrieve it; if it's not specified,
1156 * use our actual size for the output size
1157 */
1158 if (orig_argc >= 2)
1159 dst_cnt = (size_t)CVmBif::pop_int_val(vmg0_);
1160 else
1161 dst_cnt = src_cnt;
1162
1163 /*
1164 * in no case will the result list have more elements than we can
1165 * actually supply
1166 */
1167 if (start_idx >= src_cnt)
1168 {
1169 /* we're starting past our last element - we can't supply anything */
1170 dst_cnt = 0;
1171 }
1172 else if (src_cnt - start_idx < dst_cnt)
1173 {
1174 /* we can't supply as many values as requested - lower the size */
1175 dst_cnt = src_cnt - start_idx;
1176 }
1177
1178 /* push a self-reference for garbage collection protection */
1179 G_stk->push()->set_obj(self);
1180
1181 /* create the new list */
1182 retval->set_obj(CVmObjList::create(vmg_ FALSE, dst_cnt));
1183 lst = (CVmObjList *)vm_objp(vmg_ retval->val.obj);
1184
1185 /* set the list elements */
1186 for (idx = 0 ; idx < dst_cnt ; ++idx)
1187 {
1188 vm_val_t val;
1189
1190 /* get my element at this index */
1191 get_element(idx + start_idx, &val);
1192
1193 /* store the element in the list */
1194 lst->cons_set_element(idx, &val);
1195 }
1196
1197 /* discard the self-reference */
1198 G_stk->discard();
1199
1200 /* handled */
1201 return TRUE;
1202 }
1203
1204 /* ------------------------------------------------------------------------ */
1205 /*
1206 * property evaluator - copy from another vector or list
1207 */
getp_copy_from(VMG_ vm_obj_id_t self,vm_val_t * retval,uint * argc)1208 int CVmObjVector::getp_copy_from(VMG_ vm_obj_id_t self, vm_val_t *retval,
1209 uint *argc)
1210 {
1211 vm_val_t src_val;
1212 size_t src_cnt;
1213 size_t dst_cnt;
1214 size_t src_start;
1215 size_t dst_start;
1216 size_t copy_cnt;
1217 const char *src_lst;
1218 int src_is_list;
1219 CVmObjVector *src_vec;
1220 size_t i;
1221 static CVmNativeCodeDesc desc(4);
1222
1223 /* check arguments */
1224 if (get_prop_check_argc(retval, argc, &desc))
1225 return TRUE;
1226
1227 /* retrieve the source value */
1228 G_stk->pop(&src_val);
1229
1230 /* get the source starting index */
1231 src_start = (size_t)CVmBif::pop_int_val(vmg0_);
1232
1233 /* get the destination starting index */
1234 dst_start = (size_t)CVmBif::pop_int_val(vmg0_);
1235
1236 /* get the number of elements to copy */
1237 copy_cnt = (size_t)CVmBif::pop_int_val(vmg0_);
1238
1239 /* if either starting index is below 1, force it to 1 */
1240 if (src_start < 1)
1241 src_start = 1;
1242 if (dst_start < 1)
1243 dst_start = 1;
1244
1245 /* adjust the starting indices to 0-based values */
1246 --src_start;
1247 --dst_start;
1248
1249 /* note the destination length (i.e., my element count) */
1250 dst_cnt = get_element_count();
1251
1252 /* get the source value */
1253 if ((src_lst = src_val.get_as_list(vmg0_)) != 0)
1254 {
1255 /* note that it's a list */
1256 src_is_list = TRUE;
1257
1258 /* there's no source vector */
1259 src_vec = 0;
1260
1261 /* note the source size */
1262 src_cnt = vmb_get_len(src_lst);
1263 }
1264 else if (src_val.typ == VM_OBJ
1265 && (vm_objp(vmg_ src_val.val.obj)->get_metaclass_reg()
1266 == metaclass_reg_))
1267 {
1268 /* it's a vector */
1269 src_vec = (CVmObjVector *)vm_objp(vmg_ src_val.val.obj);
1270
1271 /* it's not a list */
1272 src_is_list = FALSE;
1273
1274 /* note the soure size */
1275 src_cnt = src_vec->get_element_count();
1276 }
1277 else
1278 err_throw(VMERR_BAD_TYPE_BIF);
1279
1280 /* limit our copying to the remaining elements of the source */
1281 if (src_start >= src_cnt)
1282 copy_cnt = 0;
1283 else if (src_start + copy_cnt >= src_cnt)
1284 copy_cnt = src_cnt - src_start;
1285
1286 /* expand the vector if necessary to make room for added elements */
1287 if (dst_start + copy_cnt - 1 > dst_cnt)
1288 set_element_count_undo(vmg_ self, dst_start + copy_cnt - 1);
1289
1290 /* set the list elements */
1291 for (i = 0 ; i < copy_cnt ; ++i)
1292 {
1293 vm_val_t val;
1294
1295 /* get my element at this index */
1296 if (src_is_list)
1297 CVmObjList::index_list(vmg_ &val, src_lst, i + src_start + 1);
1298 else
1299 src_vec->get_element(i + src_start, &val);
1300
1301 /* set my element at this index, recording undo for the change */
1302 set_element_undo(vmg_ self, i + dst_start, &val);
1303 }
1304
1305 /* the return value is 'self' */
1306 retval->set_obj(self);
1307
1308 /* handled */
1309 return TRUE;
1310 }
1311
1312 /* ------------------------------------------------------------------------ */
1313 /*
1314 * property evaluator - fill with a value
1315 */
getp_fill_val(VMG_ vm_obj_id_t self,vm_val_t * retval,uint * argc)1316 int CVmObjVector::getp_fill_val(VMG_ vm_obj_id_t self, vm_val_t *retval,
1317 uint *argc)
1318 {
1319 vm_val_t fill_val;
1320 size_t cnt;
1321 size_t start_idx;
1322 size_t end_idx;
1323 size_t idx;
1324 uint orig_argc = (argc != 0 ? *argc : 0);
1325 static CVmNativeCodeDesc desc(1, 2);
1326
1327 /* check arguments */
1328 if (get_prop_check_argc(retval, argc, &desc))
1329 return TRUE;
1330
1331 /* note our size */
1332 cnt = get_element_count();
1333
1334 /* the return value is 'self' */
1335 retval->set_obj(self);
1336
1337 /* get the fill value */
1338 G_stk->pop(&fill_val);
1339
1340 /*
1341 * if there's a starting index, retrieve it; otherwise, start at our
1342 * first element
1343 */
1344 if (orig_argc >= 2)
1345 start_idx = (size_t)CVmBif::pop_int_val(vmg0_);
1346 else
1347 start_idx = 1;
1348
1349 /* if the starting index is below 1, force it to 1 */
1350 if (start_idx < 1)
1351 start_idx = 1;
1352
1353 /* adjust the starting index to a 0-based index */
1354 --start_idx;
1355
1356 /*
1357 * if there's a count argument, retrieve it; if it's not specified,
1358 * use our actual size for the count
1359 */
1360 if (orig_argc >= 3)
1361 end_idx = start_idx + (size_t)CVmBif::pop_int_val(vmg0_);
1362 else
1363 end_idx = cnt;
1364
1365 /* push self and the fill value for gc protection */
1366 G_stk->push(retval);
1367 G_stk->push(&fill_val);
1368
1369 /* expand the vector to the requested size */
1370 if (end_idx > cnt)
1371 set_element_count_undo(vmg_ self, end_idx);
1372
1373 /* set the elements to the fill value */
1374 for (idx = start_idx ; idx < end_idx ; ++idx)
1375 {
1376 /* set the element to the fill value, recording undo for the change */
1377 set_element_undo(vmg_ self, idx, &fill_val);
1378 }
1379
1380 /* discard GC protection */
1381 G_stk->discard(2);
1382
1383 /* handled */
1384 return TRUE;
1385 }
1386
1387 /* ------------------------------------------------------------------------ */
1388 /*
1389 * property evaluator - get the number of elements
1390 */
getp_get_size(VMG_ vm_obj_id_t,vm_val_t * retval,uint * argc)1391 int CVmObjVector::getp_get_size(VMG_ vm_obj_id_t, vm_val_t *retval,
1392 uint *argc)
1393 {
1394 static CVmNativeCodeDesc desc(0);
1395
1396 /* check arguments */
1397 if (get_prop_check_argc(retval, argc, &desc))
1398 return TRUE;
1399
1400 /* return my element count */
1401 retval->set_int(get_element_count());
1402
1403 /* handled */
1404 return TRUE;
1405 }
1406
1407 /* ------------------------------------------------------------------------ */
1408 /*
1409 * property evaluator - select a subset; returns a new vector consisting
1410 * of the subset of the original vector
1411 */
getp_subset(VMG_ vm_obj_id_t self,vm_val_t * retval,uint * argc)1412 int CVmObjVector::getp_subset(VMG_ vm_obj_id_t self, vm_val_t *retval,
1413 uint *argc)
1414 {
1415 const vm_val_t *func_val;
1416 size_t src;
1417 size_t dst;
1418 size_t cnt;
1419 CVmObjVector *new_vec;
1420 static CVmNativeCodeDesc desc(1);
1421
1422 /* check arguments */
1423 if (get_prop_check_argc(retval, argc, &desc))
1424 return TRUE;
1425
1426 /* get the function pointer argument, but leave it on the stack */
1427 func_val = G_stk->get(0);
1428
1429 /* push a self-reference while allocating to protect from gc */
1430 G_interpreter->push_obj(vmg_ self);
1431
1432 /*
1433 * allocate the new vector that we'll return - all we know at this
1434 * point is that the new vector won't be larger than the current
1435 * vector, so allocate at the current size
1436 */
1437 cnt = get_element_count();
1438 retval->set_obj(create(vmg_ FALSE, cnt));
1439
1440 /* get the return value as an vector */
1441 new_vec = (CVmObjVector *)vm_objp(vmg_ retval->val.obj);
1442
1443 /*
1444 * push a reference to the new list to protect it from the garbage
1445 * collector, which could be invoked in the course of executing the
1446 * user callback
1447 */
1448 G_stk->push(retval);
1449
1450 /*
1451 * Go through each element of our list, and invoke the callback on
1452 * each element. If the element passes, write it to the current
1453 * output location in the list; otherwise, just skip it.
1454 *
1455 * Note that we're using the same list as source and destination,
1456 * which is easy because the list will either shrink or stay the
1457 * same - we'll never need to insert new elements.
1458 */
1459 for (src = dst = 0 ; src < cnt ; ++src)
1460 {
1461 vm_val_t ele;
1462 const vm_val_t *val;
1463
1464 /* get this element from the source vector */
1465 get_element(src, &ele);
1466
1467 /* push the element as the callback's argument */
1468 G_stk->push(&ele);
1469
1470 /* invoke the callback */
1471 G_interpreter->call_func_ptr(vmg_ func_val, 1, "Vector.subset", 0);
1472
1473 /* get the result from R0 */
1474 val = G_interpreter->get_r0();
1475
1476 /*
1477 * if the callback returned non-nil and non-zero, include this
1478 * element in the result
1479 */
1480 if (val->typ == VM_NIL
1481 || (val->typ == VM_INT && val->val.intval == 0))
1482 {
1483 /* it's nil or zero - don't include it in the result */
1484 }
1485 else
1486 {
1487 /*
1488 * include this element in the result (there's no need to
1489 * save undo, since the whole vector is new)
1490 */
1491 new_vec->set_element(dst, &ele);
1492
1493 /* advance the output index */
1494 ++dst;
1495 }
1496 }
1497
1498 /* set the actual number of elements in the result */
1499 new_vec->set_element_count(dst);
1500
1501 /* discard our gc protection (self, return value) and our arguments */
1502 G_stk->discard(3);
1503
1504 /* handled */
1505 return TRUE;
1506 }
1507
1508 /* ------------------------------------------------------------------------ */
1509 /*
1510 * property evaluator - apply a callback to each element
1511 */
getp_apply_all(VMG_ vm_obj_id_t self,vm_val_t * retval,uint * argc)1512 int CVmObjVector::getp_apply_all(VMG_ vm_obj_id_t self, vm_val_t *retval,
1513 uint *argc)
1514 {
1515 const vm_val_t *func_val;
1516 size_t idx;
1517 static CVmNativeCodeDesc desc(1);
1518
1519 /* check arguments */
1520 if (get_prop_check_argc(retval, argc, &desc))
1521 return TRUE;
1522
1523 /* get the function pointer argument, but leave it on the stack */
1524 func_val = G_stk->get(0);
1525
1526 /* push a self-reference while working to protect from gc */
1527 G_interpreter->push_obj(vmg_ self);
1528
1529 /*
1530 * we're going to return the 'self' object, since we update the vector
1531 * in-place
1532 */
1533 retval->set_obj(self);
1534
1535 /*
1536 * Go through each element of our vector, invoking the callback on
1537 * each element. Replace each element with the result of the
1538 * callback. Note that we intentionally re-check the element count on
1539 * each iteration, in case the callback changes the number of
1540 * elements.
1541 */
1542 for (idx = 0 ; idx < get_element_count() ; ++idx)
1543 {
1544 /* push this element as the callback's argument */
1545 push_element(vmg_ idx);
1546
1547 /* invoke the callback */
1548 G_interpreter->call_func_ptr(vmg_ func_val, 1, "Vector.applyAll", 0);
1549
1550 /* replace this element with the result */
1551 set_element_undo(vmg_ self, idx, G_interpreter->get_r0());
1552 }
1553
1554 /* discard our gc protection (self) and our arguments */
1555 G_stk->discard(2);
1556
1557 /* handled */
1558 return TRUE;
1559 }
1560
1561 /* ------------------------------------------------------------------------ */
1562 /*
1563 * property evaluator - find the first element matching a condition
1564 */
getp_index_which(VMG_ vm_obj_id_t self,vm_val_t * retval,uint * argc)1565 int CVmObjVector::getp_index_which(VMG_ vm_obj_id_t self, vm_val_t *retval,
1566 uint *argc)
1567 {
1568 /* use our general handler, working forwards */
1569 return gen_index_which(vmg_ self, retval, argc, TRUE, FALSE);
1570 }
1571
1572 /*
1573 * property evaluator - lastIndexWhich
1574 */
getp_last_index_which(VMG_ vm_obj_id_t self,vm_val_t * retval,uint * argc)1575 int CVmObjVector::getp_last_index_which(VMG_ vm_obj_id_t self,
1576 vm_val_t *retval, uint *argc)
1577 {
1578 /* use our general handler, working backwards */
1579 return gen_index_which(vmg_ self, retval, argc, FALSE, FALSE);
1580 }
1581
1582 /*
1583 * general handler for indexWhich and lastIndexWhich
1584 */
gen_index_which(VMG_ vm_obj_id_t self,vm_val_t * retval,uint * argc,int forward,int count_only)1585 int CVmObjVector::gen_index_which(VMG_ vm_obj_id_t self, vm_val_t *retval,
1586 uint *argc, int forward, int count_only)
1587 {
1588 const vm_val_t *func_val;
1589 size_t cnt;
1590 size_t idx;
1591 int val_cnt;
1592 static CVmNativeCodeDesc desc(1);
1593
1594 /* check arguments */
1595 if (get_prop_check_argc(retval, argc, &desc))
1596 return TRUE;
1597
1598 /* get the function pointer argument, but leave it on the stack */
1599 func_val = G_stk->get(0);
1600
1601 /* push a self-reference while working to protect from gc */
1602 G_interpreter->push_obj(vmg_ self);
1603
1604 /* presume we won't find a matching element */
1605 retval->set_nil();
1606 val_cnt = 0;
1607
1608 /* get the number of elements to visit */
1609 cnt = get_element_count();
1610
1611 /* start at the first or last element, depending on direction */
1612 idx = (forward ? 0 : cnt);
1613
1614 /*
1615 * Go through each element of our vector, and invoke the callback on
1616 * each element, looking for the first one that returns true.
1617 */
1618 for (;;)
1619 {
1620 /* if we've reached the last element, we can stop looking */
1621 if (forward ? idx >= cnt : idx == 0)
1622 break;
1623
1624 /* if we're going backwards, decrement the index */
1625 if (!forward)
1626 --idx;
1627
1628 /* push the element as the callback's argument */
1629 push_element(vmg_ idx);
1630
1631 /* invoke the callback */
1632 G_interpreter->call_func_ptr(vmg_ func_val, 1,
1633 "Vector.indexWhich", 0);
1634
1635 /*
1636 * if the callback returned true, we've found the element we're
1637 * looking for
1638 */
1639 if (G_interpreter->get_r0()->typ == VM_NIL
1640 || (G_interpreter->get_r0()->typ == VM_INT
1641 && G_interpreter->get_r0()->val.intval == 0))
1642 {
1643 /* nil or zero - this one failed the test, so keep looking */
1644 }
1645 else
1646 {
1647 /* it passed the test - check what we're doing */
1648 if (count_only)
1649 {
1650 /* we only want the count */
1651 ++val_cnt;
1652 }
1653 else
1654 {
1655 /* we want the (1-based) index - return it */
1656 retval->set_int(idx + 1);
1657
1658 /* found it - no need to keep looking */
1659 break;
1660 }
1661 }
1662
1663 /* if we're going forwards, increment the index */
1664 if (forward)
1665 ++idx;
1666 }
1667
1668 /* discard our gc protection (self) and our arguments */
1669 G_stk->discard(2);
1670
1671 /* return the count if desired */
1672 if (count_only)
1673 retval->set_int(val_cnt);
1674
1675 /* handled */
1676 return TRUE;
1677 }
1678
1679 /* ------------------------------------------------------------------------ */
1680 /*
1681 * property evaluator - lastValWhich
1682 */
getp_last_val_which(VMG_ vm_obj_id_t self,vm_val_t * retval,uint * argc)1683 int CVmObjVector::getp_last_val_which(VMG_ vm_obj_id_t self,
1684 vm_val_t *retval, uint *argc)
1685 {
1686 /* get the index of the value using lastIndexWhich */
1687 getp_last_index_which(vmg_ self, retval, argc);
1688
1689 /* if the return value is a valid index, get the value at the index */
1690 if (retval->typ == VM_INT)
1691 {
1692 int idx;
1693
1694 /* get the element as the return value */
1695 idx = (int)retval->val.intval;
1696 get_element(idx - 1, retval);
1697 }
1698
1699 /* handled */
1700 return TRUE;
1701 }
1702
1703 /* ------------------------------------------------------------------------ */
1704 /*
1705 * property evaluator - valWhich
1706 */
getp_val_which(VMG_ vm_obj_id_t self,vm_val_t * retval,uint * argc)1707 int CVmObjVector::getp_val_which(VMG_ vm_obj_id_t self, vm_val_t *retval,
1708 uint *argc)
1709 {
1710 /* get the index of the value using indexWhich */
1711 getp_index_which(vmg_ self, retval, argc);
1712
1713 /* if the return value is a valid index, get the value at the index */
1714 if (retval->typ == VM_INT)
1715 {
1716 int idx;
1717
1718 /* get the element as the return value */
1719 idx = (int)retval->val.intval;
1720 get_element(idx - 1, retval);
1721 }
1722
1723 /* handled */
1724 return TRUE;
1725 }
1726
1727 /* ------------------------------------------------------------------------ */
1728 /*
1729 * property evaluator - call a callback for each element
1730 */
getp_for_each(VMG_ vm_obj_id_t self,vm_val_t * retval,uint * argc)1731 int CVmObjVector::getp_for_each(VMG_ vm_obj_id_t self, vm_val_t *retval,
1732 uint *argc)
1733 {
1734 /* use the general forEach processor */
1735 return for_each_gen(vmg_ self, retval, argc, FALSE);
1736 }
1737
1738 /*
1739 * property evaluator - call a callback for each element
1740 */
getp_for_each_assoc(VMG_ vm_obj_id_t self,vm_val_t * retval,uint * argc)1741 int CVmObjVector::getp_for_each_assoc(VMG_ vm_obj_id_t self,
1742 vm_val_t *retval, uint *argc)
1743 {
1744 /* use the general forEach processor */
1745 return for_each_gen(vmg_ self, retval, argc, TRUE);
1746 }
1747
1748
1749 /*
1750 * General forEach/forEachAssoc processor
1751 */
for_each_gen(VMG_ vm_obj_id_t self,vm_val_t * retval,uint * argc,int pass_key_to_cb)1752 int CVmObjVector::for_each_gen(VMG_ vm_obj_id_t self,
1753 vm_val_t *retval, uint *argc,
1754 int pass_key_to_cb)
1755 {
1756 const vm_val_t *func_val;
1757 size_t idx;
1758 static CVmNativeCodeDesc desc(1);
1759
1760 /* check arguments */
1761 if (get_prop_check_argc(retval, argc, &desc))
1762 return TRUE;
1763
1764 /* get the function pointer argument, but leave it on the stack */
1765 func_val = G_stk->get(0);
1766
1767 /* push a self-reference while working to protect from gc */
1768 G_interpreter->push_obj(vmg_ self);
1769
1770 /* no return value */
1771 retval->set_nil();
1772
1773 /*
1774 * Invoke the callback on each element. Note that we intentionally do
1775 * not cache the element count, since it is possible that a program
1776 * could change the vector size in the course of an iteration; if we
1777 * cached the size, and the actual size were reduced during the
1778 * iteration, we would visit invalid elements past the new end of the
1779 * vector. To avoid this possibility, we re-check the current element
1780 * count on each iteration to make sure we haven't run off the end of
1781 * the vector.
1782 */
1783 for (idx = 0 ; idx < get_element_count() ; ++idx)
1784 {
1785 /* push the element as the callback's argument */
1786 push_element(vmg_ idx);
1787
1788 /* push the index argument if desired */
1789 if (pass_key_to_cb)
1790 G_stk->push()->set_int(idx + 1);
1791
1792 /* invoke the callback */
1793 G_interpreter->call_func_ptr(vmg_ func_val, pass_key_to_cb ? 2 : 1,
1794 "Vector.forEach", 0);
1795 }
1796
1797 /* discard our gc protection (self) and our arguments */
1798 G_stk->discard(2);
1799
1800 /* handled */
1801 return TRUE;
1802 }
1803
1804 /* ------------------------------------------------------------------------ */
1805 /*
1806 * property evaluator - mapAll
1807 */
getp_map_all(VMG_ vm_obj_id_t self,vm_val_t * retval,uint * argc)1808 int CVmObjVector::getp_map_all(VMG_ vm_obj_id_t self, vm_val_t *retval,
1809 uint *argc)
1810 {
1811 const vm_val_t *func_val;
1812 size_t idx;
1813 CVmObjVector *new_vec;
1814 static CVmNativeCodeDesc desc(1);
1815
1816 /* check arguments */
1817 if (get_prop_check_argc(retval, argc, &desc))
1818 return TRUE;
1819
1820 /* get the function pointer argument, but leave it on the stack */
1821 func_val = G_stk->get(0);
1822
1823 /* push a self-reference while working to protect from gc */
1824 G_interpreter->push_obj(vmg_ self);
1825
1826 /*
1827 * allocate a new vector for the return value - the new vector will
1828 * have the same size as the original, since we're mapping each
1829 * element of the old vector to the corresponding element of the new
1830 * vector
1831 */
1832 retval->set_obj(create(vmg_ FALSE, get_allocated_count()));
1833
1834 /* get the return value as an vector */
1835 new_vec = (CVmObjVector *)vm_objp(vmg_ retval->val.obj);
1836
1837 /* set the result element count the same as my own */
1838 new_vec->set_element_count(get_element_count());
1839
1840 /*
1841 * push a reference to the new list to protect it from the garbage
1842 * collector, which could be invoked in the course of executing the
1843 * user callback
1844 */
1845 G_stk->push(retval);
1846
1847 /*
1848 * Go through each element of our vector, and invoke the callback on
1849 * each element, storing the result in the corresponding element of
1850 * the new vector. Note that we re-check the element count on each
1851 * iteration, in case the callback changes it on us.
1852 */
1853 for (idx = 0 ; idx < get_element_count() ; ++idx)
1854 {
1855 /* push the element as the callback's argument */
1856 push_element(vmg_ idx);
1857
1858 /* invoke the callback */
1859 G_interpreter->call_func_ptr(vmg_ func_val, 1, "Vector.mapAll", 0);
1860
1861 /*
1862 * replace this element with the result (there's no need to save
1863 * undo, since the whole vector is new)
1864 */
1865 new_vec->set_element(idx, G_interpreter->get_r0());
1866 }
1867
1868 /* discard our gc protection (self, new vector) and our arguments */
1869 G_stk->discard(3);
1870
1871 /* handled */
1872 return TRUE;
1873 }
1874
1875 /* ------------------------------------------------------------------------ */
1876 /*
1877 * property evaluator - indexOf
1878 */
getp_index_of(VMG_ vm_obj_id_t self,vm_val_t * retval,uint * argc)1879 int CVmObjVector::getp_index_of(VMG_ vm_obj_id_t self, vm_val_t *retval,
1880 uint *argc)
1881 {
1882 /* use our general handler, going forwards */
1883 return gen_index_of(vmg_ self, retval, argc, TRUE, FALSE);
1884 }
1885
1886 /*
1887 * property evaluator - lastIndexOf
1888 */
getp_last_index_of(VMG_ vm_obj_id_t self,vm_val_t * retval,uint * argc)1889 int CVmObjVector::getp_last_index_of(VMG_ vm_obj_id_t self, vm_val_t *retval,
1890 uint *argc)
1891 {
1892 /* use our general handler, going backwards */
1893 return gen_index_of(vmg_ self, retval, argc, FALSE, FALSE);
1894 }
1895
1896 /*
1897 * general handler for indexOf and lastIndexOf - searches the vector
1898 * either forwards of backwards for a given value
1899 */
gen_index_of(VMG_ vm_obj_id_t self,vm_val_t * retval,uint * argc,int forward,int count_only)1900 int CVmObjVector::gen_index_of(VMG_ vm_obj_id_t self, vm_val_t *retval,
1901 uint *argc, int forward, int count_only)
1902 {
1903 const vm_val_t *val;
1904 size_t cnt;
1905 size_t idx;
1906 int val_cnt;
1907 static CVmNativeCodeDesc desc(1);
1908
1909 /* check arguments */
1910 if (get_prop_check_argc(retval, argc, &desc))
1911 return TRUE;
1912
1913 /* get the value, but leave it on the stack */
1914 val = G_stk->get(0);
1915
1916 /* push a self-reference while working to protect from gc */
1917 G_interpreter->push_obj(vmg_ self);
1918
1919 /* presume we won't find a matching element */
1920 retval->set_nil();
1921 val_cnt = 0;
1922
1923 /* get the number of elements to visit */
1924 cnt = get_element_count();
1925
1926 /* start at the first or last element, depending on the direction */
1927 idx = (forward ? 0 : cnt);
1928
1929 /* scan the list, looking for the element */
1930 for (;;)
1931 {
1932 vm_val_t ele;
1933
1934 /* if we've reached the last element, stop looking */
1935 if (forward ? idx >= cnt : idx == 0)
1936 break;
1937
1938 /* if we're going backwards, move to the next element position */
1939 if (!forward)
1940 --idx;
1941
1942 /* get this element */
1943 get_element(idx, &ele);
1944
1945 /* if the element matches the search value, return its index */
1946 if (ele.equals(vmg_ val))
1947 {
1948 /* it matches - see what we're doing */
1949 if (count_only)
1950 {
1951 /* they only want the count */
1952 ++val_cnt;
1953 }
1954 else
1955 {
1956 /* this is the one - return its 1-based index */
1957 retval->set_int(idx + 1);
1958
1959 /* foind it - no need to keep searching */
1960 break;
1961 }
1962 }
1963
1964 /* if we're going forwards, move to the next element */
1965 if (forward)
1966 ++idx;
1967 }
1968
1969 /* discard our gc protection (self) and our arguments */
1970 G_stk->discard(2);
1971
1972 /* return the count if desired */
1973 if (count_only)
1974 retval->set_int(val_cnt);
1975
1976 /* handled */
1977 return TRUE;
1978 }
1979
1980 /* ------------------------------------------------------------------------ */
1981 /*
1982 * property evaluator - countOf
1983 */
getp_count_of(VMG_ vm_obj_id_t self,vm_val_t * retval,uint * argc)1984 int CVmObjVector::getp_count_of(VMG_ vm_obj_id_t self, vm_val_t *retval,
1985 uint *argc)
1986 {
1987 /* use our general handler to obtain the count */
1988 return gen_index_of(vmg_ self, retval, argc, TRUE, TRUE);
1989 }
1990
1991 /* ------------------------------------------------------------------------ */
1992 /*
1993 * property evaluator - countWhich
1994 */
getp_count_which(VMG_ vm_obj_id_t self,vm_val_t * retval,uint * argc)1995 int CVmObjVector::getp_count_which(VMG_ vm_obj_id_t self, vm_val_t *retval,
1996 uint *argc)
1997 {
1998 /* use our general handler to obtain the count */
1999 return gen_index_which(vmg_ self, retval, argc, TRUE, TRUE);
2000 }
2001
2002 /* ------------------------------------------------------------------------ */
2003 /*
2004 * property evaluator - getUnique; returns a new vector consisting of the
2005 * unique elements of the original vector
2006 */
getp_get_unique(VMG_ vm_obj_id_t self,vm_val_t * retval,uint * argc)2007 int CVmObjVector::getp_get_unique(VMG_ vm_obj_id_t self, vm_val_t *retval,
2008 uint *argc)
2009 {
2010 CVmObjVector *new_vec;
2011 size_t cnt;
2012 static CVmNativeCodeDesc desc(0);
2013
2014 /* check arguments */
2015 if (get_prop_check_argc(retval, argc, &desc))
2016 return TRUE;
2017
2018 /* put myself on the stack for GC protection */
2019 G_interpreter->push_obj(vmg_ self);
2020
2021 /*
2022 * allocate a new vector for the return value - the new vector will
2023 * never be any larger than the original
2024 */
2025 cnt = get_element_count();
2026 retval->set_obj(create(vmg_ FALSE, cnt));
2027
2028 /* push a reference to the new list for gc protection */
2029 G_stk->push(retval);
2030
2031 /* get the return value as a vector */
2032 new_vec = (CVmObjVector *)vm_objp(vmg_ retval->val.obj);
2033
2034 /* start out with the result element count the same as my own */
2035 new_vec->set_element_count(cnt);
2036
2037 /* copy my elements to the new vector */
2038 memcpy(new_vec->get_element_ptr(0), get_element_ptr(0),
2039 calc_alloc_ele(cnt));
2040
2041 /* uniquify the result */
2042 new_vec->cons_uniquify(vmg0_);
2043
2044 /* discard the gc protection */
2045 G_stk->discard(2);
2046
2047 /* handled */
2048 return TRUE;
2049 }
2050
2051 /*
2052 * For construction, eliminate repeated elements of the vector, leaving
2053 * only the unique elements. Reduces the size of the vector to the size
2054 * required to accommodate the unique elements.
2055 */
cons_uniquify(VMG0_)2056 void CVmObjVector::cons_uniquify(VMG0_)
2057 {
2058 size_t cnt;
2059 size_t src, dst;
2060
2061 /* get the number of elements */
2062 cnt = get_element_count();
2063
2064 /* loop through the list and look for repeated values */
2065 for (src = dst = 0 ; src < cnt ; ++src)
2066 {
2067 size_t idx;
2068 vm_val_t src_val;
2069 int found;
2070
2071 /*
2072 * look for a copy of this source value already in the output list
2073 */
2074 get_element(src, &src_val);
2075 for (idx = 0, found = FALSE ; idx < dst ; ++idx)
2076 {
2077 vm_val_t idx_val;
2078
2079 /* get this value */
2080 get_element(idx, &idx_val);
2081
2082 /* if it's equal to the current source value, note it */
2083 if (src_val.equals(vmg_ &idx_val))
2084 {
2085 /* note that we found it */
2086 found = TRUE;
2087
2088 /* no need to look any further */
2089 break;
2090 }
2091 }
2092
2093 /* if we didn't find the value, copy it to the output list */
2094 if (!found)
2095 {
2096 /*
2097 * add it to the output vector - since this is a
2098 * construction-only method, there's no need to save undo (if
2099 * we apply undo, we'll undo the entire construction of the
2100 * vector, hence there's no need to track changes made since
2101 * creation)
2102 */
2103 set_element(dst, &src_val);
2104
2105 /* count it in the output */
2106 ++dst;
2107 }
2108 }
2109
2110 /* adjust the size of the result list */
2111 set_element_count(dst);
2112 }
2113
2114 /* ------------------------------------------------------------------------ */
2115 /*
2116 * property evaluator - appendUnique
2117 */
getp_append_unique(VMG_ vm_obj_id_t self,vm_val_t * retval,uint * argc)2118 int CVmObjVector::getp_append_unique(VMG_ vm_obj_id_t self, vm_val_t *retval,
2119 uint *argc)
2120 {
2121 vm_val_t *val2;
2122 size_t cnt2;
2123 size_t cnt;
2124 static CVmNativeCodeDesc desc(1);
2125 size_t i;
2126
2127 /* check arguments */
2128 if (get_prop_check_argc(retval, argc, &desc))
2129 return TRUE;
2130
2131 /* get the other value, but leave it on the stack */
2132 val2 = G_stk->get(0);
2133
2134 /* the return value is 'self' */
2135 retval->set_obj(self);
2136
2137 /* put myself on the stack for GC protection */
2138 G_stk->push(retval);
2139
2140 /* get the number of elements in the original */
2141 cnt = get_element_count();
2142
2143 /* get the number of elements to append */
2144 cnt2 = val2->get_coll_addsub_rhs_ele_cnt(vmg0_);
2145
2146 /* expand myself to make room for the new elements */
2147 expand_by(vmg_ self, cnt2);
2148
2149 /* append each element of the right-hand side */
2150 for (i = 0 ; i < cnt2 ; ++i)
2151 {
2152 vm_val_t ele;
2153
2154 /* get this element */
2155 val2->get_coll_addsub_rhs_ele(vmg_ i + 1, &ele);
2156
2157 /* store it */
2158 set_element_undo(vmg_ self, cnt + i, &ele);
2159 }
2160
2161 /* uniquify the result */
2162 uniquify_for_append(vmg_ self);
2163
2164 /* discard the gc protection and arguments */
2165 G_stk->discard(2);
2166
2167 /* handled */
2168 return TRUE;
2169 }
2170
2171 /*
2172 * uniquify after an appendUnique operation
2173 */
uniquify_for_append(VMG_ vm_obj_id_t self)2174 void CVmObjVector::uniquify_for_append(VMG_ vm_obj_id_t self)
2175 {
2176 size_t cnt;
2177 size_t src, dst;
2178
2179 /* get the number of elements */
2180 cnt = get_element_count();
2181
2182 /* loop through the list and look for repeated values */
2183 for (src = dst = 0 ; src < cnt ; ++src)
2184 {
2185 size_t idx;
2186 vm_val_t src_val;
2187 int found;
2188
2189 /* look for a copy of this value elsewhere in the vector */
2190 get_element(src, &src_val);
2191 for (idx = 0, found = FALSE ; idx < dst ; ++idx)
2192 {
2193 vm_val_t idx_val;
2194
2195 /* get this value */
2196 get_element(idx, &idx_val);
2197
2198 /* if it's equal to the current source value, note it */
2199 if (src_val.equals(vmg_ &idx_val))
2200 {
2201 /* note that we found it */
2202 found = TRUE;
2203
2204 /* no need to look any further */
2205 break;
2206 }
2207 }
2208
2209 /* if we didn't find the value, copy it to the output list */
2210 if (!found)
2211 {
2212 /*
2213 * Add it to the output vector if we're actually changing this
2214 * element (i.e., the destination and source indices are
2215 * unequal).
2216 */
2217 if (dst != src)
2218 set_element_undo(vmg_ self, dst, &src_val);
2219
2220 /* count it in the output */
2221 ++dst;
2222 }
2223 }
2224
2225 /* adjust the size of the result list */
2226 set_element_count_undo(vmg_ self, dst);
2227 }
2228
2229 /* ------------------------------------------------------------------------ */
2230 /*
2231 * sorter for vector
2232 */
2233 class CVmQSortVector: public CVmQSortVal
2234 {
2235 public:
CVmQSortVector()2236 CVmQSortVector()
2237 {
2238 vec_ = 0;
2239 self_ = VM_INVALID_OBJ;
2240 }
2241
2242 /* get an element from the vector */
get_ele(VMG_ size_t idx,vm_val_t * val)2243 void get_ele(VMG_ size_t idx, vm_val_t *val)
2244 { vec_->get_element(idx, val); }
2245
2246 /* store an element */
set_ele(VMG_ size_t idx,const vm_val_t * val)2247 void set_ele(VMG_ size_t idx, const vm_val_t *val)
2248 { vec_->set_element_undo(vmg_ self_, idx, val); }
2249
2250 /* our vector object */
2251 CVmObjVector *vec_;
2252 vm_obj_id_t self_;
2253 };
2254
2255 /*
2256 * property evaluator - sort
2257 */
getp_sort(VMG_ vm_obj_id_t self,vm_val_t * retval,uint * in_argc)2258 int CVmObjVector::getp_sort(VMG_ vm_obj_id_t self, vm_val_t *retval,
2259 uint *in_argc)
2260 {
2261 size_t len;
2262 uint argc = (in_argc == 0 ? 0 : *in_argc);
2263 CVmQSortVector sorter;
2264 static CVmNativeCodeDesc desc(0, 2);
2265
2266 /* check arguments */
2267 if (get_prop_check_argc(retval, in_argc, &desc))
2268 return TRUE;
2269
2270 /* remember the length of my list */
2271 len = get_element_count();
2272
2273 /* set the vector object in the sorter */
2274 sorter.vec_ = this;
2275 sorter.self_ = self;
2276
2277 /* if we have an 'descending' flag, note it */
2278 if (argc >= 1)
2279 sorter.descending_ = (G_stk->get(0)->typ != VM_NIL);
2280
2281 /*
2282 * if we have a comparison function, note it, but leave it on the
2283 * stack for gc protection
2284 */
2285 if (argc >= 2)
2286 sorter.compare_fn_ = *G_stk->get(1);
2287
2288 /* put myself on the stack for GC protection */
2289 G_interpreter->push_obj(vmg_ self);
2290
2291 /* sort the vector, if we have any elements */
2292 if (len != 0)
2293 sorter.sort(vmg_ 0, len - 1);
2294
2295 /* discard the gc protection and arguments */
2296 G_stk->discard(1 + argc);
2297
2298 /* return myself */
2299 retval->set_obj(self);
2300
2301 /* handled */
2302 return TRUE;
2303 }
2304
2305 /* ------------------------------------------------------------------------ */
2306 /*
2307 * property evaluator - set the length
2308 */
getp_set_length(VMG_ vm_obj_id_t self,vm_val_t * retval,uint * argc)2309 int CVmObjVector::getp_set_length(VMG_ vm_obj_id_t self, vm_val_t *retval,
2310 uint *argc)
2311 {
2312 size_t old_len;
2313 size_t new_len;
2314 size_t idx;
2315 vm_val_t nil_val;
2316 static CVmNativeCodeDesc desc(1);
2317
2318 /* check arguments */
2319 if (get_prop_check_argc(retval, argc, &desc))
2320 return TRUE;
2321
2322 /* the return value is 'self' */
2323 retval->set_obj(self);
2324
2325 /* note the old length */
2326 old_len = get_element_count();
2327
2328 /* get the new length */
2329 new_len = (size_t)CVmBif::pop_int_val(vmg0_);
2330
2331 /* can't go less than zero */
2332 if (new_len < 0)
2333 err_throw(VMERR_BAD_VAL_BIF);
2334
2335 /* set the vector to its new size */
2336 set_element_count_undo(vmg_ self, new_len);
2337
2338 /*
2339 * Set each newly added element to nil. Note that we don't bother
2340 * saving undo for these changes: to undo this change, the only thing
2341 * we'll have to do is reduce the vector size to its previous size,
2342 * and we've already saved undo for the size change.
2343 */
2344 nil_val.set_nil();
2345 for (idx = old_len ; idx < new_len ; ++idx)
2346 set_element(idx, &nil_val);
2347
2348 /* handled */
2349 return TRUE;
2350 }
2351
2352 /* ------------------------------------------------------------------------ */
2353 /*
2354 * property evaluator - insert one or more elements at a given index
2355 */
getp_insert_at(VMG_ vm_obj_id_t self,vm_val_t * retval,uint * in_argc)2356 int CVmObjVector::getp_insert_at(VMG_ vm_obj_id_t self, vm_val_t *retval,
2357 uint *in_argc)
2358 {
2359 size_t idx;
2360 size_t ins_idx;
2361 size_t old_cnt;
2362 size_t add_cnt;
2363 size_t new_cnt;
2364 uint argc = (in_argc != 0 ? *in_argc : 0);
2365 static CVmNativeCodeDesc desc(2, 0, TRUE);
2366
2367 /* we must have at least two arguments */
2368 if (get_prop_check_argc(retval, in_argc, &desc))
2369 return TRUE;
2370
2371 /* get the starting insertion index */
2372 ins_idx = (size_t)CVmBif::pop_int_val(vmg0_);
2373
2374 /* the return value is 'self' */
2375 retval->set_obj(self);
2376
2377 /* note the original size of the vector */
2378 old_cnt = get_element_count();
2379
2380 /*
2381 * note the number of items we're adding - we're adding each
2382 * argument, other than the first
2383 */
2384 add_cnt = argc - 1;
2385
2386 /*
2387 * note the new size - we're inserting one element for each argument
2388 * past the first one
2389 */
2390 new_cnt = old_cnt + add_cnt;
2391
2392 /*
2393 * if the starting index is out of range, it's an error - it can
2394 * range from 1 to one higher than the current element count (the
2395 * top end of the range allows us to insert elements after all of
2396 * the current elements)
2397 */
2398 if (ins_idx < 1 || ins_idx > get_element_count() + 1)
2399 err_throw(VMERR_INDEX_OUT_OF_RANGE);
2400
2401 /* adjust the starting index to a zero-based value */
2402 --ins_idx;
2403
2404 /* create the new elements */
2405 set_element_count_undo(vmg_ self, new_cnt);
2406
2407 /*
2408 * Move the existing elements to their new slots. Start with the
2409 * starting index, and move it to its new position, since the starting
2410 * index is the first element that needs to be moved. Note that we
2411 * start at the top and work down, to avoid overwriting any
2412 * overlapping part of the new position block and the old position
2413 * block.
2414 *
2415 * If we're inserting after the last element, there's no moving that
2416 * needs to be done.
2417 */
2418 if (ins_idx < old_cnt)
2419 {
2420 idx = old_cnt;
2421 for (;;)
2422 {
2423 size_t new_idx;
2424 vm_val_t ele;
2425
2426 /* move to the previous element */
2427 --idx;
2428
2429 /* get the value at this slot */
2430 get_element(idx, &ele);
2431
2432 /* calculate the new slot */
2433 new_idx = idx + add_cnt;
2434
2435 /*
2436 * if the new index is within the old size range, keep undo
2437 * for the change; otherwise, no undo is necessary, since when
2438 * we undo we'll be completely dropping this new slot
2439 */
2440 if (new_idx < old_cnt)
2441 set_element_undo(vmg_ self, new_idx, &ele);
2442 else
2443 set_element(new_idx, &ele);
2444
2445 /* if this was the last element, we're done */
2446 if (idx == ins_idx)
2447 break;
2448 }
2449 }
2450
2451 /* add the new items */
2452 for (idx = ins_idx ; add_cnt != 0 ; ++idx, --add_cnt)
2453 {
2454 vm_val_t val;
2455
2456 /* pop the next argument value */
2457 G_stk->pop(&val);
2458
2459 /* store the item, keeping undo only if it's in the old size range */
2460 if (idx < old_cnt)
2461 set_element_undo(vmg_ self, idx, &val);
2462 else
2463 set_element(idx, &val);
2464 }
2465
2466 /* handled */
2467 return TRUE;
2468 }
2469
2470
2471 /* ------------------------------------------------------------------------ */
2472 /*
2473 * property evaluator - append an element
2474 */
getp_prepend(VMG_ vm_obj_id_t self,vm_val_t * retval,uint * argc)2475 int CVmObjVector::getp_prepend(VMG_ vm_obj_id_t self, vm_val_t *retval,
2476 uint *argc)
2477 {
2478 vm_val_t val;
2479 size_t cnt;
2480 size_t i;
2481 static CVmNativeCodeDesc desc(1);
2482
2483 /* check arguments */
2484 if (get_prop_check_argc(retval, argc, &desc))
2485 return TRUE;
2486
2487 /* get the old size */
2488 cnt = get_element_count();
2489
2490 /* expand the vector by one element */
2491 set_element_count_undo(vmg_ self, cnt + 1);
2492
2493 /* move each existing element up one slot to make room for the new one */
2494 for (i = cnt ; i != 0 ; --i)
2495 {
2496 /* get this element */
2497 get_element(i - 1, &val);
2498
2499 /*
2500 * bump it up to the next slot - keep undo except for the last
2501 * slot, which is newly added and thus doesn't have an old value
2502 * that we need to keep
2503 */
2504 if (i == cnt)
2505 set_element(i, &val);
2506 else
2507 set_element_undo(vmg_ self, i, &val);
2508 }
2509
2510 /* retrieve the value for the new element */
2511 G_stk->pop(&val);
2512
2513 /*
2514 * set the first element - note that there's no need to save undo,
2515 * since undoing this operation will simply discard this element (in
2516 * other words, there's no previous value for this element since it's
2517 * being created anew here)
2518 */
2519 set_element(0, &val);
2520
2521 /* the return value is 'self' */
2522 retval->set_obj(self);
2523
2524 /* handled */
2525 return TRUE;
2526 }
2527
2528 /* ------------------------------------------------------------------------ */
2529 /*
2530 * property evaluator - remove an element at the given position
2531 */
getp_remove_element_at(VMG_ vm_obj_id_t self,vm_val_t * retval,uint * argc)2532 int CVmObjVector::getp_remove_element_at(VMG_ vm_obj_id_t self,
2533 vm_val_t *retval, uint *argc)
2534 {
2535 size_t start_idx;
2536 static CVmNativeCodeDesc desc(1);
2537
2538 /* check arguments */
2539 if (get_prop_check_argc(retval, argc, &desc))
2540 return TRUE;
2541
2542 /* get the deletion index */
2543 start_idx = (size_t)CVmBif::pop_int_val(vmg0_);
2544
2545 /* make sure the index is in range */
2546 if (start_idx < 1 || start_idx > get_element_count())
2547 err_throw(VMERR_INDEX_OUT_OF_RANGE);
2548
2549 /* adjust to a zero-based index */
2550 --start_idx;
2551
2552 /* the return value is 'self' */
2553 retval->set_obj(self);
2554
2555 /* delete one element */
2556 remove_elements_undo(vmg_ self, start_idx, 1);
2557
2558
2559 /* handled */
2560 return TRUE;
2561 }
2562
2563 /* ------------------------------------------------------------------------ */
2564 /*
2565 * property evaluator - remove element(s) matching a given value
2566 */
getp_remove_element(VMG_ vm_obj_id_t self,vm_val_t * retval,uint * argc)2567 int CVmObjVector::getp_remove_element(VMG_ vm_obj_id_t self,
2568 vm_val_t *retval, uint *argc)
2569 {
2570 vm_val_t *del_val;
2571 static CVmNativeCodeDesc desc(1);
2572 size_t src;
2573 size_t dst;
2574 size_t old_cnt;
2575
2576 /* check arguments */
2577 if (get_prop_check_argc(retval, argc, &desc))
2578 return TRUE;
2579
2580 /* get the value to be removed */
2581 del_val = G_stk->get(0);
2582
2583 /* the return value is 'self'; leave it on the stack */
2584 retval->set_obj(self);
2585 G_stk->push(retval);
2586
2587 /* get the original number of elements */
2588 old_cnt = get_element_count();
2589
2590 /* delete each element matching the given value */
2591 for (src = dst = 0 ; src < old_cnt ; ++src)
2592 {
2593 vm_val_t ele_val;
2594
2595 /* get this element */
2596 get_element(src, &ele_val);
2597
2598 /*
2599 * if this element doesn't match the value to be deleted, add it
2600 * to the result vector
2601 */
2602 if (!ele_val.equals(vmg_ del_val))
2603 {
2604 /*
2605 * This element is a keeper - if we're moving it to a new
2606 * position (i.e., we've deleted any elements before this
2607 * one), store it at its new position, keeping undo
2608 * information. If we're storing it at its same position,
2609 * there's no need to re-store the value or keep any undo,
2610 * since no change is involved.
2611 */
2612 if (dst != src)
2613 set_element_undo(vmg_ self, dst, &ele_val);
2614
2615 /* increment the write index past this element */
2616 ++dst;
2617 }
2618 }
2619
2620 /* if the element count changed, set the new element count */
2621 if (dst != old_cnt)
2622 set_element_count_undo(vmg_ self, dst);
2623
2624 /* discard gc protection and arguments */
2625 G_stk->discard(2);
2626
2627 /* handled */
2628 return TRUE;
2629 }
2630
2631 /* ------------------------------------------------------------------------ */
2632 /*
2633 * property evaluator - remove a range of elements
2634 */
getp_remove_range(VMG_ vm_obj_id_t self,vm_val_t * retval,uint * argc)2635 int CVmObjVector::getp_remove_range(VMG_ vm_obj_id_t self,
2636 vm_val_t *retval, uint *argc)
2637 {
2638 size_t start_idx;
2639 size_t end_idx;
2640 static CVmNativeCodeDesc desc(2);
2641
2642 /* check arguments */
2643 if (get_prop_check_argc(retval, argc, &desc))
2644 return TRUE;
2645
2646 /* get the starting index */
2647 start_idx = (size_t)CVmBif::pop_int_val(vmg0_);
2648
2649 /*
2650 * make sure the index is in range - it must refer to an existing
2651 * element
2652 */
2653 if (start_idx < 1 || start_idx > get_element_count())
2654 err_throw(VMERR_INDEX_OUT_OF_RANGE);
2655
2656 /* get the ending index */
2657 end_idx = (size_t)CVmBif::pop_int_val(vmg0_);
2658
2659 /*
2660 * make sure it's in range - it must refer to an existing element,
2661 * and it must be greater than or equal to the starting index
2662 */
2663 if (end_idx < start_idx || end_idx > get_element_count())
2664 err_throw(VMERR_INDEX_OUT_OF_RANGE);
2665
2666 /* adjust to zero-based indices */
2667 --start_idx;
2668 --end_idx;
2669
2670 /* the return value is 'self' */
2671 retval->set_obj(self);
2672
2673 /*
2674 * delete the elements - the number of elements we're deleting is
2675 * one higher than the difference of the starting and ending indices
2676 * (if the two indices are the same, we're deleting just the one
2677 * element)
2678 */
2679 remove_elements_undo(vmg_ self, start_idx, end_idx - start_idx + 1);
2680
2681 /* handled */
2682 return TRUE;
2683 }
2684
2685 /* ------------------------------------------------------------------------ */
2686 /*
2687 * property evaluator - append an element
2688 */
getp_append(VMG_ vm_obj_id_t self,vm_val_t * retval,uint * argc)2689 int CVmObjVector::getp_append(VMG_ vm_obj_id_t self, vm_val_t *retval,
2690 uint *argc)
2691 {
2692 vm_val_t *valp;
2693 size_t cnt;
2694 static CVmNativeCodeDesc desc(1);
2695
2696 /* check arguments */
2697 if (get_prop_check_argc(retval, argc, &desc))
2698 return TRUE;
2699
2700 /* get the value to append, but leave it on the stack */
2701 valp = G_stk->get(0);
2702
2703 /* get the old size */
2704 cnt = get_element_count();
2705
2706 /* the result object is 'self' */
2707 retval->set_obj(self);
2708
2709 /* push a self-reference for gc protection */
2710 G_stk->push(retval);
2711
2712 /* expand myself by one element to make room for the addition */
2713 expand_by(vmg_ self, 1);
2714
2715 /* add the new element, saving undo */
2716 set_element_undo(vmg_ self, cnt, valp);
2717
2718 /* discard the argument and gc protection */
2719 G_stk->discard(2);
2720
2721 /* handled */
2722 return TRUE;
2723 }
2724
2725 /* ------------------------------------------------------------------------ */
2726 /*
2727 * property evaluator - append elements from a collection, or append a
2728 * single non-collection element
2729 */
getp_append_all(VMG_ vm_obj_id_t self,vm_val_t * retval,uint * argc)2730 int CVmObjVector::getp_append_all(VMG_ vm_obj_id_t self, vm_val_t *retval,
2731 uint *argc)
2732 {
2733 vm_val_t *valp;
2734 size_t cnt;
2735 static CVmNativeCodeDesc desc(1);
2736 size_t add_cnt;
2737 size_t i;
2738
2739 /* check arguments */
2740 if (get_prop_check_argc(retval, argc, &desc))
2741 return TRUE;
2742
2743 /* get the value to append, but leave it on the stack */
2744 valp = G_stk->get(0);
2745
2746 /* the result object is 'self' */
2747 retval->set_obj(self);
2748
2749 /* push a self-reference for gc protection */
2750 G_stk->push(retval);
2751
2752 /* get the old size */
2753 cnt = get_element_count();
2754
2755 /* get the number of items to add */
2756 add_cnt = valp->get_coll_addsub_rhs_ele_cnt(vmg0_);
2757
2758 /* expand myself to make room for the new elements */
2759 expand_by(vmg_ self, add_cnt);
2760
2761 /* add the new elements */
2762 for (i = 0 ; i < add_cnt ; ++i)
2763 {
2764 vm_val_t ele;
2765
2766 /* get the next new element */
2767 valp->get_coll_addsub_rhs_ele(vmg_ i + 1, &ele);
2768
2769 /*
2770 * add the new element - note that we don't need to keep undo
2771 * because we created this new element in this same operation, and
2772 * hence undoing the operation will truncate the vector to exclude
2773 * the element, and hence we don't need an old value for the
2774 * element
2775 */
2776 set_element(cnt + i, &ele);
2777 }
2778
2779 /* discard the argument and gc protection */
2780 G_stk->discard(2);
2781
2782 /* handled */
2783 return TRUE;
2784 }
2785
2786 /* ------------------------------------------------------------------------ */
2787 /*
2788 * Delete a range of elements
2789 */
remove_elements_undo(VMG_ vm_obj_id_t self,size_t start_idx,size_t del_cnt)2790 void CVmObjVector::remove_elements_undo(VMG_ vm_obj_id_t self,
2791 size_t start_idx, size_t del_cnt)
2792 {
2793 size_t idx;
2794 size_t cnt;
2795
2796 /* note the original size of the vector */
2797 cnt = get_element_count();
2798
2799 /* move all of the elements to their new slots, keeping undo */
2800 for (idx = start_idx ; idx < cnt ; ++idx)
2801 {
2802 vm_val_t val;
2803 size_t move_idx;
2804
2805 /*
2806 * calculate the index of the element to replace this one - it's
2807 * past us by the number of items being deleted
2808 */
2809 move_idx = idx + del_cnt;
2810
2811 /*
2812 * if we're moving from a valid index in the old vector, get the
2813 * element; otherwise, replace the value with nil (but still
2814 * replace the value, since we want to keep undo for its
2815 * original value)
2816 */
2817 if (move_idx < cnt)
2818 get_element(move_idx, &val);
2819 else
2820 val.set_nil();
2821
2822 /* store the element at its new position */
2823 set_element_undo(vmg_ self, idx, &val);
2824 }
2825
2826 /*
2827 * Reduce the size, keeping undo. Note that it's important we do
2828 * this last: undo is applied in reverse order, so when applying
2829 * undo, we first want to increase the vector's size to its original
2830 * size, then we want to apply the element value restorations.
2831 */
2832 set_element_count_undo(vmg_ self, cnt - del_cnt);
2833 }
2834
2835