Combined Library

From Second Life Wiki
Revision as of 01:47, 1 February 2008 by Strife Onizuka (talk | contribs)
Jump to navigation Jump to search

The Combined Library is comprised of about 55 functions all of which are released under CC-by v3.0 license.

The library is still being worked on so only some of the compiled functions will be posted at this time. All functions in the library are hand optimized. To understand the logic, it is best to pull the code apart but be wary of LSL's strict order of operations.

You can download the latest release from here: CombinedLibrary.zip

String Functions

Replace

The design of the logic had to overcome two hurdles. The first was keeping it from searching previous replacements (otherwise you could fall into an infinite loop or infinitely grow the memory). The second was so that it could do null replacements. Both of these hurdles were overcome but at the cost of some readability.

The way it works is it keeps an Unsearched Buffer (UB) which is a subset of the Input Buffer (IB) and it records the Position of the UB in the IB (P). Each iteration it searches the UB and adds that resulting index to the P, then it uses P as the index to replace that section in the IB, finally it recalculates the new P, then is uses IB with the new P to update UB. <lsl> string str_replace(string src, string from, string to) {//replaces all occurrences of 'from' with 'to' in 'src'.

   integer len = (~-(llStringLength(from)));
   if(~len)
   {
       string  buffer = src;
       integer b_pos = -1;
       integer to_len = (~-(llStringLength(to)));
       @loop;//instead of a while loop, saves 5 bytes (and run faster).
       integer to_pos = ~llSubStringIndex(buffer, from);
       if(to_pos)
       {

// b_pos -= to_pos; // src = llInsertString(llDeleteSubString(src, b_pos, b_pos + len), b_pos, to); // b_pos += to_len; // buffer = llGetSubString(src, (-~(b_pos)), 0x8000);

           buffer = llGetSubString(src = llInsertString(llDeleteSubString(src, b_pos -= to_pos, b_pos + len), b_pos, to), (-~(b_pos += to_len)), 0x8000);
           jump loop;
       }
   }
   return src;

} </lsl>

Trim

<lsl> string TrimRight(string src, string chrs) {//Trims characters from the right end of the string

   integer i = llStringLength(src);
   do;while(~llSubStringIndex(chrs, llGetSubString(src, i = ~ -i, i)) && i);
   return llDeleteSubString(src, -~i, 0xFFFF);

}

string TrimLeft(string src, string chrs) {//Trims characters from the left end of the string

   integer i = ~llStringLength(src);
   do;while(i && ~llSubStringIndex(chrs, llGetSubString(src, (i = -~i), i)));
   return llDeleteSubString(src, 0xFFFF0000, ~-i);

}

string TrimBoth(string src, string chrs) {//Trims characters from both ends of the string

   integer i = ~llStringLength(src);
   do;while(i && ~llSubStringIndex(chrs, llGetSubString(src, (i = -~i), i)));
   i = llStringLength(src = llDeleteSubString(src, 0xFFFF0000, (~-(i))));
   do;while(~llSubStringIndex(chrs, llGetSubString(src, (i = ~-i), i)) && i);
   return llDeleteSubString(src, (-~(i)), 0xFFFF);

} </lsl>

Unicode functions

LSL uses UTF-8 to as the base format for strings. UTF-8 is an encoding system for storing Unicode characters, each Unicode character has a number. These functions allow for the conversion between the string form and the integer form. They may not be pretty but they are tight and get the job done quickly.

UTF8 to Unicode Integer

