| | 1 | /* $Header$ */ |
| | 2 | |
| | 3 | /* |
| | 4 | * Copyright (c) 2001, 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 | vmlookup.h - LookupTable metaclass |
| | 12 | Function |
| | 13 | |
| | 14 | Notes |
| | 15 | |
| | 16 | Modified |
| | 17 | 02/06/01 MJRoberts - Creation |
| | 18 | */ |
| | 19 | |
| | 20 | #ifndef VMLOOKUP_H |
| | 21 | #define VMLOOKUP_H |
| | 22 | |
| | 23 | #include "t3std.h" |
| | 24 | #include "vmtype.h" |
| | 25 | #include "vmglob.h" |
| | 26 | #include "vmobj.h" |
| | 27 | #include "vmundo.h" |
| | 28 | #include "vmcoll.h" |
| | 29 | #include "vmiter.h" |
| | 30 | |
| | 31 | |
| | 32 | /* ------------------------------------------------------------------------ */ |
| | 33 | /* |
| | 34 | * The image file data block is arranged as follows: |
| | 35 | * |
| | 36 | *. UINT2 bucket_count |
| | 37 | *. UINT2 value_count |
| | 38 | *. UINT2 first_free_index |
| | 39 | *. UINT2 bucket_index[1] |
| | 40 | *. UINT2 bucket_index[2] |
| | 41 | *. ... |
| | 42 | *. UINT2 bucket_index[N] |
| | 43 | *. value[1] |
| | 44 | *. value[2] |
| | 45 | *. value[3] |
| | 46 | *. etc |
| | 47 | * |
| | 48 | * value_count gives the number of value slots allocated. Free value |
| | 49 | * slots are kept in a linked list, the head of which is at the 1-based |
| | 50 | * index given by first_free_index. If first_free_index is zero, there |
| | 51 | * are no free value slots. |
| | 52 | * |
| | 53 | * Each bucket_index[i] is the 1-based index of the first value in the |
| | 54 | * chain for that hash bucket. If the value in a bucket_index[i] is zero, |
| | 55 | * there are values for that bucket. |
| | 56 | * |
| | 57 | * Each free entry has a VM_EMPTY value stored in its key to indicate that |
| | 58 | * it's empty. |
| | 59 | * |
| | 60 | * Each value[i] looks like this: |
| | 61 | * |
| | 62 | *. DATAHOLDER key |
| | 63 | *. DATAHOLDER value |
| | 64 | *. UINT2 next_index |
| | 65 | * |
| | 66 | * next_index gives the 1-based index of the next value in the chain for |
| | 67 | * that bucket; a value of zero indicates that this is the last value in |
| | 68 | * the chain. |
| | 69 | */ |
| | 70 | |
| | 71 | /* value entry size */ |
| | 72 | #define VMLOOKUP_VALUE_SIZE (VMB_DATAHOLDER + VMB_DATAHOLDER + VMB_UINT2) |
| | 73 | |
| | 74 | /* ------------------------------------------------------------------------ */ |
| | 75 | /* |
| | 76 | * in-memory value entry structure |
| | 77 | */ |
| | 78 | struct vm_lookup_val |
| | 79 | { |
| | 80 | /* the key */ |
| | 81 | vm_val_t key; |
| | 82 | |
| | 83 | /* the value */ |
| | 84 | vm_val_t val; |
| | 85 | |
| | 86 | /* next entry in same hash bucket */ |
| | 87 | vm_lookup_val *nxt; |
| | 88 | }; |
| | 89 | |
| | 90 | /* |
| | 91 | * Our in-memory extension data structure, which mimics the image file |
| | 92 | * structure but uses native types. |
| | 93 | */ |
| | 94 | struct vm_lookup_ext |
| | 95 | { |
| | 96 | /* allocate the structure, given the number of buckets and values */ |
| | 97 | static vm_lookup_ext *alloc_ext(VMG_ class CVmObjLookupTable *self, |
| | 98 | uint bucket_cnt, uint value_cnt); |
| | 99 | |
| | 100 | /* |
| | 101 | * Initialize the extension - puts all values into the free list and |
| | 102 | * clears all buckets. We don't do this automatically as part of |
| | 103 | * allocation, because some types of allocation set up the buckets and |
| | 104 | * free list from a known data set and thus are more efficient if they |
| | 105 | * skip the initialization step. |
| | 106 | */ |
| | 107 | void init_ext(); |
| | 108 | |
| | 109 | /* |
| | 110 | * Reallocate the structure with a larger number of values. Copies |
| | 111 | * all of the data from the original hash table into the new hash |
| | 112 | * table, and deletes the old structure. |
| | 113 | */ |
| | 114 | static vm_lookup_ext *expand_ext(VMG_ class CVmObjLookupTable *self, |
| | 115 | vm_lookup_ext *old_ext, |
| | 116 | uint new_value_cnt); |
| | 117 | |
| | 118 | /* |
| | 119 | * Copy the given extension's data into myself. This can only be used |
| | 120 | * when we have the same bucket count as the original (the entry count |
| | 121 | * need not be the same, but it must be large enough to hold all of |
| | 122 | * the data from the original). |
| | 123 | * |
| | 124 | * This loses any data previously in the table. |
| | 125 | */ |
| | 126 | void copy_ext_from(vm_lookup_ext *old_ext); |
| | 127 | |
| | 128 | /* allocate a value entry out of my free list */ |
| | 129 | vm_lookup_val *alloc_val_entry() |
| | 130 | { |
| | 131 | vm_lookup_val *entry; |
| | 132 | |
| | 133 | /* if the free list is empty, return failure */ |
| | 134 | if (first_free == 0) |
| | 135 | return 0; |
| | 136 | |
| | 137 | /* take the first item off the free list */ |
| | 138 | entry = first_free; |
| | 139 | |
| | 140 | /* unlink it from the free list */ |
| | 141 | first_free = first_free->nxt; |
| | 142 | |
| | 143 | /* return the allocated item */ |
| | 144 | return entry; |
| | 145 | } |
| | 146 | |
| | 147 | /* |
| | 148 | * Add a value into the given hash bucket. The caller is responsible |
| | 149 | * for ensuring there's enough room. |
| | 150 | */ |
| | 151 | void add_val(uint hash, const vm_val_t *key, const vm_val_t *val) |
| | 152 | { |
| | 153 | vm_lookup_val *entry; |
| | 154 | |
| | 155 | /* allocate a new entry */ |
| | 156 | entry = alloc_val_entry(); |
| | 157 | |
| | 158 | /* set it up with the new data */ |
| | 159 | entry->key = *key; |
| | 160 | entry->val = *val; |
| | 161 | |
| | 162 | /* link it into the given bucket */ |
| | 163 | entry->nxt = buckets[hash]; |
| | 164 | buckets[hash] = entry; |
| | 165 | } |
| | 166 | |
| | 167 | /* |
| | 168 | * Given a pool index, retrieve a value entry from our pool of value |
| | 169 | * entries. This has nothing to do with the hash bucket lists or the |
| | 170 | * free list - this is simply the nth entry in the master pool of all |
| | 171 | * values. |
| | 172 | */ |
| | 173 | vm_lookup_val *idx_to_val(uint idx) const |
| | 174 | { |
| | 175 | vm_lookup_val *pool; |
| | 176 | |
| | 177 | /* the pool of values starts immediately after the buckets */ |
| | 178 | pool = (vm_lookup_val *)(void *)&buckets[bucket_cnt]; |
| | 179 | |
| | 180 | /* return the nth element of the pool */ |
| | 181 | return &pool[idx]; |
| | 182 | } |
| | 183 | |
| | 184 | /* given a value entry, get the pool index */ |
| | 185 | uint val_to_idx(vm_lookup_val *val) const |
| | 186 | { |
| | 187 | return (val - idx_to_val(0)); |
| | 188 | } |
| | 189 | |
| | 190 | /* |
| | 191 | * Convert an image-file or save-file index to a value pointer. These |
| | 192 | * are given as 1-based pointers, with the special value zero used to |
| | 193 | * indicate a null pointer. |
| | 194 | */ |
| | 195 | vm_lookup_val *img_idx_to_val(uint idx) const |
| | 196 | { |
| | 197 | if (idx == 0) |
| | 198 | return 0; |
| | 199 | else |
| | 200 | return idx_to_val(idx - 1); |
| | 201 | } |
| | 202 | |
| | 203 | /* convert a value pointer to an image file index */ |
| | 204 | uint val_to_img_idx(vm_lookup_val *val) |
| | 205 | { |
| | 206 | /* |
| | 207 | * use zero for a null pointer; otherwise, use a 1-based index in |
| | 208 | * our master value pool |
| | 209 | */ |
| | 210 | if (val == 0) |
| | 211 | return 0; |
| | 212 | else |
| | 213 | return (val - idx_to_val(0)) + 1; |
| | 214 | } |
| | 215 | |
| | 216 | /* number of buckets and number of allocated value entries */ |
| | 217 | uint bucket_cnt; |
| | 218 | uint value_cnt; |
| | 219 | |
| | 220 | /* pointer to the first free value */ |
| | 221 | vm_lookup_val *first_free; |
| | 222 | |
| | 223 | /* |
| | 224 | * buckets (we overallocate the structure to make room): each bucket |
| | 225 | * points to the first entry in the list of entries at this hash value |
| | 226 | */ |
| | 227 | vm_lookup_val *buckets[1]; |
| | 228 | }; |
| | 229 | |
| | 230 | |
| | 231 | /* ------------------------------------------------------------------------ */ |
| | 232 | /* |
| | 233 | * undo action codes |
| | 234 | */ |
| | 235 | enum lookuptab_undo_action |
| | 236 | { |
| | 237 | /* |
| | 238 | * null record - we use this to mark a record that has become |
| | 239 | * irrelevant because of a stale weak reference |
| | 240 | */ |
| | 241 | LOOKUPTAB_UNDO_NULL, |
| | 242 | |
| | 243 | /* we added this word to the dictionary (undo by deleting it) */ |
| | 244 | LOOKUPTAB_UNDO_ADD, |
| | 245 | |
| | 246 | /* we deleted this word from the dictionary (undo by adding it back) */ |
| | 247 | LOOKUPTAB_UNDO_DEL, |
| | 248 | |
| | 249 | /* we modified the value for a given key */ |
| | 250 | LOOKUPTAB_UNDO_MOD |
| | 251 | }; |
| | 252 | |
| | 253 | |
| | 254 | /* ------------------------------------------------------------------------ */ |
| | 255 | /* |
| | 256 | * LookupTable metaclass |
| | 257 | */ |
| | 258 | |
| | 259 | class CVmObjLookupTable: public CVmObjCollection |
| | 260 | { |
| | 261 | friend class CVmObjIterLookupTable; |
| | 262 | friend class CVmMetaclassLookupTable; |
| | 263 | |
| | 264 | public: |
| | 265 | /* metaclass registration object */ |
| | 266 | static class CVmMetaclass *metaclass_reg_; |
| | 267 | class CVmMetaclass *get_metaclass_reg() const { return metaclass_reg_; } |
| | 268 | |
| | 269 | /* am I of the given metaclass? */ |
| | 270 | virtual int is_of_metaclass(class CVmMetaclass *meta) const |
| | 271 | { |
| | 272 | /* try my own metaclass and my base class */ |
| | 273 | return (meta == metaclass_reg_ |
| | 274 | || CVmObjCollection::is_of_metaclass(meta)); |
| | 275 | } |
| | 276 | |
| | 277 | /* create */ |
| | 278 | static vm_obj_id_t create(VMG_ int in_root_set, |
| | 279 | uint bucket_count, uint init_capacity); |
| | 280 | |
| | 281 | /* create dynamically using stack arguments */ |
| | 282 | static vm_obj_id_t create_from_stack(VMG_ const uchar **pc_ptr, |
| | 283 | uint argc); |
| | 284 | |
| | 285 | /* |
| | 286 | * call a static property - we don't have any of our own, so simply |
| | 287 | * "inherit" the base class handling |
| | 288 | */ |
| | 289 | static int call_stat_prop(VMG_ vm_val_t *result, |
| | 290 | const uchar **pc_ptr, uint *argc, |
| | 291 | vm_prop_id_t prop) |
| | 292 | { |
| | 293 | return CVmObjCollection:: |
| | 294 | call_stat_prop(vmg_ result, pc_ptr, argc, prop); |
| | 295 | } |
| | 296 | |
| | 297 | /* reserve constant data */ |
| | 298 | virtual void reserve_const_data(VMG_ class CVmConstMapper *mapper, |
| | 299 | vm_obj_id_t self) |
| | 300 | { |
| | 301 | /* we cannot be converted to constant data */ |
| | 302 | } |
| | 303 | |
| | 304 | /* convert to constant data */ |
| | 305 | virtual void convert_to_const_data(VMG_ class CVmConstMapper *mapper, |
| | 306 | vm_obj_id_t self); |
| | 307 | |
| | 308 | /* notify of deletion */ |
| | 309 | void notify_delete(VMG_ int in_root_set); |
| | 310 | |
| | 311 | /* set a property */ |
| | 312 | void set_prop(VMG_ class CVmUndo *undo, |
| | 313 | vm_obj_id_t self, vm_prop_id_t prop, const vm_val_t *val); |
| | 314 | |
| | 315 | /* get a property */ |
| | 316 | int get_prop(VMG_ vm_prop_id_t prop, vm_val_t *val, |
| | 317 | vm_obj_id_t self, vm_obj_id_t *source_obj, uint *argc); |
| | 318 | |
| | 319 | /* |
| | 320 | * receive savepoint notification - we don't keep any |
| | 321 | * savepoint-relative records, so we don't need to do anything here |
| | 322 | */ |
| | 323 | void notify_new_savept() { } |
| | 324 | |
| | 325 | /* apply an undo record */ |
| | 326 | void apply_undo(VMG_ struct CVmUndoRecord *rec); |
| | 327 | |
| | 328 | /* discard an undo record */ |
| | 329 | void discard_undo(VMG_ struct CVmUndoRecord *); |
| | 330 | |
| | 331 | /* mark undo references */ |
| | 332 | void mark_undo_ref(VMG_ struct CVmUndoRecord *rec); |
| | 333 | |
| | 334 | /* mark references */ |
| | 335 | void mark_refs(VMG_ uint); |
| | 336 | |
| | 337 | /* we keep only strong references */ |
| | 338 | void remove_stale_weak_refs(VMG0_) { } |
| | 339 | void remove_stale_undo_weak_ref(VMG_ struct CVmUndoRecord *) { } |
| | 340 | |
| | 341 | /* load from an image file */ |
| | 342 | void load_from_image(VMG_ vm_obj_id_t self, const char *ptr, size_t siz); |
| | 343 | |
| | 344 | /* reload from an image file */ |
| | 345 | void reload_from_image(VMG_ vm_obj_id_t self, |
| | 346 | const char *ptr, size_t siz); |
| | 347 | |
| | 348 | /* rebuild for image file */ |
| | 349 | virtual ulong rebuild_image(VMG_ char *buf, ulong buflen); |
| | 350 | |
| | 351 | /* save to a file */ |
| | 352 | void save_to_file(VMG_ class CVmFile *fp); |
| | 353 | |
| | 354 | /* restore from a file */ |
| | 355 | void restore_from_file(VMG_ vm_obj_id_t self, |
| | 356 | class CVmFile *fp, class CVmObjFixup *fixups); |
| | 357 | |
| | 358 | /* |
| | 359 | * determine if we've been changed since loading - assume we have (if |
| | 360 | * we haven't, the only harm is the cost of unnecessarily reloading or |
| | 361 | * saving) |
| | 362 | */ |
| | 363 | int is_changed_since_load() const { return TRUE; } |
| | 364 | |
| | 365 | /* get a value by index */ |
| | 366 | void index_val(VMG_ vm_val_t *result, |
| | 367 | vm_obj_id_t self, |
| | 368 | const vm_val_t *index_val); |
| | 369 | |
| | 370 | /* set a value by index */ |
| | 371 | void set_index_val(VMG_ vm_val_t *new_container, |
| | 372 | vm_obj_id_t self, |
| | 373 | const vm_val_t *index_val, |
| | 374 | const vm_val_t *new_val); |
| | 375 | |
| | 376 | /* add an entry - does not generate undo */ |
| | 377 | void add_entry(VMG_ const vm_val_t *key, const vm_val_t *val); |
| | 378 | |
| | 379 | protected: |
| | 380 | /* get and range-check the constructor arguments */ |
| | 381 | static void get_constructor_args(VMG_ uint argc, size_t *bucket_count, |
| | 382 | size_t *init_capacity); |
| | 383 | |
| | 384 | /* load or reload image data */ |
| | 385 | void load_image_data(VMG_ const char *ptr, size_t siz); |
| | 386 | |
| | 387 | /* create a new object as a copy of this object */ |
| | 388 | vm_obj_id_t create_copy(VMG0_); |
| | 389 | |
| | 390 | /* add an entry, generating undo */ |
| | 391 | void add_entry_undo(VMG_ vm_obj_id_t self, |
| | 392 | const vm_val_t *key, const vm_val_t *val); |
| | 393 | |
| | 394 | /* delete an entry - does not generate undo */ |
| | 395 | void del_entry(VMG_ const vm_val_t *key); |
| | 396 | |
| | 397 | /* |
| | 398 | * Unlink an entry - does not generate undo. 'prv_entry' is the |
| | 399 | * previous entry in the hash chain containing this entry, and |
| | 400 | * 'hashval' is the bucket containing the entry. Pass null for |
| | 401 | * 'prv_entry' when the entry to unlink is the first entry in its hash |
| | 402 | * chain. |
| | 403 | */ |
| | 404 | void unlink_entry(VMG_ vm_lookup_val *entry, uint hashval, |
| | 405 | vm_lookup_val *prv_entry); |
| | 406 | |
| | 407 | /* |
| | 408 | * modify an entry - changes the value associated with the given key; |
| | 409 | * does not generate undo |
| | 410 | */ |
| | 411 | void mod_entry(VMG_ const vm_val_t *key, const vm_val_t *val); |
| | 412 | |
| | 413 | /* find an entry */ |
| | 414 | vm_lookup_val *find_entry(VMG_ const vm_val_t *key, |
| | 415 | uint *hashval_p, vm_lookup_val **prv_entry_p); |
| | 416 | |
| | 417 | /* |
| | 418 | * Check the table to make sure there's enough free space to add one |
| | 419 | * new item, and expand the table if necessary. |
| | 420 | */ |
| | 421 | void expand_if_needed(VMG0_); |
| | 422 | |
| | 423 | /* allocate a new entry, expanding the table if necessary */ |
| | 424 | vm_lookup_val *alloc_new_entry(VMG0_); |
| | 425 | |
| | 426 | /* calculate a value's hash code */ |
| | 427 | uint calc_key_hash(VMG_ const vm_val_t *key); |
| | 428 | |
| | 429 | /* get my extension data */ |
| | 430 | vm_lookup_ext *get_ext() const { return (vm_lookup_ext *)ext_; } |
| | 431 | |
| | 432 | /* get the hash bucket count */ |
| | 433 | uint get_bucket_count() const { return get_ext()->bucket_cnt; } |
| | 434 | |
| | 435 | /* get the value entry count */ |
| | 436 | uint get_entry_count() const { return get_ext()->value_cnt; } |
| | 437 | |
| | 438 | /* get/set the first-free item */ |
| | 439 | vm_lookup_val *get_first_free() const { return get_ext()->first_free; } |
| | 440 | void set_first_free(vm_lookup_val *p) { get_ext()->first_free = p; } |
| | 441 | |
| | 442 | /* get a bucket's first entry given a hash code */ |
| | 443 | vm_lookup_val **get_bucket(uint hash) const |
| | 444 | { return &get_ext()->buckets[hash]; } |
| | 445 | |
| | 446 | /* set a bucket's contents given a hash code */ |
| | 447 | void set_bucket(uint hash, vm_lookup_val *p) |
| | 448 | { get_ext()->buckets[hash] = p; } |
| | 449 | |
| | 450 | /* set an entry, keeping undo for the change */ |
| | 451 | void set_entry_val_undo(VMG_ vm_obj_id_t self, |
| | 452 | vm_lookup_val *entry, const vm_val_t *val); |
| | 453 | |
| | 454 | /* create an iterator */ |
| | 455 | virtual void new_iterator(VMG_ vm_val_t *retval, |
| | 456 | const vm_val_t *self_val); |
| | 457 | |
| | 458 | /* create a live iterator */ |
| | 459 | virtual void new_live_iterator(VMG_ vm_val_t *retval, |
| | 460 | const vm_val_t *self_val); |
| | 461 | |
| | 462 | /* create a lookup table with no initial contents */ |
| | 463 | CVmObjLookupTable() { ext_ = 0; } |
| | 464 | |
| | 465 | /* |
| | 466 | * Create a lookup table with a given number of hash table buckets, |
| | 467 | * and the given number of entry slots. The hash table bucket count |
| | 468 | * is fixed for the life of the object. The entry slot count is |
| | 469 | * merely advisory: the table size will be increased as necessary to |
| | 470 | * accommodate new elements. |
| | 471 | */ |
| | 472 | CVmObjLookupTable(VMG_ size_t hash_count, size_t entry_count); |
| | 473 | |
| | 474 | /* property evaluator - undefined function */ |
| | 475 | int getp_undef(VMG_ vm_obj_id_t, vm_val_t *, uint *) { return FALSE; } |
| | 476 | |
| | 477 | /* property evaluator - remove an entry given the key */ |
| | 478 | int getp_remove_entry(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); |
| | 479 | |
| | 480 | /* property evaluator - determine if a given key is in the table */ |
| | 481 | int getp_key_present(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); |
| | 482 | |
| | 483 | /* property evaluator - apply a callback to each element */ |
| | 484 | int getp_apply_all(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); |
| | 485 | |
| | 486 | /* property evaluator - call a callback on each element */ |
| | 487 | int getp_for_each(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); |
| | 488 | |
| | 489 | /* property evaluator - call a callback on each element */ |
| | 490 | int getp_for_each_assoc(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); |
| | 491 | |
| | 492 | /* general forEach/forEachAssoc processor */ |
| | 493 | int for_each_gen(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc, |
| | 494 | int pass_key_to_cb); |
| | 495 | |
| | 496 | /* get the number of buckets in the table */ |
| | 497 | int getp_count_buckets(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); |
| | 498 | |
| | 499 | /* get the number of entries in the table */ |
| | 500 | int getp_count_entries(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); |
| | 501 | |
| | 502 | /* make a list of all of the keys in the table */ |
| | 503 | int getp_keys_to_list(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); |
| | 504 | |
| | 505 | /* make a list of all of the values in the table */ |
| | 506 | int getp_vals_to_list(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc); |
| | 507 | |
| | 508 | /* general handler for making a list of keys or values */ |
| | 509 | int make_list(VMG_ vm_obj_id_t self, |
| | 510 | vm_val_t *retval, uint *argc, int store_keys); |
| | 511 | |
| | 512 | /* add a record to the global undo stream */ |
| | 513 | void add_undo_rec(VMG_ vm_obj_id_t self, |
| | 514 | enum lookuptab_undo_action action, |
| | 515 | const vm_val_t *key, |
| | 516 | const vm_val_t *old_entry_val); |
| | 517 | |
| | 518 | /* property evaluation function table */ |
| | 519 | static int (CVmObjLookupTable::*func_table_[])(VMG_ vm_obj_id_t self, |
| | 520 | vm_val_t *retval, uint *argc); |
| | 521 | }; |
| | 522 | |
| | 523 | /* ------------------------------------------------------------------------ */ |
| | 524 | /* |
| | 525 | * WeakRefLookupTable - a subclass of LookupTable that places weak |
| | 526 | * references on its values. The keys are still strong references. |
| | 527 | */ |
| | 528 | class CVmObjWeakRefLookupTable: public CVmObjLookupTable |
| | 529 | { |
| | 530 | friend class CVmMetaclassWeakRefLookupTable; |
| | 531 | |
| | 532 | public: |
| | 533 | /* metaclass registration object */ |
| | 534 | static class CVmMetaclass *metaclass_reg_; |
| | 535 | class CVmMetaclass *get_metaclass_reg() const { return metaclass_reg_; } |
| | 536 | |
| | 537 | /* am I of the given metaclass? */ |
| | 538 | virtual int is_of_metaclass(class CVmMetaclass *meta) const |
| | 539 | { |
| | 540 | /* try my own metaclass and my base class */ |
| | 541 | return (meta == metaclass_reg_ |
| | 542 | || CVmObjLookupTable::is_of_metaclass(meta)); |
| | 543 | } |
| | 544 | |
| | 545 | /* create */ |
| | 546 | static vm_obj_id_t create(VMG_ int in_root_set, |
| | 547 | uint bucket_count, uint init_capacity); |
| | 548 | |
| | 549 | /* create dynamically using stack arguments */ |
| | 550 | static vm_obj_id_t create_from_stack(VMG_ const uchar **pc_ptr, |
| | 551 | uint argc); |
| | 552 | |
| | 553 | /* |
| | 554 | * call a static property - we don't have any of our own, so simply |
| | 555 | * "inherit" the base class handling |
| | 556 | */ |
| | 557 | static int call_stat_prop(VMG_ vm_val_t *result, |
| | 558 | const uchar **pc_ptr, uint *argc, |
| | 559 | vm_prop_id_t prop) |
| | 560 | { |
| | 561 | return CVmObjLookupTable:: |
| | 562 | call_stat_prop(vmg_ result, pc_ptr, argc, prop); |
| | 563 | } |
| | 564 | |
| | 565 | /* mark references */ |
| | 566 | void mark_refs(VMG_ uint); |
| | 567 | |
| | 568 | /* mark undo references */ |
| | 569 | void mark_undo_ref(VMG_ struct CVmUndoRecord *rec); |
| | 570 | |
| | 571 | /* remove stale weak references */ |
| | 572 | void remove_stale_undo_weak_ref(VMG_ struct CVmUndoRecord *rec); |
| | 573 | |
| | 574 | /* remove stale weak references */ |
| | 575 | void remove_stale_weak_refs(VMG0_); |
| | 576 | |
| | 577 | protected: |
| | 578 | /* create a lookup table with no initial contents */ |
| | 579 | CVmObjWeakRefLookupTable() { ext_ = 0; } |
| | 580 | |
| | 581 | /* construct */ |
| | 582 | CVmObjWeakRefLookupTable(VMG_ size_t hash_count, size_t entry_count) |
| | 583 | : CVmObjLookupTable(vmg_ hash_count, entry_count) { } |
| | 584 | }; |
| | 585 | |
| | 586 | /* ------------------------------------------------------------------------ */ |
| | 587 | /* |
| | 588 | * LookupTable Registration table object |
| | 589 | */ |
| | 590 | class CVmMetaclassLookupTable: public CVmMetaclass |
| | 591 | { |
| | 592 | public: |
| | 593 | /* get the global name */ |
| | 594 | const char *get_meta_name() const { return "lookuptable/030002"; } |
| | 595 | |
| | 596 | /* create from image file */ |
| | 597 | void create_for_image_load(VMG_ vm_obj_id_t id) |
| | 598 | { |
| | 599 | new (vmg_ id) CVmObjLookupTable(); |
| | 600 | G_obj_table->set_obj_gc_characteristics(id, TRUE, FALSE); |
| | 601 | } |
| | 602 | |
| | 603 | /* create from restoring from saved state */ |
| | 604 | void create_for_restore(VMG_ vm_obj_id_t id) |
| | 605 | { |
| | 606 | new (vmg_ id) CVmObjLookupTable(); |
| | 607 | G_obj_table->set_obj_gc_characteristics(id, TRUE, FALSE); |
| | 608 | } |
| | 609 | |
| | 610 | /* create dynamically using stack arguments */ |
| | 611 | vm_obj_id_t create_from_stack(VMG_ const uchar **pc_ptr, uint argc) |
| | 612 | { return CVmObjLookupTable::create_from_stack(vmg_ pc_ptr, argc); } |
| | 613 | |
| | 614 | /* call a static property */ |
| | 615 | int call_stat_prop(VMG_ vm_val_t *result, |
| | 616 | const uchar **pc_ptr, uint *argc, |
| | 617 | vm_prop_id_t prop) |
| | 618 | { |
| | 619 | return CVmObjLookupTable:: |
| | 620 | call_stat_prop(vmg_ result, pc_ptr, argc, prop); |
| | 621 | } |
| | 622 | |
| | 623 | /* I'm a Collection object */ |
| | 624 | CVmMetaclass *get_supermeta_reg() const |
| | 625 | { return CVmObjCollection::metaclass_reg_; } |
| | 626 | }; |
| | 627 | |
| | 628 | /* |
| | 629 | * WeakRefLookupTable registration object |
| | 630 | */ |
| | 631 | class CVmMetaclassWeakRefLookupTable: public CVmMetaclass |
| | 632 | { |
| | 633 | public: |
| | 634 | /* get the global name */ |
| | 635 | const char *get_meta_name() const { return "weakreflookuptable/030000"; } |
| | 636 | |
| | 637 | /* create from image file */ |
| | 638 | void create_for_image_load(VMG_ vm_obj_id_t id) |
| | 639 | { new (vmg_ id) CVmObjWeakRefLookupTable(); } |
| | 640 | |
| | 641 | /* create from restoring from saved state */ |
| | 642 | void create_for_restore(VMG_ vm_obj_id_t id) |
| | 643 | { new (vmg_ id) CVmObjWeakRefLookupTable(); } |
| | 644 | |
| | 645 | /* create dynamically using stack arguments */ |
| | 646 | vm_obj_id_t create_from_stack(VMG_ const uchar **pc_ptr, uint argc) |
| | 647 | { return CVmObjWeakRefLookupTable:: |
| | 648 | create_from_stack(vmg_ pc_ptr, argc); } |
| | 649 | |
| | 650 | /* call a static property */ |
| | 651 | int call_stat_prop(VMG_ vm_val_t *result, |
| | 652 | const uchar **pc_ptr, uint *argc, |
| | 653 | vm_prop_id_t prop) |
| | 654 | { |
| | 655 | return CVmObjWeakRefLookupTable:: |
| | 656 | call_stat_prop(vmg_ result, pc_ptr, argc, prop); |
| | 657 | } |
| | 658 | |
| | 659 | /* I'm a LookupTable object */ |
| | 660 | CVmMetaclass *get_supermeta_reg() const |
| | 661 | { return CVmObjLookupTable::metaclass_reg_; } |
| | 662 | }; |
| | 663 | |
| | 664 | /* ------------------------------------------------------------------------ */ |
| | 665 | /* |
| | 666 | * LookupTable Iterator subclass. This iterator is tightly coupled with |
| | 667 | * the LookupTable class, since we must look into the internal |
| | 668 | * implementation of the LookupTable to implement an iterator on it. |
| | 669 | */ |
| | 670 | |
| | 671 | /* |
| | 672 | * The extension data for an lookup table iterator consists of a reference |
| | 673 | * to the associated LookupTable object, the current bucket index, and the |
| | 674 | * index of the current entry. |
| | 675 | * |
| | 676 | * DATAHOLDER lookuptable_value |
| | 677 | *. UINT2 cur_entry_index (1-based; zero is invalid) |
| | 678 | *. UINT2 flags |
| | 679 | * |
| | 680 | * The flag values are: |
| | 681 | * |
| | 682 | * VMOBJITERLOOKUPTABLE_UNDO - we've saved undo for this savepoint. If |
| | 683 | * this is set, we won't save additional undo for the same savepoint. |
| | 684 | */ |
| | 685 | |
| | 686 | /* total extension size */ |
| | 687 | #define VMOBJITERLOOKUPTABLE_EXT_SIZE (VMB_DATAHOLDER + 2 + 2) |
| | 688 | |
| | 689 | /* |
| | 690 | * flag bits |
| | 691 | */ |
| | 692 | |
| | 693 | /* we've saved undo for the current savepoint */ |
| | 694 | #define VMOBJITERLOOKUPTABLE_UNDO 0x0001 |
| | 695 | |
| | 696 | /* |
| | 697 | * LookupTable iterator class |
| | 698 | */ |
| | 699 | class CVmObjIterLookupTable: public CVmObjIter |
| | 700 | { |
| | 701 | friend class CVmMetaclassIterLookupTable; |
| | 702 | |
| | 703 | public: |
| | 704 | /* metaclass registration object */ |
| | 705 | static class CVmMetaclass *metaclass_reg_; |
| | 706 | class CVmMetaclass *get_metaclass_reg() const { return metaclass_reg_; } |
| | 707 | |
| | 708 | /* am I of the given metaclass? */ |
| | 709 | virtual int is_of_metaclass(class CVmMetaclass *meta) const |
| | 710 | { |
| | 711 | /* try my own metaclass and my base class */ |
| | 712 | return (meta == metaclass_reg_ |
| | 713 | || CVmObjIter::is_of_metaclass(meta)); |
| | 714 | } |
| | 715 | |
| | 716 | /* |
| | 717 | * call a static property - we don't have any of our own, so simply |
| | 718 | * "inherit" the base class handling |
| | 719 | */ |
| | 720 | static int call_stat_prop(VMG_ vm_val_t *result, |
| | 721 | const uchar **pc_ptr, uint *argc, |
| | 722 | vm_prop_id_t prop) |
| | 723 | { |
| | 724 | return CVmObjIter::call_stat_prop(vmg_ result, pc_ptr, argc, prop); |
| | 725 | } |
| | 726 | |
| | 727 | /* |
| | 728 | * Create a lookup table iterator. This method is to be called by the |
| | 729 | * lookup table to create an iterator for its value. |
| | 730 | */ |
| | 731 | static vm_obj_id_t create_for_coll(VMG_ const vm_val_t *coll); |
| | 732 | |
| | 733 | /* notify of deletion */ |
| | 734 | void notify_delete(VMG_ int in_root_set); |
| | 735 | |
| | 736 | /* |
| | 737 | * notify of a new savepoint - clear the 'undo' flag, since we cannot |
| | 738 | * have created any undo information yet for the new savepoint |
| | 739 | */ |
| | 740 | void notify_new_savept() |
| | 741 | { set_flags(get_flags() & ~VMOBJITERLOOKUPTABLE_UNDO); } |
| | 742 | |
| | 743 | /* apply undo */ |
| | 744 | void apply_undo(VMG_ struct CVmUndoRecord *rec); |
| | 745 | |
| | 746 | /* mark references */ |
| | 747 | void mark_refs(VMG_ uint state); |
| | 748 | |
| | 749 | /* there are no references in our undo stream */ |
| | 750 | void mark_undo_ref(VMG_ struct CVmUndoRecord *) { } |
| | 751 | |
| | 752 | /* we keep only strong references */ |
| | 753 | void remove_stale_weak_refs(VMG0_) { } |
| | 754 | void remove_stale_undo_weak_ref(VMG_ struct CVmUndoRecord *) { } |
| | 755 | |
| | 756 | /* load from an image file */ |
| | 757 | void load_from_image(VMG_ vm_obj_id_t self, const char *ptr, size_t siz); |
| | 758 | |
| | 759 | /* reload from an image file */ |
| | 760 | void reload_from_image(VMG_ vm_obj_id_t self, |
| | 761 | const char *ptr, size_t siz); |
| | 762 | |
| | 763 | /* |
| | 764 | * determine if the object has been changed since it was loaded - |
| | 765 | * assume we have |
| | 766 | */ |
| | 767 | int is_changed_since_load() const { return TRUE; } |
| | 768 | |
| | 769 | /* save to a file */ |
| | 770 | void save_to_file(VMG_ class CVmFile *fp); |
| | 771 | |
| | 772 | /* restore from a file */ |
| | 773 | void restore_from_file(VMG_ vm_obj_id_t self, |
| | 774 | class CVmFile *fp, class CVmObjFixup *fixups); |
| | 775 | |
| | 776 | /* rebuild for image file */ |
| | 777 | virtual ulong rebuild_image(VMG_ char *buf, ulong buflen); |
| | 778 | |
| | 779 | /* convert to constant data */ |
| | 780 | virtual void convert_to_const_data(VMG_ class CVmConstMapper *mapper, |
| | 781 | vm_obj_id_t self); |
| | 782 | |
| | 783 | protected: |
| | 784 | /* create */ |
| | 785 | CVmObjIterLookupTable() { ext_ = 0; } |
| | 786 | |
| | 787 | /* create */ |
| | 788 | CVmObjIterLookupTable(VMG_ const vm_val_t *coll); |
| | 789 | |
| | 790 | /* find the first valid entry, starting at the given index */ |
| | 791 | uint find_first_valid_entry(VMG_ uint entry) const; |
| | 792 | |
| | 793 | /* property evaluator - get next value */ |
| | 794 | virtual int getp_get_next(VMG_ vm_obj_id_t self, vm_val_t *retval, |
| | 795 | uint *argc); |
| | 796 | |
| | 797 | /* property evaluator - is next value available? */ |
| | 798 | virtual int getp_is_next_avail(VMG_ vm_obj_id_t self, vm_val_t *retval, |
| | 799 | uint *argc); |
| | 800 | |
| | 801 | /* property evaluator - reset to first item */ |
| | 802 | virtual int getp_reset_iter(VMG_ vm_obj_id_t self, vm_val_t *retval, |
| | 803 | uint *argc); |
| | 804 | |
| | 805 | /* property evaluator - get current key */ |
| | 806 | virtual int getp_get_cur_key(VMG_ vm_obj_id_t self, vm_val_t *retval, |
| | 807 | uint *argc); |
| | 808 | |
| | 809 | /* property evaluator - get current value */ |
| | 810 | virtual int getp_get_cur_val(VMG_ vm_obj_id_t self, vm_val_t *retval, |
| | 811 | uint *argc); |
| | 812 | |
| | 813 | /* get information on the current entry */ |
| | 814 | void get_cur_entry(VMG_ vm_val_t *valp, vm_val_t *keyp) const; |
| | 815 | |
| | 816 | /* get my collection value */ |
| | 817 | void get_coll_val(vm_val_t *val) const { vmb_get_dh(ext_, val); } |
| | 818 | |
| | 819 | /* get/set the current entry index (without saving undo) */ |
| | 820 | long get_entry_index() const |
| | 821 | { return osrp2(ext_ + VMB_DATAHOLDER); } |
| | 822 | void set_entry_index(uint idx) |
| | 823 | { oswp2(ext_ + VMB_DATAHOLDER, idx); } |
| | 824 | |
| | 825 | /* update the entry index value, saving undo if necessary */ |
| | 826 | void set_entry_index_undo(VMG_ vm_obj_id_t self, uint entry); |
| | 827 | |
| | 828 | /* get/set the flags */ |
| | 829 | uint get_flags() const |
| | 830 | { return osrp2(ext_ + VMB_DATAHOLDER + 2); } |
| | 831 | void set_flags(uint flags) const |
| | 832 | { oswp2(ext_ + VMB_DATAHOLDER + 2, flags); } |
| | 833 | }; |
| | 834 | |
| | 835 | /* |
| | 836 | * Registration table object for lookup table iterators |
| | 837 | */ |
| | 838 | class CVmMetaclassIterLookupTable: public CVmMetaclass |
| | 839 | { |
| | 840 | public: |
| | 841 | /* get the global name */ |
| | 842 | const char *get_meta_name() const |
| | 843 | { return "lookuptable-iterator/030000"; } |
| | 844 | |
| | 845 | /* create from image file */ |
| | 846 | void create_for_image_load(VMG_ vm_obj_id_t id) |
| | 847 | { |
| | 848 | new (vmg_ id) CVmObjIterLookupTable(); |
| | 849 | G_obj_table->set_obj_gc_characteristics(id, TRUE, FALSE); |
| | 850 | } |
| | 851 | |
| | 852 | /* create from restoring from saved state */ |
| | 853 | void create_for_restore(VMG_ vm_obj_id_t id) |
| | 854 | { |
| | 855 | new (vmg_ id) CVmObjIterLookupTable(); |
| | 856 | G_obj_table->set_obj_gc_characteristics(id, TRUE, FALSE); |
| | 857 | } |
| | 858 | |
| | 859 | /* create dynamically using stack arguments */ |
| | 860 | vm_obj_id_t create_from_stack(VMG_ const uchar **pc_ptr, uint argc) |
| | 861 | { |
| | 862 | err_throw(VMERR_BAD_DYNAMIC_NEW); |
| | 863 | AFTER_ERR_THROW(return VM_INVALID_OBJ;) |
| | 864 | } |
| | 865 | |
| | 866 | /* call a static property */ |
| | 867 | int call_stat_prop(VMG_ vm_val_t *result, |
| | 868 | const uchar **pc_ptr, uint *argc, |
| | 869 | vm_prop_id_t prop) |
| | 870 | { |
| | 871 | return CVmObjIterLookupTable:: |
| | 872 | call_stat_prop(vmg_ result, pc_ptr, argc, prop); |
| | 873 | } |
| | 874 | }; |
| | 875 | |
| | 876 | |
| | 877 | #endif /* VMLOOKUP_H */ |
| | 878 | |
| | 879 | /* |
| | 880 | * Register the classes |
| | 881 | */ |
| | 882 | VM_REGISTER_METACLASS(CVmObjLookupTable) |
| | 883 | VM_REGISTER_METACLASS(CVmObjWeakRefLookupTable) |
| | 884 | VM_REGISTER_METACLASS(CVmObjIterLookupTable) |
| | 885 | |