/** 
 * @file lscript_alloc.cpp
 * @brief general heap management for scripting system
 *
 * $LicenseInfo:firstyear=2002&license=viewergpl$
 * 
 * Copyright (c) 2002-2008, Linden Research, Inc.
 * 
 * Second Life Viewer Source Code
 * The source code in this file ("Source Code") is provided by Linden Lab
 * to you under the terms of the GNU General Public License, version 2.0
 * ("GPL"), unless you have obtained a separate licensing agreement
 * ("Other License"), formally executed by you and Linden Lab.  Terms of
 * the GPL can be found in doc/GPL-license.txt in this distribution, or
 * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
 * 
 * There are special exceptions to the terms and conditions of the GPL as
 * it is applied to this Source Code. View the full text of the exception
 * in the file doc/FLOSS-exception.txt in this software distribution, or
 * online at http://secondlifegrid.net/programs/open_source/licensing/flossexception
 * 
 * By copying, modifying or distributing this software, you acknowledge
 * that you have read and understood your obligations described above,
 * and agree to abide by those obligations.
 * 
 * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
 * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
 * COMPLETENESS OR PERFORMANCE.
 * $/LicenseInfo$
 */

// #define at top of file accelerates gcc compiles
// Under gcc 2.9, the manual is unclear if comments can appear above #ifndef
// Under gcc 3, the manual explicitly states comments can appear above the #ifndef

#include "linden_common.h"
#include "lscript_alloc.h"
#include "llrand.h"

// supported data types

//	basic types
//	integer			4 bytes of integer data
//	float			4 bytes of float data
//	string data		null terminated 1 byte string
//	key data		null terminated 1 byte string
//	vector data		12 bytes of 3 floats
//	quaternion data	16 bytes of 4 floats

//	list type
//	list data		4 bytes of number of entries followed by pointer

//	string pointer		4 bytes of address of string data on the heap (only used in list data)
//  key pointer			4 bytes of address of key data on the heap (only used in list data)

// heap format
// 
// 4 byte offset to next block (in bytes)
// 1 byte of type of variable or empty
// 2 bytes of reference count
// nn bytes of data

void reset_hp_to_safe_spot(const U8 *buffer)
{
	set_register((U8 *)buffer, LREG_HP, TOP_OF_MEMORY);
}

// create a heap from the HR to TM
BOOL lsa_create_heap(U8 *heap_start, S32 size)
{
	LLScriptAllocEntry entry(size, LST_NULL);

	S32 position = 0;

	alloc_entry2bytestream(heap_start, position, entry);

	return TRUE;
}

S32 lsa_heap_top(U8 *heap_start, S32 maxtop)
{
	S32 offset = 0;
	LLScriptAllocEntry entry;
	bytestream2alloc_entry(entry, heap_start, offset);

	while (offset + entry.mSize < maxtop)
	{
		offset += entry.mSize;
		bytestream2alloc_entry(entry, heap_start, offset);
	}
	return offset + entry.mSize;
}


// adding to heap
//	if block is empty
//		if block is at least block size + 4 larger than data
//			split block
//			insert data into first part
//			return address
//		else
//			insert data into block
//			return address
//	else
//		if next block is >= SP 
//			set Stack-Heap collision
//			return NULL
//		if next block is empty
//			merge next block with current block
//			go to start of algorithm
//		else
//			move to next block
//			go to start of algorithm