Converts a character into it an integer. <lsl> integer UTF8ToUnicodeInteger(string input)//Mono Safe, LSO Safe {

   integer result = llBase64ToInteger(llStringToBase64(input = llGetSubString(input,0,0)));
   if(result & 0x80000000){//multibyte, continuing to use base64 is impractical because it requires smart shifting.
       integer end = (integer)("0x"+llGetSubString(input = (string)llParseString2List(llEscapeURL(input),(list)"%",[]),-8,-1));
       integer begin = (integer)("0x"+llDeleteSubString(input,-8,-1));
       return  (   (  0x0000003f &  end       ) |
                   (( 0x00003f00 &  end) >> 2 ) | 
                   (( 0x003f0000 &  end) >> 4 ) | 
                   (( 0x3f000000 &  end) >> 6 ) |
                   (( 0x0000003f &  begin) << 24) |
                   (( 0x00000100 &  begin) << 22) ) & 
                   (0x7FFFFFFF >> (5 * ((integer)(llLog(~result) / 0.69314718055994530941723212145818) - 25)));
   }
   return result >> 24;

} </lsl>

Unicode Integer to UTF8

Convert an integer into a character. <lsl> string UnicodeIntegerToUTF8(integer input)//Mono Safe, LSO Safe {

   integer bytes = llCeil(llLog(input) / 0.69314718055994530941723212145818);
   bytes = (input >= 0x80) * (bytes + ~(((1 << bytes) - input) > 0)) / 5;//adjust
   string result = "%" + byte2hex((input >> (6 * bytes)) | ((0x3F80 >> bytes) << !bytes));
   while (bytes)
       result += "%" + byte2hex((((input >> (6 * (bytes = ~-bytes))) | 0x80) & 0xBF));
   return llUnescapeURL(result);

}

string byte2hex(integer x)//Mono Safe, LSO Safe {//Helper function for use with unicode characters.

   integer y = (x >> 4) & 0xF;
   return llGetSubString(hexc, y, y) + llGetSubString(hexc, x & 0xF, x & 0xF);

}//This function would benifit greatly from the DUP opcode, it would remove 19 bytes.

string hexc="0123456789ABCDEF"; </lsl>

List Functions

Replace

The design of the logic had to overcome two hurdles. The first was keeping it from searching previous replacements (otherwise you could fall into an infinite loop or infinitely grow the memory). The second was so that it could do null replacements. Both of these hurdles were overcome but at the cost of some readability.

The way it works is it keeps an Unsearched Buffer (UB) which is a subset of the Input Buffer (IB) and it records the Position of the UB in the IB (P). Each iteration it searches the UB and adds that resulting index to the P, then it uses P as the index to replace that section in the IB, finally it recalculates the new P, then is uses IB with the new P to update UB. <lsl> list ListReplace(list src, list from, list to) {//replaces all occurrences of 'from' with 'to' in 'src'.

   integer len = ~([] != from);
   if(~len)
   {
       list  buffer = src;
       integer b_pos = -1;
       integer to_len = ~([] != to);
       @loop;//instead of a while loop, saves 5 bytes (and run faster).
       integer to_pos = ~llListFindList(buffer, from);
       if(to_pos)
       {

// b_pos -= to_pos; // src = llListReplaceList(src, to, b_pos, b_pos + len); // b_pos += to_len; // buffer = llList2List(src, (-~(b_pos)), 0x4000);

           buffer = llList2List(src = llListReplaceList(src, to, b_pos -= to_pos, b_pos + len), (-~(b_pos += to_len)), 0x4000);
           jump loop;
       }
   }
   return src;

} </lsl>

Compare

Compares two lists, returns true if identical.

Note: It will ignore the sign on zero (0.0 == -0.0). If the sign on zero is important to you, follow the instructions in the comments on how to enable checking for this. Currently no functions in LSL treat negative zero differently then positive zero. <lsl> integer ListCompare(list input_a, list input_b) {

   integer b = input_a != input_b;
   if(!b)
   {
       if((b = [] != input_a))
       {
           integer counter = b;
           do
           {
               if((b = llGetListEntryType(input_a,counter)) - llGetListEntryType(input_b,counter))
                   jump end_a;
               //Don't need to bother with TYPE_KEY
               if(b == TYPE_FLOAT) {
                   if((b = (llList2Float(input_a,counter) != llList2Float(input_b,counter))))
                       jump end_b;
               } else if(b == TYPE_VECTOR) {//it costs more to update b and jump out then to just return
                   if((b = (llList2Vector(input_a,counter) != llList2Vector(input_b,counter))))//so just return.
                       jump end_c;
               } else if(b == TYPE_ROTATION) {
                   if((b = (llList2Rot(input_a,counter) != llList2Rot(input_b,counter))))
                       jump end_d;
               }
               else//comment this line out if you care about the sign on zero.
                   if((b = (llList2String(input_a,counter) != llList2String(input_b,counter))))
                       jump end_e;
           }while((counter = -~counter));
           //if you get here, b equals zero, so we don't even have to change it's value for the return.
       }
   }
   @end_a;@end_b;@end_c;@end_d;@end_e;
   return !b;

} </lsl>

