1 /* $Header$ */
2 
3 /*
4  *   Copyright (c) 2000, 2002 Michael J. Roberts.  All Rights Reserved.
5  *
6  *   Please see the accompanying license file, LICENSE.TXT, for information
7  *   on using and copying this software.
8  */
9 /*
10 Name
11   vmvec.h - T3 VM Vector class
12 Function
13   A Vector is a subclass of Collection.  A Vector is a List whose
14   elements can be changed in-place, and whose size can change.
15 Notes
16 
17 Modified
18   05/14/00 MJRoberts  - Creation
19 */
20 
21 #ifndef VMVEC_H
22 #define VMVEC_H
23 
24 #include <stdlib.h>
25 #include "vmtype.h"
26 #include "vmobj.h"
27 #include "vmcoll.h"
28 #include "vmglob.h"
29 #include "vmstack.h"
30 
31 /* ------------------------------------------------------------------------ */
32 /*
33  *   Vectors are very similar to lists, but a vector's contents can be
34  *   modified during execution.  Because vectors are dynamic, a vector's
35  *   image file data are copied into the heap.
36  *
37  *   A vector's image file data structure looks like this:
38  *
39  *   UINT2 elements_allocated
40  *.  UINT2 number_of_elements_used
41  *.  DATAHOLDER element[1]
42  *.  DATAHOLDER element[2]
43  *.  etc, up to number_of_elements_used (NOT the number allocated)
44  *
45  *   The object extension for a a vector is almost identical, but adds a
46  *   prefix giving the "allocated" size of the vector, and a suffix with a
47  *   pointer to our original image data (if applicable) plus one bit per
48  *   element after the end of the element[] list for undo bits:
49  *
50  *   UINT2 allocated_elements
51  *.  UINT2 number_of_elements
52  *.  DATAHOLDER element[1]
53  *.  DATAHOLDER element[2]
54  *.  etc
55  *.  undo_bits
56  *
57  *   The allocated_elements counter gives the total number of elements
58  *   allocated in the vector; this can be greater than the number of
59  *   elements actually in use, in which case we have free space that we can
60  *   use to add elements without reallocating the vector's memory.
61  *
62  *   The undo_bits are packed 8 bits per byte.  Note that the undo_bits
63  *   follow all allocated slots, so these do not change location when we
64  *   change the number of elements actually in use.
65  */
66 
67 /* ------------------------------------------------------------------------ */
68 /*
69  *   Vector metaclass
70  */
71 class CVmObjVector: public CVmObjCollection
72 {
73     friend class CVmMetaclassVector;
74     friend class CVmQSortVector;
75 
76 public:
77     /* metaclass registration object */
78     static class CVmMetaclass *metaclass_reg_;
get_metaclass_reg()79     class CVmMetaclass *get_metaclass_reg() const { return metaclass_reg_; }
80 
81     /* am I of the given metaclass? */
is_of_metaclass(class CVmMetaclass * meta)82     virtual int is_of_metaclass(class CVmMetaclass *meta) const
83     {
84         /* try my own metaclass and my base class */
85         return (meta == metaclass_reg_
86                 || CVmObjCollection::is_of_metaclass(meta));
87     }
88 
89     /* create dynamically using stack arguments */
90     static vm_obj_id_t create_from_stack(VMG_ const uchar **pc_ptr,
91                                          uint argc);
92 
93     /* create with no initial contents */
94     static vm_obj_id_t create(VMG_ int in_root_set);
95 
96     /* create with a given number of elements */
97     static vm_obj_id_t create(VMG_ int in_root_set, size_t element_count);
98 
99     /*
100      *   determine if an object is a vector - it is if the object's
101      *   virtual metaclass registration index matches the class's static
102      *   index
103      */
is_vector_obj(VMG_ vm_obj_id_t obj)104     static int is_vector_obj(VMG_ vm_obj_id_t obj)
105         { return vm_objp(vmg_ obj)->is_of_metaclass(metaclass_reg_); }
106 
107     /* set a property */
108     void set_prop(VMG_ class CVmUndo *undo,
109                   vm_obj_id_t self, vm_prop_id_t prop, const vm_val_t *val);
110 
111     /* get a property */
112     int get_prop(VMG_ vm_prop_id_t prop, vm_val_t *val,
113                  vm_obj_id_t self, vm_obj_id_t *source_obj, uint *argc);
114 
115     /* add a value to the vector, yielding a new vector */
116     void add_val(VMG_ vm_val_t *result,
117                  vm_obj_id_t self, const vm_val_t *val);
118 
119     /* subtract a value from the vector, yielding a new vector */
120     void sub_val(VMG_ vm_val_t *result,
121                  vm_obj_id_t self, const vm_val_t *val);
122 
123     /* undo operations */
124     void notify_new_savept();
125     void apply_undo(VMG_ struct CVmUndoRecord *rec);
126     void mark_undo_ref(VMG_ struct CVmUndoRecord *rec);
127 
128     /* we keep only strong references */
remove_stale_undo_weak_ref(VMG_ struct CVmUndoRecord *)129     void remove_stale_undo_weak_ref(VMG_ struct CVmUndoRecord *) { }
130 
131     /* mark references */
132     void mark_refs(VMG_ uint state);
133 
134     /*
135      *   remove weak references - we keep only normal (strong) references,
136      *   so this routine doesn't need to do anything
137      */
remove_stale_weak_refs(VMG0_)138     void remove_stale_weak_refs(VMG0_) { }
139 
140     /* rebuild for image file */
141     virtual ulong rebuild_image(VMG_ char *buf, ulong buflen);
142 
143     /* reserve constant data */
144     virtual void reserve_const_data(VMG_ class CVmConstMapper *mapper,
145                                     vm_obj_id_t self);
146 
147     /* convert to constant data */
148     virtual void convert_to_const_data(VMG_ class CVmConstMapper *mapper,
149                                        vm_obj_id_t self);
150 
151     /* save to a file */
152     void save_to_file(VMG_ class CVmFile *fp);
153 
154     /* restore from a file */
155     void restore_from_file(VMG_ vm_obj_id_t self,
156                            class CVmFile *fp, class CVmObjFixup *fixups);
157 
158     /* load from an image file */
159     void load_from_image(VMG_ vm_obj_id_t self, const char *ptr, size_t siz);
160 
161     /* reload from the image file */
162     void reload_from_image(VMG_ vm_obj_id_t self,
163                            const char *ptr, size_t siz);
164 
165     /*
166      *   When we're the right-hand side of a '+' or '-' operation whose
167      *   left-hand side is another collection type that treats these
168      *   operators as concatenation/set subtraction, add/subtract our
169      *   elements individually.
170      */
get_coll_addsub_rhs_ele_cnt(VMG0_)171     virtual size_t get_coll_addsub_rhs_ele_cnt(VMG0_) const
172         { return get_element_count(); }
get_coll_addsub_rhs_ele(VMG_ vm_val_t * result,vm_obj_id_t self,size_t idx)173     virtual void get_coll_addsub_rhs_ele(VMG_ vm_val_t *result,
174                                          vm_obj_id_t self, size_t idx)
175     {
176         vm_val_t idx_val;
177 
178         /* set up the index value */
179         idx_val.set_int((int)idx);
180 
181         /* index the vector */
182         index_val(vmg_ result, self, &idx_val);
183     }
184 
185     /* index the vector */
186     void index_val(VMG_ vm_val_t *result, vm_obj_id_t self,
187                    const vm_val_t *index_val);
188 
189     /* set an indexed element of the vector */
190     void set_index_val(VMG_ vm_val_t *new_container, vm_obj_id_t self,
191                        const vm_val_t *index_val, const vm_val_t *new_val);
192 
193     /*
194      *   Check a value for equality.  We will match any list or vector with
195      *   the same number of elements and the same value for each element.
196      */
197     int equals(VMG_ vm_obj_id_t self, const vm_val_t *val, int depth) const;
198 
199     /* calculate a hash value for the vector */
200     uint calc_hash(VMG_ vm_obj_id_t self, int depth) const;
201 
202     /*
203      *   assume that we've been changed since loading, if we came from the
204      *   image file
205      */
is_changed_since_load()206     int is_changed_since_load() const { return TRUE; }
207 
208     /*
209      *   get the allocated number of elements of the vector - this will be
210      *   greater than or equal to get_element_count(), and reflects the
211      *   actual allocated size
212      */
get_allocated_count()213     size_t get_allocated_count() const
214         { return vmb_get_len(get_vector_ext_ptr()); }
215 
216     /* get the number of elements in the vector */
get_element_count()217     size_t get_element_count() const
218         { return vmb_get_len(get_vector_ext_ptr() + 2); }
219 
220 protected:
221     /* load image data */
222     void load_image_data(VMG_ const char *ptr, size_t siz);
223 
224     /* for construction, remove all redundant elements */
225     void cons_uniquify(VMG0_);
226 
227     /* uniquify the value for an appendUnique operation */
228     void uniquify_for_append(VMG_ vm_obj_id_t self);
229 
230     /* create an iterator */
231     virtual void new_iterator(VMG_ vm_val_t *retval,
232                               const vm_val_t *self_val);
233 
234     /* create a live iterator */
235     virtual void new_live_iterator(VMG_ vm_val_t *retval,
236                                    const vm_val_t *self_val);
237 
238     /* create a copy of the object */
239     vm_obj_id_t create_copy(VMG_ const vm_val_t *self_val);
240 
241     /* create with no initial contents */
CVmObjVector()242     CVmObjVector() { ext_ = 0; }
243 
244     /* create with a given number of elements */
245     CVmObjVector(VMG_ size_t element_count);
246 
247     /* get a pointer to my vector data */
get_vector_ext_ptr()248     const char *get_vector_ext_ptr() const { return ext_; }
249 
250     /*
251      *   get my extension data pointer for construction purposes - this is a
252      *   writable pointer, so that a caller can fill our data buffer during
253      *   construction
254      */
cons_get_vector_ext_ptr()255     char *cons_get_vector_ext_ptr() const { return ext_; }
256 
257     /*
258      *   Calculate the amount of space we need to store a list of a given
259      *   length.  We require two bytes for the length prefix, plus the space
260      *   for each element, plus the space for the undo bits (one bit per
261      *   element, but we pack eight bits per byte).
262      */
calc_alloc(size_t elecnt)263     static size_t calc_alloc(size_t elecnt)
264     {
265         return (calc_alloc_prefix_and_ele(elecnt)
266                 + ((elecnt + 7) & ~7));
267     }
268 
269     /*
270      *   calculate the allocation amount, including only the count prefix
271      *   and element data
272      */
calc_alloc_prefix_and_ele(size_t elecnt)273     static size_t calc_alloc_prefix_and_ele(size_t elecnt)
274     {
275         return (2*VMB_LEN + calc_alloc_ele(elecnt));
276     }
277 
278     /* calculate the allocation amount, including only the element storage */
calc_alloc_ele(size_t elecnt)279     static size_t calc_alloc_ele(size_t elecnt)
280     {
281         return elecnt * VMB_DATAHOLDER;
282     }
283 
284     /* notify of deletion */
285     void notify_delete(VMG_ int in_root_set);
286 
287     /* allocate space for the vector, given the number of elements */
288     void alloc_vector(VMG_ size_t element_count);
289 
290     /* expand to accommodate the given number of new elements */
291     void expand_by(VMG_ vm_obj_id_t self, size_t added_elements);
292 
293     /* get the allocation count increment */
get_alloc_count_increment()294     size_t get_alloc_count_increment() const
295     {
296         /*
297          *   we might want to make this parameterizable; for now use a
298          *   fixed increment size
299          */
300         return 16;
301     }
302 
303     /* get an element */
get_element(size_t idx,vm_val_t * val)304     void get_element(size_t idx, vm_val_t *val) const
305     {
306         /* get the data from the data holder in our extension */
307         vmb_get_dh(get_element_ptr(idx), val);
308     }
309 
310     /* push an element onto the stack */
push_element(VMG_ size_t idx)311     void push_element(VMG_ size_t idx) const
312     {
313         vm_val_t *p;
314 
315         /* push a stack element */
316         p = G_stk->push();
317 
318         /*
319          *   get the data from the data holder in our extension directly into
320          *   our new stack element
321          */
322         vmb_get_dh(get_element_ptr(idx), p);
323     }
324 
325     /* set an element, recording undo for the change */
326     void set_element_undo(VMG_ vm_obj_id_t self,
327                           size_t idx, const vm_val_t *val);
328 
329     /* set an element */
set_element(size_t idx,const vm_val_t * val)330     void set_element(size_t idx, const vm_val_t *val)
331     {
332         /* set the element's data holder from the value */
333         vmb_put_dh(get_element_ptr(idx), val);
334     }
335 
336     /* set the allocated size */
set_allocated_count(size_t cnt)337     void set_allocated_count(size_t cnt)
338         { vmb_put_len(cons_get_vector_ext_ptr(), cnt); }
339 
340     /* set the number of elements in the vector */
set_element_count(size_t cnt)341     void set_element_count(size_t cnt)
342         { vmb_put_len(cons_get_vector_ext_ptr() + 2, cnt); }
343 
344     /* given an index, get a pointer to the element's data in the list */
get_element_ptr(size_t idx)345     char *get_element_ptr(size_t idx) const
346     {
347         /*
348          *   figure out where this element's data holder is by skipping the
349          *   count prefix, then skipping past preceding data holders
350          */
351         return cons_get_vector_ext_ptr() + 2*VMB_LEN + (idx * VMB_DATAHOLDER);
352     }
353 
354     /* get a constant pointer to an element */
get_element_ptr_const(const char * ext,size_t idx)355     static const char *get_element_ptr_const(const char *ext, size_t idx)
356     {
357         return ext + 2*VMB_LEN + (idx * VMB_DATAHOLDER);
358     }
359 
360     /* increment an element pointer */
inc_element_ptr(char ** ptr)361     static void inc_element_ptr(char **ptr) { *ptr += VMB_DATAHOLDER; }
362 
363     /* get a given undo bit */
get_undo_bit(size_t idx)364     int get_undo_bit(size_t idx) const
365     {
366         char *p;
367         int bitnum;
368 
369         /* get the byte containing the bit for this index */
370         p = get_undo_ptr() + (idx >> 3);
371 
372         /* get the bit number within the byte */
373         bitnum = (idx & 7);
374 
375         /* get this bit */
376         return ((*p & (1 << bitnum)) != 0);
377     }
378 
379     /* set a given undo bit */
set_undo_bit(size_t idx,int val)380     void set_undo_bit(size_t idx, int val)
381     {
382         char *p;
383         int bitnum;
384 
385         /* get the byte containing the bit for this index */
386         p = get_undo_ptr() + (idx >> 3);
387 
388         /* get the bit number within the byte */
389         bitnum = (idx & 7);
390 
391         /* set or clear the bit */
392         if (val)
393             *p |= (1 << bitnum);
394         else
395             *p &= ~(1 << bitnum);
396     }
397 
398     /* clear all undo bits */
clear_undo_bits()399     void clear_undo_bits()
400     {
401         memset(get_undo_ptr(), 0, ((get_allocated_count() + 7) & ~7));
402     }
403 
404     /* get a pointer to the first byte of the undo bits */
get_undo_ptr()405     char *get_undo_ptr() const
406     {
407         /* the undo bits follow the last element data */
408         return (cons_get_vector_ext_ptr()
409                 + 2*VMB_LEN
410                 + get_allocated_count()*VMB_DATAHOLDER);
411     }
412 
413     /* set the element count, saving undo for the change */
414     void set_element_count_undo(VMG_ vm_obj_id_t self,
415                                 size_t new_element_count);
416 
417     /* delete elements from the vector, keeping undo */
418     void remove_elements_undo(VMG_ vm_obj_id_t self,
419                               size_t start_idx, size_t del_cnt);
420 
421     /* general handler for indexOf, lastIndexOf, and countOf */
422     int gen_index_of(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc,
423                      int forward, int count_only);
424 
425     /* general handler for indexWhich, lastIndexWhich, and countWhich */
426     int gen_index_which(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc,
427                         int forward, int count_only);
428 
429     /* property evaluator - undefined function */
getp_undef(VMG_ vm_obj_id_t,vm_val_t *,uint *)430     int getp_undef(VMG_ vm_obj_id_t, vm_val_t *, uint *) { return FALSE; }
431 
432     /* property evaluator - convert to a list */
433     int getp_to_list(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc);
434 
435     /* property evaluator - get the number of elements */
436     int getp_get_size(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc);
437 
438     /* property evaluator - copy from another vector or list */
439     int getp_copy_from(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc);
440 
441     /* property evaluator - fill with a value */
442     int getp_fill_val(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc);
443 
444     /* property evaluator - select a subset */
445     int getp_subset(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc);
446 
447     /* property evaluator - apply a callback to each element */
448     int getp_apply_all(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc);
449 
450     /* property evaluator - find the first element satisfying a condition */
451     int getp_index_which(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc);
452 
453     /* property evaluator - call a callback on each element */
454     int getp_for_each(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc);
455 
456     /* property evaluator - call a callback(key,val) on each element */
457     int getp_for_each_assoc(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc);
458 
459     /* general processor for forEach/forEachAssoc */
460     int for_each_gen(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc,
461                      int pass_key_to_cb);
462 
463     /* property evaluator - mapAll */
464     int getp_map_all(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc);
465 
466     /* property evaluator - indexOf */
467     int getp_index_of(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc);
468 
469     /* property evaluator - valWhich */
470     int getp_val_which(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc);
471 
472     /* property evaluator - lastIndexOf */
473     int getp_last_index_of(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc);
474 
475     /* property evaluator - lastIndexWhich */
476     int getp_last_index_which(VMG_ vm_obj_id_t self,
477                               vm_val_t *val, uint *argc);
478 
479     /* property evaluator - valWhich */
480     int getp_last_val_which(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc);
481 
482     /* property evaluator - countOf */
483     int getp_count_of(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc);
484 
485     /* property evaluator - countWhich */
486     int getp_count_which(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc);
487 
488     /* property evaluator - getUnique */
489     int getp_get_unique(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc);
490 
491     /* property evaluator - appendUnique */
492     int getp_append_unique(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc);
493 
494     /* property evaluator - sort */
495     int getp_sort(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc);
496 
497     /* property evaluator - set the length */
498     int getp_set_length(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc);
499 
500     /* property evaluator - insertAt */
501     int getp_insert_at(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc);
502 
503     /* property evaluator - remove a single element at a given index */
504     int getp_remove_element_at(VMG_ vm_obj_id_t self, vm_val_t *retval,
505                                uint *argc);
506 
507     /* property evaluator - removeRange */
508     int getp_remove_range(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc);
509 
510     /* property evaluator - append a value */
511     int getp_append(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc);
512 
513     /* property evaluator - prepend a value */
514     int getp_prepend(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc);
515 
516     /* property evaluator - append everything from a collection */
517     int getp_append_all(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc);
518 
519     /* property evaluator - remove any elements with a given value */
520     int getp_remove_element(VMG_ vm_obj_id_t self, vm_val_t *retval,
521                             uint *argc);
522 
523     /* property evaluation function table */
524     static int (CVmObjVector::*func_table_[])(VMG_ vm_obj_id_t self,
525                                               vm_val_t *retval, uint *argc);
526 };
527 
528 
529 /* ------------------------------------------------------------------------ */
530 /*
531  *   Registration table object
532  */
533 class CVmMetaclassVector: public CVmMetaclass
534 {
535 public:
536     /* get the global name */
get_meta_name()537     const char *get_meta_name() const { return "vector/030004"; }
538 
539     /* create from image file */
create_for_image_load(VMG_ vm_obj_id_t id)540     void create_for_image_load(VMG_ vm_obj_id_t id)
541     {
542         new (vmg_ id) CVmObjVector();
543         G_obj_table->set_obj_gc_characteristics(id, TRUE, FALSE);
544     }
545 
546     /* create from restoring from saved state */
create_for_restore(VMG_ vm_obj_id_t id)547     void create_for_restore(VMG_ vm_obj_id_t id)
548     {
549         new (vmg_ id) CVmObjVector();
550         G_obj_table->set_obj_gc_characteristics(id, TRUE, FALSE);
551     }
552 
553     /* create dynamically using stack arguments */
create_from_stack(VMG_ const uchar ** pc_ptr,uint argc)554     vm_obj_id_t create_from_stack(VMG_ const uchar **pc_ptr, uint argc)
555         { return CVmObjVector::create_from_stack(vmg_ pc_ptr, argc); }
556 
557     /* call a static property */
call_stat_prop(VMG_ vm_val_t * result,const uchar ** pc_ptr,uint * argc,vm_prop_id_t prop)558     int call_stat_prop(VMG_ vm_val_t *result,
559                        const uchar **pc_ptr, uint *argc,
560                        vm_prop_id_t prop)
561     {
562         return CVmObjVector::call_stat_prop(vmg_ result, pc_ptr, argc, prop);
563     }
564 
565     /* I'm a Collection object */
get_supermeta_reg()566     CVmMetaclass *get_supermeta_reg() const
567         { return CVmObjCollection::metaclass_reg_; }
568 };
569 
570 #endif /* VMVEC_H */
571 
572 /*
573  *   Register the class
574  */
575 VM_REGISTER_METACLASS(CVmObjVector)
576