So here goes:
CODE
// Generates a random string of length len
// from the characters passed to it.
string randomSeperatorFrom(string chars, integer len) {
integer numChars = llStringLength(chars);
integer firstIndex = (integer) llFrand(numChars);
string first = llGetSubString(chars, firstIndex, firstIndex);
if (len == 1)
return first;
chars = llDeleteSubString(chars, firstIndex, firstIndex);
--numChars;
// Generate the last character.
integer lastIndex = (integer) llFrand(numChars);
string last = llGetSubString(chars, lastIndex, lastIndex);
if (len == 2)
return first + last;
chars = llDeleteSubString(chars, lastIndex, lastIndex);
--numChars;
// Generate the string between the first and last chars.
string middle;
integer i;
for (i = 2; i < len; ++i) {
integer randIndex = (integer) llFrand(numChars);
middle += llGetSubString(chars, randIndex, randIndex);
}
return first + middle + last;
}
string SEPERATOR_CHARS = "`~!@#$%^&*()-_+[]{}\|'\";/?.>,<";
integer SEPERATOR_LEN = 3;
string dumpList2String(list src) {
// Generate a seperator not present in any of the
// elements in the list.
string chars = (string) src; // Squashes all elements together.
string seperator;
do {
seperator = randomSeperator(SEPERATOR_CHARS, SEPERATOR_LEN);
} while (llSubStringIndex(chars, seperator) != -1);
return seperator + llDumpList2String(src, seperator);
}
list parseStringKeepNulls(string src) {
// The seperator should be the first SEPERATOR_LEN
// characters in the string.
return llParseStringKeepNulls(llDeleteSubString(src, 0, SEPERATOR_LEN - 1),
[llGetSubString(src, 0, SEPERATOR_LEN - 1)], []);
}
string NULL = "";
list listReplaceList(list dest, list src, integer start, integer ensureLocation) {
if (ensureLocation && llGetListEntryType(dest, start - 1) == TYPE_INVALID) {
// Fill with nulls until we get to the starting position.
integer len = llGetListLength(dest);
for (len = llGetListLength(dest); len < start; ++len) {
dest += NULL;
}
}
return llListReplaceList(dest, src, start, start + llGetListLength(src) - 1);
}
list getElementAt(list array, list index){
if (index == [])
return array;
integer numIndicies = llGetListLength(index);
list src = array;
integer i;
for (i = 0; i < numIndicies - 1; ++i) {
integer listIndex = llList2Integer(index, i);
string element = llList2String(src, listIndex);
if (llGetSubString(element, 0, 0) == "l")
element = llDeleteSubString(element, 0, 0);
src = parseStringKeepNulls(element);
}
string element = llList2String(src, llList2Integer(index, -1));
if (llGetSubString(element, 0, 0) == "l") {
// Caller is retreiving a dimension.
return parseStringKeepNulls(llDeleteSubString(element, 0, 0));
} else {
return [element];
}
}
// To set an element, we need to extract
// each list starting from where exactly the
// new data will be. In a set of russian dolls, this
// would be equivelant to grabbing the littlest one
// first. then gradually adding back on the layers over it.
// I think its easiest to do this recursively.
list setElementAt(list array, list data, list index) {
string element;
if (llGetListLength(data) > 1) {
// Data is a new dimension:
element = "l" + dumpList2String(data);
} else {
// Data is a single element
element = llList2String(data, 0);
}
if (llGetListLength(index) > 1) {
// index is in the form [a,b,c,d]
// here, we grab the list that d is in.
list containerIndex = llDeleteSubList(index, -1, -1);
list dest = getElementAt(array, containerIndex);
integer listIndex = llList2Integer(index, -1); // Grab d
dest = listReplaceList(dest, [element], listIndex, TRUE); // replace the element in d's list.
// Make sure the recursion treats the container like a list, not an element.
if (llGetListLength(dest) == 1)
dest += "";
return setElementAt(array, dest, containerIndex);
} else {
return listReplaceList(array, [element], llList2Integer(index, 0), TRUE);
}
}
Features:
1. Dynamic arrays: No need to specify dimension length!
2. "Ragged" arrays: Its truly lists in lists - any size list can be put into any other list.
Testing welcome

... and before you say "didn't you do this already?" -- eh, nevermind

==Chris
Also see the wiki library post for more details.