cfad47cfa3/tads3/vmbytarr.h

4b825dc642cb6eb9a060e54bf8d69288fbee4904cfad47cfa334b206c65f22086bcc5d63e6f70944
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_;
75
    class CVmMetaclass *get_metaclass_reg() const { return metaclass_reg_; }
76
77
    /* am I of the given metaclass? */
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
     */
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 */
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 */
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
     */
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 */
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 */
153
    void mark_undo_ref(VMG_ struct CVmUndoRecord *) { }
154
    void remove_stale_undo_weak_ref(VMG_ struct CVmUndoRecord *) { }
155
    void mark_refs(VMG_ uint /*state*/) { }
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
     */
196
    int is_changed_since_load() const { return TRUE; }
197
198
    /* get the number of elements in the array */
199
    unsigned long get_element_count() const
200
        { return t3rp4u(get_ext_ptr()); }
201
202
    /* 
203
     *   construction: copy (without undo) bytes from a buffer into the byte
204
     *   array 
205
     */
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 */
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 */
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
     */
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 */
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
     */
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
     */
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
     */
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 */
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 */
407
    const char *get_meta_name() const { return "bytearray/030001"; }
408
409
    /* create from image file */
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 */
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 */
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 */
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)