S32 lsa_heap_add_data(U8 *buffer, LLScriptLibData *data, S32 heapsize, BOOL b_delete)
{
	if (get_register(buffer, LREG_FR))
		return 1;
	LLScriptAllocEntry entry, nextentry;
	S32 hr = get_register(buffer, LREG_HR);
	S32 hp = get_register(buffer, LREG_HP);
	S32 current_offset, next_offset, offset = hr;
	S32 size = 0;

	switch(data->mType)
	{
	case LST_INTEGER:
		size = 4;
		break;
	case LST_FLOATINGPOINT:
		size = 4;
		break;
	case LST_KEY:
	        // NOTE: babbage: defensive as some library calls set data to NULL
	        size = data->mKey ? (S32)strlen(data->mKey) + 1 : 1; /*Flawfinder: ignore*/
		break;
	case LST_STRING:
                // NOTE: babbage: defensive as some library calls set data to NULL
            	size = data->mString ? (S32)strlen(data->mString) + 1 : 1; /*Flawfinder: ignore*/
		break;
	case LST_LIST:
		//	list data		4 bytes of number of entries followed by number of pointer
		size = 4 + 4*data->getListLength();
		if (data->checkForMultipleLists())
		{
			set_fault(buffer, LSRF_NESTING_LISTS);
		}
		break;
	case LST_VECTOR:
		size = 12;
		break;
	case LST_QUATERNION:
		size = 16;
		break;
	default:
		break;
	}

	current_offset = offset;
	bytestream2alloc_entry(entry, buffer, offset);

	do
	{
		hp = get_register(buffer, LREG_HP);
		if (!entry.mType)
		{
			if (entry.mSize >= size + SIZEOF_SCRIPT_ALLOC_ENTRY + 4)
			{
				offset = current_offset;
				lsa_split_block(buffer, offset, size, entry);
				entry.mType = data->mType;
				entry.mSize = size;
				entry.mReferenceCount = 1;
				offset = current_offset;
				alloc_entry2bytestream(buffer, offset, entry);
				lsa_insert_data(buffer, offset, data, entry, heapsize);
				hp = get_register(buffer, LREG_HP);
				S32 new_hp = current_offset + size + 2*SIZEOF_SCRIPT_ALLOC_ENTRY;
				if (new_hp >= hr + heapsize)
				{
					break;
				}
				if (new_hp > hp)
				{
					set_register(buffer, LREG_HP, new_hp);
					hp = get_register(buffer, LREG_HP);
				}
				if (b_delete)
					delete data;
	// this bit of nastiness is to get around that code paths to local variables can result in lack of initialization
	// and function clean up of ref counts isn't based on scope (a mistake, I know)
				if (current_offset <= hp)
					return current_offset - hr + 1;
				else
					return hp - hr + 1;
			}
			else if (entry.mSize >= size)
			{
				entry.mType = data->mType;
				entry.mReferenceCount = 1;
				offset = current_offset;
				alloc_entry2bytestream(buffer, offset, entry);
				lsa_insert_data(buffer, offset, data, entry, heapsize);
				hp = get_register(buffer, LREG_HP);
				if (b_delete)
					delete data;
	// this bit of nastiness is to get around that code paths to local variables can result in lack of initialization
	// and function clean up of ref counts isn't based on scope (a mistake, I know)
				return current_offset - hr + 1;
			}
		}
		offset += entry.mSize;
		if (offset < hr + heapsize)
		{
			next_offset = offset;
			bytestream2alloc_entry(nextentry, buffer, offset);
			if (!nextentry.mType && !entry.mType)
			{
				entry.mSize += nextentry.mSize + SIZEOF_SCRIPT_ALLOC_ENTRY;
				offset = current_offset;
				alloc_entry2bytestream(buffer, offset, entry);
			}
			else
			{
				current_offset = next_offset;
				entry = nextentry;
			}

			// this works whether we are bumping out or coming in
			S32 new_hp = current_offset + size + 2*SIZEOF_SCRIPT_ALLOC_ENTRY;

			// make sure we aren't about to be stupid
			if (new_hp >= hr + heapsize)
			{
				break;
			}
			if (new_hp > hp)
			{
				set_register(buffer, LREG_HP, new_hp);
				hp = get_register(buffer, LREG_HP);
			}
		}
		else
		{
			break;
		}
	} while (1);
	set_fault(buffer, LSRF_STACK_HEAP_COLLISION);
	reset_hp_to_safe_spot(buffer);
	if (b_delete)
		delete data;
	return 0;
}

// split block
//	set offset to point to new block
//	set offset of new block to point to original offset - block size - data size
//	set new block to empty
//	set new block reference count to 0
void lsa_split_block(U8 *buffer, S32 &offset, S32 size, LLScriptAllocEntry &entry)
{
	if (get_register(buffer, LREG_FR))
		return;
	LLScriptAllocEntry newentry;

	newentry.mSize = entry.mSize - SIZEOF_SCRIPT_ALLOC_ENTRY - size;
	entry.mSize -= newentry.mSize + SIZEOF_SCRIPT_ALLOC_ENTRY;

	alloc_entry2bytestream(buffer, offset, entry);
	S32 orig_offset = offset + size;
	alloc_entry2bytestream(buffer, orig_offset, newentry);
}

