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_ &copy_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