Files
SingularityViewer/indra/lscript/lscript_library/lscript_alloc.cpp
2010-04-02 02:48:44 -03:00

1143 lines
27 KiB
C++

/**
* @file lscript_alloc.cpp
* @brief general heap management for scripting system
*
* $LicenseInfo:firstyear=2002&license=viewergpl$
*
* Copyright (c) 2002-2009, 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;
}