// insert data
//	if data is non-list type
//		set type to basic type, set reference count to 1, copy data, return address
//	else
//		set type to list data type, set reference count to 1
//		save length of list
//		for each list entry
//			insert data
//			return address

void lsa_insert_data(U8 *buffer, S32 &offset, LLScriptLibData *data, LLScriptAllocEntry &entry, S32 heapsize)
{
	if (get_register(buffer, LREG_FR))
		return;
	if (data->mType != LST_LIST)
	{
		switch(data->mType)
		{
		case LST_INTEGER:
			integer2bytestream(buffer, offset, data->mInteger);
			break;
		case LST_FLOATINGPOINT:
			float2bytestream(buffer, offset, data->mFP);
			break;
		case LST_KEY:
		        char2bytestream(buffer, offset, data->mKey ? data->mKey : "");
			break;
		case LST_STRING:
		        char2bytestream(buffer, offset, data->mString ? data->mString : "");
			break;
		case LST_VECTOR:
			vector2bytestream(buffer, offset, data->mVec);
			break;
		case LST_QUATERNION:
			quaternion2bytestream(buffer, offset, data->mQuat);
			break;
		default:
			break;
		}
	}
	else
	{
		// store length of list
		integer2bytestream(buffer, offset, data->getListLength());
		data = data->mListp;
		while(data)
		{
			// store entry and then store address if valid
			S32 address = lsa_heap_add_data(buffer, data, heapsize, FALSE);
			integer2bytestream(buffer, offset, address);
			data = data->mListp;
		}
	}
}

S32 lsa_create_data_block(U8 **buffer, LLScriptLibData *data, S32 base_offset)
{
	S32 offset = 0;
	S32 size = 0;

	LLScriptAllocEntry entry;

	if (!data)
	{
		entry.mType = LST_NULL;
		entry.mReferenceCount = 0;
		entry.mSize = MAX_HEAP_SIZE;
		size = SIZEOF_SCRIPT_ALLOC_ENTRY;
		*buffer = new U8[size];
		alloc_entry2bytestream(*buffer, offset, entry);
		return size;
	}

	entry.mType = data->mType;
	entry.mReferenceCount = 1;

	if (data->mType != LST_LIST)
	{
		if (  (data->mType != LST_STRING)
			&&(data->mType != LST_KEY))
		{
			size = LSCRIPTDataSize[data->mType];
		}
		else
		{
			if (data->mType == LST_STRING)
			{
				if (data->mString)
				{
					size = (S32)strlen(data->mString) + 1;		/*Flawfinder: ignore*/
				}
				else
				{
					size = 1;
				}
			}
			if (data->mType == LST_KEY)
			{
				if (data->mKey)
				{
					size = (S32)strlen(data->mKey) + 1;		/*Flawfinder: ignore*/
				}
				else
				{
					size = 1;
				}
			}
		}
		entry.mSize = size;
		size += SIZEOF_SCRIPT_ALLOC_ENTRY;
		*buffer = new U8[size];
		alloc_entry2bytestream(*buffer, offset, entry);

		switch(data->mType)
		{
		case LST_INTEGER:
			integer2bytestream(*buffer, offset, data->mInteger);
			break;
		case LST_FLOATINGPOINT:
			float2bytestream(*buffer, offset, data->mFP);
			break;
		case LST_KEY:
			if (data->mKey)
				char2bytestream(*buffer, offset, data->mKey);
			else
				byte2bytestream(*buffer, offset, 0);
			break;
		case LST_STRING:
			if (data->mString)
				char2bytestream(*buffer, offset, data->mString);
			else
				byte2bytestream(*buffer, offset, 0);
			break;
		case LST_VECTOR:
			vector2bytestream(*buffer, offset, data->mVec);
			break;
		case LST_QUATERNION:
			quaternion2bytestream(*buffer, offset, data->mQuat);
			break;
		default:
			break;
		}
	}
	else
	{
		U8 *listbuf;
		S32 length = data->getListLength();
		size = 4 * length + 4;
		entry.mSize = size;

		size += SIZEOF_SCRIPT_ALLOC_ENTRY;
		*buffer = new U8[size];

		alloc_entry2bytestream(*buffer, offset, entry);
		// store length of list
		integer2bytestream(*buffer, offset, length);
		data = data->mListp;
		while(data)
		{
	// this bit of nastiness is to get around that code paths to local variables can result in lack of initialization
	// and function clean up of ref counts isn't based on scope (a mistake, I know)
			integer2bytestream(*buffer, offset, size + base_offset + 1);

			S32 listsize = lsa_create_data_block(&listbuf, data, base_offset + size);
			if (listsize)
			{
				U8 *tbuff = new U8[size + listsize];
				if (tbuff == NULL)
				{
					llerrs << "Memory Allocation Failed" << llendl;
				}
				memcpy(tbuff, *buffer, size);	/*Flawfinder: ignore*/
				memcpy(tbuff + size, listbuf, listsize);		/*Flawfinder: ignore*/
				size += listsize;
				delete [] *buffer;
				delete [] listbuf;
				*buffer = tbuff;
			}
			data = data->mListp;
		}
	}
	return size;
}

