| | 1 | #ifdef RCSID |
| | 2 | static char RCSid[] = |
| | 3 | "$Header$"; |
| | 4 | #endif |
| | 5 | |
| | 6 | /* |
| | 7 | * Copyright (c) 2000, 2002 Michael J. Roberts. All Rights Reserved. |
| | 8 | * |
| | 9 | * Please see the accompanying license file, LICENSE.TXT, for information |
| | 10 | * on using and copying this software. |
| | 11 | */ |
| | 12 | /* |
| | 13 | Name |
| | 14 | vmvec.cpp - T3 VM Vector metaclass |
| | 15 | Function |
| | 16 | |
| | 17 | Notes |
| | 18 | |
| | 19 | Modified |
| | 20 | 05/14/00 MJRoberts - Creation |
| | 21 | */ |
| | 22 | |
| | 23 | #include <stdlib.h> |
| | 24 | |
| | 25 | #include "t3std.h" |
| | 26 | #include "vmtype.h" |
| | 27 | #include "vmmcreg.h" |
| | 28 | #include "vmmeta.h" |
| | 29 | #include "vmglob.h" |
| | 30 | #include "vmobj.h" |
| | 31 | #include "vmvec.h" |
| | 32 | #include "vmlst.h" |
| | 33 | #include "vmerr.h" |
| | 34 | #include "vmerrnum.h" |
| | 35 | #include "vmfile.h" |
| | 36 | #include "vmstack.h" |
| | 37 | #include "vmbif.h" |
| | 38 | #include "vmundo.h" |
| | 39 | #include "vmrun.h" |
| | 40 | #include "vmiter.h" |
| | 41 | #include "vmsort.h" |
| | 42 | |
| | 43 | |
| | 44 | /* ------------------------------------------------------------------------ */ |
| | 45 | /* |
| | 46 | * statics |
| | 47 | */ |
| | 48 | |
| | 49 | /* metaclass registration object */ |
| | 50 | static CVmMetaclassVector metaclass_reg_obj; |
| | 51 | CVmMetaclass *CVmObjVector::metaclass_reg_ = &metaclass_reg_obj; |
| | 52 | |
| | 53 | /* function table */ |
| | 54 | int (CVmObjVector:: |
| | 55 | *CVmObjVector::func_table_[])(VMG_ vm_obj_id_t self, |
| | 56 | vm_val_t *retval, uint *argc) = |
| | 57 | { |
| | 58 | &CVmObjVector::getp_undef, |
| | 59 | &CVmObjVector::getp_to_list, |
| | 60 | &CVmObjVector::getp_get_size, |
| | 61 | &CVmObjVector::getp_copy_from, |
| | 62 | &CVmObjVector::getp_fill_val, |
| | 63 | &CVmObjVector::getp_subset, |
| | 64 | &CVmObjVector::getp_apply_all, |
| | 65 | &CVmObjVector::getp_index_which, |
| | 66 | &CVmObjVector::getp_for_each, |
| | 67 | &CVmObjVector::getp_for_each_assoc, |
| | 68 | &CVmObjVector::getp_map_all, |
| | 69 | &CVmObjVector::getp_index_of, |
| | 70 | &CVmObjVector::getp_val_which, |
| | 71 | &CVmObjVector::getp_last_index_of, |
| | 72 | &CVmObjVector::getp_last_index_which, |
| | 73 | &CVmObjVector::getp_last_val_which, |
| | 74 | &CVmObjVector::getp_count_of, |
| | 75 | &CVmObjVector::getp_count_which, |
| | 76 | &CVmObjVector::getp_get_unique, |
| | 77 | &CVmObjVector::getp_append_unique, |
| | 78 | &CVmObjVector::getp_sort, |
| | 79 | &CVmObjVector::getp_set_length, |
| | 80 | &CVmObjVector::getp_insert_at, |
| | 81 | &CVmObjVector::getp_remove_element_at, |
| | 82 | &CVmObjVector::getp_remove_range, |
| | 83 | &CVmObjVector::getp_append, |
| | 84 | &CVmObjVector::getp_prepend, |
| | 85 | &CVmObjVector::getp_append_all, |
| | 86 | &CVmObjVector::getp_remove_element |
| | 87 | }; |
| | 88 | |
| | 89 | /* ------------------------------------------------------------------------ */ |
| | 90 | /* |
| | 91 | * create with a given number of elements |
| | 92 | */ |
| | 93 | CVmObjVector::CVmObjVector(VMG_ size_t element_count) |
| | 94 | { |
| | 95 | /* allocate space */ |
| | 96 | alloc_vector(vmg_ element_count); |
| | 97 | |
| | 98 | /* |
| | 99 | * a vector intially has no elements in use; the element_count |
| | 100 | * merely gives the number of slots to be allocated initially (this |
| | 101 | * won't affect the allocation count, which is stored separately) |
| | 102 | */ |
| | 103 | set_element_count(0); |
| | 104 | } |
| | 105 | |
| | 106 | /* ------------------------------------------------------------------------ */ |
| | 107 | /* |
| | 108 | * allocate space |
| | 109 | */ |
| | 110 | void CVmObjVector::alloc_vector(VMG_ size_t cnt) |
| | 111 | { |
| | 112 | /* allocate space for the given number of elements */ |
| | 113 | ext_ = (char *)G_mem->get_var_heap()->alloc_mem(calc_alloc(cnt), this); |
| | 114 | |
| | 115 | /* set the element count and allocated count */ |
| | 116 | set_allocated_count(cnt); |
| | 117 | set_element_count(cnt); |
| | 118 | |
| | 119 | /* clear the undo bits */ |
| | 120 | clear_undo_bits(); |
| | 121 | } |
| | 122 | |
| | 123 | /* ------------------------------------------------------------------------ */ |
| | 124 | /* |
| | 125 | * notify of deletion |
| | 126 | */ |
| | 127 | void CVmObjVector::notify_delete(VMG_ int) |
| | 128 | { |
| | 129 | /* free our extension */ |
| | 130 | if (ext_ != 0) |
| | 131 | G_mem->get_var_heap()->free_mem(ext_); |
| | 132 | } |
| | 133 | |
| | 134 | /* ------------------------------------------------------------------------ */ |
| | 135 | /* |
| | 136 | * create dynamically using stack arguments |
| | 137 | */ |
| | 138 | vm_obj_id_t CVmObjVector::create_from_stack(VMG_ const uchar **pc_ptr, |
| | 139 | uint argc) |
| | 140 | { |
| | 141 | vm_val_t *arg1; |
| | 142 | vm_obj_id_t id; |
| | 143 | CVmObjVector *vec; |
| | 144 | size_t cnt = 0; |
| | 145 | size_t copy_cnt; |
| | 146 | const char *src_lst; |
| | 147 | CVmObjVector *src_vec; |
| | 148 | size_t i; |
| | 149 | |
| | 150 | /* check arguments */ |
| | 151 | if (argc < 1 || argc > 2) |
| | 152 | err_throw(VMERR_WRONG_NUM_OF_ARGS); |
| | 153 | |
| | 154 | /* get the first argument */ |
| | 155 | arg1 = G_stk->get(0); |
| | 156 | |
| | 157 | /* presume we will have no source argument */ |
| | 158 | copy_cnt = 0; |
| | 159 | src_lst = 0; |
| | 160 | src_vec = 0; |
| | 161 | |
| | 162 | /* check what kind of argument we have */ |
| | 163 | if (arg1->typ == VM_INT) |
| | 164 | { |
| | 165 | /* get the number of elements to allocate */ |
| | 166 | cnt = (size_t)arg1->val.intval; |
| | 167 | } |
| | 168 | else |
| | 169 | { |
| | 170 | /* anything else is invalid */ |
| | 171 | err_throw(VMERR_BAD_TYPE_BIF); |
| | 172 | } |
| | 173 | |
| | 174 | /* if there's a second argument, it's a source list/vector */ |
| | 175 | if (argc >= 2) |
| | 176 | { |
| | 177 | vm_val_t *arg2; |
| | 178 | |
| | 179 | /* get the second argument */ |
| | 180 | arg2 = G_stk->get(1); |
| | 181 | |
| | 182 | /* see what we have */ |
| | 183 | if ((src_lst = arg2->get_as_list(vmg0_)) != 0) |
| | 184 | { |
| | 185 | /* it's a list - copy its elements */ |
| | 186 | copy_cnt = vmb_get_len(src_lst); |
| | 187 | } |
| | 188 | else if (arg2->typ == VM_OBJ |
| | 189 | && (vm_objp(vmg_ arg2->val.obj) |
| | 190 | ->is_of_metaclass(metaclass_reg_))) |
| | 191 | { |
| | 192 | /* it's a vector */ |
| | 193 | src_vec = (CVmObjVector *)vm_objp(vmg_ arg2->val.obj); |
| | 194 | copy_cnt = src_vec->get_element_count(); |
| | 195 | } |
| | 196 | else if (arg2->typ == VM_INT) |
| | 197 | { |
| | 198 | /* they want an explicit initial size */ |
| | 199 | copy_cnt = (size_t)arg2->val.intval; |
| | 200 | } |
| | 201 | else |
| | 202 | { |
| | 203 | /* invalid source type */ |
| | 204 | err_throw(VMERR_BAD_TYPE_BIF); |
| | 205 | } |
| | 206 | |
| | 207 | /* |
| | 208 | * make sure we allocate enough initial space to store the source |
| | 209 | * object we're copying |
| | 210 | */ |
| | 211 | if (copy_cnt > cnt) |
| | 212 | cnt = copy_cnt; |
| | 213 | } |
| | 214 | |
| | 215 | /* create the vector with the given number of elements */ |
| | 216 | id = vm_new_id(vmg_ FALSE, TRUE, FALSE); |
| | 217 | vec = new (vmg_ id) CVmObjVector(vmg_ cnt); |
| | 218 | |
| | 219 | /* copy or initialize the elements */ |
| | 220 | for (i = 0 ; i < copy_cnt ; ++i) |
| | 221 | { |
| | 222 | vm_val_t ele; |
| | 223 | |
| | 224 | /* |
| | 225 | * get my element at this index: if we have a source list or |
| | 226 | * vector, take the current element from that list or vector; |
| | 227 | * otherwise, initialize the element to nil |
| | 228 | */ |
| | 229 | if (src_lst != 0) |
| | 230 | CVmObjList::index_list(vmg_ &ele, src_lst, i + 1); |
| | 231 | else if (src_vec != 0) |
| | 232 | src_vec->get_element(i, &ele); |
| | 233 | else |
| | 234 | ele.set_nil(); |
| | 235 | |
| | 236 | /* |
| | 237 | * set my element at this index - this is construction, so there's |
| | 238 | * no need to save undo information |
| | 239 | */ |
| | 240 | vec->set_element(i, &ele); |
| | 241 | } |
| | 242 | |
| | 243 | /* set the initial size */ |
| | 244 | vec->set_element_count(copy_cnt); |
| | 245 | |
| | 246 | /* discard arguments */ |
| | 247 | G_stk->discard(argc); |
| | 248 | |
| | 249 | /* return the new object */ |
| | 250 | return id; |
| | 251 | } |
| | 252 | |
| | 253 | /* ------------------------------------------------------------------------ */ |
| | 254 | /* |
| | 255 | * save to a file |
| | 256 | */ |
| | 257 | void CVmObjVector::save_to_file(VMG_ class CVmFile *fp) |
| | 258 | { |
| | 259 | size_t ele_cnt; |
| | 260 | size_t alloc_cnt; |
| | 261 | |
| | 262 | /* get our element count and full allocation count */ |
| | 263 | alloc_cnt = get_allocated_count(); |
| | 264 | ele_cnt = get_element_count(); |
| | 265 | |
| | 266 | /* write the counts and the elements */ |
| | 267 | fp->write_int2(alloc_cnt); |
| | 268 | fp->write_int2(ele_cnt); |
| | 269 | fp->write_bytes(get_element_ptr(0), calc_alloc_ele(ele_cnt)); |
| | 270 | } |
| | 271 | |
| | 272 | /* |
| | 273 | * restore from a file |
| | 274 | */ |
| | 275 | void CVmObjVector::restore_from_file(VMG_ vm_obj_id_t self, |
| | 276 | CVmFile *fp, CVmObjFixup *fixups) |
| | 277 | { |
| | 278 | size_t ele_cnt; |
| | 279 | size_t alloc_cnt; |
| | 280 | |
| | 281 | /* read the element count and the full allocation count */ |
| | 282 | alloc_cnt = fp->read_uint2(); |
| | 283 | ele_cnt = fp->read_uint2(); |
| | 284 | |
| | 285 | /* free any existing extension */ |
| | 286 | if (ext_ != 0) |
| | 287 | { |
| | 288 | G_mem->get_var_heap()->free_mem(ext_); |
| | 289 | ext_ = 0; |
| | 290 | } |
| | 291 | |
| | 292 | /* allocate the space at the allocation count */ |
| | 293 | alloc_vector(vmg_ alloc_cnt); |
| | 294 | |
| | 295 | /* set the actual in-use element count */ |
| | 296 | set_element_count(ele_cnt); |
| | 297 | |
| | 298 | /* read the contents */ |
| | 299 | fp->read_bytes(get_element_ptr(0), calc_alloc_ele(ele_cnt)); |
| | 300 | |
| | 301 | /* fix up object references in the values */ |
| | 302 | fixups->fix_dh_array(vmg_ get_element_ptr(0), ele_cnt); |
| | 303 | } |
| | 304 | |
| | 305 | /* ------------------------------------------------------------------------ */ |
| | 306 | /* |
| | 307 | * create with no initial contents |
| | 308 | */ |
| | 309 | vm_obj_id_t CVmObjVector::create(VMG_ int in_root_set) |
| | 310 | { |
| | 311 | vm_obj_id_t id = vm_new_id(vmg_ in_root_set, TRUE, FALSE); |
| | 312 | new (vmg_ id) CVmObjVector(); |
| | 313 | return id; |
| | 314 | } |
| | 315 | |
| | 316 | /* |
| | 317 | * create with a given number of elements |
| | 318 | */ |
| | 319 | vm_obj_id_t CVmObjVector::create(VMG_ int in_root_set, size_t element_count) |
| | 320 | { |
| | 321 | vm_obj_id_t id = vm_new_id(vmg_ in_root_set, TRUE, FALSE); |
| | 322 | new (vmg_ id) CVmObjVector(vmg_ element_count); |
| | 323 | return id; |
| | 324 | } |
| | 325 | |
| | 326 | |
| | 327 | /* ------------------------------------------------------------------------ */ |
| | 328 | /* |
| | 329 | * Create a copy of this object |
| | 330 | */ |
| | 331 | vm_obj_id_t CVmObjVector::create_copy(VMG_ const vm_val_t *self_val) |
| | 332 | { |
| | 333 | vm_obj_id_t new_id; |
| | 334 | CVmObjVector *new_vec; |
| | 335 | |
| | 336 | /* save the original object on the stack for gc protection */ |
| | 337 | G_stk->push(self_val); |
| | 338 | |
| | 339 | /* create a new vector with the same parameters as this one */ |
| | 340 | new_id = vm_new_id(vmg_ FALSE, TRUE, FALSE); |
| | 341 | new_vec = new (vmg_ new_id) CVmObjVector(vmg_ get_allocated_count()); |
| | 342 | new_vec->set_element_count(get_element_count()); |
| | 343 | |
| | 344 | /* copy the elements from the old vector to the new vector */ |
| | 345 | memcpy(new_vec->get_element_ptr(0), get_element_ptr(0), |
| | 346 | calc_alloc_ele(get_element_count())); |
| | 347 | |
| | 348 | /* done with the gc protection - discard it */ |
| | 349 | G_stk->discard(1); |
| | 350 | |
| | 351 | /* return the new vector's object ID */ |
| | 352 | return new_id; |
| | 353 | } |
| | 354 | |
| | 355 | /* ------------------------------------------------------------------------ */ |
| | 356 | /* |
| | 357 | * Set the allocation size - this is the number of elements we've allocated |
| | 358 | * space for, which might exceed the number of elements we're actually |
| | 359 | * storing currently. |
| | 360 | */ |
| | 361 | void CVmObjVector::set_allocated_count(size_t cnt) |
| | 362 | { |
| | 363 | /* if the new size exceeds the maximum allowed size, throw an error */ |
| | 364 | if (cnt > 65535) |
| | 365 | err_throw(VMERR_OUT_OF_RANGE); |
| | 366 | |
| | 367 | /* set the new allocated size */ |
| | 368 | vmb_put_len(cons_get_vector_ext_ptr(), cnt); |
| | 369 | } |
| | 370 | |
| | 371 | /* ------------------------------------------------------------------------ */ |
| | 372 | /* |
| | 373 | * Create an iterator |
| | 374 | */ |
| | 375 | void CVmObjVector::new_iterator(VMG_ vm_val_t *retval, |
| | 376 | const vm_val_t *self_val) |
| | 377 | { |
| | 378 | vm_val_t copy_val; |
| | 379 | |
| | 380 | /* |
| | 381 | * Create a copy of the vector - since a vector can change, we must |
| | 382 | * always create an iterator based on a copy, so that the iterator has |
| | 383 | * a consistent view of the original as of the time the iterator was |
| | 384 | * created. |
| | 385 | */ |
| | 386 | copy_val.set_obj(create_copy(vmg_ self_val)); |
| | 387 | |
| | 388 | /* push the copy for gc protection */ |
| | 389 | G_stk->push(©_val); |
| | 390 | |
| | 391 | /* |
| | 392 | * Set up a new indexed iterator object. The first valid index for a |
| | 393 | * vector is always 1, and the last valid index is the same as the |
| | 394 | * number of elements. |
| | 395 | */ |
| | 396 | retval->set_obj(CVmObjIterIdx::create_for_coll(vmg_ ©_val, |
| | 397 | 1, get_element_count())); |
| | 398 | |
| | 399 | /* discard gc protection */ |
| | 400 | G_stk->discard(); |
| | 401 | } |
| | 402 | |
| | 403 | /* |
| | 404 | * Create a live iterator |
| | 405 | */ |
| | 406 | void CVmObjVector::new_live_iterator(VMG_ vm_val_t *retval, |
| | 407 | const vm_val_t *self_val) |
| | 408 | { |
| | 409 | /* |
| | 410 | * Set up a new indexed iterator object. The first valid index for a |
| | 411 | * vector is always 1, and the last valid index is the same as the |
| | 412 | * number of elements. Since we want a "live" iterator, create the |
| | 413 | * iterator with a direct reference to the vector. |
| | 414 | */ |
| | 415 | retval->set_obj(CVmObjIterIdx::create_for_coll(vmg_ self_val, |
| | 416 | 1, get_element_count())); |
| | 417 | } |
| | 418 | |
| | 419 | /* ------------------------------------------------------------------------ */ |
| | 420 | /* |
| | 421 | * set a property |
| | 422 | */ |
| | 423 | void CVmObjVector::set_prop(VMG_ class CVmUndo *, |
| | 424 | vm_obj_id_t, vm_prop_id_t, |
| | 425 | const vm_val_t *) |
| | 426 | { |
| | 427 | err_throw(VMERR_INVALID_SETPROP); |
| | 428 | } |
| | 429 | |
| | 430 | /* ------------------------------------------------------------------------ */ |
| | 431 | /* |
| | 432 | * get a property |
| | 433 | */ |
| | 434 | int CVmObjVector::get_prop(VMG_ vm_prop_id_t prop, vm_val_t *retval, |
| | 435 | vm_obj_id_t self, vm_obj_id_t *source_obj, |
| | 436 | uint *argc) |
| | 437 | { |
| | 438 | uint func_idx; |
| | 439 | |
| | 440 | /* translate the property index to an index into our function table */ |
| | 441 | func_idx = G_meta_table |
| | 442 | ->prop_to_vector_idx(metaclass_reg_->get_reg_idx(), prop); |
| | 443 | |
| | 444 | /* call the appropriate function */ |
| | 445 | if ((this->*func_table_[func_idx])(vmg_ self, retval, argc)) |
| | 446 | { |
| | 447 | *source_obj = metaclass_reg_->get_class_obj(vmg0_); |
| | 448 | return TRUE; |
| | 449 | } |
| | 450 | |
| | 451 | /* inherit our base class handling */ |
| | 452 | return CVmObjCollection:: |
| | 453 | get_prop(vmg_ prop, retval, self, source_obj, argc); |
| | 454 | } |
| | 455 | |
| | 456 | /* ------------------------------------------------------------------------ */ |
| | 457 | /* |
| | 458 | * Expand the vector to accommodate the given number of new elements. |
| | 459 | * We'll increase the element count, and we'll save undo for the change. |
| | 460 | * If necessary, we'll re-allocate our memory extension block at a |
| | 461 | * larger size. |
| | 462 | */ |
| | 463 | void CVmObjVector::expand_by(VMG_ vm_obj_id_t self, size_t added_elements) |
| | 464 | { |
| | 465 | size_t new_ele_cnt; |
| | 466 | |
| | 467 | /* calculate the element count */ |
| | 468 | new_ele_cnt = get_element_count() + added_elements; |
| | 469 | |
| | 470 | /* remember the new size, saving undo */ |
| | 471 | set_element_count_undo(vmg_ self, new_ele_cnt); |
| | 472 | } |
| | 473 | |
| | 474 | |
| | 475 | /* ------------------------------------------------------------------------ */ |
| | 476 | /* |
| | 477 | * Set the element count, keeping undo for the change |
| | 478 | */ |
| | 479 | void CVmObjVector::set_element_count_undo(VMG_ vm_obj_id_t self, |
| | 480 | size_t new_ele_cnt) |
| | 481 | { |
| | 482 | /* add undo if necessary */ |
| | 483 | if (G_undo != 0) |
| | 484 | { |
| | 485 | size_t old_ele_cnt; |
| | 486 | vm_val_t old_val; |
| | 487 | size_t idx; |
| | 488 | vm_val_t nil_val; |
| | 489 | |
| | 490 | /* get the old size */ |
| | 491 | old_ele_cnt = get_element_count(); |
| | 492 | |
| | 493 | /* |
| | 494 | * If we're shrinking the vector, save undo for each element we're |
| | 495 | * dropping off the end; to do this, simply set each element we're |
| | 496 | * losing to nil, since this will add an undo record with the |
| | 497 | * original value of the element. |
| | 498 | * |
| | 499 | * Note that we must save undo for dropped elements BEFORE we set |
| | 500 | * the vector's new sizd. We must perform the steps in this order |
| | 501 | * because undo is applied in reverse order: when we read back the |
| | 502 | * undo, we'll first change the vector size to its old size (since |
| | 503 | * that's the last saved undo instruction), then we'll set the |
| | 504 | * elements to their old values. |
| | 505 | */ |
| | 506 | nil_val.set_nil(); |
| | 507 | for (idx = new_ele_cnt ; idx < old_ele_cnt ; ++idx) |
| | 508 | set_element_undo(vmg_ self, idx, &nil_val); |
| | 509 | |
| | 510 | /* set up an integer with the pre-modification element count */ |
| | 511 | old_val.set_int(old_ele_cnt); |
| | 512 | |
| | 513 | /* |
| | 514 | * add the undo record - use the special index value 0xffffffff |
| | 515 | * as the key; this will never be a valid index for a real |
| | 516 | * element, because we could never address an element with an |
| | 517 | * index this high in a 32-bit address space |
| | 518 | */ |
| | 519 | G_undo->add_new_record_int_key(vmg_ self, 0xffffffffU, &old_val); |
| | 520 | } |
| | 521 | |
| | 522 | /* |
| | 523 | * if the new size is larger than our current allocation size, |
| | 524 | * increase the allocated size |
| | 525 | */ |
| | 526 | if (new_ele_cnt > get_allocated_count()) |
| | 527 | { |
| | 528 | size_t new_alloc_cnt; |
| | 529 | size_t new_mem_size; |
| | 530 | |
| | 531 | /* bump up the allocated size */ |
| | 532 | new_alloc_cnt = get_allocated_count() + get_alloc_count_increment(); |
| | 533 | |
| | 534 | /* if that's still not big enough, go up to the requested size */ |
| | 535 | if (new_alloc_cnt < new_ele_cnt) |
| | 536 | new_alloc_cnt = new_ele_cnt; |
| | 537 | |
| | 538 | /* get the new size we need */ |
| | 539 | new_mem_size = calc_alloc(new_alloc_cnt); |
| | 540 | |
| | 541 | /* reallocate our memory at the new, larger size */ |
| | 542 | ext_ = (char *)G_mem->get_var_heap() |
| | 543 | ->realloc_mem(new_mem_size, ext_, this); |
| | 544 | |
| | 545 | /* set the new allocation size */ |
| | 546 | set_allocated_count(new_alloc_cnt); |
| | 547 | |
| | 548 | /* |
| | 549 | * clear the undo bits (it's easier than moving them, and the only |
| | 550 | * cost is that we might generate some redundant undo records) |
| | 551 | */ |
| | 552 | clear_undo_bits(); |
| | 553 | } |
| | 554 | |
| | 555 | /* |
| | 556 | * if we're expanding the in-use size of the vector, set each |
| | 557 | * newly-added element to nil |
| | 558 | */ |
| | 559 | if (new_ele_cnt > get_element_count()) |
| | 560 | { |
| | 561 | size_t i; |
| | 562 | vm_val_t nil_val; |
| | 563 | |
| | 564 | /* |
| | 565 | * set the old value for each new element to nil (we don't have to |
| | 566 | * save undo for this, since the undo operation for the change in |
| | 567 | * size will simply discard all of the new elements) |
| | 568 | */ |
| | 569 | nil_val.set_nil(); |
| | 570 | for (i = get_element_count() ; i < new_ele_cnt ; ++i) |
| | 571 | set_element(i, &nil_val); |
| | 572 | } |
| | 573 | |
| | 574 | /* set the new element count */ |
| | 575 | set_element_count(new_ele_cnt); |
| | 576 | } |
| | 577 | |
| | 578 | /* ------------------------------------------------------------------------ */ |
| | 579 | /* |
| | 580 | * notify of new undo savepoint |
| | 581 | */ |
| | 582 | void CVmObjVector::notify_new_savept() |
| | 583 | { |
| | 584 | /* |
| | 585 | * clear the undo bits - we obviously have no undo for the new |
| | 586 | * savepoint yet |
| | 587 | */ |
| | 588 | clear_undo_bits(); |
| | 589 | } |
| | 590 | |
| | 591 | /* ------------------------------------------------------------------------ */ |
| | 592 | /* |
| | 593 | * apply undo |
| | 594 | */ |
| | 595 | void CVmObjVector::apply_undo(VMG_ struct CVmUndoRecord *rec) |
| | 596 | { |
| | 597 | /* |
| | 598 | * if the record refers to index 0xffffffff, this is a size-change |
| | 599 | * record; otherwise, it's an ordinary element change record |
| | 600 | */ |
| | 601 | if ((size_t)rec->id.intval == 0xffffffffU) |
| | 602 | { |
| | 603 | /* |
| | 604 | * it's a size change - apply the new size, which is stored as |
| | 605 | * an integer in the old value record |
| | 606 | */ |
| | 607 | set_element_count((size_t)rec->oldval.val.intval); |
| | 608 | } |
| | 609 | else |
| | 610 | { |
| | 611 | /* it's an ordinary index record - set the element from undo data */ |
| | 612 | set_element((size_t)rec->id.intval, &rec->oldval); |
| | 613 | } |
| | 614 | } |
| | 615 | |
| | 616 | /* ------------------------------------------------------------------------ */ |
| | 617 | /* |
| | 618 | * mark references from an undo record |
| | 619 | */ |
| | 620 | void CVmObjVector::mark_undo_ref(VMG_ CVmUndoRecord *rec) |
| | 621 | { |
| | 622 | /* if the undo record refers to an object, mark the object */ |
| | 623 | if (rec->oldval.typ == VM_OBJ) |
| | 624 | G_obj_table->mark_all_refs(rec->oldval.val.obj, VMOBJ_REACHABLE); |
| | 625 | } |
| | 626 | |
| | 627 | /* ------------------------------------------------------------------------ */ |
| | 628 | /* |
| | 629 | * mark references |
| | 630 | */ |
| | 631 | void CVmObjVector::mark_refs(VMG_ uint state) |
| | 632 | { |
| | 633 | size_t cnt; |
| | 634 | char *p; |
| | 635 | |
| | 636 | /* get my element count */ |
| | 637 | cnt = get_element_count(); |
| | 638 | |
| | 639 | /* mark as referenced each object in the vector */ |
| | 640 | for (p = get_element_ptr(0) ; cnt != 0 ; --cnt, inc_element_ptr(&p)) |
| | 641 | { |
| | 642 | /* |
| | 643 | * if this is an object, mark it as referenced, and mark its |
| | 644 | * references as referenced |
| | 645 | */ |
| | 646 | if (vmb_get_dh_type(p) == VM_OBJ) |
| | 647 | G_obj_table->mark_all_refs(vmb_get_dh_obj(p), state); |
| | 648 | } |
| | 649 | } |
| | 650 | |
| | 651 | /* ------------------------------------------------------------------------ */ |
| | 652 | /* |
| | 653 | * load from an image file |
| | 654 | */ |
| | 655 | void CVmObjVector::load_from_image(VMG_ vm_obj_id_t self, |
| | 656 | const char *ptr, size_t siz) |
| | 657 | { |
| | 658 | /* load from the image data */ |
| | 659 | load_image_data(vmg_ ptr, siz); |
| | 660 | |
| | 661 | /* |
| | 662 | * save our image data pointer in the object table, so that we can |
| | 663 | * access it (without storing it ourselves) during a reload |
| | 664 | */ |
| | 665 | G_obj_table->save_image_pointer(self, ptr, siz); |
| | 666 | } |
| | 667 | |
| | 668 | /* |
| | 669 | * reload the object from image data |
| | 670 | */ |
| | 671 | void CVmObjVector::reload_from_image(VMG_ vm_obj_id_t, |
| | 672 | const char *ptr, size_t siz) |
| | 673 | { |
| | 674 | /* load the image data */ |
| | 675 | load_image_data(vmg_ ptr, siz); |
| | 676 | } |
| | 677 | |
| | 678 | /* |
| | 679 | * load the data from an image file |
| | 680 | */ |
| | 681 | void CVmObjVector::load_image_data(VMG_ const char *ptr, size_t siz) |
| | 682 | { |
| | 683 | size_t alloc_cnt; |
| | 684 | size_t ele_cnt; |
| | 685 | |
| | 686 | /* if we already have memory allocated, free it */ |
| | 687 | if (ext_ != 0) |
| | 688 | { |
| | 689 | G_mem->get_var_heap()->free_mem(ext_); |
| | 690 | ext_ = 0; |
| | 691 | } |
| | 692 | |
| | 693 | /* get the allocation size and the actual in-use size */ |
| | 694 | alloc_cnt = vmb_get_len(ptr); |
| | 695 | ele_cnt = vmb_get_len(ptr + VMB_LEN); |
| | 696 | |
| | 697 | /* make sure we allocate at least as much as is actually in use */ |
| | 698 | if (alloc_cnt < ele_cnt) |
| | 699 | alloc_cnt = ele_cnt; |
| | 700 | |
| | 701 | /* make sure the size isn't larger than we'd expect */ |
| | 702 | if (siz > VMB_LEN*2 + (VMB_DATAHOLDER * ele_cnt)) |
| | 703 | siz = VMB_LEN*2 + (VMB_DATAHOLDER * ele_cnt); |
| | 704 | |
| | 705 | /* |
| | 706 | * allocate memory at the new allocation size as indicated in the |
| | 707 | * image data |
| | 708 | */ |
| | 709 | alloc_vector(vmg_ alloc_cnt); |
| | 710 | |
| | 711 | /* set the in-use element count */ |
| | 712 | set_element_count(ele_cnt); |
| | 713 | |
| | 714 | /* |
| | 715 | * if the size is smaller than we'd expect from the initialized |
| | 716 | * element count, set extra elements to nil |
| | 717 | */ |
| | 718 | if (siz < VMB_LEN*2 + (VMB_DATAHOLDER * ele_cnt)) |
| | 719 | { |
| | 720 | size_t i; |
| | 721 | vm_val_t nil_val; |
| | 722 | |
| | 723 | /* set all elements to nil */ |
| | 724 | nil_val.set_nil(); |
| | 725 | for (i = 0 ; i < ele_cnt ; ++i) |
| | 726 | set_element(i, &nil_val); |
| | 727 | } |
| | 728 | |
| | 729 | /* copy the initialized elements from the image data */ |
| | 730 | memcpy(get_element_ptr(0), ptr + VMB_LEN*2, siz - VMB_LEN*2); |
| | 731 | |
| | 732 | /* we're resetting to initial state, so forget all undo */ |
| | 733 | clear_undo_bits(); |
| | 734 | } |
| | 735 | |
| | 736 | /* ------------------------------------------------------------------------ */ |
| | 737 | /* |
| | 738 | * index the vector |
| | 739 | */ |
| | 740 | void CVmObjVector::index_val(VMG_ vm_val_t *result, vm_obj_id_t, |
| | 741 | const vm_val_t *index_val) |
| | 742 | { |
| | 743 | uint32 idx; |
| | 744 | |
| | 745 | /* get the index value as an integer */ |
| | 746 | idx = index_val->num_to_int(); |
| | 747 | |
| | 748 | /* make sure it's in range - 1 to our element count, inclusive */ |
| | 749 | if (idx < 1 || idx > get_element_count()) |
| | 750 | err_throw(VMERR_INDEX_OUT_OF_RANGE); |
| | 751 | |
| | 752 | /* |
| | 753 | * get the indexed element and store it in the result, adjusting the |
| | 754 | * index to the C-style 0-based range |
| | 755 | */ |
| | 756 | get_element(idx - 1, result); |
| | 757 | } |
| | 758 | |
| | 759 | /* ------------------------------------------------------------------------ */ |
| | 760 | /* |
| | 761 | * set an indexed element of the vector |
| | 762 | */ |
| | 763 | void CVmObjVector::set_index_val(VMG_ vm_val_t *new_container, |
| | 764 | vm_obj_id_t self, |
| | 765 | const vm_val_t *index_val, |
| | 766 | const vm_val_t *new_val) |
| | 767 | { |
| | 768 | uint32 idx; |
| | 769 | |
| | 770 | /* get the index value as an integer */ |
| | 771 | idx = index_val->num_to_int(); |
| | 772 | |
| | 773 | /* make sure it's at least 1 */ |
| | 774 | if (idx < 1) |
| | 775 | err_throw(VMERR_INDEX_OUT_OF_RANGE); |
| | 776 | |
| | 777 | /* |
| | 778 | * if it's higher than the current length, extend the vector with nil |
| | 779 | * entries to the requested size |
| | 780 | */ |
| | 781 | if (idx > get_element_count()) |
| | 782 | { |
| | 783 | size_t i; |
| | 784 | vm_val_t nil_val; |
| | 785 | |
| | 786 | /* note the first new element index */ |
| | 787 | i = get_element_count(); |
| | 788 | |
| | 789 | /* extend the vector */ |
| | 790 | set_element_count_undo(vmg_ self, idx); |
| | 791 | |
| | 792 | /* |
| | 793 | * Fill in entries between the old length and the new length with |
| | 794 | * nil. Note that we don't have to fill in the very last element, |
| | 795 | * since we'll explicitly set it to the new value momentarily |
| | 796 | * anyway. Note also that we don't need to keep undo for the |
| | 797 | * initializations, since on undo we'll truncate the vector to |
| | 798 | * remove the newly-added elements and thus won't need to restore |
| | 799 | * any values for the slots. |
| | 800 | */ |
| | 801 | for (nil_val.set_nil() ; i < (size_t)idx - 1 ; ++i) |
| | 802 | set_element(i, &nil_val); |
| | 803 | |
| | 804 | /* |
| | 805 | * set the new value - this doesn't require undo since we had to |
| | 806 | * expand the vector to make room for it |
| | 807 | */ |
| | 808 | set_element(idx - 1, new_val); |
| | 809 | } |
| | 810 | else |
| | 811 | { |
| | 812 | /* set the element and record undo, using a zero-based index */ |
| | 813 | set_element_undo(vmg_ self, (size_t)idx - 1, new_val); |
| | 814 | } |
| | 815 | |
| | 816 | /* the result is the original vector value */ |
| | 817 | new_container->set_obj(self); |
| | 818 | } |
| | 819 | |
| | 820 | /* ------------------------------------------------------------------------ */ |
| | 821 | /* |
| | 822 | * Set an element and record undo for the change - takes a C-style 0-based |
| | 823 | * index. |
| | 824 | */ |
| | 825 | void CVmObjVector::set_element_undo(VMG_ vm_obj_id_t self, |
| | 826 | size_t idx, const vm_val_t *new_val) |
| | 827 | { |
| | 828 | /* if we don't have undo for this element already, save undo now */ |
| | 829 | if (G_undo != 0 && !get_undo_bit(idx)) |
| | 830 | { |
| | 831 | vm_val_t old_val; |
| | 832 | |
| | 833 | /* get the pre-modification value of this element */ |
| | 834 | get_element(idx, &old_val); |
| | 835 | |
| | 836 | /* add the undo record */ |
| | 837 | G_undo->add_new_record_int_key(vmg_ self, idx, &old_val); |
| | 838 | |
| | 839 | /* |
| | 840 | * set the undo bit to indicate that we now have undo for this |
| | 841 | * element |
| | 842 | */ |
| | 843 | set_undo_bit(idx, TRUE); |
| | 844 | } |
| | 845 | |
| | 846 | /* get the indexed element and store it in the result */ |
| | 847 | set_element(idx, new_val); |
| | 848 | } |
| | 849 | |
| | 850 | /* ------------------------------------------------------------------------ */ |
| | 851 | /* |
| | 852 | * Check a value for equality. We will match any list or vector with the |
| | 853 | * same number of elements and the same value for each element. |
| | 854 | */ |
| | 855 | int CVmObjVector::equals(VMG_ vm_obj_id_t self, const vm_val_t *val, |
| | 856 | int depth) const |
| | 857 | { |
| | 858 | const char *p; |
| | 859 | int p_is_list; |
| | 860 | CVmObjVector *vec2; |
| | 861 | size_t cnt; |
| | 862 | size_t cnt2; |
| | 863 | size_t idx; |
| | 864 | |
| | 865 | /* if the recursion depth is excessive, throw an error */ |
| | 866 | if (depth > VM_MAX_TREE_DEPTH_EQ) |
| | 867 | err_throw(VMERR_TREE_TOO_DEEP_EQ); |
| | 868 | |
| | 869 | /* if the other value is a reference to myself, we certainly match */ |
| | 870 | if (val->typ == VM_OBJ && val->val.obj == self) |
| | 871 | { |
| | 872 | /* no need to look at the contents if this is a reference to me */ |
| | 873 | return TRUE; |
| | 874 | } |
| | 875 | |
| | 876 | /* try it as a list and as a vector */ |
| | 877 | if ((p = val->get_as_list(vmg0_)) != 0) |
| | 878 | { |
| | 879 | /* it's a list */ |
| | 880 | p_is_list = TRUE; |
| | 881 | |
| | 882 | /* get the number of elements in the list */ |
| | 883 | cnt2 = vmb_get_len(p); |
| | 884 | |
| | 885 | /* the second element isn't a vector */ |
| | 886 | vec2 = 0; |
| | 887 | } |
| | 888 | else |
| | 889 | { |
| | 890 | /* it's not a list */ |
| | 891 | p_is_list = FALSE; |
| | 892 | |
| | 893 | /* check to see if it's a vector */ |
| | 894 | if (val->typ == VM_OBJ |
| | 895 | && (vm_objp(vmg_ val->val.obj)->is_of_metaclass(metaclass_reg_))) |
| | 896 | { |
| | 897 | /* it's a vector - cast it accordingly */ |
| | 898 | vec2 = (CVmObjVector *)vm_objp(vmg_ val->val.obj); |
| | 899 | |
| | 900 | /* get the number of elements */ |
| | 901 | cnt2 = vec2->get_element_count(); |
| | 902 | } |
| | 903 | else |
| | 904 | { |
| | 905 | /* it's some other type, so it can't be equal */ |
| | 906 | return FALSE; |
| | 907 | } |
| | 908 | } |
| | 909 | |
| | 910 | /* if the sizes don't match, the values are not equal */ |
| | 911 | cnt = get_element_count(); |
| | 912 | if (cnt != cnt2) |
| | 913 | return FALSE; |
| | 914 | |
| | 915 | /* compare element by element */ |
| | 916 | for (idx = 0 ; idx < cnt ; ++idx) |
| | 917 | { |
| | 918 | vm_val_t val1; |
| | 919 | vm_val_t val2; |
| | 920 | |
| | 921 | /* get this element of self */ |
| | 922 | get_element(idx, &val1); |
| | 923 | |
| | 924 | /* get this element of the other value */ |
| | 925 | if (p_is_list) |
| | 926 | CVmObjList::index_list(vmg_ &val2, p, idx + 1); |
| | 927 | else |
| | 928 | vec2->get_element(idx, &val2); |
| | 929 | |
| | 930 | /* if these elements aren't equal, our values aren't equal */ |
| | 931 | if (!val1.equals(vmg_ &val2, depth + 1)) |
| | 932 | return FALSE; |
| | 933 | |
| | 934 | /* |
| | 935 | * if the other value is a list, re-translate it in case we did |
| | 936 | * any constant data swapping |
| | 937 | */ |
| | 938 | if (p_is_list) |
| | 939 | p = val->get_as_list(vmg0_); |
| | 940 | } |
| | 941 | |
| | 942 | /* we didn't find any differences, so the values are equal */ |
| | 943 | return TRUE; |
| | 944 | } |
| | 945 | |
| | 946 | /* ------------------------------------------------------------------------ */ |
| | 947 | /* |
| | 948 | * Hash value calculation |
| | 949 | */ |
| | 950 | uint CVmObjVector::calc_hash(VMG_ vm_obj_id_t self, int depth) const |
| | 951 | { |
| | 952 | uint hash; |
| | 953 | uint i; |
| | 954 | |
| | 955 | /* if the recursion depth is excessive, throw an error */ |
| | 956 | if (depth > VM_MAX_TREE_DEPTH_EQ) |
| | 957 | err_throw(VMERR_TREE_TOO_DEEP_EQ); |
| | 958 | |
| | 959 | /* combine the hash values of the members of the vector */ |
| | 960 | for (hash = 0, i = 0 ; i < get_element_count() ; ++i) |
| | 961 | { |
| | 962 | vm_val_t ele; |
| | 963 | |
| | 964 | /* get this element */ |
| | 965 | get_element(i, &ele); |
| | 966 | |
| | 967 | /* |
| | 968 | * Calculate its hash value and add it into the combined hash. |
| | 969 | * |
| | 970 | * Note that we have to increase the recursion depth, because we're |
| | 971 | * recursively calculating the hash value of our contents, and our |
| | 972 | * contents can refer (directly or indirectly) back to this object. |
| | 973 | * In other words, we can have cycles in the reference tree from |
| | 974 | * this object back to itself, so we need to keep track of the |
| | 975 | * recursion depth to ensure we don't loop forever. |
| | 976 | */ |
| | 977 | hash += ele.calc_hash(vmg_ depth + 1); |
| | 978 | } |
| | 979 | |
| | 980 | /* return the combined hash */ |
| | 981 | return hash; |
| | 982 | } |
| | 983 | |
| | 984 | /* ------------------------------------------------------------------------ */ |
| | 985 | /* |
| | 986 | * add a value to the vector |
| | 987 | */ |
| | 988 | void CVmObjVector::add_val(VMG_ vm_val_t *result, |
| | 989 | vm_obj_id_t self, const vm_val_t *val) |
| | 990 | { |
| | 991 | size_t idx; |
| | 992 | size_t rhs_cnt; |
| | 993 | size_t i; |
| | 994 | CVmObjVector *new_vec; |
| | 995 | |
| | 996 | /* push the value to append, for gc protection */ |
| | 997 | G_stk->push(val); |
| | 998 | |
| | 999 | /* |
| | 1000 | * remember the index of the first new element - this is simply one |
| | 1001 | * higher than the last valid current index |
| | 1002 | */ |
| | 1003 | idx = get_element_count(); |
| | 1004 | |
| | 1005 | /* get the number of elements to add */ |
| | 1006 | rhs_cnt = val->get_coll_addsub_rhs_ele_cnt(vmg0_); |
| | 1007 | |
| | 1008 | /* |
| | 1009 | * allocate a copy of the vector for the result - make it big enough |
| | 1010 | * for my elements plus the elements to be appended |
| | 1011 | */ |
| | 1012 | result->set_obj(create(vmg_ FALSE, idx + rhs_cnt)); |
| | 1013 | |
| | 1014 | /* get the return value as a vector */ |
| | 1015 | new_vec = (CVmObjVector *)vm_objp(vmg_ result->val.obj); |
| | 1016 | |
| | 1017 | /* |
| | 1018 | * set the element count in the new vector (we're creating it anew, so |
| | 1019 | * there's no need to save undo) |
| | 1020 | */ |
| | 1021 | new_vec->set_element_count(idx + rhs_cnt); |
| | 1022 | |
| | 1023 | /* push the result for gc protection */ |
| | 1024 | G_stk->push(result); |
| | 1025 | |
| | 1026 | /* copy my elements into the result */ |
| | 1027 | memcpy(new_vec->get_element_ptr(0), get_element_ptr(0), |
| | 1028 | calc_alloc_ele(idx)); |
| | 1029 | |
| | 1030 | /* add the new elements */ |
| | 1031 | for (i = 0 ; i < rhs_cnt ; ++i, ++idx) |
| | 1032 | { |
| | 1033 | vm_val_t ele; |
| | 1034 | |
| | 1035 | /* get this element from the right-hand side */ |
| | 1036 | val->get_coll_addsub_rhs_ele(vmg_ i + 1, &ele); |
| | 1037 | |
| | 1038 | /* store the element in the result */ |
| | 1039 | new_vec->set_element(idx, &ele); |
| | 1040 | } |
| | 1041 | |
| | 1042 | /* discard the gc protect */ |
| | 1043 | G_stk->discard(2); |
| | 1044 | } |
| | 1045 | |
| | 1046 | /* ------------------------------------------------------------------------ */ |
| | 1047 | /* |
| | 1048 | * subtract a value from the vector |
| | 1049 | */ |
| | 1050 | void CVmObjVector::sub_val(VMG_ vm_val_t *result, |
| | 1051 | vm_obj_id_t self, const vm_val_t *val) |
| | 1052 | { |
| | 1053 | size_t ele_cnt; |
| | 1054 | size_t rhs_cnt; |
| | 1055 | size_t i; |
| | 1056 | CVmObjVector *new_vec; |
| | 1057 | size_t new_cnt; |
| | 1058 | |
| | 1059 | /* push the value to append, for gc protection */ |
| | 1060 | G_stk->push(val); |
| | 1061 | |
| | 1062 | /* note the initial element count */ |
| | 1063 | ele_cnt = get_element_count(); |
| | 1064 | |
| | 1065 | /* get the number of elements to remove */ |
| | 1066 | rhs_cnt = val->get_coll_addsub_rhs_ele_cnt(vmg0_); |
| | 1067 | |
| | 1068 | /* |
| | 1069 | * allocate a new vector for the result - the new object will be no |
| | 1070 | * larger than the current object, so allocate at the same size |
| | 1071 | */ |
| | 1072 | result->set_obj(create(vmg_ FALSE, ele_cnt)); |
| | 1073 | |
| | 1074 | /* push the result for gc protection */ |
| | 1075 | G_stk->push(result); |
| | 1076 | |
| | 1077 | /* get the return value as a vector */ |
| | 1078 | new_vec = (CVmObjVector *)vm_objp(vmg_ result->val.obj); |
| | 1079 | |
| | 1080 | /* |
| | 1081 | * copy each element from me to the new vector, but don't copy |
| | 1082 | * elements found in the right-hand side |
| | 1083 | */ |
| | 1084 | for (i = 0, new_cnt = 0 ; i < ele_cnt ; ++i) |
| | 1085 | { |
| | 1086 | vm_val_t ele; |
| | 1087 | size_t j; |
| | 1088 | int found; |
| | 1089 | |
| | 1090 | /* get this element of self */ |
| | 1091 | get_element(i, &ele); |
| | 1092 | |
| | 1093 | /* scan the right side for the element */ |
| | 1094 | for (found = FALSE, j = 0 ; j < rhs_cnt ; ++j) |
| | 1095 | { |
| | 1096 | vm_val_t rh_ele; |
| | 1097 | |
| | 1098 | /* get this right-hand element */ |
| | 1099 | val->get_coll_addsub_rhs_ele(vmg_ j + 1, &rh_ele); |
| | 1100 | |
| | 1101 | /* |
| | 1102 | * if this right-hand element matches this left-hand element, |
| | 1103 | * note that we have a match |
| | 1104 | */ |
| | 1105 | if (rh_ele.equals(vmg_ &ele)) |
| | 1106 | { |
| | 1107 | /* note that we found it */ |
| | 1108 | found = TRUE; |
| | 1109 | |
| | 1110 | /* no need to look any further - one match is enough */ |
| | 1111 | break; |
| | 1112 | } |
| | 1113 | } |
| | 1114 | |
| | 1115 | /* |
| | 1116 | * If we didn't find the value, include it in the result vector. |
| | 1117 | * Note that we don't have to save undo, since we're still |
| | 1118 | * constructing the new vector. |
| | 1119 | */ |
| | 1120 | if (!found) |
| | 1121 | { |
| | 1122 | /* store the element in the result vector */ |
| | 1123 | new_vec->set_element(new_cnt, &ele); |
| | 1124 | |
| | 1125 | /* count it */ |
| | 1126 | ++new_cnt; |
| | 1127 | } |
| | 1128 | } |
| | 1129 | |
| | 1130 | /* |
| | 1131 | * update the new vector's size - we don't have to save undo for this |
| | 1132 | * change, because we're still constructing the new vector |
| | 1133 | */ |
| | 1134 | new_vec->set_element_count(new_cnt); |
| | 1135 | |
| | 1136 | /* discard the gc protection */ |
| | 1137 | G_stk->discard(2); |
| | 1138 | } |
| | 1139 | |
| | 1140 | /* ------------------------------------------------------------------------ */ |
| | 1141 | /* |
| | 1142 | * property evaluator - convert to a list |
| | 1143 | */ |
| | 1144 | int CVmObjVector::getp_to_list(VMG_ vm_obj_id_t self, vm_val_t *retval, |
| | 1145 | uint *argc) |
| | 1146 | { |
| | 1147 | size_t src_cnt; |
| | 1148 | size_t dst_cnt; |
| | 1149 | size_t start_idx; |
| | 1150 | CVmObjList *lst; |
| | 1151 | size_t idx; |
| | 1152 | uint orig_argc = (argc != 0 ? *argc : 0); |
| | 1153 | static CVmNativeCodeDesc desc(0, 2); |
| | 1154 | |
| | 1155 | /* check arguments */ |
| | 1156 | if (get_prop_check_argc(retval, argc, &desc)) |
| | 1157 | return TRUE; |
| | 1158 | |
| | 1159 | /* note our size */ |
| | 1160 | src_cnt = get_element_count(); |
| | 1161 | |
| | 1162 | /* |
| | 1163 | * if there's a starting index, retrieve it; otherwise, start at our |
| | 1164 | * first element |
| | 1165 | */ |
| | 1166 | if (orig_argc >= 1) |
| | 1167 | start_idx = (size_t)CVmBif::pop_int_val(vmg0_); |
| | 1168 | else |
| | 1169 | start_idx = 1; |
| | 1170 | |
| | 1171 | /* if the starting index is below 1, force it to 1 */ |
| | 1172 | if (start_idx < 1) |
| | 1173 | start_idx = 1; |
| | 1174 | |
| | 1175 | /* adjust the starting index to a 0-based index */ |
| | 1176 | --start_idx; |
| | 1177 | |
| | 1178 | /* |
| | 1179 | * if there's a size argument, retrieve it; if it's not specified, |
| | 1180 | * use our actual size for the output size |
| | 1181 | */ |
| | 1182 | if (orig_argc >= 2) |
| | 1183 | dst_cnt = (size_t)CVmBif::pop_int_val(vmg0_); |
| | 1184 | else |
| | 1185 | dst_cnt = src_cnt; |
| | 1186 | |
| | 1187 | /* |
| | 1188 | * in no case will the result list have more elements than we can |
| | 1189 | * actually supply |
| | 1190 | */ |
| | 1191 | if (start_idx >= src_cnt) |
| | 1192 | { |
| | 1193 | /* we're starting past our last element - we can't supply anything */ |
| | 1194 | dst_cnt = 0; |
| | 1195 | } |
| | 1196 | else if (src_cnt - start_idx < dst_cnt) |
| | 1197 | { |
| | 1198 | /* we can't supply as many values as requested - lower the size */ |
| | 1199 | dst_cnt = src_cnt - start_idx; |
| | 1200 | } |
| | 1201 | |
| | 1202 | /* push a self-reference for garbage collection protection */ |
| | 1203 | G_stk->push()->set_obj(self); |
| | 1204 | |
| | 1205 | /* create the new list */ |
| | 1206 | retval->set_obj(CVmObjList::create(vmg_ FALSE, dst_cnt)); |
| | 1207 | lst = (CVmObjList *)vm_objp(vmg_ retval->val.obj); |
| | 1208 | |
| | 1209 | /* set the list elements */ |
| | 1210 | for (idx = 0 ; idx < dst_cnt ; ++idx) |
| | 1211 | { |
| | 1212 | vm_val_t val; |
| | 1213 | |
| | 1214 | /* get my element at this index */ |
| | 1215 | get_element(idx + start_idx, &val); |
| | 1216 | |
| | 1217 | /* store the element in the list */ |
| | 1218 | lst->cons_set_element(idx, &val); |
| | 1219 | } |
| | 1220 | |
| | 1221 | /* discard the self-reference */ |
| | 1222 | G_stk->discard(); |
| | 1223 | |
| | 1224 | /* handled */ |
| | 1225 | return TRUE; |
| | 1226 | } |
| | 1227 | |
| | 1228 | /* ------------------------------------------------------------------------ */ |
| | 1229 | /* |
| | 1230 | * property evaluator - copy from another vector or list |
| | 1231 | */ |
| | 1232 | int CVmObjVector::getp_copy_from(VMG_ vm_obj_id_t self, vm_val_t *retval, |
| | 1233 | uint *argc) |
| | 1234 | { |
| | 1235 | vm_val_t src_val; |
| | 1236 | size_t src_cnt = 0; |
| | 1237 | size_t dst_cnt; |
| | 1238 | size_t src_start; |
| | 1239 | size_t dst_start; |
| | 1240 | size_t copy_cnt; |
| | 1241 | const char *src_lst; |
| | 1242 | int src_is_list = FALSE; |
| | 1243 | CVmObjVector *src_vec = 0; |
| | 1244 | size_t i; |
| | 1245 | static CVmNativeCodeDesc desc(4); |
| | 1246 | |
| | 1247 | /* check arguments */ |
| | 1248 | if (get_prop_check_argc(retval, argc, &desc)) |
| | 1249 | return TRUE; |
| | 1250 | |
| | 1251 | /* retrieve the source value */ |
| | 1252 | G_stk->pop(&src_val); |
| | 1253 | |
| | 1254 | /* get the source starting index */ |
| | 1255 | src_start = (size_t)CVmBif::pop_int_val(vmg0_); |
| | 1256 | |
| | 1257 | /* get the destination starting index */ |
| | 1258 | dst_start = (size_t)CVmBif::pop_int_val(vmg0_); |
| | 1259 | |
| | 1260 | /* get the number of elements to copy */ |
| | 1261 | copy_cnt = (size_t)CVmBif::pop_int_val(vmg0_); |
| | 1262 | |
| | 1263 | /* if either starting index is below 1, force it to 1 */ |
| | 1264 | if (src_start < 1) |
| | 1265 | src_start = 1; |
| | 1266 | if (dst_start < 1) |
| | 1267 | dst_start = 1; |
| | 1268 | |
| | 1269 | /* adjust the starting indices to 0-based values */ |
| | 1270 | --src_start; |
| | 1271 | --dst_start; |
| | 1272 | |
| | 1273 | /* note the destination length (i.e., my element count) */ |
| | 1274 | dst_cnt = get_element_count(); |
| | 1275 | |
| | 1276 | /* get the source value */ |
| | 1277 | if ((src_lst = src_val.get_as_list(vmg0_)) != 0) |
| | 1278 | { |
| | 1279 | /* note that it's a list */ |
| | 1280 | src_is_list = TRUE; |
| | 1281 | |
| | 1282 | /* note the source size */ |
| | 1283 | src_cnt = vmb_get_len(src_lst); |
| | 1284 | } |
| | 1285 | else if (src_val.typ == VM_OBJ |
| | 1286 | && (vm_objp(vmg_ src_val.val.obj)->get_metaclass_reg() |
| | 1287 | == metaclass_reg_)) |
| | 1288 | { |
| | 1289 | /* it's a vector */ |
| | 1290 | src_vec = (CVmObjVector *)vm_objp(vmg_ src_val.val.obj); |
| | 1291 | |
| | 1292 | /* note the soure size */ |
| | 1293 | src_cnt = src_vec->get_element_count(); |
| | 1294 | } |
| | 1295 | else |
| | 1296 | err_throw(VMERR_BAD_TYPE_BIF); |
| | 1297 | |
| | 1298 | /* limit our copying to the remaining elements of the source */ |
| | 1299 | if (src_start >= src_cnt) |
| | 1300 | copy_cnt = 0; |
| | 1301 | else if (src_start + copy_cnt >= src_cnt) |
| | 1302 | copy_cnt = src_cnt - src_start; |
| | 1303 | |
| | 1304 | /* expand the vector if necessary to make room for added elements */ |
| | 1305 | if (dst_start + copy_cnt > dst_cnt) |
| | 1306 | set_element_count_undo(vmg_ self, dst_start + copy_cnt); |
| | 1307 | |
| | 1308 | /* set the list elements */ |
| | 1309 | for (i = 0 ; i < copy_cnt ; ++i) |
| | 1310 | { |
| | 1311 | vm_val_t val; |
| | 1312 | |
| | 1313 | /* get my element at this index */ |
| | 1314 | if (src_is_list) |
| | 1315 | CVmObjList::index_list(vmg_ &val, src_lst, i + src_start + 1); |
| | 1316 | else |
| | 1317 | src_vec->get_element(i + src_start, &val); |
| | 1318 | |
| | 1319 | /* set my element at this index, recording undo for the change */ |
| | 1320 | set_element_undo(vmg_ self, i + dst_start, &val); |
| | 1321 | } |
| | 1322 | |
| | 1323 | /* the return value is 'self' */ |
| | 1324 | retval->set_obj(self); |
| | 1325 | |
| | 1326 | /* handled */ |
| | 1327 | return TRUE; |
| | 1328 | } |
| | 1329 | |
| | 1330 | /* ------------------------------------------------------------------------ */ |
| | 1331 | /* |
| | 1332 | * property evaluator - fill with a value |
| | 1333 | */ |
| | 1334 | int CVmObjVector::getp_fill_val(VMG_ vm_obj_id_t self, vm_val_t *retval, |
| | 1335 | uint *argc) |
| | 1336 | { |
| | 1337 | vm_val_t fill_val; |
| | 1338 | size_t cnt; |
| | 1339 | size_t start_idx; |
| | 1340 | size_t end_idx; |
| | 1341 | size_t idx; |
| | 1342 | uint orig_argc = (argc != 0 ? *argc : 0); |
| | 1343 | static CVmNativeCodeDesc desc(1, 2); |
| | 1344 | |
| | 1345 | /* check arguments */ |
| | 1346 | if (get_prop_check_argc(retval, argc, &desc)) |
| | 1347 | return TRUE; |
| | 1348 | |
| | 1349 | /* note our size */ |
| | 1350 | cnt = get_element_count(); |
| | 1351 | |
| | 1352 | /* the return value is 'self' */ |
| | 1353 | retval->set_obj(self); |
| | 1354 | |
| | 1355 | /* get the fill value */ |
| | 1356 | G_stk->pop(&fill_val); |
| | 1357 | |
| | 1358 | /* |
| | 1359 | * if there's a starting index, retrieve it; otherwise, start at our |
| | 1360 | * first element |
| | 1361 | */ |
| | 1362 | if (orig_argc >= 2) |
| | 1363 | start_idx = (size_t)CVmBif::pop_int_val(vmg0_); |
| | 1364 | else |
| | 1365 | start_idx = 1; |
| | 1366 | |
| | 1367 | /* if the starting index is below 1, force it to 1 */ |
| | 1368 | if (start_idx < 1) |
| | 1369 | start_idx = 1; |
| | 1370 | |
| | 1371 | /* adjust the starting index to a 0-based index */ |
| | 1372 | --start_idx; |
| | 1373 | |
| | 1374 | /* |
| | 1375 | * if there's a count argument, retrieve it; if it's not specified, |
| | 1376 | * use our actual size for the count |
| | 1377 | */ |
| | 1378 | if (orig_argc >= 3) |
| | 1379 | end_idx = start_idx + (size_t)CVmBif::pop_int_val(vmg0_); |
| | 1380 | else |
| | 1381 | end_idx = cnt; |
| | 1382 | |
| | 1383 | /* push self and the fill value for gc protection */ |
| | 1384 | G_stk->push(retval); |
| | 1385 | G_stk->push(&fill_val); |
| | 1386 | |
| | 1387 | /* expand the vector to the requested size */ |
| | 1388 | if (end_idx > cnt) |
| | 1389 | set_element_count_undo(vmg_ self, end_idx); |
| | 1390 | |
| | 1391 | /* set the elements to the fill value */ |
| | 1392 | for (idx = start_idx ; idx < end_idx ; ++idx) |
| | 1393 | { |
| | 1394 | /* set the element to the fill value, recording undo for the change */ |
| | 1395 | set_element_undo(vmg_ self, idx, &fill_val); |
| | 1396 | } |
| | 1397 | |
| | 1398 | /* discard GC protection */ |
| | 1399 | G_stk->discard(2); |
| | 1400 | |
| | 1401 | /* handled */ |
| | 1402 | return TRUE; |
| | 1403 | } |
| | 1404 | |
| | 1405 | /* ------------------------------------------------------------------------ */ |
| | 1406 | /* |
| | 1407 | * property evaluator - get the number of elements |
| | 1408 | */ |
| | 1409 | int CVmObjVector::getp_get_size(VMG_ vm_obj_id_t, vm_val_t *retval, |
| | 1410 | uint *argc) |
| | 1411 | { |
| | 1412 | static CVmNativeCodeDesc desc(0); |
| | 1413 | |
| | 1414 | /* check arguments */ |
| | 1415 | if (get_prop_check_argc(retval, argc, &desc)) |
| | 1416 | return TRUE; |
| | 1417 | |
| | 1418 | /* return my element count */ |
| | 1419 | retval->set_int(get_element_count()); |
| | 1420 | |
| | 1421 | /* handled */ |
| | 1422 | return TRUE; |
| | 1423 | } |
| | 1424 | |
| | 1425 | /* ------------------------------------------------------------------------ */ |
| | 1426 | /* |
| | 1427 | * property evaluator - select a subset; returns a new vector consisting |
| | 1428 | * of the subset of the original vector |
| | 1429 | */ |
| | 1430 | int CVmObjVector::getp_subset(VMG_ vm_obj_id_t self, vm_val_t *retval, |
| | 1431 | uint *argc) |
| | 1432 | { |
| | 1433 | const vm_val_t *func_val; |
| | 1434 | size_t src; |
| | 1435 | size_t dst; |
| | 1436 | size_t cnt; |
| | 1437 | CVmObjVector *new_vec; |
| | 1438 | static CVmNativeCodeDesc desc(1); |
| | 1439 | |
| | 1440 | /* check arguments */ |
| | 1441 | if (get_prop_check_argc(retval, argc, &desc)) |
| | 1442 | return TRUE; |
| | 1443 | |
| | 1444 | /* get the function pointer argument, but leave it on the stack */ |
| | 1445 | func_val = G_stk->get(0); |
| | 1446 | |
| | 1447 | /* push a self-reference while allocating to protect from gc */ |
| | 1448 | G_interpreter->push_obj(vmg_ self); |
| | 1449 | |
| | 1450 | /* |
| | 1451 | * allocate the new vector that we'll return - all we know at this |
| | 1452 | * point is that the new vector won't be larger than the current |
| | 1453 | * vector, so allocate at the current size |
| | 1454 | */ |
| | 1455 | cnt = get_element_count(); |
| | 1456 | retval->set_obj(create(vmg_ FALSE, cnt)); |
| | 1457 | |
| | 1458 | /* get the return value as an vector */ |
| | 1459 | new_vec = (CVmObjVector *)vm_objp(vmg_ retval->val.obj); |
| | 1460 | |
| | 1461 | /* |
| | 1462 | * push a reference to the new list to protect it from the garbage |
| | 1463 | * collector, which could be invoked in the course of executing the |
| | 1464 | * user callback |
| | 1465 | */ |
| | 1466 | G_stk->push(retval); |
| | 1467 | |
| | 1468 | /* |
| | 1469 | * Go through each element of our list, and invoke the callback on |
| | 1470 | * each element. If the element passes, write it to the current |
| | 1471 | * output location in the list; otherwise, just skip it. |
| | 1472 | * |
| | 1473 | * Note that we're using the same list as source and destination, |
| | 1474 | * which is easy because the list will either shrink or stay the |
| | 1475 | * same - we'll never need to insert new elements. |
| | 1476 | */ |
| | 1477 | for (src = dst = 0 ; src < cnt ; ++src) |
| | 1478 | { |
| | 1479 | vm_val_t ele; |
| | 1480 | const vm_val_t *val; |
| | 1481 | |
| | 1482 | /* get this element from the source vector */ |
| | 1483 | get_element(src, &ele); |
| | 1484 | |
| | 1485 | /* push the element as the callback's argument */ |
| | 1486 | G_stk->push(&ele); |
| | 1487 | |
| | 1488 | /* invoke the callback */ |
| | 1489 | G_interpreter->call_func_ptr(vmg_ func_val, 1, "Vector.subset", 0); |
| | 1490 | |
| | 1491 | /* get the result from R0 */ |
| | 1492 | val = G_interpreter->get_r0(); |
| | 1493 | |
| | 1494 | /* |
| | 1495 | * if the callback returned non-nil and non-zero, include this |
| | 1496 | * element in the result |
| | 1497 | */ |
| | 1498 | if (val->typ == VM_NIL |
| | 1499 | || (val->typ == VM_INT && val->val.intval == 0)) |
| | 1500 | { |
| | 1501 | /* it's nil or zero - don't include it in the result */ |
| | 1502 | } |
| | 1503 | else |
| | 1504 | { |
| | 1505 | /* |
| | 1506 | * include this element in the result (there's no need to |
| | 1507 | * save undo, since the whole vector is new) |
| | 1508 | */ |
| | 1509 | new_vec->set_element(dst, &ele); |
| | 1510 | |
| | 1511 | /* advance the output index */ |
| | 1512 | ++dst; |
| | 1513 | } |
| | 1514 | } |
| | 1515 | |
| | 1516 | /* set the actual number of elements in the result */ |
| | 1517 | new_vec->set_element_count(dst); |
| | 1518 | |
| | 1519 | /* discard our gc protection (self, return value) and our arguments */ |
| | 1520 | G_stk->discard(3); |
| | 1521 | |
| | 1522 | /* handled */ |
| | 1523 | return TRUE; |
| | 1524 | } |
| | 1525 | |
| | 1526 | /* ------------------------------------------------------------------------ */ |
| | 1527 | /* |
| | 1528 | * property evaluator - apply a callback to each element |
| | 1529 | */ |
| | 1530 | int CVmObjVector::getp_apply_all(VMG_ vm_obj_id_t self, vm_val_t *retval, |
| | 1531 | uint *argc) |
| | 1532 | { |
| | 1533 | const vm_val_t *func_val; |
| | 1534 | size_t idx; |
| | 1535 | static CVmNativeCodeDesc desc(1); |
| | 1536 | |
| | 1537 | /* check arguments */ |
| | 1538 | if (get_prop_check_argc(retval, argc, &desc)) |
| | 1539 | return TRUE; |
| | 1540 | |
| | 1541 | /* get the function pointer argument, but leave it on the stack */ |
| | 1542 | func_val = G_stk->get(0); |
| | 1543 | |
| | 1544 | /* push a self-reference while working to protect from gc */ |
| | 1545 | G_interpreter->push_obj(vmg_ self); |
| | 1546 | |
| | 1547 | /* |
| | 1548 | * we're going to return the 'self' object, since we update the vector |
| | 1549 | * in-place |
| | 1550 | */ |
| | 1551 | retval->set_obj(self); |
| | 1552 | |
| | 1553 | /* |
| | 1554 | * Go through each element of our vector, invoking the callback on |
| | 1555 | * each element. Replace each element with the result of the |
| | 1556 | * callback. Note that we intentionally re-check the element count on |
| | 1557 | * each iteration, in case the callback changes the number of |
| | 1558 | * elements. |
| | 1559 | */ |
| | 1560 | for (idx = 0 ; idx < get_element_count() ; ++idx) |
| | 1561 | { |
| | 1562 | /* push this element as the callback's argument */ |
| | 1563 | push_element(vmg_ idx); |
| | 1564 | |
| | 1565 | /* invoke the callback */ |
| | 1566 | G_interpreter->call_func_ptr(vmg_ func_val, 1, "Vector.applyAll", 0); |
| | 1567 | |
| | 1568 | /* replace this element with the result */ |
| | 1569 | set_element_undo(vmg_ self, idx, G_interpreter->get_r0()); |
| | 1570 | } |
| | 1571 | |
| | 1572 | /* discard our gc protection (self) and our arguments */ |
| | 1573 | G_stk->discard(2); |
| | 1574 | |
| | 1575 | /* handled */ |
| | 1576 | return TRUE; |
| | 1577 | } |
| | 1578 | |
| | 1579 | /* ------------------------------------------------------------------------ */ |
| | 1580 | /* |
| | 1581 | * property evaluator - find the first element matching a condition |
| | 1582 | */ |
| | 1583 | int CVmObjVector::getp_index_which(VMG_ vm_obj_id_t self, vm_val_t *retval, |
| | 1584 | uint *argc) |
| | 1585 | { |
| | 1586 | /* use our general handler, working forwards */ |
| | 1587 | return gen_index_which(vmg_ self, retval, argc, TRUE, FALSE); |
| | 1588 | } |
| | 1589 | |
| | 1590 | /* |
| | 1591 | * property evaluator - lastIndexWhich |
| | 1592 | */ |
| | 1593 | int CVmObjVector::getp_last_index_which(VMG_ vm_obj_id_t self, |
| | 1594 | vm_val_t *retval, uint *argc) |
| | 1595 | { |
| | 1596 | /* use our general handler, working backwards */ |
| | 1597 | return gen_index_which(vmg_ self, retval, argc, FALSE, FALSE); |
| | 1598 | } |
| | 1599 | |
| | 1600 | /* |
| | 1601 | * general handler for indexWhich and lastIndexWhich |
| | 1602 | */ |
| | 1603 | int CVmObjVector::gen_index_which(VMG_ vm_obj_id_t self, vm_val_t *retval, |
| | 1604 | uint *argc, int forward, int count_only) |
| | 1605 | { |
| | 1606 | const vm_val_t *func_val; |
| | 1607 | size_t cnt; |
| | 1608 | size_t idx; |
| | 1609 | int val_cnt; |
| | 1610 | static CVmNativeCodeDesc desc(1); |
| | 1611 | |
| | 1612 | /* check arguments */ |
| | 1613 | if (get_prop_check_argc(retval, argc, &desc)) |
| | 1614 | return TRUE; |
| | 1615 | |
| | 1616 | /* get the function pointer argument, but leave it on the stack */ |
| | 1617 | func_val = G_stk->get(0); |
| | 1618 | |
| | 1619 | /* push a self-reference while working to protect from gc */ |
| | 1620 | G_interpreter->push_obj(vmg_ self); |
| | 1621 | |
| | 1622 | /* presume we won't find a matching element */ |
| | 1623 | retval->set_nil(); |
| | 1624 | val_cnt = 0; |
| | 1625 | |
| | 1626 | /* get the number of elements to visit */ |
| | 1627 | cnt = get_element_count(); |
| | 1628 | |
| | 1629 | /* start at the first or last element, depending on direction */ |
| | 1630 | idx = (forward ? 0 : cnt); |
| | 1631 | |
| | 1632 | /* |
| | 1633 | * Go through each element of our vector, and invoke the callback on |
| | 1634 | * each element, looking for the first one that returns true. |
| | 1635 | */ |
| | 1636 | for (;;) |
| | 1637 | { |
| | 1638 | /* if we've reached the last element, we can stop looking */ |
| | 1639 | if (forward ? idx >= cnt : idx == 0) |
| | 1640 | break; |
| | 1641 | |
| | 1642 | /* if we're going backwards, decrement the index */ |
| | 1643 | if (!forward) |
| | 1644 | --idx; |
| | 1645 | |
| | 1646 | /* push the element as the callback's argument */ |
| | 1647 | push_element(vmg_ idx); |
| | 1648 | |
| | 1649 | /* invoke the callback */ |
| | 1650 | G_interpreter->call_func_ptr(vmg_ func_val, 1, |
| | 1651 | "Vector.indexWhich", 0); |
| | 1652 | |
| | 1653 | /* |
| | 1654 | * if the callback returned true, we've found the element we're |
| | 1655 | * looking for |
| | 1656 | */ |
| | 1657 | if (G_interpreter->get_r0()->typ == VM_NIL |
| | 1658 | || (G_interpreter->get_r0()->typ == VM_INT |
| | 1659 | && G_interpreter->get_r0()->val.intval == 0)) |
| | 1660 | { |
| | 1661 | /* nil or zero - this one failed the test, so keep looking */ |
| | 1662 | } |
| | 1663 | else |
| | 1664 | { |
| | 1665 | /* it passed the test - check what we're doing */ |
| | 1666 | if (count_only) |
| | 1667 | { |
| | 1668 | /* we only want the count */ |
| | 1669 | ++val_cnt; |
| | 1670 | } |
| | 1671 | else |
| | 1672 | { |
| | 1673 | /* we want the (1-based) index - return it */ |
| | 1674 | retval->set_int(idx + 1); |
| | 1675 | |
| | 1676 | /* found it - no need to keep looking */ |
| | 1677 | break; |
| | 1678 | } |
| | 1679 | } |
| | 1680 | |
| | 1681 | /* if we're going forwards, increment the index */ |
| | 1682 | if (forward) |
| | 1683 | ++idx; |
| | 1684 | } |
| | 1685 | |
| | 1686 | /* discard our gc protection (self) and our arguments */ |
| | 1687 | G_stk->discard(2); |
| | 1688 | |
| | 1689 | /* return the count if desired */ |
| | 1690 | if (count_only) |
| | 1691 | retval->set_int(val_cnt); |
| | 1692 | |
| | 1693 | /* handled */ |
| | 1694 | return TRUE; |
| | 1695 | } |
| | 1696 | |
| | 1697 | /* ------------------------------------------------------------------------ */ |
| | 1698 | /* |
| | 1699 | * property evaluator - lastValWhich |
| | 1700 | */ |
| | 1701 | int CVmObjVector::getp_last_val_which(VMG_ vm_obj_id_t self, |
| | 1702 | vm_val_t *retval, uint *argc) |
| | 1703 | { |
| | 1704 | /* get the index of the value using lastIndexWhich */ |
| | 1705 | getp_last_index_which(vmg_ self, retval, argc); |
| | 1706 | |
| | 1707 | /* if the return value is a valid index, get the value at the index */ |
| | 1708 | if (retval->typ == VM_INT) |
| | 1709 | { |
| | 1710 | int idx; |
| | 1711 | |
| | 1712 | /* get the element as the return value */ |
| | 1713 | idx = (int)retval->val.intval; |
| | 1714 | get_element(idx - 1, retval); |
| | 1715 | } |
| | 1716 | |
| | 1717 | /* handled */ |
| | 1718 | return TRUE; |
| | 1719 | } |
| | 1720 | |
| | 1721 | /* ------------------------------------------------------------------------ */ |
| | 1722 | /* |
| | 1723 | * property evaluator - valWhich |
| | 1724 | */ |
| | 1725 | int CVmObjVector::getp_val_which(VMG_ vm_obj_id_t self, vm_val_t *retval, |
| | 1726 | uint *argc) |
| | 1727 | { |
| | 1728 | /* get the index of the value using indexWhich */ |
| | 1729 | getp_index_which(vmg_ self, retval, argc); |
| | 1730 | |
| | 1731 | /* if the return value is a valid index, get the value at the index */ |
| | 1732 | if (retval->typ == VM_INT) |
| | 1733 | { |
| | 1734 | int idx; |
| | 1735 | |
| | 1736 | /* get the element as the return value */ |
| | 1737 | idx = (int)retval->val.intval; |
| | 1738 | get_element(idx - 1, retval); |
| | 1739 | } |
| | 1740 | |
| | 1741 | /* handled */ |
| | 1742 | return TRUE; |
| | 1743 | } |
| | 1744 | |
| | 1745 | /* ------------------------------------------------------------------------ */ |
| | 1746 | /* |
| | 1747 | * property evaluator - call a callback for each element |
| | 1748 | */ |
| | 1749 | int CVmObjVector::getp_for_each(VMG_ vm_obj_id_t self, vm_val_t *retval, |
| | 1750 | uint *argc) |
| | 1751 | { |
| | 1752 | /* use the general forEach processor */ |
| | 1753 | return for_each_gen(vmg_ self, retval, argc, FALSE); |
| | 1754 | } |
| | 1755 | |
| | 1756 | /* |
| | 1757 | * property evaluator - call a callback for each element |
| | 1758 | */ |
| | 1759 | int CVmObjVector::getp_for_each_assoc(VMG_ vm_obj_id_t self, |
| | 1760 | vm_val_t *retval, uint *argc) |
| | 1761 | { |
| | 1762 | /* use the general forEach processor */ |
| | 1763 | return for_each_gen(vmg_ self, retval, argc, TRUE); |
| | 1764 | } |
| | 1765 | |
| | 1766 | |
| | 1767 | /* |
| | 1768 | * General forEach/forEachAssoc processor |
| | 1769 | */ |
| | 1770 | int CVmObjVector::for_each_gen(VMG_ vm_obj_id_t self, |
| | 1771 | vm_val_t *retval, uint *argc, |
| | 1772 | int pass_key_to_cb) |
| | 1773 | { |
| | 1774 | const vm_val_t *func_val; |
| | 1775 | size_t idx; |
| | 1776 | static CVmNativeCodeDesc desc(1); |
| | 1777 | |
| | 1778 | /* check arguments */ |
| | 1779 | if (get_prop_check_argc(retval, argc, &desc)) |
| | 1780 | return TRUE; |
| | 1781 | |
| | 1782 | /* get the function pointer argument, but leave it on the stack */ |
| | 1783 | func_val = G_stk->get(0); |
| | 1784 | |
| | 1785 | /* push a self-reference while working to protect from gc */ |
| | 1786 | G_interpreter->push_obj(vmg_ self); |
| | 1787 | |
| | 1788 | /* no return value */ |
| | 1789 | retval->set_nil(); |
| | 1790 | |
| | 1791 | /* |
| | 1792 | * Invoke the callback on each element. Note that we intentionally do |
| | 1793 | * not cache the element count, since it is possible that a program |
| | 1794 | * could change the vector size in the course of an iteration; if we |
| | 1795 | * cached the size, and the actual size were reduced during the |
| | 1796 | * iteration, we would visit invalid elements past the new end of the |
| | 1797 | * vector. To avoid this possibility, we re-check the current element |
| | 1798 | * count on each iteration to make sure we haven't run off the end of |
| | 1799 | * the vector. |
| | 1800 | */ |
| | 1801 | for (idx = 0 ; idx < get_element_count() ; ++idx) |
| | 1802 | { |
| | 1803 | /* push the element as the callback's argument */ |
| | 1804 | push_element(vmg_ idx); |
| | 1805 | |
| | 1806 | /* push the index argument if desired */ |
| | 1807 | if (pass_key_to_cb) |
| | 1808 | G_stk->push()->set_int(idx + 1); |
| | 1809 | |
| | 1810 | /* invoke the callback */ |
| | 1811 | G_interpreter->call_func_ptr(vmg_ func_val, pass_key_to_cb ? 2 : 1, |
| | 1812 | "Vector.forEach", 0); |
| | 1813 | } |
| | 1814 | |
| | 1815 | /* discard our gc protection (self) and our arguments */ |
| | 1816 | G_stk->discard(2); |
| | 1817 | |
| | 1818 | /* handled */ |
| | 1819 | return TRUE; |
| | 1820 | } |
| | 1821 | |
| | 1822 | /* ------------------------------------------------------------------------ */ |
| | 1823 | /* |
| | 1824 | * property evaluator - mapAll |
| | 1825 | */ |
| | 1826 | int CVmObjVector::getp_map_all(VMG_ vm_obj_id_t self, vm_val_t *retval, |
| | 1827 | uint *argc) |
| | 1828 | { |
| | 1829 | const vm_val_t *func_val; |
| | 1830 | size_t idx; |
| | 1831 | CVmObjVector *new_vec; |
| | 1832 | static CVmNativeCodeDesc desc(1); |
| | 1833 | |
| | 1834 | /* check arguments */ |
| | 1835 | if (get_prop_check_argc(retval, argc, &desc)) |
| | 1836 | return TRUE; |
| | 1837 | |
| | 1838 | /* get the function pointer argument, but leave it on the stack */ |
| | 1839 | func_val = G_stk->get(0); |
| | 1840 | |
| | 1841 | /* push a self-reference while working to protect from gc */ |
| | 1842 | G_interpreter->push_obj(vmg_ self); |
| | 1843 | |
| | 1844 | /* |
| | 1845 | * allocate a new vector for the return value - the new vector will |
| | 1846 | * have the same size as the original, since we're mapping each |
| | 1847 | * element of the old vector to the corresponding element of the new |
| | 1848 | * vector |
| | 1849 | */ |
| | 1850 | retval->set_obj(create(vmg_ FALSE, get_allocated_count())); |
| | 1851 | |
| | 1852 | /* get the return value as an vector */ |
| | 1853 | new_vec = (CVmObjVector *)vm_objp(vmg_ retval->val.obj); |
| | 1854 | |
| | 1855 | /* set the result element count the same as my own */ |
| | 1856 | new_vec->set_element_count(get_element_count()); |
| | 1857 | |
| | 1858 | /* |
| | 1859 | * push a reference to the new list to protect it from the garbage |
| | 1860 | * collector, which could be invoked in the course of executing the |
| | 1861 | * user callback |
| | 1862 | */ |
| | 1863 | G_stk->push(retval); |
| | 1864 | |
| | 1865 | /* |
| | 1866 | * Go through each element of our vector, and invoke the callback on |
| | 1867 | * each element, storing the result in the corresponding element of |
| | 1868 | * the new vector. Note that we re-check the element count on each |
| | 1869 | * iteration, in case the callback changes it on us. |
| | 1870 | */ |
| | 1871 | for (idx = 0 ; idx < get_element_count() ; ++idx) |
| | 1872 | { |
| | 1873 | /* push the element as the callback's argument */ |
| | 1874 | push_element(vmg_ idx); |
| | 1875 | |
| | 1876 | /* invoke the callback */ |
| | 1877 | G_interpreter->call_func_ptr(vmg_ func_val, 1, "Vector.mapAll", 0); |
| | 1878 | |
| | 1879 | /* |
| | 1880 | * replace this element with the result (there's no need to save |
| | 1881 | * undo, since the whole vector is new) |
| | 1882 | */ |
| | 1883 | new_vec->set_element(idx, G_interpreter->get_r0()); |
| | 1884 | } |
| | 1885 | |
| | 1886 | /* discard our gc protection (self, new vector) and our arguments */ |
| | 1887 | G_stk->discard(3); |
| | 1888 | |
| | 1889 | /* handled */ |
| | 1890 | return TRUE; |
| | 1891 | } |
| | 1892 | |
| | 1893 | /* ------------------------------------------------------------------------ */ |
| | 1894 | /* |
| | 1895 | * property evaluator - indexOf |
| | 1896 | */ |
| | 1897 | int CVmObjVector::getp_index_of(VMG_ vm_obj_id_t self, vm_val_t *retval, |
| | 1898 | uint *argc) |
| | 1899 | { |
| | 1900 | /* use our general handler, going forwards */ |
| | 1901 | return gen_index_of(vmg_ self, retval, argc, TRUE, FALSE); |
| | 1902 | } |
| | 1903 | |
| | 1904 | /* |
| | 1905 | * property evaluator - lastIndexOf |
| | 1906 | */ |
| | 1907 | int CVmObjVector::getp_last_index_of(VMG_ vm_obj_id_t self, vm_val_t *retval, |
| | 1908 | uint *argc) |
| | 1909 | { |
| | 1910 | /* use our general handler, going backwards */ |
| | 1911 | return gen_index_of(vmg_ self, retval, argc, FALSE, FALSE); |
| | 1912 | } |
| | 1913 | |
| | 1914 | /* |
| | 1915 | * general handler for indexOf and lastIndexOf - searches the vector |
| | 1916 | * either forwards of backwards for a given value |
| | 1917 | */ |
| | 1918 | int CVmObjVector::gen_index_of(VMG_ vm_obj_id_t self, vm_val_t *retval, |
| | 1919 | uint *argc, int forward, int count_only) |
| | 1920 | { |
| | 1921 | const vm_val_t *val; |
| | 1922 | size_t cnt; |
| | 1923 | size_t idx; |
| | 1924 | int val_cnt; |
| | 1925 | static CVmNativeCodeDesc desc(1); |
| | 1926 | |
| | 1927 | /* check arguments */ |
| | 1928 | if (get_prop_check_argc(retval, argc, &desc)) |
| | 1929 | return TRUE; |
| | 1930 | |
| | 1931 | /* get the value, but leave it on the stack */ |
| | 1932 | val = G_stk->get(0); |
| | 1933 | |
| | 1934 | /* push a self-reference while working to protect from gc */ |
| | 1935 | G_interpreter->push_obj(vmg_ self); |
| | 1936 | |
| | 1937 | /* presume we won't find a matching element */ |
| | 1938 | retval->set_nil(); |
| | 1939 | val_cnt = 0; |
| | 1940 | |
| | 1941 | /* get the number of elements to visit */ |
| | 1942 | cnt = get_element_count(); |
| | 1943 | |
| | 1944 | /* start at the first or last element, depending on the direction */ |
| | 1945 | idx = (forward ? 0 : cnt); |
| | 1946 | |
| | 1947 | /* scan the list, looking for the element */ |
| | 1948 | for (;;) |
| | 1949 | { |
| | 1950 | vm_val_t ele; |
| | 1951 | |
| | 1952 | /* if we've reached the last element, stop looking */ |
| | 1953 | if (forward ? idx >= cnt : idx == 0) |
| | 1954 | break; |
| | 1955 | |
| | 1956 | /* if we're going backwards, move to the next element position */ |
| | 1957 | if (!forward) |
| | 1958 | --idx; |
| | 1959 | |
| | 1960 | /* get this element */ |
| | 1961 | get_element(idx, &ele); |
| | 1962 | |
| | 1963 | /* if the element matches the search value, return its index */ |
| | 1964 | if (ele.equals(vmg_ val)) |
| | 1965 | { |
| | 1966 | /* it matches - see what we're doing */ |
| | 1967 | if (count_only) |
| | 1968 | { |
| | 1969 | /* they only want the count */ |
| | 1970 | ++val_cnt; |
| | 1971 | } |
| | 1972 | else |
| | 1973 | { |
| | 1974 | /* this is the one - return its 1-based index */ |
| | 1975 | retval->set_int(idx + 1); |
| | 1976 | |
| | 1977 | /* foind it - no need to keep searching */ |
| | 1978 | break; |
| | 1979 | } |
| | 1980 | } |
| | 1981 | |
| | 1982 | /* if we're going forwards, move to the next element */ |
| | 1983 | if (forward) |
| | 1984 | ++idx; |
| | 1985 | } |
| | 1986 | |
| | 1987 | /* discard our gc protection (self) and our arguments */ |
| | 1988 | G_stk->discard(2); |
| | 1989 | |
| | 1990 | /* return the count if desired */ |
| | 1991 | if (count_only) |
| | 1992 | retval->set_int(val_cnt); |
| | 1993 | |
| | 1994 | /* handled */ |
| | 1995 | return TRUE; |
| | 1996 | } |
| | 1997 | |
| | 1998 | /* ------------------------------------------------------------------------ */ |
| | 1999 | /* |
| | 2000 | * property evaluator - countOf |
| | 2001 | */ |
| | 2002 | int CVmObjVector::getp_count_of(VMG_ vm_obj_id_t self, vm_val_t *retval, |
| | 2003 | uint *argc) |
| | 2004 | { |
| | 2005 | /* use our general handler to obtain the count */ |
| | 2006 | return gen_index_of(vmg_ self, retval, argc, TRUE, TRUE); |
| | 2007 | } |
| | 2008 | |
| | 2009 | /* ------------------------------------------------------------------------ */ |
| | 2010 | /* |
| | 2011 | * property evaluator - countWhich |
| | 2012 | */ |
| | 2013 | int CVmObjVector::getp_count_which(VMG_ vm_obj_id_t self, vm_val_t *retval, |
| | 2014 | uint *argc) |
| | 2015 | { |
| | 2016 | /* use our general handler to obtain the count */ |
| | 2017 | return gen_index_which(vmg_ self, retval, argc, TRUE, TRUE); |
| | 2018 | } |
| | 2019 | |
| | 2020 | /* ------------------------------------------------------------------------ */ |
| | 2021 | /* |
| | 2022 | * property evaluator - getUnique; returns a new vector consisting of the |
| | 2023 | * unique elements of the original vector |
| | 2024 | */ |
| | 2025 | int CVmObjVector::getp_get_unique(VMG_ vm_obj_id_t self, vm_val_t *retval, |
| | 2026 | uint *argc) |
| | 2027 | { |
| | 2028 | CVmObjVector *new_vec; |
| | 2029 | size_t cnt; |
| | 2030 | static CVmNativeCodeDesc desc(0); |
| | 2031 | |
| | 2032 | /* check arguments */ |
| | 2033 | if (get_prop_check_argc(retval, argc, &desc)) |
| | 2034 | return TRUE; |
| | 2035 | |
| | 2036 | /* put myself on the stack for GC protection */ |
| | 2037 | G_interpreter->push_obj(vmg_ self); |
| | 2038 | |
| | 2039 | /* |
| | 2040 | * allocate a new vector for the return value - the new vector will |
| | 2041 | * never be any larger than the original |
| | 2042 | */ |
| | 2043 | cnt = get_element_count(); |
| | 2044 | retval->set_obj(create(vmg_ FALSE, cnt)); |
| | 2045 | |
| | 2046 | /* push a reference to the new list for gc protection */ |
| | 2047 | G_stk->push(retval); |
| | 2048 | |
| | 2049 | /* get the return value as a vector */ |
| | 2050 | new_vec = (CVmObjVector *)vm_objp(vmg_ retval->val.obj); |
| | 2051 | |
| | 2052 | /* start out with the result element count the same as my own */ |
| | 2053 | new_vec->set_element_count(cnt); |
| | 2054 | |
| | 2055 | /* copy my elements to the new vector */ |
| | 2056 | memcpy(new_vec->get_element_ptr(0), get_element_ptr(0), |
| | 2057 | calc_alloc_ele(cnt)); |
| | 2058 | |
| | 2059 | /* uniquify the result */ |
| | 2060 | new_vec->cons_uniquify(vmg0_); |
| | 2061 | |
| | 2062 | /* discard the gc protection */ |
| | 2063 | G_stk->discard(2); |
| | 2064 | |
| | 2065 | /* handled */ |
| | 2066 | return TRUE; |
| | 2067 | } |
| | 2068 | |
| | 2069 | /* |
| | 2070 | * For construction, eliminate repeated elements of the vector, leaving |
| | 2071 | * only the unique elements. Reduces the size of the vector to the size |
| | 2072 | * required to accommodate the unique elements. |
| | 2073 | */ |
| | 2074 | void CVmObjVector::cons_uniquify(VMG0_) |
| | 2075 | { |
| | 2076 | size_t cnt; |
| | 2077 | size_t src, dst; |
| | 2078 | |
| | 2079 | /* get the number of elements */ |
| | 2080 | cnt = get_element_count(); |
| | 2081 | |
| | 2082 | /* loop through the list and look for repeated values */ |
| | 2083 | for (src = dst = 0 ; src < cnt ; ++src) |
| | 2084 | { |
| | 2085 | size_t idx; |
| | 2086 | vm_val_t src_val; |
| | 2087 | int found; |
| | 2088 | |
| | 2089 | /* |
| | 2090 | * look for a copy of this source value already in the output list |
| | 2091 | */ |
| | 2092 | get_element(src, &src_val); |
| | 2093 | for (idx = 0, found = FALSE ; idx < dst ; ++idx) |
| | 2094 | { |
| | 2095 | vm_val_t idx_val; |
| | 2096 | |
| | 2097 | /* get this value */ |
| | 2098 | get_element(idx, &idx_val); |
| | 2099 | |
| | 2100 | /* if it's equal to the current source value, note it */ |
| | 2101 | if (src_val.equals(vmg_ &idx_val)) |
| | 2102 | { |
| | 2103 | /* note that we found it */ |
| | 2104 | found = TRUE; |
| | 2105 | |
| | 2106 | /* no need to look any further */ |
| | 2107 | break; |
| | 2108 | } |
| | 2109 | } |
| | 2110 | |
| | 2111 | /* if we didn't find the value, copy it to the output list */ |
| | 2112 | if (!found) |
| | 2113 | { |
| | 2114 | /* |
| | 2115 | * add it to the output vector - since this is a |
| | 2116 | * construction-only method, there's no need to save undo (if |
| | 2117 | * we apply undo, we'll undo the entire construction of the |
| | 2118 | * vector, hence there's no need to track changes made since |
| | 2119 | * creation) |
| | 2120 | */ |
| | 2121 | set_element(dst, &src_val); |
| | 2122 | |
| | 2123 | /* count it in the output */ |
| | 2124 | ++dst; |
| | 2125 | } |
| | 2126 | } |
| | 2127 | |
| | 2128 | /* adjust the size of the result list */ |
| | 2129 | set_element_count(dst); |
| | 2130 | } |
| | 2131 | |
| | 2132 | /* ------------------------------------------------------------------------ */ |
| | 2133 | /* |
| | 2134 | * property evaluator - appendUnique |
| | 2135 | */ |
| | 2136 | int CVmObjVector::getp_append_unique(VMG_ vm_obj_id_t self, vm_val_t *retval, |
| | 2137 | uint *argc) |
| | 2138 | { |
| | 2139 | vm_val_t *val2; |
| | 2140 | size_t cnt2; |
| | 2141 | size_t cnt; |
| | 2142 | static CVmNativeCodeDesc desc(1); |
| | 2143 | size_t i; |
| | 2144 | |
| | 2145 | /* check arguments */ |
| | 2146 | if (get_prop_check_argc(retval, argc, &desc)) |
| | 2147 | return TRUE; |
| | 2148 | |
| | 2149 | /* get the other value, but leave it on the stack */ |
| | 2150 | val2 = G_stk->get(0); |
| | 2151 | |
| | 2152 | /* the return value is 'self' */ |
| | 2153 | retval->set_obj(self); |
| | 2154 | |
| | 2155 | /* put myself on the stack for GC protection */ |
| | 2156 | G_stk->push(retval); |
| | 2157 | |
| | 2158 | /* get the number of elements in the original */ |
| | 2159 | cnt = get_element_count(); |
| | 2160 | |
| | 2161 | /* get the number of elements to append */ |
| | 2162 | cnt2 = val2->get_coll_addsub_rhs_ele_cnt(vmg0_); |
| | 2163 | |
| | 2164 | /* expand myself to make room for the new elements */ |
| | 2165 | expand_by(vmg_ self, cnt2); |
| | 2166 | |
| | 2167 | /* append each element of the right-hand side */ |
| | 2168 | for (i = 0 ; i < cnt2 ; ++i) |
| | 2169 | { |
| | 2170 | vm_val_t ele; |
| | 2171 | |
| | 2172 | /* get this element */ |
| | 2173 | val2->get_coll_addsub_rhs_ele(vmg_ i + 1, &ele); |
| | 2174 | |
| | 2175 | /* store it */ |
| | 2176 | set_element_undo(vmg_ self, cnt + i, &ele); |
| | 2177 | } |
| | 2178 | |
| | 2179 | /* uniquify the result */ |
| | 2180 | uniquify_for_append(vmg_ self); |
| | 2181 | |
| | 2182 | /* discard the gc protection and arguments */ |
| | 2183 | G_stk->discard(2); |
| | 2184 | |
| | 2185 | /* handled */ |
| | 2186 | return TRUE; |
| | 2187 | } |
| | 2188 | |
| | 2189 | /* |
| | 2190 | * uniquify after an appendUnique operation |
| | 2191 | */ |
| | 2192 | void CVmObjVector::uniquify_for_append(VMG_ vm_obj_id_t self) |
| | 2193 | { |
| | 2194 | size_t cnt; |
| | 2195 | size_t src, dst; |
| | 2196 | |
| | 2197 | /* get the number of elements */ |
| | 2198 | cnt = get_element_count(); |
| | 2199 | |
| | 2200 | /* loop through the list and look for repeated values */ |
| | 2201 | for (src = dst = 0 ; src < cnt ; ++src) |
| | 2202 | { |
| | 2203 | size_t idx; |
| | 2204 | vm_val_t src_val; |
| | 2205 | int found; |
| | 2206 | |
| | 2207 | /* look for a copy of this value elsewhere in the vector */ |
| | 2208 | get_element(src, &src_val); |
| | 2209 | for (idx = 0, found = FALSE ; idx < dst ; ++idx) |
| | 2210 | { |
| | 2211 | vm_val_t idx_val; |
| | 2212 | |
| | 2213 | /* get this value */ |
| | 2214 | get_element(idx, &idx_val); |
| | 2215 | |
| | 2216 | /* if it's equal to the current source value, note it */ |
| | 2217 | if (src_val.equals(vmg_ &idx_val)) |
| | 2218 | { |
| | 2219 | /* note that we found it */ |
| | 2220 | found = TRUE; |
| | 2221 | |
| | 2222 | /* no need to look any further */ |
| | 2223 | break; |
| | 2224 | } |
| | 2225 | } |
| | 2226 | |
| | 2227 | /* if we didn't find the value, copy it to the output list */ |
| | 2228 | if (!found) |
| | 2229 | { |
| | 2230 | /* |
| | 2231 | * Add it to the output vector if we're actually changing this |
| | 2232 | * element (i.e., the destination and source indices are |
| | 2233 | * unequal). |
| | 2234 | */ |
| | 2235 | if (dst != src) |
| | 2236 | set_element_undo(vmg_ self, dst, &src_val); |
| | 2237 | |
| | 2238 | /* count it in the output */ |
| | 2239 | ++dst; |
| | 2240 | } |
| | 2241 | } |
| | 2242 | |
| | 2243 | /* adjust the size of the result list */ |
| | 2244 | set_element_count_undo(vmg_ self, dst); |
| | 2245 | } |
| | 2246 | |
| | 2247 | /* ------------------------------------------------------------------------ */ |
| | 2248 | /* |
| | 2249 | * sorter for vector |
| | 2250 | */ |
| | 2251 | class CVmQSortVector: public CVmQSortVal |
| | 2252 | { |
| | 2253 | public: |
| | 2254 | CVmQSortVector() |
| | 2255 | { |
| | 2256 | vec_ = 0; |
| | 2257 | self_ = VM_INVALID_OBJ; |
| | 2258 | } |
| | 2259 | |
| | 2260 | /* get an element from the vector */ |
| | 2261 | void get_ele(VMG_ size_t idx, vm_val_t *val) |
| | 2262 | { vec_->get_element(idx, val); } |
| | 2263 | |
| | 2264 | /* store an element */ |
| | 2265 | void set_ele(VMG_ size_t idx, const vm_val_t *val) |
| | 2266 | { vec_->set_element_undo(vmg_ self_, idx, val); } |
| | 2267 | |
| | 2268 | /* our vector object */ |
| | 2269 | CVmObjVector *vec_; |
| | 2270 | vm_obj_id_t self_; |
| | 2271 | }; |
| | 2272 | |
| | 2273 | /* |
| | 2274 | * property evaluator - sort |
| | 2275 | */ |
| | 2276 | int CVmObjVector::getp_sort(VMG_ vm_obj_id_t self, vm_val_t *retval, |
| | 2277 | uint *in_argc) |
| | 2278 | { |
| | 2279 | size_t len; |
| | 2280 | uint argc = (in_argc == 0 ? 0 : *in_argc); |
| | 2281 | CVmQSortVector sorter; |
| | 2282 | static CVmNativeCodeDesc desc(0, 2); |
| | 2283 | |
| | 2284 | /* check arguments */ |
| | 2285 | if (get_prop_check_argc(retval, in_argc, &desc)) |
| | 2286 | return TRUE; |
| | 2287 | |
| | 2288 | /* remember the length of my list */ |
| | 2289 | len = get_element_count(); |
| | 2290 | |
| | 2291 | /* set the vector object in the sorter */ |
| | 2292 | sorter.vec_ = this; |
| | 2293 | sorter.self_ = self; |
| | 2294 | |
| | 2295 | /* if we have an 'descending' flag, note it */ |
| | 2296 | if (argc >= 1) |
| | 2297 | sorter.descending_ = (G_stk->get(0)->typ != VM_NIL); |
| | 2298 | |
| | 2299 | /* |
| | 2300 | * if we have a comparison function, note it, but leave it on the |
| | 2301 | * stack for gc protection |
| | 2302 | */ |
| | 2303 | if (argc >= 2) |
| | 2304 | sorter.compare_fn_ = *G_stk->get(1); |
| | 2305 | |
| | 2306 | /* put myself on the stack for GC protection */ |
| | 2307 | G_interpreter->push_obj(vmg_ self); |
| | 2308 | |
| | 2309 | /* sort the vector, if we have any elements */ |
| | 2310 | if (len != 0) |
| | 2311 | sorter.sort(vmg_ 0, len - 1); |
| | 2312 | |
| | 2313 | /* discard the gc protection and arguments */ |
| | 2314 | G_stk->discard(1 + argc); |
| | 2315 | |
| | 2316 | /* return myself */ |
| | 2317 | retval->set_obj(self); |
| | 2318 | |
| | 2319 | /* handled */ |
| | 2320 | return TRUE; |
| | 2321 | } |
| | 2322 | |
| | 2323 | /* ------------------------------------------------------------------------ */ |
| | 2324 | /* |
| | 2325 | * property evaluator - set the length |
| | 2326 | */ |
| | 2327 | int CVmObjVector::getp_set_length(VMG_ vm_obj_id_t self, vm_val_t *retval, |
| | 2328 | uint *argc) |
| | 2329 | { |
| | 2330 | size_t old_len; |
| | 2331 | size_t new_len; |
| | 2332 | size_t idx; |
| | 2333 | vm_val_t nil_val; |
| | 2334 | static CVmNativeCodeDesc desc(1); |
| | 2335 | |
| | 2336 | /* check arguments */ |
| | 2337 | if (get_prop_check_argc(retval, argc, &desc)) |
| | 2338 | return TRUE; |
| | 2339 | |
| | 2340 | /* the return value is 'self' */ |
| | 2341 | retval->set_obj(self); |
| | 2342 | |
| | 2343 | /* note the old length */ |
| | 2344 | old_len = get_element_count(); |
| | 2345 | |
| | 2346 | /* get the new length */ |
| | 2347 | new_len = (size_t)CVmBif::pop_int_val(vmg0_); |
| | 2348 | |
| | 2349 | /* can't go less than zero */ |
| | 2350 | if (new_len < 0) |
| | 2351 | err_throw(VMERR_BAD_VAL_BIF); |
| | 2352 | |
| | 2353 | /* set the vector to its new size */ |
| | 2354 | set_element_count_undo(vmg_ self, new_len); |
| | 2355 | |
| | 2356 | /* |
| | 2357 | * Set each newly added element to nil. Note that we don't bother |
| | 2358 | * saving undo for these changes: to undo this change, the only thing |
| | 2359 | * we'll have to do is reduce the vector size to its previous size, |
| | 2360 | * and we've already saved undo for the size change. |
| | 2361 | */ |
| | 2362 | nil_val.set_nil(); |
| | 2363 | for (idx = old_len ; idx < new_len ; ++idx) |
| | 2364 | set_element(idx, &nil_val); |
| | 2365 | |
| | 2366 | /* handled */ |
| | 2367 | return TRUE; |
| | 2368 | } |
| | 2369 | |
| | 2370 | /* ------------------------------------------------------------------------ */ |
| | 2371 | /* |
| | 2372 | * property evaluator - insert one or more elements at a given index |
| | 2373 | */ |
| | 2374 | int CVmObjVector::getp_insert_at(VMG_ vm_obj_id_t self, vm_val_t *retval, |
| | 2375 | uint *in_argc) |
| | 2376 | { |
| | 2377 | size_t idx; |
| | 2378 | size_t ins_idx; |
| | 2379 | size_t old_cnt; |
| | 2380 | size_t add_cnt; |
| | 2381 | size_t new_cnt; |
| | 2382 | uint argc = (in_argc != 0 ? *in_argc : 0); |
| | 2383 | static CVmNativeCodeDesc desc(2, 0, TRUE); |
| | 2384 | |
| | 2385 | /* we must have at least two arguments */ |
| | 2386 | if (get_prop_check_argc(retval, in_argc, &desc)) |
| | 2387 | return TRUE; |
| | 2388 | |
| | 2389 | /* get the starting insertion index */ |
| | 2390 | ins_idx = (size_t)CVmBif::pop_int_val(vmg0_); |
| | 2391 | |
| | 2392 | /* the return value is 'self' */ |
| | 2393 | retval->set_obj(self); |
| | 2394 | |
| | 2395 | /* note the original size of the vector */ |
| | 2396 | old_cnt = get_element_count(); |
| | 2397 | |
| | 2398 | /* |
| | 2399 | * note the number of items we're adding - we're adding each |
| | 2400 | * argument, other than the first |
| | 2401 | */ |
| | 2402 | add_cnt = argc - 1; |
| | 2403 | |
| | 2404 | /* |
| | 2405 | * note the new size - we're inserting one element for each argument |
| | 2406 | * past the first one |
| | 2407 | */ |
| | 2408 | new_cnt = old_cnt + add_cnt; |
| | 2409 | |
| | 2410 | /* |
| | 2411 | * if the starting index is out of range, it's an error - it can |
| | 2412 | * range from 1 to one higher than the current element count (the |
| | 2413 | * top end of the range allows us to insert elements after all of |
| | 2414 | * the current elements) |
| | 2415 | */ |
| | 2416 | if (ins_idx < 1 || ins_idx > get_element_count() + 1) |
| | 2417 | err_throw(VMERR_INDEX_OUT_OF_RANGE); |
| | 2418 | |
| | 2419 | /* adjust the starting index to a zero-based value */ |
| | 2420 | --ins_idx; |
| | 2421 | |
| | 2422 | /* create the new elements */ |
| | 2423 | set_element_count_undo(vmg_ self, new_cnt); |
| | 2424 | |
| | 2425 | /* |
| | 2426 | * Move the existing elements to their new slots. Start with the |
| | 2427 | * starting index, and move it to its new position, since the starting |
| | 2428 | * index is the first element that needs to be moved. Note that we |
| | 2429 | * start at the top and work down, to avoid overwriting any |
| | 2430 | * overlapping part of the new position block and the old position |
| | 2431 | * block. |
| | 2432 | * |
| | 2433 | * If we're inserting after the last element, there's no moving that |
| | 2434 | * needs to be done. |
| | 2435 | */ |
| | 2436 | if (ins_idx < old_cnt) |
| | 2437 | { |
| | 2438 | idx = old_cnt; |
| | 2439 | for (;;) |
| | 2440 | { |
| | 2441 | size_t new_idx; |
| | 2442 | vm_val_t ele; |
| | 2443 | |
| | 2444 | /* move to the previous element */ |
| | 2445 | --idx; |
| | 2446 | |
| | 2447 | /* get the value at this slot */ |
| | 2448 | get_element(idx, &ele); |
| | 2449 | |
| | 2450 | /* calculate the new slot */ |
| | 2451 | new_idx = idx + add_cnt; |
| | 2452 | |
| | 2453 | /* |
| | 2454 | * if the new index is within the old size range, keep undo |
| | 2455 | * for the change; otherwise, no undo is necessary, since when |
| | 2456 | * we undo we'll be completely dropping this new slot |
| | 2457 | */ |
| | 2458 | if (new_idx < old_cnt) |
| | 2459 | set_element_undo(vmg_ self, new_idx, &ele); |
| | 2460 | else |
| | 2461 | set_element(new_idx, &ele); |
| | 2462 | |
| | 2463 | /* if this was the last element, we're done */ |
| | 2464 | if (idx == ins_idx) |
| | 2465 | break; |
| | 2466 | } |
| | 2467 | } |
| | 2468 | |
| | 2469 | /* add the new items */ |
| | 2470 | for (idx = ins_idx ; add_cnt != 0 ; ++idx, --add_cnt) |
| | 2471 | { |
| | 2472 | vm_val_t val; |
| | 2473 | |
| | 2474 | /* pop the next argument value */ |
| | 2475 | G_stk->pop(&val); |
| | 2476 | |
| | 2477 | /* store the item, keeping undo only if it's in the old size range */ |
| | 2478 | if (idx < old_cnt) |
| | 2479 | set_element_undo(vmg_ self, idx, &val); |
| | 2480 | else |
| | 2481 | set_element(idx, &val); |
| | 2482 | } |
| | 2483 | |
| | 2484 | /* handled */ |
| | 2485 | return TRUE; |
| | 2486 | } |
| | 2487 | |
| | 2488 | |
| | 2489 | /* ------------------------------------------------------------------------ */ |
| | 2490 | /* |
| | 2491 | * property evaluator - append an element |
| | 2492 | */ |
| | 2493 | int CVmObjVector::getp_prepend(VMG_ vm_obj_id_t self, vm_val_t *retval, |
| | 2494 | uint *argc) |
| | 2495 | { |
| | 2496 | vm_val_t val; |
| | 2497 | size_t cnt; |
| | 2498 | size_t i; |
| | 2499 | static CVmNativeCodeDesc desc(1); |
| | 2500 | |
| | 2501 | /* check arguments */ |
| | 2502 | if (get_prop_check_argc(retval, argc, &desc)) |
| | 2503 | return TRUE; |
| | 2504 | |
| | 2505 | /* get the old size */ |
| | 2506 | cnt = get_element_count(); |
| | 2507 | |
| | 2508 | /* expand the vector by one element */ |
| | 2509 | set_element_count_undo(vmg_ self, cnt + 1); |
| | 2510 | |
| | 2511 | /* move each existing element up one slot to make room for the new one */ |
| | 2512 | for (i = cnt ; i != 0 ; --i) |
| | 2513 | { |
| | 2514 | /* get this element */ |
| | 2515 | get_element(i - 1, &val); |
| | 2516 | |
| | 2517 | /* |
| | 2518 | * bump it up to the next slot - keep undo except for the last |
| | 2519 | * slot, which is newly added and thus doesn't have an old value |
| | 2520 | * that we need to keep |
| | 2521 | */ |
| | 2522 | if (i == cnt) |
| | 2523 | set_element(i, &val); |
| | 2524 | else |
| | 2525 | set_element_undo(vmg_ self, i, &val); |
| | 2526 | } |
| | 2527 | |
| | 2528 | /* retrieve the value for the new element */ |
| | 2529 | G_stk->pop(&val); |
| | 2530 | |
| | 2531 | /* |
| | 2532 | * set the first element - note that there's no need to save undo, |
| | 2533 | * since undoing this operation will simply discard this element (in |
| | 2534 | * other words, there's no previous value for this element since it's |
| | 2535 | * being created anew here) |
| | 2536 | */ |
| | 2537 | set_element(0, &val); |
| | 2538 | |
| | 2539 | /* the return value is 'self' */ |
| | 2540 | retval->set_obj(self); |
| | 2541 | |
| | 2542 | /* handled */ |
| | 2543 | return TRUE; |
| | 2544 | } |
| | 2545 | |
| | 2546 | /* ------------------------------------------------------------------------ */ |
| | 2547 | /* |
| | 2548 | * property evaluator - remove an element at the given position |
| | 2549 | */ |
| | 2550 | int CVmObjVector::getp_remove_element_at(VMG_ vm_obj_id_t self, |
| | 2551 | vm_val_t *retval, uint *argc) |
| | 2552 | { |
| | 2553 | size_t start_idx; |
| | 2554 | static CVmNativeCodeDesc desc(1); |
| | 2555 | |
| | 2556 | /* check arguments */ |
| | 2557 | if (get_prop_check_argc(retval, argc, &desc)) |
| | 2558 | return TRUE; |
| | 2559 | |
| | 2560 | /* get the deletion index */ |
| | 2561 | start_idx = (size_t)CVmBif::pop_int_val(vmg0_); |
| | 2562 | |
| | 2563 | /* make sure the index is in range */ |
| | 2564 | if (start_idx < 1 || start_idx > get_element_count()) |
| | 2565 | err_throw(VMERR_INDEX_OUT_OF_RANGE); |
| | 2566 | |
| | 2567 | /* adjust to a zero-based index */ |
| | 2568 | --start_idx; |
| | 2569 | |
| | 2570 | /* the return value is 'self' */ |
| | 2571 | retval->set_obj(self); |
| | 2572 | |
| | 2573 | /* delete one element */ |
| | 2574 | remove_elements_undo(vmg_ self, start_idx, 1); |
| | 2575 | |
| | 2576 | |
| | 2577 | /* handled */ |
| | 2578 | return TRUE; |
| | 2579 | } |
| | 2580 | |
| | 2581 | /* ------------------------------------------------------------------------ */ |
| | 2582 | /* |
| | 2583 | * property evaluator - remove element(s) matching a given value |
| | 2584 | */ |
| | 2585 | int CVmObjVector::getp_remove_element(VMG_ vm_obj_id_t self, |
| | 2586 | vm_val_t *retval, uint *argc) |
| | 2587 | { |
| | 2588 | vm_val_t *del_val; |
| | 2589 | static CVmNativeCodeDesc desc(1); |
| | 2590 | size_t src; |
| | 2591 | size_t dst; |
| | 2592 | size_t old_cnt; |
| | 2593 | |
| | 2594 | /* check arguments */ |
| | 2595 | if (get_prop_check_argc(retval, argc, &desc)) |
| | 2596 | return TRUE; |
| | 2597 | |
| | 2598 | /* get the value to be removed */ |
| | 2599 | del_val = G_stk->get(0); |
| | 2600 | |
| | 2601 | /* the return value is 'self'; leave it on the stack */ |
| | 2602 | retval->set_obj(self); |
| | 2603 | G_stk->push(retval); |
| | 2604 | |
| | 2605 | /* get the original number of elements */ |
| | 2606 | old_cnt = get_element_count(); |
| | 2607 | |
| | 2608 | /* delete each element matching the given value */ |
| | 2609 | for (src = dst = 0 ; src < old_cnt ; ++src) |
| | 2610 | { |
| | 2611 | vm_val_t ele_val; |
| | 2612 | |
| | 2613 | /* get this element */ |
| | 2614 | get_element(src, &ele_val); |
| | 2615 | |
| | 2616 | /* |
| | 2617 | * if this element doesn't match the value to be deleted, add it |
| | 2618 | * to the result vector |
| | 2619 | */ |
| | 2620 | if (!ele_val.equals(vmg_ del_val)) |
| | 2621 | { |
| | 2622 | /* |
| | 2623 | * This element is a keeper - if we're moving it to a new |
| | 2624 | * position (i.e., we've deleted any elements before this |
| | 2625 | * one), store it at its new position, keeping undo |
| | 2626 | * information. If we're storing it at its same position, |
| | 2627 | * there's no need to re-store the value or keep any undo, |
| | 2628 | * since no change is involved. |
| | 2629 | */ |
| | 2630 | if (dst != src) |
| | 2631 | set_element_undo(vmg_ self, dst, &ele_val); |
| | 2632 | |
| | 2633 | /* increment the write index past this element */ |
| | 2634 | ++dst; |
| | 2635 | } |
| | 2636 | } |
| | 2637 | |
| | 2638 | /* if the element count changed, set the new element count */ |
| | 2639 | if (dst != old_cnt) |
| | 2640 | set_element_count_undo(vmg_ self, dst); |
| | 2641 | |
| | 2642 | /* discard gc protection and arguments */ |
| | 2643 | G_stk->discard(2); |
| | 2644 | |
| | 2645 | /* handled */ |
| | 2646 | return TRUE; |
| | 2647 | } |
| | 2648 | |
| | 2649 | /* ------------------------------------------------------------------------ */ |
| | 2650 | /* |
| | 2651 | * property evaluator - remove a range of elements |
| | 2652 | */ |
| | 2653 | int CVmObjVector::getp_remove_range(VMG_ vm_obj_id_t self, |
| | 2654 | vm_val_t *retval, uint *argc) |
| | 2655 | { |
| | 2656 | size_t start_idx; |
| | 2657 | size_t end_idx; |
| | 2658 | static CVmNativeCodeDesc desc(2); |
| | 2659 | |
| | 2660 | /* check arguments */ |
| | 2661 | if (get_prop_check_argc(retval, argc, &desc)) |
| | 2662 | return TRUE; |
| | 2663 | |
| | 2664 | /* get the starting index */ |
| | 2665 | start_idx = (size_t)CVmBif::pop_int_val(vmg0_); |
| | 2666 | |
| | 2667 | /* |
| | 2668 | * make sure the index is in range - it must refer to an existing |
| | 2669 | * element |
| | 2670 | */ |
| | 2671 | if (start_idx < 1 || start_idx > get_element_count()) |
| | 2672 | err_throw(VMERR_INDEX_OUT_OF_RANGE); |
| | 2673 | |
| | 2674 | /* get the ending index */ |
| | 2675 | end_idx = (size_t)CVmBif::pop_int_val(vmg0_); |
| | 2676 | |
| | 2677 | /* |
| | 2678 | * make sure it's in range - it must refer to an existing element, |
| | 2679 | * and it must be greater than or equal to the starting index |
| | 2680 | */ |
| | 2681 | if (end_idx < start_idx || end_idx > get_element_count()) |
| | 2682 | err_throw(VMERR_INDEX_OUT_OF_RANGE); |
| | 2683 | |
| | 2684 | /* adjust to zero-based indices */ |
| | 2685 | --start_idx; |
| | 2686 | --end_idx; |
| | 2687 | |
| | 2688 | /* the return value is 'self' */ |
| | 2689 | retval->set_obj(self); |
| | 2690 | |
| | 2691 | /* |
| | 2692 | * delete the elements - the number of elements we're deleting is |
| | 2693 | * one higher than the difference of the starting and ending indices |
| | 2694 | * (if the two indices are the same, we're deleting just the one |
| | 2695 | * element) |
| | 2696 | */ |
| | 2697 | remove_elements_undo(vmg_ self, start_idx, end_idx - start_idx + 1); |
| | 2698 | |
| | 2699 | /* handled */ |
| | 2700 | return TRUE; |
| | 2701 | } |
| | 2702 | |
| | 2703 | /* ------------------------------------------------------------------------ */ |
| | 2704 | /* |
| | 2705 | * property evaluator - append an element |
| | 2706 | */ |
| | 2707 | int CVmObjVector::getp_append(VMG_ vm_obj_id_t self, vm_val_t *retval, |
| | 2708 | uint *argc) |
| | 2709 | { |
| | 2710 | vm_val_t *valp; |
| | 2711 | size_t cnt; |
| | 2712 | static CVmNativeCodeDesc desc(1); |
| | 2713 | |
| | 2714 | /* check arguments */ |
| | 2715 | if (get_prop_check_argc(retval, argc, &desc)) |
| | 2716 | return TRUE; |
| | 2717 | |
| | 2718 | /* get the value to append, but leave it on the stack */ |
| | 2719 | valp = G_stk->get(0); |
| | 2720 | |
| | 2721 | /* get the old size */ |
| | 2722 | cnt = get_element_count(); |
| | 2723 | |
| | 2724 | /* the result object is 'self' */ |
| | 2725 | retval->set_obj(self); |
| | 2726 | |
| | 2727 | /* push a self-reference for gc protection */ |
| | 2728 | G_stk->push(retval); |
| | 2729 | |
| | 2730 | /* expand myself by one element to make room for the addition */ |
| | 2731 | expand_by(vmg_ self, 1); |
| | 2732 | |
| | 2733 | /* add the new element, saving undo */ |
| | 2734 | set_element_undo(vmg_ self, cnt, valp); |
| | 2735 | |
| | 2736 | /* discard the argument and gc protection */ |
| | 2737 | G_stk->discard(2); |
| | 2738 | |
| | 2739 | /* handled */ |
| | 2740 | return TRUE; |
| | 2741 | } |
| | 2742 | |
| | 2743 | /* ------------------------------------------------------------------------ */ |
| | 2744 | /* |
| | 2745 | * property evaluator - append elements from a collection, or append a |
| | 2746 | * single non-collection element |
| | 2747 | */ |
| | 2748 | int CVmObjVector::getp_append_all(VMG_ vm_obj_id_t self, vm_val_t *retval, |
| | 2749 | uint *argc) |
| | 2750 | { |
| | 2751 | vm_val_t *valp; |
| | 2752 | size_t cnt; |
| | 2753 | static CVmNativeCodeDesc desc(1); |
| | 2754 | size_t add_cnt; |
| | 2755 | size_t i; |
| | 2756 | |
| | 2757 | /* check arguments */ |
| | 2758 | if (get_prop_check_argc(retval, argc, &desc)) |
| | 2759 | return TRUE; |
| | 2760 | |
| | 2761 | /* get the value to append, but leave it on the stack */ |
| | 2762 | valp = G_stk->get(0); |
| | 2763 | |
| | 2764 | /* the result object is 'self' */ |
| | 2765 | retval->set_obj(self); |
| | 2766 | |
| | 2767 | /* push a self-reference for gc protection */ |
| | 2768 | G_stk->push(retval); |
| | 2769 | |
| | 2770 | /* get the old size */ |
| | 2771 | cnt = get_element_count(); |
| | 2772 | |
| | 2773 | /* get the number of items to add */ |
| | 2774 | add_cnt = valp->get_coll_addsub_rhs_ele_cnt(vmg0_); |
| | 2775 | |
| | 2776 | /* expand myself to make room for the new elements */ |
| | 2777 | expand_by(vmg_ self, add_cnt); |
| | 2778 | |
| | 2779 | /* add the new elements */ |
| | 2780 | for (i = 0 ; i < add_cnt ; ++i) |
| | 2781 | { |
| | 2782 | vm_val_t ele; |
| | 2783 | |
| | 2784 | /* get the next new element */ |
| | 2785 | valp->get_coll_addsub_rhs_ele(vmg_ i + 1, &ele); |
| | 2786 | |
| | 2787 | /* |
| | 2788 | * add the new element - note that we don't need to keep undo |
| | 2789 | * because we created this new element in this same operation, and |
| | 2790 | * hence undoing the operation will truncate the vector to exclude |
| | 2791 | * the element, and hence we don't need an old value for the |
| | 2792 | * element |
| | 2793 | */ |
| | 2794 | set_element(cnt + i, &ele); |
| | 2795 | } |
| | 2796 | |
| | 2797 | /* discard the argument and gc protection */ |
| | 2798 | G_stk->discard(2); |
| | 2799 | |
| | 2800 | /* handled */ |
| | 2801 | return TRUE; |
| | 2802 | } |
| | 2803 | |
| | 2804 | /* ------------------------------------------------------------------------ */ |
| | 2805 | /* |
| | 2806 | * Delete a range of elements |
| | 2807 | */ |
| | 2808 | void CVmObjVector::remove_elements_undo(VMG_ vm_obj_id_t self, |
| | 2809 | size_t start_idx, size_t del_cnt) |
| | 2810 | { |
| | 2811 | size_t idx; |
| | 2812 | size_t cnt; |
| | 2813 | |
| | 2814 | /* note the original size of the vector */ |
| | 2815 | cnt = get_element_count(); |
| | 2816 | |
| | 2817 | /* move all of the elements to their new slots, keeping undo */ |
| | 2818 | for (idx = start_idx ; idx < cnt ; ++idx) |
| | 2819 | { |
| | 2820 | vm_val_t val; |
| | 2821 | size_t move_idx; |
| | 2822 | |
| | 2823 | /* |
| | 2824 | * calculate the index of the element to replace this one - it's |
| | 2825 | * past us by the number of items being deleted |
| | 2826 | */ |
| | 2827 | move_idx = idx + del_cnt; |
| | 2828 | |
| | 2829 | /* |
| | 2830 | * if we're moving from a valid index in the old vector, get the |
| | 2831 | * element; otherwise, replace the value with nil (but still |
| | 2832 | * replace the value, since we want to keep undo for its |
| | 2833 | * original value) |
| | 2834 | */ |
| | 2835 | if (move_idx < cnt) |
| | 2836 | get_element(move_idx, &val); |
| | 2837 | else |
| | 2838 | val.set_nil(); |
| | 2839 | |
| | 2840 | /* store the element at its new position */ |
| | 2841 | set_element_undo(vmg_ self, idx, &val); |
| | 2842 | } |
| | 2843 | |
| | 2844 | /* |
| | 2845 | * Reduce the size, keeping undo. Note that it's important we do |
| | 2846 | * this last: undo is applied in reverse order, so when applying |
| | 2847 | * undo, we first want to increase the vector's size to its original |
| | 2848 | * size, then we want to apply the element value restorations. |
| | 2849 | */ |
| | 2850 | set_element_count_undo(vmg_ self, cnt - del_cnt); |
| | 2851 | } |
| | 2852 | |