Base64 & Hex Encoding

Parameter & Return Based

Binary functions that use parameters and returns <lsl> integer ReadBase64Integer(string data, integer index) {

   integer S = (index << 5) % 6;
   index = llBase64ToInteger(llGetSubString((data = llGetSubString(data, index = ((index << 5) / 6), index+6)) + "AAAAAA", 0, 5)) << S;
   if(S)
       index = index | (llBase64ToInteger(llGetSubString("A" + (llDeleteSubString(data, 0, 1)) + "AAAAA", 0, 5)) >> (6 - S));
   return index;

}

list ReadBase64IntegerPair(string data, integer index) {

   integer S = (index << 5) % 6;
   index = llBase64ToInteger(llGetSubString("A" + (llDeleteSubString(data = llGetSubString(data, index = ((index << 5) / 6), index + 11), 0, 2)) + "AAAAA", 0, 5));
   return [
       (llBase64ToInteger(llGetSubString((data) + "AAAAAA", 0, 5)) << S) | (index >> (12 - S)),
       ((llBase64ToInteger(llGetSubString((llDeleteSubString(data,0,5)) + "AAAAAA", 0, 5)) >> (4 - S)) & ~(0xF0000000 << S)) | (index << (20 + S))
   ];

}

string WriteBase64Integer(string data, integer index, integer value) {

   integer S = 12 - ((index % 3) << 1);
   return  llDeleteSubString(
       llInsertString(
       data,
       index = ((index << 4) / 3), 	
           llInsertString(
               llIntegerToBase64(
                   (llBase64ToInteger(llGetSubString((data = llGetSubString(data, index, index+7)) + "AAAAAA", 0, 5)) & (0xFFF00000 << S)) | 
                   ((value >> (12 - S)) & ~(0xFFF00000 << S))
               ), 2, 
               llIntegerToBase64(
                   (llBase64ToInteger(llGetSubString((llDeleteSubString(data, 0, 1)) + "AAAAAA", 0, 5)) & ~(0xFFFFFFFF << S)) | 
                   (value << S)
       )	)	), index+7, index + 22);//insert it then remove the old and the extra.

}

string WriteBase64IntegerPair(string data, integer index, integer low, integer high) {

   integer S = 10 - ((index << 5) % 6);
   return  llDeleteSubString(
       llInsertString(
           data,
           index = ((index << 5) / 6),
           llInsertString(
               llInsertString(
                   llIntegerToBase64(
                       (llBase64ToInteger(llGetSubString((data = llGetSubString(data, index, index + 12)) + "AAAAAA", 0, 5)) & (0xFFC00000 << S)) | 
                       ((low >> (10 - S)) & ~(0xFFC00000 << S))
                   ), 3,
                   llIntegerToBase64(
                       ((high >> (24 - S)) & ~(0xFFFFFF00 << S)) | 
                       (low << (8 + S))
               )   ), 7,
               llIntegerToBase64(
                   (llBase64ToInteger(llGetSubString((llDeleteSubString(data, 0, 6)) + "AAAAAA", 0, 5)) & ~(0xFFFFFFFF << S)) | 
                   (high << S)
       )   )   ), index + 12, index + 35);//insert it then remove the old and the extra.

}

