1 /*
2  *   Copyright (c) 2001, 2002 Michael J. Roberts.  All Rights Reserved.
3  *
4  *   Please see the accompanying license file, LICENSE.TXT, for information
5  *   on using and copying this software.
6  */
7 /*
8 Name
9   vmbytarr.h - T3 ByteArray metaclass
10 Function
11 
12 Notes
13 
14 Modified
15   06/05/01 MJRoberts  - Creation
16 */
17 
18 #ifndef VMBYTARR_H
19 #define VMBYTARR_H
20 
21 #include <stdlib.h>
22 #include "os.h"
23 #include "vmtype.h"
24 #include "vmobj.h"
25 #include "vmglob.h"
26 
27 /* ------------------------------------------------------------------------ */
28 /*
29  *   A ByteArray is simply an array of byte values.  This class provides a
30  *   simple, fast mechanism to store blocks of binary data, so it is not a
31  *   subclass of Array and is not a Collection.
32  *
33  *   The image file data for a byte array is simple:
34  *
35  *   UINT4 number of bytes
36  *.  BYTE bytes[1..number_of_bytes]
37  *
38  *   Internally, we store the array data in chunks of 32k each.  Our
39  *   extension is a first-level page table, pointing to the chunks:
40  *
41  *   UINT4 number of elements
42  *.  unsigned char **page0
43  *.  unsigned char **page1
44  *.  ...
45  *
46  *   Each pageN pointer points to a second-level page table, which consists
47  *   of (up to) 8192 pointers to the actual pages.  Since a page is 32k, and
48  *   we can store 8k pointers per second-level table, each second-level
49  *   table is capable of referencing 256MB.  By design, we can store up to
50  *   4GB, so we need at most 16 second-level tables.
51  *
52  *   The extension is allocated according to the actual number of
53  *   second-level tables we require for the element count.  Each
54  *   second-level page is allocated to 8192*sizeof(char *), except the last
55  *   second-level page, which is allocated to N*sizeof(char *) where N is
56  *   the number of elements required in the last second-level table.  Each
57  *   page is allocated to 32K bytes, except the last, which is allocated to
58  *   the actual size needed.
59  *
60  *   To access an element at index i, we calculate s1 (the page table
61  *   selector) as i/(32k*8k) == i/256M; s2 (the page selector within the
62  *   selected page table) as (i%256M)/32k; and s3 (the byte selector within
63  *   the page) as i%32k.  The byte is then accessed as
64  *
65  *   page[s1][s2][s3]
66  */
67 class CVmObjByteArray: public CVmObject
68 {
69     friend class CVmMetaclassByteArray;
70     friend class bytearray_undo_rec;
71 
72 public:
73     /* metaclass registration object */
74     static class CVmMetaclass *metaclass_reg_;
get_metaclass_reg()75     class CVmMetaclass *get_metaclass_reg() const { return metaclass_reg_; }
76 
77     /* am I of the given metaclass? */
is_of_metaclass(class CVmMetaclass * meta)78     virtual int is_of_metaclass(class CVmMetaclass *meta) const
79     {
80         /* try my own metaclass and my base class */
81         return (meta == metaclass_reg_
82                 || CVmObject::is_of_metaclass(meta));
83     }
84 
85     /* create dynamically using stack arguments */
86     static vm_obj_id_t create_from_stack(VMG_ const uchar **pc_ptr,
87                                          uint argc);
88 
89     /*
90      *   call a static property - we don't have any of our own, so simply
91      *   "inherit" the base class handling
92      */
call_stat_prop(VMG_ vm_val_t * result,const uchar ** pc_ptr,uint * argc,vm_prop_id_t prop)93     static int call_stat_prop(VMG_ vm_val_t *result,
94                               const uchar **pc_ptr, uint *argc,
95                               vm_prop_id_t prop)
96     {
97         /* explicitly inherit our superclass handling */
98         return CVmObject::call_stat_prop(vmg_ result, pc_ptr, argc, prop);
99     }
100 
101     /* reserve constant data */
reserve_const_data(VMG_ class CVmConstMapper * mapper,vm_obj_id_t self)102     virtual void reserve_const_data(VMG_ class CVmConstMapper *mapper,
103                                     vm_obj_id_t self)
104     {
105         /*
106          *   we reference no other objects and cannot ourselves be converted
107          *   to constant data, so there's nothing to do here
108          */
109     }
110 
111     /* convert to constant data */
convert_to_const_data(VMG_ class CVmConstMapper * mapper,vm_obj_id_t self)112     virtual void convert_to_const_data(VMG_ class CVmConstMapper *mapper,
113                                        vm_obj_id_t self)
114     {
115         /*
116          *   we reference no data and cannot be converted to constant data,
117          *   so there's nothing to do
118          */
119     }
120 
121     /* create an array with no initial contents */
122     static vm_obj_id_t create(VMG_ int in_root_set);
123 
124     /* create an array with a given number of elements */
125     static vm_obj_id_t create(VMG_ int in_root_set,
126                               unsigned long element_count);
127 
128     /*
129      *   determine if an object is a byte array - it is if the object's
130      *   virtual metaclass registration index matches the class's static
131      *   index
132      */
is_byte_array(VMG_ vm_obj_id_t obj)133     static int is_byte_array(VMG_ vm_obj_id_t obj)
134         { return vm_objp(vmg_ obj)->is_of_metaclass(metaclass_reg_); }
135 
136     /* notify of deletion */
137     void notify_delete(VMG_ int in_root_set);
138 
139     /* set a property */
140     void set_prop(VMG_ class CVmUndo *undo,
141                   vm_obj_id_t self, vm_prop_id_t prop, const vm_val_t *val);
142 
143     /* get a property */
144     int get_prop(VMG_ vm_prop_id_t prop, vm_val_t *val,
145                  vm_obj_id_t self, vm_obj_id_t *source_obj, uint *argc);
146 
147     /* undo operations */
notify_new_savept()148     void notify_new_savept() { }
149     void apply_undo(VMG_ struct CVmUndoRecord *rec);
150     void discard_undo(VMG_ struct CVmUndoRecord *);
151 
152     /* our data are just bytes - we reference nothing */
mark_undo_ref(VMG_ struct CVmUndoRecord *)153     void mark_undo_ref(VMG_ struct CVmUndoRecord *) { }
remove_stale_undo_weak_ref(VMG_ struct CVmUndoRecord *)154     void remove_stale_undo_weak_ref(VMG_ struct CVmUndoRecord *) { }
mark_refs(VMG_ uint)155     void mark_refs(VMG_ uint /*state*/) { }
remove_stale_weak_refs(VMG0_)156     void remove_stale_weak_refs(VMG0_) { }
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     /* rebuild for image file */
162     virtual ulong rebuild_image(VMG_ char *buf, ulong buflen);
163 
164     /* reload from the image file */
165     void reload_from_image(VMG_ vm_obj_id_t self,
166                            const char *ptr, size_t siz);
167 
168     /* save to a file */
169     void save_to_file(VMG_ class CVmFile *fp);
170 
171     /* restore from a file */
172     void restore_from_file(VMG_ vm_obj_id_t self,
173                            class CVmFile *fp, class CVmObjFixup *fixup);
174 
175     /* index the array */
176     void index_val(VMG_ vm_val_t *result, vm_obj_id_t self,
177                    const vm_val_t *index_val);
178 
179     /* set an indexed element of the array */
180     void set_index_val(VMG_ vm_val_t *new_container, vm_obj_id_t self,
181                        const vm_val_t *index_val, const vm_val_t *new_val);
182 
183     /*
184      *   Check a value for equality.  We will match another byte array with
185      *   the same number of elements and the same value for each element.
186      */
187     int equals(VMG_ vm_obj_id_t self, const vm_val_t *val, int depth) const;
188 
189     /* calculate a hash value for the array */
190     uint calc_hash(VMG_ vm_obj_id_t self, int depth) const;
191 
192     /*
193      *   assume that we've been changed since loading, if we came from the
194      *   image file
195      */
is_changed_since_load()196     int is_changed_since_load() const { return TRUE; }
197 
198     /* get the number of elements in the array */
get_element_count()199     unsigned long get_element_count() const
200         { return osrp4(get_ext_ptr()); }
201 
202     /*
203      *   construction: copy (without undo) bytes from a buffer into the byte
204      *   array
205      */
cons_copy_from_buf(const unsigned char * buf,unsigned long idx,size_t cnt)206     void cons_copy_from_buf(const unsigned char *buf,
207                             unsigned long idx, size_t cnt)
208     {
209         /* copy the bytes into our array */
210         copy_from_buf(buf, idx, cnt);
211     }
212 
213     /*
214      *   Write the specified region of the array to a file.  Returns zero on
215      *   success, non-zero on failure.
216      */
217     int write_to_file(osfildef *fp, unsigned long start_idx,
218                       unsigned long len) const;
219 
220     /*
221      *   Read bytes from a file into a region of the array, replacing
222      *   existing bytes in the array; saves undo for the change.  Returns
223      *   the number of bytes actually read from the file, which will be less
224      *   than the number of bytes requested if we reach the end of the file
225      *   before satisfying the request.
226      */
227     unsigned long read_from_file(osfildef *fp, unsigned long start_idx,
228                                  unsigned long len);
229 
230     /*
231      *   write to a 'data' mode file - returns zero on success, non-zero on
232      *   failure
233      */
234     int write_to_data_file(osfildef *fp);
235 
236     /*
237      *   read from a 'data' mode file, creating a new ByteArray object to
238      *   hold the bytes from the file
239      */
240     static int read_from_data_file(VMG_ vm_val_t *retval, osfildef *fp);
241 
242 protected:
243     /* load image data */
244     virtual void load_image_data(VMG_ const char *ptr, size_t siz);
245 
246     /* create a list with no initial contents */
CVmObjByteArray()247     CVmObjByteArray() { ext_ = 0; }
248 
249     /*
250      *   create a list with a given number of elements, for construction
251      *   of the list element-by-element
252      */
253     CVmObjByteArray(VMG_ unsigned long byte_count);
254 
255     /* get a pointer to my extension */
get_ext_ptr()256     const char *get_ext_ptr() const { return ext_; }
257 
258     /*
259      *   get my extension data pointer for construction purposes - this is
260      *   a writable pointer, so that a caller can fill our data buffer
261      *   during construction
262      */
cons_get_ext_ptr()263     char *cons_get_ext_ptr() const { return ext_; }
264 
265     /* allocate space for the array, given the number of elements */
266     void alloc_array(VMG_ unsigned long element_count);
267 
268     /* property evaluator - undefined function */
getp_undef(VMG_ vm_obj_id_t,vm_val_t *,uint *)269     int getp_undef(VMG_ vm_obj_id_t, vm_val_t *, uint *) { return FALSE; }
270 
271     /* property evaluator - length */
272     int getp_length(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc);
273 
274     /* property evaluator - subarray */
275     int getp_subarray(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc);
276 
277     /* property evaluator - copy from another byte array */
278     int getp_copy_from(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc);
279 
280     /* property evaluator - fill with a value */
281     int getp_fill_val(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc);
282 
283     /* property evaluator - convert to string */
284     int getp_to_string(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc);
285 
286     /* property evaluator - read integer */
287     int getp_read_int(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc);
288 
289     /* property evaluator - write integer */
290     int getp_write_int(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc);
291 
292     /*
293      *   Given a 1-based index, get a pointer to the byte at the index, and
294      *   the number of contiguous bytes available starting with that byte.
295      *   The available byte count doesn't take into account a short last
296      *   page, but simply returns the maximum number of bytes that would be
297      *   available on the page if it were allocated to full size; the caller
298      *   is responsible for ensuring that there is no reading or writing
299      *   past the end of the array.
300      */
get_ele_ptr(unsigned long idx,size_t * bytes_avail)301     unsigned char *get_ele_ptr(unsigned long idx, size_t *bytes_avail) const
302     {
303         size_t s1;
304         size_t s2;
305         size_t s3;
306 
307         /* convert to a zero-based index */
308         --idx;
309 
310         /*
311          *   calculate the page table index - since each page holds 32k
312          *   bytes and each page table points to 8k pages, divide by 32k*8k
313          *   == 2^15*2^13 == 2^28
314          */
315         s1 = idx >> 28;
316 
317         /*
318          *   calculate the page index within the page table - each page
319          *   holds 32k, so calculate the excess from the page table selector
320          *   (i.e, idx % 32k*8k) and then divide by 32k == 2^15
321          */
322         s2 = (idx & 0x0FFFFFFF) >> 15;
323 
324         /*
325          *   calculate the page offset - this is simply the excess from the
326          *   page index
327          */
328         s3 = idx & 0x7FFF;
329 
330         /*
331          *   Each page holds 32k, so the number of contiguous bytes starting
332          *   at this byte is 32k less the index.
333          */
334         *bytes_avail = (32*1024) - s3;
335 
336         /*
337          *   dereference the extension to get the page table, deference the
338          *   page table to get the page, and index the page by the offset
339          */
340         return get_page_table_ptr(s1)[s2] + s3;
341     }
342 
343     /*
344      *   Given a page table selector, return a pointer to the selected page
345      *   table.
346      */
get_page_table_ptr(size_t s)347     unsigned char **get_page_table_ptr(size_t s) const
348     {
349         return get_page_table_array()[s];
350     }
351 
352     /*
353      *   Get a pointer to the page table array
354      */
get_page_table_array()355     unsigned char ***get_page_table_array() const
356     {
357         /* the page table array starts after the element count */
358         return (unsigned char ***)(ext_ + 4);
359     }
360 
361     /* fill the given (1-based index) range with the given byte value */
362     void fill_with(unsigned char val, unsigned long start_idx,
363                    unsigned long cnt);
364 
365     /* copy bytes from another byte array into this one */
366     void copy_from(unsigned long dst_idx,
367                    CVmObjByteArray *src_arr,
368                    unsigned long src_start_idx, unsigned long cnt);
369 
370     /* move bytes within this array */
371     void move_bytes(unsigned long dst_idx, unsigned long src_idx,
372                     unsigned long cnt);
373 
374     /* copy bytes into a buffer */
375     void copy_to_buf(unsigned char *buf, unsigned long idx, size_t cnt) const;
376 
377     /* copy bytes from a buffer into the array */
378     void copy_from_buf(const unsigned char *buf,
379                        unsigned long idx, size_t cnt);
380 
381     /* map to a string */
382     size_t map_to_string(unsigned long idx, unsigned long len,
383                          class CVmObjString *str, size_t str_len,
384                          class CCharmapToUni *mapper);
385 
386     /* save undo for a change to a range of the array */
387     void save_undo(VMG_ vm_obj_id_t self, unsigned long start_idx,
388                    unsigned long cnt);
389 
390     /* set the number of bytes in the array */
set_element_count(unsigned long cnt)391     void set_element_count(unsigned long cnt)
392         { oswp4(cons_get_ext_ptr(), cnt); }
393 
394     /* property evaluation function table */
395     static int (CVmObjByteArray::*func_table_[])(
396         VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc);
397 };
398 
399 /* ------------------------------------------------------------------------ */
400 /*
401  *   Registration table object
402  */
403 class CVmMetaclassByteArray: public CVmMetaclass
404 {
405 public:
406     /* get the global name */
get_meta_name()407     const char *get_meta_name() const { return "bytearray/030001"; }
408 
409     /* create from image file */
create_for_image_load(VMG_ vm_obj_id_t id)410     void create_for_image_load(VMG_ vm_obj_id_t id)
411     {
412         new (vmg_ id) CVmObjByteArray();
413         G_obj_table->set_obj_gc_characteristics(id, FALSE, FALSE);
414     }
415 
416     /* create from restoring from saved state */
create_for_restore(VMG_ vm_obj_id_t id)417     void create_for_restore(VMG_ vm_obj_id_t id)
418     {
419         new (vmg_ id) CVmObjByteArray();
420         G_obj_table->set_obj_gc_characteristics(id, FALSE, FALSE);
421     }
422 
423     /* create dynamically using stack arguments */
create_from_stack(VMG_ const uchar ** pc_ptr,uint argc)424     vm_obj_id_t create_from_stack(VMG_ const uchar **pc_ptr, uint argc)
425         { return CVmObjByteArray::create_from_stack(vmg_ pc_ptr, argc); }
426 
427     /* call a static property */
call_stat_prop(VMG_ vm_val_t * result,const uchar ** pc_ptr,uint * argc,vm_prop_id_t prop)428     int call_stat_prop(VMG_ vm_val_t *result,
429                        const uchar **pc_ptr, uint *argc,
430                        vm_prop_id_t prop)
431     {
432         return CVmObjByteArray::
433             call_stat_prop(vmg_ result, pc_ptr, argc, prop);
434     }
435 };
436 
437 #endif /* VMBYTARR_H */
438 
439 
440 /*
441  *   Register the class
442  */
443 VM_REGISTER_METACLASS(CVmObjByteArray)
444