PageRenderTime 13ms CodeModel.GetById 2ms app.highlight 5ms RepoModel.GetById 1ms app.codeStats 0ms

/indra/lscript/lscript_execute/lscript_heapruntime.cpp

https://bitbucket.org/lindenlab/viewer-beta/
C++ | 519 lines | 292 code | 77 blank | 150 comment | 36 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
 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