cfad47cfa3/tads3/vmvec.cpp

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