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