string DwordList2Hex(list in) {

   string out = ""; integer len = ~(in != []);
   while((len = -~len))
   {
       integer int = llList2Integer(in, len);
       integer j = 8;
       string mout = "";
       do
       {
           mout = llGetSubString(hexc, int & 15, int & 15) + mout;
           int = int >> 4;
       }while((j = ~-j));
       out += mout;
   }
   return out;

}

string HexToBase64(string a) {

   string d = "";
   integer e = (llStringLength(a += "0000000000000000") - 9) & 0xFFFFFFF8;
   integer g;
   do{
       d = WriteBase64IntegerPair(d, g >> 3, ((integer)("0x"+(llGetSubString(a, g, g + 7)))), ((integer)("0x"+(llGetSubString(a, g + 8, g + 15)))));
   }while((g += 16) < e);
   return d;

}

list Base64ToHex(string a) {

   list out = [];
   integer len = ((llStringLength(a = TrimRight(a,"A=")) + 4) * 6) >> 5;
   integer i = -1;
   integer int;
   while((i = -~i) <= len)
   {
       if((int = ReadBase64Integer(a, i)) || (i ^ len))
       {
           integer j = 8;
           string mout = "";
           do
           {
               mout = llGetSubString(hexc, int & 15, int & 15) + mout;
               int = int >> 4;
           }while((j = ~-j));
           out += mout;
       }
   }
   return out;

}

integer ReadBase64Byte(string data, integer index) {

   return 0xFF & (
       llBase64ToInteger(llGetSubString((llGetSubString(data, (index << 3) / 6, (-~((index << 3) / 6)))) + "AAAAAA", 0, 5)) >> 
       (24 - ((index << 3) % 6)));

}

string WriteBase64Byte(string data, integer index, integer value) {

   integer S = 24 - ((index = (index << 3)) % 6);
   return llDeleteSubString(llInsertString(data, index /= 6,
       llIntegerToBase64(
           (llBase64ToInteger(llGetSubString((llGetSubString(data, index, (-~(index)))) + "AAAAAA", 0, 5)) & ~(0xFF << S)) | 
           ((value & 0xFF) << S)
   )   ), index + 2, index + 9);//insert it then remove the old and the extra.

}

list Base64ToDwordList(string a) {

   integer len = (6 * (llStringLength(a = TrimRight(a,"A=")) + 4)) >> 5;
   integer i = -1;
   list out;
   integer int;
   while(PPi(i) <= len)
       if((int = ReadBase64Integer(a, i)) || (i ^ len))
           out += int;
   return out;

}

string DwordListToBase64(list a) {

   integer len = (a != []);
   integer i = -1;
   string out;
   while((i = -~i) < len)
       out = WriteBase64Integer(out, i, llList2Integer(a, i));
   return TrimRight(out,"A");

}

string hexc="0123456789ABCDEF"; </lsl>

Global Buffers

These versions of the functions depend upon global buffers. Not all the functions have been ported to this model (because it is very complicated to build functions with macros so they can be flipped from one mode to the other). <lsl> integer v0; integer v1; string v;

ReadBase64Integer(integer index) {

   integer S = (index << 5) % 6;
   string T = llGetSubString(v, index = ((index << 5) / 6), index+6);
   v0 = llBase64ToInteger(llGetSubString((T) + "AAAAAA", 0, 5)) << S;
   if(S)
       v0 = v0 | (llBase64ToInteger(llGetSubString("A" + (llDeleteSubString(T, 0, 1)) + "AAAAA", 0, 5)) >> (6 - S));

}

ReadBase64IntegerPair(integer index) {

   integer S = (index << 5) % 6;
   string buf = llGetSubString(v, index = ((index << 5) / 6), index + 11);
   index = llBase64ToInteger(llGetSubString("A" + (llDeleteSubString(buf, 0, 2)) + "AAAAA", 0, 5));
       v0 = (llBase64ToInteger(llGetSubString((buf) + "AAAAAA", 0, 5)) << S) | (index >> (12 - S));
       v1 = ((llBase64ToInteger(llGetSubString((llDeleteSubString(buf,0,5)) + "AAAAAA", 0, 5)) >> (4 - S)) & ~(0xF0000000 << S)) | (index << (20 + S));

}

