/indra/lscript/lscript_execute/lscript_heapruntime.cpp

https://bitbucket.org/lindenlab/viewer-beta/ · C++ · 519 lines · 292 code · 77 blank · 150 comment · 37 complexity · f007ecc8088a44f610857249ed6ee12e MD5 · raw file

  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. #if 0
  27. #include "linden_common.h"
  28. #include "lscript_heapruntime.h"
  29. #include "lscript_execute.h"
  30. /*
  31. String Heap Format
  32. Byte Description
  33. 0x0 - 0xnn Single byte string including null terminator
  34. List Heap Format
  35. Byte Description
  36. 0x0 Next Entry Type
  37. 0: End of list
  38. 1: Integer
  39. 2: Floating point
  40. 3: String
  41. 4: Vector
  42. 5: Quaternion
  43. 6: List
  44. 0x1 - 0x4 Integer, Floating Point, String Address, List Address
  45. or
  46. 0x1 - 0xd Vector
  47. or
  48. 0x1 - 0x11 Quaternion
  49. . . .
  50. Heap Block Format
  51. Byte Description
  52. 0x0 - 0x3 Offset to Next Block
  53. 0x4 - 0x7 Object Reference Count
  54. 0x8 Block Type
  55. 0: Empty
  56. 3: String
  57. 6: List
  58. 0x9 - 0xM Object Data
  59. Heap Management
  60. Adding Data
  61. 1) Set last empty spot to zero.
  62. 2) Go to start of the heap (HR).
  63. 3) Get next 4 bytes of offset.
  64. 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.
  65. 5) Skip 4 bytes.
  66. 6) Get next 1 byte of entry type.
  67. 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.
  68. 8) Skip forward by offset and go to step 3.
  69. 9) If the spot is empty, check to see if the size needed == offset - 9.
  70. 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.
  71. 11) If size needed < offset - 9 then we can stick in data and add in an empty block.
  72. 12) Otherwise, we need to keep looking. Go to step 3.
  73. Increasing reference counts
  74. Decreasing reference counts
  75. 1) Set entry type to 0.
  76. 2) If offset is non-zero and the next entry is empty, coalesce. Go to step 2.
  77. What increases reference count:
  78. Initial creation sets reference count to 1.
  79. Storing the reference increases reference count by 1.
  80. Pushing the reference increases reference count by 1.
  81. Duplicating the reference increases reference count by 1.
  82. What decreases the reference count:
  83. Popping the reference decreases reference count by 1.
  84. */
  85. LLScriptHeapRunTime::LLScriptHeapRunTime()
  86. : mLastEmpty(0), mBuffer(NULL), mCurrentPosition(0), mStackPointer(0), mHeapRegister(0), mbPrint(FALSE)
  87. {
  88. }
  89. LLScriptHeapRunTime::~LLScriptHeapRunTime()
  90. {
  91. }
  92. S32 LLScriptHeapRunTime::addData(char *string)
  93. {
  94. if (!mBuffer)
  95. return 0;
  96. S32 size = strlen(string) + 1;
  97. S32 block_offset = findOpenBlock(size + HEAP_BLOCK_HEADER_SIZE);
  98. if (mCurrentPosition)
  99. {
  100. S32 offset = mCurrentPosition;
  101. if (offset + block_offset + HEAP_BLOCK_HEADER_SIZE + LSCRIPTDataSize[LST_INTEGER] >= mStackPointer)
  102. {
  103. set_fault(mBuffer, LSRF_STACK_HEAP_COLLISION);
  104. return 0;
  105. }
  106. // cool, we've found a spot!
  107. // set offset
  108. integer2bytestream(mBuffer, offset, block_offset);
  109. // set reference count
  110. integer2bytestream(mBuffer, offset, 1);
  111. // set type
  112. *(mBuffer + offset++) = LSCRIPTTypeByte[LST_STRING];
  113. // plug in data
  114. char2bytestream(mBuffer, offset, string);
  115. if (mbPrint)
  116. printf("0x%X created ref count %d\n", mCurrentPosition - mHeapRegister, 1);
  117. // now, zero out next offset to prevent "trouble"
  118. // offset = mCurrentPosition + size + HEAP_BLOCK_HEADER_SIZE;
  119. // integer2bytestream(mBuffer, offset, 0);
  120. }
  121. return mCurrentPosition;
  122. }
  123. S32 LLScriptHeapRunTime::addData(U8 *list)
  124. {
  125. if (!mBuffer)
  126. return 0;
  127. return 0;
  128. }
  129. S32 LLScriptHeapRunTime::catStrings(S32 address1, S32 address2)
  130. {
  131. if (!mBuffer)
  132. return 0;
  133. S32 dataaddress1 = address1 + 2*LSCRIPTDataSize[LST_INTEGER] + 1;
  134. S32 dataaddress2 = address2 + 2*LSCRIPTDataSize[LST_INTEGER] + 1;
  135. S32 toffset1 = dataaddress1;
  136. safe_heap_bytestream_count_char(mBuffer, toffset1);
  137. S32 toffset2 = dataaddress2;
  138. safe_heap_bytestream_count_char(mBuffer, toffset2);
  139. // calculate new string size
  140. S32 size1 = toffset1 - dataaddress1;
  141. S32 size2 = toffset2 - dataaddress2;
  142. S32 newbuffer = size1 + size2 - 1;
  143. char *temp = new char[newbuffer];
  144. // get the strings
  145. bytestream2char(temp, mBuffer, dataaddress1);
  146. bytestream2char(temp + size1 - 1, mBuffer, dataaddress2);
  147. decreaseRefCount(address1);
  148. decreaseRefCount(address2);
  149. S32 retaddress = addData(temp);
  150. return retaddress;
  151. }
  152. S32 LLScriptHeapRunTime::cmpStrings(S32 address1, S32 address2)
  153. {
  154. if (!mBuffer)
  155. return 0;
  156. S32 dataaddress1 = address1 + 2*LSCRIPTDataSize[LST_INTEGER] + 1;
  157. S32 dataaddress2 = address2 + 2*LSCRIPTDataSize[LST_INTEGER] + 1;
  158. S32 toffset1 = dataaddress1;
  159. safe_heap_bytestream_count_char(mBuffer, toffset1);
  160. S32 toffset2 = dataaddress2;
  161. safe_heap_bytestream_count_char(mBuffer, toffset2);
  162. // calculate new string size
  163. S32 size1 = toffset1 - dataaddress1;
  164. S32 size2 = toffset2 - dataaddress2;
  165. if (size1 != size2)
  166. {
  167. return llmin(size1, size2);
  168. }
  169. else
  170. {
  171. return strncmp((char *)(mBuffer + dataaddress1), (char *)(mBuffer + dataaddress2), size1);
  172. }
  173. }
  174. void LLScriptHeapRunTime::removeData(S32 address)
  175. {
  176. if (!mBuffer)
  177. return;
  178. S32 toffset = address;
  179. // read past offset (relying on function side effect
  180. bytestream2integer(mBuffer, toffset);
  181. // make sure that reference count is 0
  182. integer2bytestream(mBuffer, toffset, 0);
  183. // show the block as empty
  184. *(mBuffer + toffset) = 0;
  185. // now, clean up the heap
  186. S32 clean = mHeapRegister;
  187. S32 tclean;
  188. S32 clean_offset;
  189. S32 nclean;
  190. S32 tnclean;
  191. S32 next_offset;
  192. U8 type;
  193. U8 ntype;
  194. for(;;)
  195. {
  196. tclean = clean;
  197. clean_offset = bytestream2integer(mBuffer, tclean);
  198. // is this block, empty?
  199. tclean += LSCRIPTDataSize[LST_INTEGER];
  200. type = *(mBuffer + tclean);
  201. if (!clean_offset)
  202. {
  203. if (!type)
  204. {
  205. // we're done! if our block is empty, we can pull in the HP and zero out our offset
  206. set_register(mBuffer, LREG_HP, clean);
  207. }
  208. return;
  209. }
  210. if (!type)
  211. {
  212. // if we're empty, try to coalesce with the next one
  213. nclean = clean + clean_offset;
  214. tnclean = nclean;
  215. next_offset = bytestream2integer(mBuffer, tnclean);
  216. tnclean += LSCRIPTDataSize[LST_INTEGER];
  217. ntype = *(mBuffer + tnclean);
  218. if (!next_offset)
  219. {
  220. // we're done! if our block is empty, we can pull in the HP and zero out our offset
  221. tclean = clean;
  222. integer2bytestream(mBuffer, tclean, 0);
  223. set_register(mBuffer, LREG_HP, clean);
  224. return;
  225. }
  226. if (!ntype)
  227. {
  228. // hooray! we can coalesce
  229. tclean = clean;
  230. integer2bytestream(mBuffer, tclean, clean_offset + next_offset);
  231. // don't skip forward so that we can keep coalescing on next pass through the loop
  232. }
  233. else
  234. {
  235. clean += clean_offset;
  236. }
  237. }
  238. else
  239. {
  240. // if not, move on to the next block
  241. clean += clean_offset;
  242. }
  243. }
  244. }
  245. void LLScriptHeapRunTime::coalesce(S32 address1, S32 address2)
  246. {
  247. // we need to bump the base offset by the second block's
  248. S32 toffset = address1;
  249. S32 offset1 = bytestream2integer(mBuffer, toffset);
  250. offset1 += bytestream2integer(mBuffer, address2);
  251. integer2bytestream(mBuffer, address1, offset1);
  252. }
  253. void LLScriptHeapRunTime::split(S32 address1, S32 size)
  254. {
  255. S32 toffset = address1;
  256. S32 oldoffset = bytestream2integer(mBuffer, toffset);
  257. // add new offset and zero out reference count and block used
  258. S32 newoffset = oldoffset - size;
  259. S32 newblockpos = address1 + size;
  260. // set new offset
  261. integer2bytestream(mBuffer, newblockpos, newoffset);
  262. // zero out reference count
  263. integer2bytestream(mBuffer, newblockpos, 0);
  264. // mark as empty
  265. *(mBuffer + newblockpos) = 0;
  266. // now, change the offset of the original block
  267. integer2bytestream(mBuffer, address1, size + HEAP_BLOCK_HEADER_SIZE);
  268. }
  269. /*
  270. For reference count changes, strings are easy. For lists, we'll need to go through the lists reducing
  271. the reference counts for any included strings and lists
  272. */
  273. void LLScriptHeapRunTime::increaseRefCount(S32 address)
  274. {
  275. if (!mBuffer)
  276. return;
  277. if (!address)
  278. {
  279. // unused temp string entry
  280. return;
  281. }
  282. // get current reference count
  283. S32 toffset = address + 4;
  284. S32 count = bytestream2integer(mBuffer, toffset);
  285. count++;
  286. if (mbPrint)
  287. printf("0x%X inc ref count %d\n", address - mHeapRegister, count);
  288. // see which type it is
  289. U8 type = *(mBuffer + toffset);
  290. if (type == LSCRIPTTypeByte[LST_STRING])
  291. {
  292. toffset = address + 4;
  293. integer2bytestream(mBuffer, toffset, count);
  294. }
  295. // TO DO: put list stuff here!
  296. else
  297. {
  298. set_fault(mBuffer, LSRF_HEAP_ERROR);
  299. }
  300. }
  301. void LLScriptHeapRunTime::decreaseRefCount(S32 address)
  302. {
  303. if (!mBuffer)
  304. return;
  305. if (!address)
  306. {
  307. // unused temp string entry
  308. return;
  309. }
  310. // get offset
  311. S32 toffset = address;
  312. // read past offset (rely on function side effect)
  313. bytestream2integer(mBuffer, toffset);
  314. // get current reference count
  315. S32 count = bytestream2integer(mBuffer, toffset);
  316. // see which type it is
  317. U8 type = *(mBuffer + toffset);
  318. if (type == LSCRIPTTypeByte[LST_STRING])
  319. {
  320. count--;
  321. if (mbPrint)
  322. printf("0x%X dec ref count %d\n", address - mHeapRegister, count);
  323. toffset = address + 4;
  324. integer2bytestream(mBuffer, toffset, count);
  325. if (!count)
  326. {
  327. // we can blow this one away
  328. removeData(address);
  329. }
  330. }
  331. // TO DO: put list stuff here!
  332. else
  333. {
  334. set_fault(mBuffer, LSRF_HEAP_ERROR);
  335. }
  336. }
  337. // if we're going to assign a variable, we need to decrement the reference count of what we were pointing at (if anything)
  338. void LLScriptHeapRunTime::releaseLocal(S32 address)
  339. {
  340. S32 hr = get_register(mBuffer, LREG_HR);
  341. address = lscript_local_get(mBuffer, address);
  342. if ( (address >= hr)
  343. &&(address < hr + get_register(mBuffer, LREG_HP)))
  344. {
  345. decreaseRefCount(address);
  346. }
  347. }
  348. void LLScriptHeapRunTime::releaseGlobal(S32 address)
  349. {
  350. // NOTA BENE: Global strings are referenced relative to the HR while local strings aren't
  351. S32 hr = get_register(mBuffer, LREG_HR);
  352. address = lscript_global_get(mBuffer, address) + hr;
  353. if ( (address >= hr)
  354. &&(address < hr + get_register(mBuffer, LREG_HP)))
  355. {
  356. decreaseRefCount(address);
  357. }
  358. }
  359. // we know the following function has "unreachable code"
  360. // don't remind us every friggin' time we compile. . .
  361. #if defined(_MSC_VER)
  362. # pragma warning(disable: 4702) // unreachable code
  363. #endif
  364. S32 LLScriptHeapRunTime::findOpenBlock(S32 size)
  365. {
  366. S32 offset;
  367. S32 toffset;
  368. U8 blocktype;
  369. while(1)
  370. {
  371. if (mCurrentPosition + size >= mStackPointer)
  372. {
  373. set_fault(mBuffer, LSRF_STACK_HEAP_COLLISION);
  374. mCurrentPosition = 0;
  375. }
  376. toffset = mCurrentPosition;
  377. offset = bytestream2integer(mBuffer, toffset);
  378. if (!offset)
  379. {
  380. // we've reached the end of Heap, return this location if we'll fit
  381. // do we need to coalesce with last empty space?
  382. if (mLastEmpty)
  383. {
  384. // ok, that everything from mLastEmpty to us is empty, so we don't need a block
  385. // zero out the last empty's offset and return it
  386. mCurrentPosition = mLastEmpty;
  387. integer2bytestream(mBuffer, mLastEmpty, 0);
  388. mLastEmpty = 0;
  389. }
  390. // now, zero out next offset to prevent "trouble"
  391. offset = mCurrentPosition + size;
  392. integer2bytestream(mBuffer, offset, 0);
  393. // set HP to appropriate value
  394. set_register(mBuffer, LREG_HP, mCurrentPosition + size);
  395. return size;
  396. }
  397. // ok, is this slot empty?
  398. toffset += LSCRIPTDataSize[LST_INTEGER];
  399. blocktype = *(mBuffer + toffset++);
  400. if (!blocktype)
  401. {
  402. // Empty block, do we need to coalesce?
  403. if (mLastEmpty)
  404. {
  405. coalesce(mLastEmpty, mCurrentPosition);
  406. mCurrentPosition = mLastEmpty;
  407. toffset = mCurrentPosition;
  408. offset = bytestream2integer(mBuffer, toffset);
  409. }
  410. // do we fit in this block?
  411. if (offset >= size)
  412. {
  413. // do we need to split the block? (only split if splitting will leave > HEAP_BLOCK_SPLIT_THRESHOLD bytes of free space)
  414. if (offset - HEAP_BLOCK_SPLIT_THRESHOLD >= size)
  415. {
  416. split(mCurrentPosition, size);
  417. return size;
  418. }
  419. else
  420. return offset;
  421. }
  422. }
  423. // nothing found, keep looking
  424. mCurrentPosition += offset;
  425. }
  426. // fake return to prevent warnings
  427. mCurrentPosition = 0;
  428. return 0;
  429. }
  430. LLScriptHeapRunTime gRunTime;
  431. #endif