/indra/lscript/lscript_execute/lscript_heapruntime.cpp
C++ | 519 lines | 292 code | 77 blank | 150 comment | 36 complexity | f007ecc8088a44f610857249ed6ee12e MD5 | raw file
Possible License(s): LGPL-2.1
1/** 2 * @file lscript_heapruntime.cpp 3 * @brief classes to manage script heap at runtime 4 * 5 * $LicenseInfo:firstyear=2002&license=viewerlgpl$ 6 * Second Life Viewer Source Code 7 * Copyright (C) 2010, Linden Research, Inc. 8 * 9 * This library is free software; you can redistribute it and/or 10 * modify it under the terms of the GNU Lesser General Public 11 * License as published by the Free Software Foundation; 12 * version 2.1 of the License only. 13 * 14 * This library is distributed in the hope that it will be useful, 15 * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 17 * Lesser General Public License for more details. 18 * 19 * You should have received a copy of the GNU Lesser General Public 20 * License along with this library; if not, write to the Free Software 21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 22 * 23 * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA 24 * $/LicenseInfo$ 25 */ 26 27#if 0 28 29#include "linden_common.h" 30 31#include "lscript_heapruntime.h" 32#include "lscript_execute.h" 33 34 35/* 36 String Heap Format 37 Byte Description 38 0x0 - 0xnn Single byte string including null terminator 39 40 List Heap Format 41 Byte Description 42 0x0 Next Entry Type 43 0: End of list 44 1: Integer 45 2: Floating point 46 3: String 47 4: Vector 48 5: Quaternion 49 6: List 50 0x1 - 0x4 Integer, Floating Point, String Address, List Address 51 or 52 0x1 - 0xd Vector 53 or 54 0x1 - 0x11 Quaternion 55 . . . 56 57 Heap Block Format 58 Byte Description 59 0x0 - 0x3 Offset to Next Block 60 0x4 - 0x7 Object Reference Count 61 0x8 Block Type 62 0: Empty 63 3: String 64 6: List 65 0x9 - 0xM Object Data 66 67 Heap Management 68 69 Adding Data 70 71 1) Set last empty spot to zero. 72 2) Go to start of the heap (HR). 73 3) Get next 4 bytes of offset. 74 4) If zero, we've reached the end of used memory. If empty spot is zero go to step 9. Otherwise set base offset to 0 and go to step 9. 75 5) Skip 4 bytes. 76 6) Get next 1 byte of entry type. 77 7) If zero, this spot is empty. If empty spot is zero, set empty spot to this address and go to step 9. Otherwise, coalesce with last empty spot and then go to step 9. 78 8) Skip forward by offset and go to step 3. 79 9) If the spot is empty, check to see if the size needed == offset - 9. 80 10) If it does, let's drop our data into this spot. Set reference count to 1. Set entry type appropriately and copy the data in. 81 11) If size needed < offset - 9 then we can stick in data and add in an empty block. 82 12) Otherwise, we need to keep looking. Go to step 3. 83 84 Increasing reference counts 85 86 Decreasing reference counts 87 1) Set entry type to 0. 88 2) If offset is non-zero and the next entry is empty, coalesce. Go to step 2. 89 90 What increases reference count: 91 Initial creation sets reference count to 1. 92 Storing the reference increases reference count by 1. 93 Pushing the reference increases reference count by 1. 94 Duplicating the reference increases reference count by 1. 95 96 What decreases the reference count: 97 Popping the reference decreases reference count by 1. 98 */ 99 100 101LLScriptHeapRunTime::LLScriptHeapRunTime() 102: mLastEmpty(0), mBuffer(NULL), mCurrentPosition(0), mStackPointer(0), mHeapRegister(0), mbPrint(FALSE) 103{ 104} 105 106LLScriptHeapRunTime::~LLScriptHeapRunTime() 107{ 108} 109 110S32 LLScriptHeapRunTime::addData(char *string) 111{ 112 if (!mBuffer) 113 return 0; 114 115 S32 size = strlen(string) + 1; 116 S32 block_offset = findOpenBlock(size + HEAP_BLOCK_HEADER_SIZE); 117 118 if (mCurrentPosition) 119 { 120 S32 offset = mCurrentPosition; 121 if (offset + block_offset + HEAP_BLOCK_HEADER_SIZE + LSCRIPTDataSize[LST_INTEGER] >= mStackPointer) 122 { 123 set_fault(mBuffer, LSRF_STACK_HEAP_COLLISION); 124 return 0; 125 } 126 // cool, we've found a spot! 127 // set offset 128 integer2bytestream(mBuffer, offset, block_offset); 129 // set reference count 130 integer2bytestream(mBuffer, offset, 1); 131 // set type 132 *(mBuffer + offset++) = LSCRIPTTypeByte[LST_STRING]; 133 // plug in data 134 char2bytestream(mBuffer, offset, string); 135 if (mbPrint) 136 printf("0x%X created ref count %d\n", mCurrentPosition - mHeapRegister, 1); 137 138 // now, zero out next offset to prevent "trouble" 139 // offset = mCurrentPosition + size + HEAP_BLOCK_HEADER_SIZE; 140 // integer2bytestream(mBuffer, offset, 0); 141 } 142 return mCurrentPosition; 143} 144 145S32 LLScriptHeapRunTime::addData(U8 *list) 146{ 147 if (!mBuffer) 148 return 0; 149 return 0; 150} 151 152S32 LLScriptHeapRunTime::catStrings(S32 address1, S32 address2) 153{ 154 if (!mBuffer) 155 return 0; 156 157 S32 dataaddress1 = address1 + 2*LSCRIPTDataSize[LST_INTEGER] + 1; 158 S32 dataaddress2 = address2 + 2*LSCRIPTDataSize[LST_INTEGER] + 1; 159 160 S32 toffset1 = dataaddress1; 161 safe_heap_bytestream_count_char(mBuffer, toffset1); 162 163 S32 toffset2 = dataaddress2; 164 safe_heap_bytestream_count_char(mBuffer, toffset2); 165 166 // calculate new string size 167 S32 size1 = toffset1 - dataaddress1; 168 S32 size2 = toffset2 - dataaddress2; 169 S32 newbuffer = size1 + size2 - 1; 170 171 char *temp = new char[newbuffer]; 172 173 // get the strings 174 bytestream2char(temp, mBuffer, dataaddress1); 175 bytestream2char(temp + size1 - 1, mBuffer, dataaddress2); 176 177 decreaseRefCount(address1); 178 decreaseRefCount(address2); 179 180 S32 retaddress = addData(temp); 181 182 return retaddress; 183} 184 185S32 LLScriptHeapRunTime::cmpStrings(S32 address1, S32 address2) 186{ 187 if (!mBuffer) 188 return 0; 189 190 S32 dataaddress1 = address1 + 2*LSCRIPTDataSize[LST_INTEGER] + 1; 191 S32 dataaddress2 = address2 + 2*LSCRIPTDataSize[LST_INTEGER] + 1; 192 193 S32 toffset1 = dataaddress1; 194 safe_heap_bytestream_count_char(mBuffer, toffset1); 195 196 S32 toffset2 = dataaddress2; 197 safe_heap_bytestream_count_char(mBuffer, toffset2); 198 199 // calculate new string size 200 S32 size1 = toffset1 - dataaddress1; 201 S32 size2 = toffset2 - dataaddress2; 202 203 if (size1 != size2) 204 { 205 return llmin(size1, size2); 206 } 207 else 208 { 209 return strncmp((char *)(mBuffer + dataaddress1), (char *)(mBuffer + dataaddress2), size1); 210 } 211} 212 213void LLScriptHeapRunTime::removeData(S32 address) 214{ 215 if (!mBuffer) 216 return; 217 218 S32 toffset = address; 219 // read past offset (relying on function side effect 220 bytestream2integer(mBuffer, toffset); 221 222 // make sure that reference count is 0 223 integer2bytestream(mBuffer, toffset, 0); 224 // show the block as empty 225 *(mBuffer + toffset) = 0; 226 227 // now, clean up the heap 228 S32 clean = mHeapRegister; 229 S32 tclean; 230 S32 clean_offset; 231 232 S32 nclean; 233 S32 tnclean; 234 S32 next_offset; 235 236 U8 type; 237 U8 ntype; 238 239 for(;;) 240 { 241 tclean = clean; 242 clean_offset = bytestream2integer(mBuffer, tclean); 243 // is this block, empty? 244 tclean += LSCRIPTDataSize[LST_INTEGER]; 245 type = *(mBuffer + tclean); 246 247 if (!clean_offset) 248 { 249 if (!type) 250 { 251 // we're done! if our block is empty, we can pull in the HP and zero out our offset 252 set_register(mBuffer, LREG_HP, clean); 253 } 254 return; 255 } 256 257 258 if (!type) 259 { 260 // if we're empty, try to coalesce with the next one 261 nclean = clean + clean_offset; 262 tnclean = nclean; 263 next_offset = bytestream2integer(mBuffer, tnclean); 264 tnclean += LSCRIPTDataSize[LST_INTEGER]; 265 ntype = *(mBuffer + tnclean); 266 267 if (!next_offset) 268 { 269 // we're done! if our block is empty, we can pull in the HP and zero out our offset 270 tclean = clean; 271 integer2bytestream(mBuffer, tclean, 0); 272 set_register(mBuffer, LREG_HP, clean); 273 return; 274 } 275 276 if (!ntype) 277 { 278 // hooray! we can coalesce 279 tclean = clean; 280 integer2bytestream(mBuffer, tclean, clean_offset + next_offset); 281 // don't skip forward so that we can keep coalescing on next pass through the loop 282 } 283 else 284 { 285 clean += clean_offset; 286 } 287 } 288 else 289 { 290 // if not, move on to the next block 291 clean += clean_offset; 292 } 293 } 294} 295 296void LLScriptHeapRunTime::coalesce(S32 address1, S32 address2) 297{ 298 // we need to bump the base offset by the second block's 299 S32 toffset = address1; 300 S32 offset1 = bytestream2integer(mBuffer, toffset); 301 offset1 += bytestream2integer(mBuffer, address2); 302 303 integer2bytestream(mBuffer, address1, offset1); 304} 305 306void LLScriptHeapRunTime::split(S32 address1, S32 size) 307{ 308 S32 toffset = address1; 309 S32 oldoffset = bytestream2integer(mBuffer, toffset); 310 311 // add new offset and zero out reference count and block used 312 S32 newoffset = oldoffset - size; 313 S32 newblockpos = address1 + size; 314 315 // set new offset 316 integer2bytestream(mBuffer, newblockpos, newoffset); 317 // zero out reference count 318 integer2bytestream(mBuffer, newblockpos, 0); 319 // mark as empty 320 *(mBuffer + newblockpos) = 0; 321 322 // now, change the offset of the original block 323 integer2bytestream(mBuffer, address1, size + HEAP_BLOCK_HEADER_SIZE); 324} 325 326/* 327 328 For reference count changes, strings are easy. For lists, we'll need to go through the lists reducing 329 the reference counts for any included strings and lists 330 331 */ 332 333void LLScriptHeapRunTime::increaseRefCount(S32 address) 334{ 335 if (!mBuffer) 336 return; 337 338 if (!address) 339 { 340 // unused temp string entry 341 return; 342 } 343 344 // get current reference count 345 S32 toffset = address + 4; 346 S32 count = bytestream2integer(mBuffer, toffset); 347 348 count++; 349 350 if (mbPrint) 351 printf("0x%X inc ref count %d\n", address - mHeapRegister, count); 352 353 // see which type it is 354 U8 type = *(mBuffer + toffset); 355 356 if (type == LSCRIPTTypeByte[LST_STRING]) 357 { 358 toffset = address + 4; 359 integer2bytestream(mBuffer, toffset, count); 360 } 361 // TO DO: put list stuff here! 362 else 363 { 364 set_fault(mBuffer, LSRF_HEAP_ERROR); 365 } 366} 367 368void LLScriptHeapRunTime::decreaseRefCount(S32 address) 369{ 370 if (!mBuffer) 371 return; 372 373 if (!address) 374 { 375 // unused temp string entry 376 return; 377 } 378 379 // get offset 380 S32 toffset = address; 381 // read past offset (rely on function side effect) 382 bytestream2integer(mBuffer, toffset); 383 384 // get current reference count 385 S32 count = bytestream2integer(mBuffer, toffset); 386 387 // see which type it is 388 U8 type = *(mBuffer + toffset); 389 390 if (type == LSCRIPTTypeByte[LST_STRING]) 391 { 392 count--; 393 394 if (mbPrint) 395 printf("0x%X dec ref count %d\n", address - mHeapRegister, count); 396 397 toffset = address + 4; 398 integer2bytestream(mBuffer, toffset, count); 399 if (!count) 400 { 401 // we can blow this one away 402 removeData(address); 403 } 404 } 405 // TO DO: put list stuff here! 406 else 407 { 408 set_fault(mBuffer, LSRF_HEAP_ERROR); 409 } 410} 411 412// if we're going to assign a variable, we need to decrement the reference count of what we were pointing at (if anything) 413void LLScriptHeapRunTime::releaseLocal(S32 address) 414{ 415 S32 hr = get_register(mBuffer, LREG_HR); 416 address = lscript_local_get(mBuffer, address); 417 if ( (address >= hr) 418 &&(address < hr + get_register(mBuffer, LREG_HP))) 419 { 420 decreaseRefCount(address); 421 } 422} 423 424void LLScriptHeapRunTime::releaseGlobal(S32 address) 425{ 426 // NOTA BENE: Global strings are referenced relative to the HR while local strings aren't 427 S32 hr = get_register(mBuffer, LREG_HR); 428 address = lscript_global_get(mBuffer, address) + hr; 429 if ( (address >= hr) 430 &&(address < hr + get_register(mBuffer, LREG_HP))) 431 { 432 decreaseRefCount(address); 433 } 434} 435 436 437// we know the following function has "unreachable code" 438// don't remind us every friggin' time we compile. . . 439 440#if defined(_MSC_VER) 441# pragma warning(disable: 4702) // unreachable code 442#endif 443 444S32 LLScriptHeapRunTime::findOpenBlock(S32 size) 445{ 446 S32 offset; 447 S32 toffset; 448 U8 blocktype; 449 450 while(1) 451 { 452 if (mCurrentPosition + size >= mStackPointer) 453 { 454 set_fault(mBuffer, LSRF_STACK_HEAP_COLLISION); 455 mCurrentPosition = 0; 456 } 457 458 toffset = mCurrentPosition; 459 offset = bytestream2integer(mBuffer, toffset); 460 if (!offset) 461 { 462 // we've reached the end of Heap, return this location if we'll fit 463 // do we need to coalesce with last empty space? 464 if (mLastEmpty) 465 { 466 // ok, that everything from mLastEmpty to us is empty, so we don't need a block 467 // zero out the last empty's offset and return it 468 mCurrentPosition = mLastEmpty; 469 integer2bytestream(mBuffer, mLastEmpty, 0); 470 mLastEmpty = 0; 471 } 472 // now, zero out next offset to prevent "trouble" 473 offset = mCurrentPosition + size; 474 integer2bytestream(mBuffer, offset, 0); 475 476 // set HP to appropriate value 477 set_register(mBuffer, LREG_HP, mCurrentPosition + size); 478 return size; 479 } 480 481 // ok, is this slot empty? 482 toffset += LSCRIPTDataSize[LST_INTEGER]; 483 484 blocktype = *(mBuffer + toffset++); 485 486 if (!blocktype) 487 { 488 // Empty block, do we need to coalesce? 489 if (mLastEmpty) 490 { 491 coalesce(mLastEmpty, mCurrentPosition); 492 mCurrentPosition = mLastEmpty; 493 toffset = mCurrentPosition; 494 offset = bytestream2integer(mBuffer, toffset); 495 } 496 497 // do we fit in this block? 498 if (offset >= size) 499 { 500 // do we need to split the block? (only split if splitting will leave > HEAP_BLOCK_SPLIT_THRESHOLD bytes of free space) 501 if (offset - HEAP_BLOCK_SPLIT_THRESHOLD >= size) 502 { 503 split(mCurrentPosition, size); 504 return size; 505 } 506 else 507 return offset; 508 } 509 } 510 // nothing found, keep looking 511 mCurrentPosition += offset; 512 } 513 // fake return to prevent warnings 514 mCurrentPosition = 0; 515 return 0; 516} 517 518LLScriptHeapRunTime gRunTime; 519#endif