// increase reference count
//	increase reference count by 1

void lsa_increase_ref_count(U8 *buffer, S32 offset)
{
	if (get_register(buffer, LREG_FR))
		return;
	// this bit of nastiness is to get around that code paths to local variables can result in lack of initialization
	// and function clean up of ref counts isn't based on scope (a mistake, I know)
	offset += get_register(buffer, LREG_HR) - 1;
	if (  (offset < get_register(buffer, LREG_HR))
		||(offset >= get_register(buffer, LREG_HP)))
	{
		set_fault(buffer, LSRF_BOUND_CHECK_ERROR);
		return;
	}
	S32 orig_offset = offset;
	LLScriptAllocEntry entry;
	bytestream2alloc_entry(entry, buffer, offset);

	entry.mReferenceCount++;

	alloc_entry2bytestream(buffer, orig_offset, entry);
}

// decrease reference count
//		decrease reference count by 1
//		if reference count == 0
//			set type to empty

void lsa_decrease_ref_count(U8 *buffer, S32 offset)
{
	if (get_register(buffer, LREG_FR))
		return;
	// this bit of nastiness is to get around that code paths to local variables can result in lack of initialization
	// and function clean up of ref counts isn't based on scope (a mistake, I know)
	offset += get_register(buffer, LREG_HR) - 1;
	if (  (offset < get_register(buffer, LREG_HR))
		||(offset >= get_register(buffer, LREG_HP)))
	{
		set_fault(buffer, LSRF_BOUND_CHECK_ERROR);
		return;
	}
	S32 orig_offset = offset;
	LLScriptAllocEntry entry;
	bytestream2alloc_entry(entry, buffer, offset);

	entry.mReferenceCount--;

	if (entry.mReferenceCount < 0)
	{
		entry.mReferenceCount = 0;
		set_fault(buffer, LSRF_HEAP_ERROR);
	}
	else if (!entry.mReferenceCount)
	{
		if (entry.mType == LST_LIST)
		{
			S32 i, num = bytestream2integer(buffer, offset);
			for (i = 0; i < num; i++)
			{
				S32 list_offset = bytestream2integer(buffer, offset);
				lsa_decrease_ref_count(buffer, list_offset);
			}
		}
		entry.mType = LST_NULL;
	}

	alloc_entry2bytestream(buffer, orig_offset, entry);
}

char gLSAStringRead[TOP_OF_MEMORY];		/*Flawfinder: ignore*/