WriteBase64Integer(integer index) {

   integer S = 12 - ((index % 3) << 1);
   v =  llDeleteSubString(
       llInsertString(
       v,
       index = ((index << 4) / 3), 	
           llInsertString(
               llIntegerToBase64(
                   (llBase64ToInteger(llGetSubString((v = llGetSubString(v, index, index+7)) + "AAAAAA", 0, 5)) & (0xFFF00000 << S)) | 
                   ((v0 >> (12 - S)) & ~(0xFFF00000 << S))
               ), 2, 
               llIntegerToBase64(
                   (llBase64ToInteger(llGetSubString((llDeleteSubString(v, 0, 1)) + "AAAAAA", 0, 5)) & ~(0xFFFFFFFF << S)) | 
                   (v0 << S)
       )	)	), index+7, index + 22);//insert it then remove the old and the extra.

}

WriteBase64IntegerPair(integer index) {

   integer S = 10 - ((index << 5) % 6);
   v =  llDeleteSubString(
       llInsertString(
           v,
           index = ((index << 5) / 6),
           llInsertString(
               llInsertString(
                   llIntegerToBase64(
                       (llBase64ToInteger(llGetSubString((v = llGetSubString(v, index, index + 12)) + "AAAAAA", 0, 5)) & (0xFFC00000 << S)) | 
                       ((v0 >> (10 - S)) & ~(0xFFC00000 << S))
                   ), 3,
                   llIntegerToBase64(
                       ((v1 >> (24 - S)) & ~(0xFFFFFF00 << S)) | 
                       (v0 << (8 + S))
               )   ), 7,
               llIntegerToBase64(
                   (llBase64ToInteger(llGetSubString((llDeleteSubString(v, 0, 6)) + "AAAAAA", 0, 5)) & ~(0xFFFFFFFF << S)) | 
                   (v1 << S)
       )   )   ), index + 12, index + 35);//insert it then remove the old and the extra.

}

HexToBase64(string a) {

   v = "";
   integer e = (llStringLength(a += "0000000000000000") - 9) & 0xFFFFFFF8;
   integer g;
   do{
       v0 = ((integer)("0x"+(llGetSubString(a, g, g + 7)))); v1 = ((integer)("0x"+(llGetSubString(a, g + 8, g + 15)))); WriteBase64IntegerPair(g >> 3);
   }while((g += 16) < e);

}

list Base64ToHex() {

   list out = [];
   integer len = ((llStringLength(v = TrimRight(v,"A=")) + 4) * 6) >> 5;
   integer i = 0;
   do{
       ReadBase64Integer(i); if(v0 || (i ^ len))
       {
           integer j = 8;
           string mout = "";
           do{
               mout = llGetSubString(hexc, v0 & 15, v0 & 15) + mout;
               v0 = v0 >> 4;
           }while((j = ~-j));
           out += mout;
       }
   }while((i = -~i) <= len);
   return out;

}

list Base64ToDwordList() {

   integer len = (6 * (llStringLength(v = TrimRight(v,"A=")) + 4)) >> 5;
   integer i = 0;
   list out = [];
   do
   {
       ReadBase64Integer(i); if(v0 || (i ^ len))
           out += v0;
   }while((i = -~i) <= len);
   return out;

}

ReadBase64Byte(integer index) {

   v0 = 0xFF & (
       llBase64ToInteger(llGetSubString((llGetSubString(v, (index << 3) / 6, (-~((index << 3) / 6)))) + "AAAAAA", 0, 5)) >> 
       (24 - ((index << 3) % 6)));

}

WriteBase64Byte(integer index) {

   integer S = 24 - ((index = (index << 3)) % 6);
   v = llDeleteSubString(llInsertString(v, index /= 6,
       llIntegerToBase64(
           (llBase64ToInteger(llGetSubString((llGetSubString(v, index, (-~(index)))) + "AAAAAA", 0, 5)) & ~(0xFF << S)) | 
           ((v0 & 0xFF) << S)
   )   ), index + 2, index + 9);//insert it then remove the old and the extra.

}

string hexc="0123456789ABCDEF"; </lsl>

Float Union Integer

The Float Union Integer functions allow for safely storing floats in integers and provide for retreival. All real numbers are supported.

SVC-1377 breaks these functions for Mono but once it is fixed (it has been imported) then they should run.

General

These versions will work well in LSO and Mono.

<lsl>integer fui(float input)//Mono Safe, LSO Safe, Doubles Unsupported, LSLEditor Unsafe {//union float to integer

   if((input) != 0.0){//is it non zero?
       integer sign = (input < 0) << 31;//the sign, but later this variable is reused to store the shift
       if((input = llFabs(input)) < 2.3509887016445750159374730744445e-38)//Denormalized range check & last stirde of normalized range
           return sign | (integer)(input / 1.4012984643248170709237295832899e-45);//the math overlaps; saves cpu time.
       integer exp = llFloor((llLog(input) / 0.69314718055994530941723212145818));//extremes will error towards extremes. following yuch corrects it.
       return (0x7FFFFF & (integer)(input * (0x1000000 >> sign))) | (((exp + 126 + (sign = ((integer)input - (3 <= (input /= (float)("0x1p"+(string)(exp -= ((exp >> 31) | 1)))))))) << 23 ) | sign);
   }//for grins, detect the sign on zero. it's not pretty but it works. the previous requires alot of unwinding to understand it.
   return ((string)input == (string)(-0.0)) << 31;

}

float iuf(integer input)//LSLEditor Unsafe, LSO Safe, Mono Safe {//union integer to float

   return llPow(2.0, (input | !input) - 150) * (((!!(input = (0xff & (input >> 23)))) << 23) | ((input & 0x7fffff))) * (1 | (input >> 31));

}//will crash if the raw exponent == 0xff; reason for crash deviates from float standard; though a crash is warented.</lsl>

LSLEditor Safe

These versions will run in LSLEditor (as well as LSO and Mono). Doubles are not supported and anything outside the float range will become signed infinity or zero respectively.

<lsl>integer fui(float input)//Mono Safe, LSO Safe, Doubles Unsupported, LSLEditor Safe {//union float to integer

   if((input) != 0.0){//is it non zero?
       integer sign = (integer)(input < 0) << 31;//the sign, but later this variable is reused to store the shift
       if((input = llFabs(input)) < 2.3509887016445750159374730744445e-38)//Denormalized range check & last stirde of normalized range
           return sign | (integer)(input / 1.4012984643248170709237295832899e-45);//the math overlaps; saves cpu time.
       if(input > 3.4028234663852885981170418348452e+38)//infinity generation for doubles x_x
           return sign | 0x7F800000;//return signed infinity
       integer exp = llFloor((llLog(input) / 0.69314718055994530941723212145818));//extremes will error towards extremes. following yuch corrects it.
       input /= llPow(2.0, exp -= ((exp >> 31) | 1));
       integer d = (integer)input - (3 <= input);
       return (0x7FFFFF & (integer)(input * (0x1000000 >> d))) | (((exp + 126 + d) << 23 ) | sign);
   }//for grins, detect the sign on zero. it's not pretty but it works. the previous requires alot of unwinding to understand it.
   return (integer)((string)input == (string)(-0.0)) << 31;

}

float iuf(integer input)//LSLEditor Safe, LSO Safe, Mono Safe {//union integer to float

   if((input & 0x7FFFFFFF) == 0x7F800000)//Infinity Check
       return ((input >> 31) | 1) / 0.0;
   integer exp = 0xff & (input >> 23);
   return llPow(2.0, (exp | !exp) - 150) * (((!!exp) << 23) | ((input & 0x7fffff))) * (1 | (input >> 31));

}//will crash if the raw exponent == 0xff; reason for crash deviates from float standard; though a crash is warented.</lsl>