LLScriptLibData *lsa_get_data(U8 *buffer, S32 &offset, BOOL b_dec_ref)
{
	if (get_register(buffer, LREG_FR))
		return (new LLScriptLibData);
	S32 orig_offset = offset;
	// this bit of nastiness is to get around that code paths to local variables can result in lack of initialization
	// and function clean up of ref counts isn't based on scope (a mistake, I know)
	offset += get_register(buffer, LREG_HR) - 1;
	if (  (offset < get_register(buffer, LREG_HR))
		||(offset >= get_register(buffer, LREG_HP)))
	{
		set_fault(buffer, LSRF_BOUND_CHECK_ERROR);
		return (new LLScriptLibData);
	}
	LLScriptAllocEntry entry;
	bytestream2alloc_entry(entry, buffer, offset);

	LLScriptLibData *retval = new LLScriptLibData;

	if (!entry.mType)
	{
		set_fault(buffer, LSRF_HEAP_ERROR);
		return retval;
	}

	retval->mType = (LSCRIPTType)entry.mType;
	if (entry.mType != LST_LIST)
	{
		switch(entry.mType)
		{
		case LST_INTEGER:
			retval->mInteger = bytestream2integer(buffer, offset);
			break;
		case LST_FLOATINGPOINT:
			retval->mFP = bytestream2float(buffer, offset);
			break;
		case LST_KEY:
			bytestream2char(gLSAStringRead, buffer, offset, sizeof(gLSAStringRead)); // global sring buffer? for real? :(
			retval->mKey = new char[strlen(gLSAStringRead) + 1];		/*Flawfinder: ignore*/
			strcpy(retval->mKey, gLSAStringRead);			/*Flawfinder: ignore*/
			break;
		case LST_STRING:
			bytestream2char(gLSAStringRead, buffer, offset, sizeof(gLSAStringRead));
			retval->mString = new char[strlen(gLSAStringRead) + 1];		/*Flawfinder: ignore*/
			strcpy(retval->mString, gLSAStringRead);			/*Flawfinder: ignore*/
			break;
		case LST_VECTOR:
			bytestream2vector(retval->mVec, buffer, offset);
			break;
		case LST_QUATERNION:
			bytestream2quaternion(retval->mQuat, buffer, offset);
			break;
		default:
			break;
		}
	}
	else
	{
		// get length of list
		S32 i, length = bytestream2integer(buffer, offset);
		LLScriptLibData *tip = retval;

		for (i = 0; i < length; i++)
		{
			S32 address = bytestream2integer(buffer, offset);
			tip->mListp = lsa_get_data(buffer, address, FALSE);
			tip = tip->mListp;
		}
	}
	if (retval->checkForMultipleLists())
	{
		set_fault(buffer, LSRF_NESTING_LISTS);
	}
	if (b_dec_ref)
	{
		lsa_decrease_ref_count(buffer, orig_offset);
	}
	return retval;
}

LLScriptLibData *lsa_get_list_ptr(U8 *buffer, S32 &offset, BOOL b_dec_ref)
{
	if (get_register(buffer, LREG_FR))
		return (new LLScriptLibData);
	S32 orig_offset = offset;
	// this bit of nastiness is to get around that code paths to local variables can result in lack of initialization
	// and function clean up of ref counts isn't based on scope (a mistake, I know)
	offset += get_register(buffer, LREG_HR) - 1;
	if (  (offset < get_register(buffer, LREG_HR))
		||(offset >= get_register(buffer, LREG_HP)))
	{
		set_fault(buffer, LSRF_BOUND_CHECK_ERROR);
		return (new LLScriptLibData);
	}
	LLScriptAllocEntry entry;
	bytestream2alloc_entry(entry, buffer, offset);

	if (!entry.mType)
	{
		set_fault(buffer, LSRF_HEAP_ERROR);
		return NULL;
	}

	LLScriptLibData base, *tip = &base;

	if (entry.mType != LST_LIST)
	{
		return NULL;
	}
	else
	{
		// get length of list
		S32 i, length = bytestream2integer(buffer, offset);

		for (i = 0; i < length; i++)
		{
			S32 address = bytestream2integer(buffer, offset);
			tip->mListp = lsa_get_data(buffer, address, FALSE);
			tip = tip->mListp;
		}
	}
	if (b_dec_ref)
	{
		lsa_decrease_ref_count(buffer, orig_offset);
	}
	tip = base.mListp;
	base.mListp = NULL;
	return tip;
}

S32 lsa_cat_strings(U8 *buffer, S32 offset1, S32 offset2, S32 heapsize)
{
	if (get_register(buffer, LREG_FR))
		return 0;
	LLScriptLibData *string1;
	LLScriptLibData *string2;
	if (offset1 != offset2)
	{
		string1 = lsa_get_data(buffer, offset1, TRUE);
		string2 = lsa_get_data(buffer, offset2, TRUE);
	}
	else
	{
		string1 = lsa_get_data(buffer, offset1, TRUE);
		string2 = lsa_get_data(buffer, offset2, TRUE);
	}

	if (  (!string1)
		||(!string2))
	{
		set_fault(buffer, LSRF_HEAP_ERROR);
		delete string1;
		delete string2;
		return 0;
	}

	char *test1 = NULL, *test2 = NULL;

	if (string1->mType == LST_STRING)
	{
		test1 = string1->mString;
	}
	else if (string1->mType == LST_KEY)
	{
		test1 = string1->mKey;
	}
	if (string2->mType == LST_STRING)
	{
		test2 = string2->mString;
	}
	else if (string2->mType == LST_KEY)
	{
		test2 = string2->mKey;
	}

	if (  (!test1)
		||(!test2))
	{
		set_fault(buffer, LSRF_HEAP_ERROR);
		delete string1;
		delete string2;
		return 0;
	}

	S32 size = (S32)strlen(test1) + (S32)strlen(test2) + 1;			/*Flawfinder: ignore*/

	LLScriptLibData *string3 = new LLScriptLibData;
	string3->mType = LST_STRING;
	string3->mString = new char[size];
	strcpy(string3->mString, test1);			/*Flawfinder: ignore*/
	strcat(string3->mString, test2);			/*Flawfinder: ignore*/

	delete string1;
	delete string2;

	return lsa_heap_add_data(buffer, string3, heapsize, TRUE);
}

S32 lsa_cmp_strings(U8 *buffer, S32 offset1, S32 offset2)
{
	if (get_register(buffer, LREG_FR))
		return 0;
	LLScriptLibData *string1;
	LLScriptLibData *string2;

	string1 = lsa_get_data(buffer, offset1, TRUE);
	string2 = lsa_get_data(buffer, offset2, TRUE);
	
	if (  (!string1)
		||(!string2))
	{
		set_fault(buffer, LSRF_HEAP_ERROR);
		delete string1;
		delete string2;
		return 0;
	}

	char *test1 = NULL, *test2 = NULL;

	if (string1->mType == LST_STRING)
	{
		test1 = string1->mString;
	}
	else if (string1->mType == LST_KEY)
	{
		test1 = string1->mKey;
	}
	if (string2->mType == LST_STRING)
	{
		test2 = string2->mString;
	}
	else if (string2->mType == LST_KEY)
	{
		test2 = string2->mKey;
	}

	if (  (!test1)
		||(!test2))
	{
		set_fault(buffer, LSRF_HEAP_ERROR);
		delete string1;
		delete string2;
		return 0;
	}
	S32 retval = strcmp(test1, test2);

	delete string1;
	delete string2;

	return retval;
}

void lsa_print_heap(U8 *buffer)
{
	S32				offset = get_register(buffer, LREG_HR);
	S32				readoffset;
	S32				ivalue;
	F32				fpvalue;
	LLVector3		vvalue;
	LLQuaternion	qvalue;
	char			string[4096];		/*Flawfinder: ignore*/

	LLScriptAllocEntry entry;

	bytestream2alloc_entry(entry, buffer, offset);

	printf("HP: [0x%X]\n", get_register(buffer, LREG_HP));
	printf("==========\n");

	while (offset + entry.mSize < MAX_HEAP_SIZE)
	{
		printf("[0x%X] ", offset);
		printf("%s ", LSCRIPTTypeNames[entry.mType]);
		printf("Ref Count: %d ", entry.mReferenceCount);
		printf("Size: %d = ", entry.mSize);

		readoffset = offset;

		switch(entry.mType)
		{
		case LST_INTEGER:
			ivalue = bytestream2integer(buffer, readoffset);
			printf("%d\n", ivalue);
			break;
		case LST_FLOATINGPOINT:
			fpvalue = bytestream2float(buffer, readoffset);
			printf("%f\n", fpvalue);
			break;
		case LST_STRING:
			bytestream2char(string, buffer, readoffset, sizeof(string));
			printf("%s\n", string);
			break;
		case LST_KEY:
			bytestream2char(string, buffer, readoffset, sizeof(string));
			printf("%s\n", string);
			break;
		case LST_VECTOR:
			bytestream2vector(vvalue, buffer, readoffset);
			printf("< %f, %f, %f >\n", vvalue.mV[VX], vvalue.mV[VY], vvalue.mV[VZ]);
			break;
		case LST_QUATERNION:
			bytestream2quaternion(qvalue, buffer, readoffset);
			printf("< %f, %f, %f, %f >\n", qvalue.mQ[VX], qvalue.mQ[VY], qvalue.mQ[VZ], qvalue.mQ[VS]);
			break;
		case LST_LIST:
			ivalue = bytestream2integer(buffer, readoffset);
			printf("%d\n", ivalue);
			break;
		default:
			printf("\n");
			break;
		}
		offset += entry.mSize;
		bytestream2alloc_entry(entry, buffer, offset);
	}
	printf("[0x%X] ", offset);
	printf("%s ", LSCRIPTTypeNames[entry.mType]);
	printf("Ref Count: %d ", entry.mReferenceCount);
	printf("Size: %d\n", entry.mSize);
	printf("==========\n");
}

void lsa_fprint_heap(U8 *buffer, LLFILE *fp)
{
	S32				offset = get_register(buffer, LREG_HR);
	S32				readoffset;
	S32				ivalue;
	F32				fpvalue;
	LLVector3		vvalue;
	LLQuaternion	qvalue;
	char			string[4096];		/*Flawfinder: ignore*/

	LLScriptAllocEntry entry;

	bytestream2alloc_entry(entry, buffer, offset);

	while (offset + entry.mSize < MAX_HEAP_SIZE)
	{
		fprintf(fp, "[0x%X] ", offset);
		fprintf(fp, "%s ", LSCRIPTTypeNames[entry.mType]);
		fprintf(fp, "Ref Count: %d ", entry.mReferenceCount);
		fprintf(fp, "Size: %d = ", entry.mSize);

		readoffset = offset;

		switch(entry.mType)
		{
		case LST_INTEGER:
			ivalue = bytestream2integer(buffer, readoffset);
			fprintf(fp, "%d\n", ivalue);
			break;
		case LST_FLOATINGPOINT:
			fpvalue = bytestream2float(buffer, readoffset);
			fprintf(fp, "%f\n", fpvalue);
			break;
		case LST_STRING:
			bytestream2char(string, buffer, readoffset, sizeof(string));
			fprintf(fp, "%s\n", string);
			break;
		case LST_KEY:
			bytestream2char(string, buffer, readoffset, sizeof(string));
			fprintf(fp, "%s\n", string);
			break;
		case LST_VECTOR:
			bytestream2vector(vvalue, buffer, readoffset);
			fprintf(fp, "< %f, %f, %f >\n", vvalue.mV[VX], vvalue.mV[VY], vvalue.mV[VZ]);
			break;
		case LST_QUATERNION:
			bytestream2quaternion(qvalue, buffer, readoffset);
			fprintf(fp, "< %f, %f, %f, %f >\n", qvalue.mQ[VX], qvalue.mQ[VY], qvalue.mQ[VZ], qvalue.mQ[VS]);
			break;
		case LST_LIST:
			ivalue = bytestream2integer(buffer, readoffset);
			fprintf(fp, "%d\n", ivalue);
			break;
		default:
			fprintf(fp, "\n");
			break;
		}
		offset += entry.mSize;
		bytestream2alloc_entry(entry, buffer, offset);
	}
	fprintf(fp, "[0x%X] ", offset);
	fprintf(fp, "%s ", LSCRIPTTypeNames[entry.mType]);
	fprintf(fp, "Ref Count: %d ", entry.mReferenceCount);
	fprintf(fp, "Size: %d", entry.mSize);
	fprintf(fp, "\n");
}

S32 lsa_cat_lists(U8 *buffer, S32 offset1, S32 offset2, S32 heapsize)
{
	if (get_register(buffer, LREG_FR))
		return 0;
	LLScriptLibData *list1;
	LLScriptLibData *list2;
	if (offset1 != offset2)
	{
		list1 = lsa_get_data(buffer, offset1, TRUE);
		list2 = lsa_get_data(buffer, offset2, TRUE);
	}
	else
	{
		list1 = lsa_get_data(buffer, offset1, TRUE);
		list2 = lsa_get_data(buffer, offset2, TRUE);
	}

	if (  (!list1)
		||(!list2))
	{
		set_fault(buffer, LSRF_HEAP_ERROR);
		delete list1;
		delete list2;
		return 0;
	}

	if (  (list1->mType != LST_LIST)
		||(list2->mType != LST_LIST))
	{
		set_fault(buffer, LSRF_HEAP_ERROR);
		delete list1;
		delete list2;
		return 0;
	}

	LLScriptLibData *runner = list1;

	while (runner->mListp)
	{
		runner = runner->mListp;
	}

	runner->mListp = list2->mListp;

	list2->mListp = NULL;

	delete list2;

	return lsa_heap_add_data(buffer, list1, heapsize, TRUE);
}


S32 lsa_cmp_lists(U8 *buffer, S32 offset1, S32 offset2)
{
	if (get_register(buffer, LREG_FR))
		return 0;
	LLScriptLibData *list1;
	LLScriptLibData *list2;
	if (offset1 != offset2)
	{
		list1 = lsa_get_data(buffer, offset1, TRUE);
		list2 = lsa_get_data(buffer, offset2, TRUE);
	}
	else
	{
		list1 = lsa_get_data(buffer, offset1, FALSE);
		list2 = lsa_get_data(buffer, offset2, TRUE);
	}

	if (  (!list1)
		||(!list2))
	{
		set_fault(buffer, LSRF_HEAP_ERROR);
		delete list1;
		delete list2;
		return 0;
	}

	if (  (list1->mType != LST_LIST)
		||(list2->mType != LST_LIST))
	{
		set_fault(buffer, LSRF_HEAP_ERROR);
		delete list1;
		delete list2;
		return 0;
	}

	S32 length1 = list1->getListLength();
	S32 length2 = list2->getListLength();
	delete list1;
	delete list2;
	return length1 - length2;
}


S32 lsa_preadd_lists(U8 *buffer, LLScriptLibData *data, S32 offset2, S32 heapsize)
{
	if (get_register(buffer, LREG_FR))
		return 0;
	LLScriptLibData *list2 = lsa_get_data(buffer, offset2, TRUE);

	if (!list2)
	{
		set_fault(buffer, LSRF_HEAP_ERROR);
		delete list2;
		return 0;
	}

	if (list2->mType != LST_LIST)
	{
		set_fault(buffer, LSRF_HEAP_ERROR);
		delete list2;
		return 0;
	}

	LLScriptLibData *runner = data->mListp;

	while (runner->mListp)
	{
		runner = runner->mListp;
	}


	runner->mListp = list2->mListp;
	list2->mListp = data->mListp;

	return lsa_heap_add_data(buffer, list2, heapsize, TRUE);
}


S32 lsa_postadd_lists(U8 *buffer, S32 offset1, LLScriptLibData *data, S32 heapsize)
{
	if (get_register(buffer, LREG_FR))
		return 0;
	LLScriptLibData *list1 = lsa_get_data(buffer, offset1, TRUE);

	if (!list1)
	{
		set_fault(buffer, LSRF_HEAP_ERROR);
		delete list1;
		return 0;
	}

	if (list1->mType != LST_LIST)
	{
		set_fault(buffer, LSRF_HEAP_ERROR);
		delete list1;
		return 0;
	}

	LLScriptLibData *runner = list1;

	while (runner->mListp)
	{
		runner = runner->mListp;
	}

	runner->mListp = data->mListp;

	return lsa_heap_add_data(buffer, list1, heapsize, TRUE);
}


LLScriptLibData* lsa_randomize(LLScriptLibData* src, S32 stride)
{
	S32 number = src->getListLength();
	if (number <= 0)
	{
		return NULL;
	}
	if (stride <= 0)
	{
		stride = 1;
	}
	if(number % stride)
	{
		LLScriptLibData* retval = src->mListp;
		src->mListp = NULL;
		return retval;
	}
	S32 buckets = number / stride;

	// Copy everything into a special vector for sorting;
	std::vector<LLScriptLibData*> sort_array;
	sort_array.reserve(number);
	LLScriptLibData* temp = src->mListp;
	while(temp)
	{
		sort_array.push_back(temp);
		temp = temp->mListp;
	}

	// We cannot simply call random_shuffle or similar algorithm since
	// we need to obey the stride. So, we iterate over what we have
	// and swap each with a random other segment.
	S32 index = 0;
	S32 ii = 0;
	for(; ii < number; ii += stride)
	{
		index = ll_rand(buckets) * stride;
		for(S32 jj = 0; jj < stride; ++jj)
		{
			std::swap(sort_array[ii + jj], sort_array[index + jj]);
		}
	}

	// copy the pointers back out
	ii = 1;
	temp = sort_array[0];
	while (ii < number)
	{
		temp->mListp = sort_array[ii++];
		temp = temp->mListp;
	}
	temp->mListp = NULL;

	src->mListp = NULL;

	LLScriptLibData* ret_value = sort_array[0];
	return ret_value;
}
