TightList
De DigiWiki.
TightList is a family of functions for encoding lists as strings and then decoding them back into lists.
There are two flavors, TightList and TightListType. The big differences are that Modèle:LSL Param preserves types and uses a 6 char header (TL uses a 1 char header).
Goals
- Minimal amount of data required without having to use a specialized decoder for data types.
- The functions must be fast.
- A standardized string format so encoders can be customized.
Points to customize
- Separator selection
- Data -> String conversion
TightList Deviations
LSL
Here are the functions in LSL, they aren't the easiest to customize in this state, it best to use the ESL versions.
//TightList family of functions Version 1.0 //Copyright Strife Onizuka 2004-2006 //Free to use and distribute as long as this message is not removed. list TightListParse(string input) { string seperator = llGetSubString(input,(0),(0));//save memory return llParseStringKeepNulls(llDeleteSubString(input,(0),(0)), [input=seperator],[]); } string TightListDump(list input, string possibles) {//TLD( complex) makes a string from a list using a seperator that is supposed to be unique to the string string buffer = (string)(input);//dump the list without a seperator integer counter = -39 - llStringLength(possibles); if(counter == -40) if(!~llSubStringIndex(buffer,possibles)) jump end;//woot, we were given a unique seperator possibles += "|/?!@#$%^&*()_=:;~`'<>{}[],.\n\" qQxXzZ\\";//"//Good character set of rarely used letters. do; while(~llSubStringIndex(buffer,llGetSubString(possibles,counter,counter)) && (counter=-~counter));//search for unique seperator possibles = llGetSubString(possibles,counter,counter); @end; buffer = "";//save memory return possibles + llDumpList2String((input = []) + input, possibles); } integer TightListTypeLength(string input) { string seperators = llGetSubString(input,(0),6); return ((llParseStringKeepNulls(llDeleteSubString(input,(0),5), [],[input=llGetSubString(seperators,(0),(0)), llGetSubString(seperators,1,1),llGetSubString(seperators,2,2),llGetSubString(c,3,3), llGetSubString(seperators,4,4),llGetSubString(seperators,5,5)]) != []) + (llSubStringIndex(seperators,llGetSubString(seperators,6,6)) < 6)) >> 1; } integer TightListTypeEntryType(string input, integer index) { string seperators = llGetSubString(input,(0),6); return llSubStringIndex(seperators, input) + ((input = llList2String(llList2List(input + llParseStringKeepNulls(llDeleteSubString(input,(0),5), [],[input=llGetSubString(seperators,(0),(0)), llGetSubString(seperators,1,1),llGetSubString(seperators,2,2),llGetSubString(seperators,3,3), llGetSubString(seperators,4,4),llGetSubString(seperators,5,5)]), (llSubStringIndex(seperators,llGetSubString(seperators,6,6)) < 6) << 1, -1), index << 1)) != ""); } list TightListTypeParse(string input) { list partial; if(llStringLength(input) > 6) { string seperators = llGetSubString(input,(0),6); integer pos = ([] != (partial = llList2List(input + llParseStringKeepNulls(llDeleteSubString(input,(0),5), [],[input=llGetSubString(seperators,(0),(0)), llGetSubString(seperators,1,1),llGetSubString(seperators,2,2),llGetSubString(seperators,3,3), llGetSubString(seperators,4,4),llGetSubString(seperators,5,5)]), (llSubStringIndex(seperators,llGetSubString(seperators,6,6)) < 6) << 1, -1))); integer type = (0); integer sub_pos = (0); do { list current = (list)(input = llList2String(partial, sub_pos= -~pos));//TYPE_STRING || TYPE_INVALID (though we don't care about invalid) if(!(type = llSubStringIndex(seperators, llList2String(partial,pos))))//TYPE_INTEGER current = (list)((integer)input); else if(type == 1)//TYPE_FLOAT current = (list)((float)input); else if(type == 3)//TYPE_KEY current = (list)((key)input); else if(type == 4)//TYPE_VECTOR current = (list)((vector)input); else if(type == 5)//TYPE_ROTATION current = (list)((rotation)input); partial = llListReplaceList(partial, current, pos, sub_pos); }while((pos= -~sub_pos) & 0x80000000); } return partial; } string TightListTypeDump(list input, string seperators) {//This function is dangerous seperators += "|/?!@#$%^&*()_=:;~`'<>{}[],.\n\" qQxXzZ\\"; //"//Buggy highlighter fix string cumulator = (string)(input); integer counter = (0); do if(~llSubStringIndex(cumulator,llGetSubString(seperators,counter,counter))) seperators = llDeleteSubString(seperators,counter,counter); else counter = -~counter; while(counter<6); seperators = llGetSubString(seperators,(0),5); cumulator = ""; if((counter = (input != []))) { do { integer type = ~-llGetListEntryType(input, counter = ~-counter); cumulator = (cumulator = llGetSubString(seperators,type,type)) + llList2String(input,counter) + cumulator; }while(counter); } return seperators + cumulator; }
ESL
Macro | Description |
---|---|
TIGHTLISTPARSE | Standard TightListParse function TightListParse(string in) |
TIGHTLISTDUMP | Standard TightListDump function TightListDump(list in, string seps) |
TIGHTLISTDUMPSIMPLE | Simple TightListDump function (takes on less parameter) TightListDump(list in) |
TIGHTLISTDUMPUNICODE requires byte2hex | A Standard version of TLD which generates it's separator systematicaly instead of using the built in list. |
TIGHTLISTUNICODESUPPORT requires byte2hex | Adds unicode generation support TIGHTLISTDUMP and TIGHTLISTDUMPSIMPLE |
TIGHTLISTTYPEQUICKREAD | A function for breaking a TightListType string into it's pairs, TightListTypeQuickRead is usualy a macro but by specifying this flag, it becomes a stand alone function (which can be advantageous if using more then one TLT function as a bytecode savings). |
NO_TIGHTLISTTYPEQUICKREAD | Causes TIGHTLISTTYPEQUICKREAD to become undefined |
TIGHTLISTTYPELENGTH | Returns the number of entries in a TLT string. TightListTypeLength(string in) |
TIGHTLISTTYPEENTRYTYPE | Returns the entry type of an entry in a TLT string. TightListTypeEntryType(string in, integer index) |
TIGHTLISTTYPEPARSE | Converts a TLT string into a fully type list. TightListTypeParse(string in) |
TIGHTLISTTYPEDUMP | Converts a list into a TLT string TightListTypeDump(list in, string seps) |
ALL_TLTD(type, input, index, output_buffer) optional | A specialized mode that passes the list entry onto another function to escape. |
TLD_DUMPLIST2STRING_MASK required if ALL_TLTD is defined | A macro that effects ALL_TLTD, a mask of types (1 << (TYPE_* - 1)) |
TLTDV(input, index) optional | Specialized function for converting a vector into a string |
TLTDR(input, index) optional | Specialized function for converting a rotation into a string |
TLTDF(input, index) optional | Specialized function for converting a float into a string |
TLTDI(input, index) optional | Specialized function for converting an integer into a string |
TLTDK(input, index) optional | Specialized function for converting a key into a string |
TLTDS(input, index) optional | Specialized function for converting a string into a string |
TLD_DUMPLIST2STRING(in, sep) Optional, used in TLD and TLTD | If left undefined, it resolves to llDumpList2String(in, sep), this is useful for when a script uses a specialized encoder for list types. |
TIGHTLISTDUMPCHARS Optional, used in TLD and TLTD | List of characters to use in TLD and TLTD |
TIGHTLISTDUMPCHARS_LENGTH | Number of characters in TIGHTLISTDUMPCHARS must be defined if TIGHTLISTDUMPCHARS is defined, otherwise it must be undefined. |
TIGHTLISTDUMPCHARS_LENGTH_1 | TIGHTLISTDUMPCHARS_LENGTH - 1 must be defined if TIGHTLISTDUMPCHARS is defined, otherwise it must be undefined. |
TIGHTLISTDUMPCHARS_LENGTH_2 | -TIGHTLISTDUMPCHARS_LENGTH - 1 must be defined if TIGHTLISTDUMPCHARS is defined, otherwise it must be undefined. |
TIGHTLISTDUMPCHARS_LENGTH_3 | -TIGHTLISTDUMPCHARS_LENGTH - 2 must be defined if TIGHTLISTDUMPCHARS is defined, otherwise it must be undefined. |
TIGHTLISTDUMPCHARS_LENGTH_4 | -TIGHTLISTDUMPCHARS_LENGTH must be defined if TIGHTLISTDUMPCHARS is defined, otherwise it must be undefined. |
NICENAMES | Translates the variable names into nice easily read names. |
ZERO_INTEGER | Used to define zero, this value does not need to be set |
CRAZY_MODE | Only works if ZERO_INTEGER is left unset, it enables a bytecode savings for ZERO_INTEGER (which comes at the cost of speed). |
Example:
#define TIGHTLISTTYPEDUMP
#define TIGHTLISTTYPEPARSE
#define TIGHTLISTTYPELENGTH
#define TIGHTLISTTYPEENTRYTYPE
#define TIGHTLISTDUMP
#define TIGHTLISTPARSE
#define NICENAMES
#include "Tightlist.esl"
string hexc="0123456789ABCDEF"; string byte2hex(integer x) {//Helper function for use with unicode characters. return llGetSubString(hexc, x = ((x >> 4) & 0xF), x) + llGetSubString(hexc, x & 0xF, x & 0xF); }
Source:
//TightList family of functions Version 1.0 //Copyright Strife Onizuka 2004-2006 //Free to use and distribute as long as this message is not removed. #ifndef TIGHTLISTDUMPCHARS #define TIGHTLISTDUMPCHARS "|/?!@#$%^&*()_=:;~`'<>{}[],.\n\" qQxXzZ\\" #define TIGHTLISTDUMPCHARS_LENGTH_1 37 #define TIGHTLISTDUMPCHARS_LENGTH 38 #define TIGHTLISTDUMPCHARS_LENGTH_4 -38 #define TIGHTLISTDUMPCHARS_LENGTH_2 -39 #define TIGHTLISTDUMPCHARS_LENGTH_3 -40 #endif #if !defined(ZERO_INTEGER) #ifndef REMOVE_ZERO_INTEGER #define REMOVE_ZERO_INTEGER #endif #ifdef CRAZY_MODE #define ZERO_INTEGER ((integer)"") #else #define ZERO_INTEGER (0) #endif #endif #if defined(TLD_DUMPLIST2STRING) #if !defined(ALL_TLTD) && defined(TLD_DUMPLIST2STRING_MASK) && (TLD_DUMPLIST2STRING_MASK > 0 || TLD_DUMPLIST2STRING_MASK < 0) #if TLD_DUMPLIST2STRING_MASK == 0x3F #define PURE_ALL_TLTD(list,pos) \ TLD_DUMPLIST2STRING(llList2List(list,pos,pos)TLD_DUMPLIST2STRING_PARAM) #else #ifndef TLD_DUMPLIST2STRING_PARAM #define TLD_DUMPLIST2STRING_PARAM #endif #define ALL_TLTD(type,list,pos,return) \ if(TLD_DUMPLIST2STRING_MASK & (1 << type)) \ return = TLD_DUMPLIST2STRING(llList2List(list,pos,pos)TLD_DUMPLIST2STRING_PARAM) #endif #endif #endif #ifndef TLD_DUMPLIST2STRING #define TLD_DUMPLIST2STRING llDumpList2String #ifndef TLD_TYPECAST_DUMPLIST2STRING #define TLD_TYPECAST_DUMPLIST2STRING(a) (string)(a) #endif #endif #ifndef TLD_TYPECAST_DUMPLIST2STRING #define TLD_TYPECAST_DUMPLIST2STRING(a) TLD_DUMPLIST2STRING(a,"") #endif #ifdef TIGHTLISTPARSE #ifdef NICENAMES #define a input #define b seperator #endif #ifdef CRAZY_MODE list TightListParse(string a) { string b = llGetSubString(a,ZERO_INTEGER,ZERO_INTEGER);//save memory return llParseStringKeepNulls(llDeleteSubString(a,(integer)(a=""),ZERO_INTEGER), [b],[]); } #else list TightListParse(string a) { string b = llGetSubString(a,ZERO_INTEGER,ZERO_INTEGER);//save memory return llParseStringKeepNulls(llDeleteSubString(a,ZERO_INTEGER,ZERO_INTEGER), [a=b],[]); } #endif #ifdef NICENAMES #undef a #undef b #endif #endif #if defined(TIGHTLISTDUMPSIMPLE) || defined(TIGHTLISTDUMP) #ifdef NICENAMES #define a input #define b possibles #define c buffer #define d counter #define e uft8char #endif #ifdef TIGHTLISTDUMPUNICODE string TightListDump(list a) {//TLD(unicode) makes a string from a list using a seperator that is supposed to be unique to the string string c = TLD_TYPECAST_DUMPLIST2STRING(a);//dump the list without a seperator integer e = ZERO_INTEGER; do //generate a seperator { integer d = ((e >= 0x80) + (e >= 0x800) + (e >= 0x10000) + (e >= 0x200000) + ((e=-~e) >= 0x4000000)); string b = "%" + byte2hex((e >> (6 * d)) | ((0x7F80 >> d) << !d)); while(d) b += "%" + byte2hex((((e >> (6 * (d=~-d))) | 0x80) & 0xBF)); }while(b != "" && ~llSubStringIndex(c,b = llUnescapeURL(b))); c = ""; return b + TLD_DUMPLIST2STRING(a, b);//pray we don't have a collision. } #elif defined(TIGHTLISTDUMPSIMPLE) string TightListDump(list a) {//TLD(simple) makes a string from a list using a seperator that is supposed to be unique to the string string b = TIGHTLISTDUMPCHARS;//Good character set of rarely used letters. string c = TLD_TYPECAST_DUMPLIST2STRING(a);//dump the list without a seperator integer d = TIGHTLISTDUMPCHARS_LENGTH_2; do; while(~llSubStringIndex(c,llGetSubString(b,d,d)) && (d= -~d));//search for unique seperator #if TIGHTLISTUNICODESUPPORT if(d) b = llGetSubString(b,d,d);//extract the seperator else { integer e = 0; do for(b = "%" + byte2hex((e >> (6 * d)) | ((0x7F80 >> d) << !(d = ((e >= 0x80) + (e >= 0x800) + (e >= 0x10000) + (e >= 0x200000) + ((e=-~e) >= 0x4000000)))));d;b += "%" + byte2hex((((e >> (6 * (d=~-d))) | 0x80) & 0xBF))); while(b != "" &&~llSubStringIndex(c,b = llUnescapeURL(b))); } c = "";//saves memory return b + TLD_DUMPLIST2STRING(a, b);//pray we don't have a collision. #else c = llGetSubString(b,d,d);//extract the seperator, saves memory (by releasing c) return c + TLD_DUMPLIST2STRING(a, c);//pray we don't have a collision. #endif } #else string TightListDump(list a, string b) {//TLD( complex) makes a string from a list using a seperator that is supposed to be unique to the string string c = TLD_TYPECAST_DUMPLIST2STRING(a);//dump the list without a seperator integer d = TIGHTLISTDUMPCHARS_LENGTH_2 - llStringLength(b); if(d == TIGHTLISTDUMPCHARS_LENGTH_3) if(!~llSubStringIndex(c,b)) jump end;//woot, we were given a unique seperator b += TIGHTLISTDUMPCHARS;//Good character set of rarely used letters. do; while(~llSubStringIndex(c,llGetSubString(b,d,d)) && (d=-~d));//search for unique seperator #if TIGHTLISTUNICODESUPPORT if(d) b = llGetSubString(b,d,d);//extract the seperator else { integer e = 0; do for(b = "%" + byte2hex((e >> (6 * d)) | ((0x7F80 >> d) << !(d = ((e >= 0x80) + (e >= 0x800) + (e >= 0x10000) + (e >= 0x200000) + ((e=-~e) >= 0x4000000)))));d;b += "%" + byte2hex((((e >> (6 * (d=~-d))) | 0x80) & 0xBF))); while(b != "" &&~llSubStringIndex(c,b = llUnescapeURL(b))); } #else b = llGetSubString(b,d,d); #endif @end; c = "";//save memory return b + TLD_DUMPLIST2STRING((a = []) + a, b); } #endif #ifdef NICENAMES #undef a #undef b #undef c #undef d #undef e #endif #endif #if defined(TIGHTLISTTYPEQUICKREAD) && defined(NO_TIGHTLISTTYPEQUICKREAD) #undef TIGHTLISTTYPEQUICKREAD #endif #if defined(TIGHTLISTTYPEQUICKREAD) || defined(TIGHTLISTTYPELENGTH) || defined(TIGHTLISTTYPEENTRYTYPE) || defined(TIGHTLISTTYPEPARSE) || defined(TIGHTLISTTYPEDUMP) #ifdef TIGHTLISTTYPEQUICKREAD #ifdef NICENAMES #define a input #define m seperators #endif list TightListTypeQuickRead(string a, string m) {//all TightListType* parse functions call QuickRead, as a macro or as a function. QuickRead #endif //{ #ifndef TightListTypeQuickRead #define TightListTypeQuickRead(a,m) llList2List(a + \ llParseStringKeepNulls(llDeleteSubString(a,ZERO_INTEGER,5), [],[a=llGetSubString(m,ZERO_INTEGER,ZERO_INTEGER), \ llGetSubString(m,1,1),llGetSubString(m,2,2),llGetSubString(m,3,3), \ llGetSubString(m,4,4),llGetSubString(m,5,5)]), \ (llSubStringIndex(m,llGetSubString(m,6,6)) < 6) << 1, -1) #ifdef TIGHTLISTTYPEQUICKREAD return TightListTypeQuickRead(a,m); #undef TightListTypeQuickRead #endif #elif defined(TIGHTLISTTYPEQUICKREAD) return TightListTypeQuickRead(a,m); #endif #ifdef TIGHTLISTTYPEQUICKREAD //} } #ifdef NICENAMES #undef a #undef m #endif #endif #endif #ifdef TIGHTLISTTYPELENGTH #ifdef QUICKREAD #define TightListTypeLength(a) ((TightListTypeQuickRead(a) != []) >> 1) #else #ifdef NICENAMES #define a input #define m seperators #endif integer TightListTypeLength(string a) { string m = llGetSubString(a,ZERO_INTEGER,6); return ((llParseStringKeepNulls(llDeleteSubString(a,ZERO_INTEGER,5), [],[a=llGetSubString(m,ZERO_INTEGER,ZERO_INTEGER), llGetSubString(m,1,1),llGetSubString(m,2,2),llGetSubString(c,3,3), llGetSubString(m,4,4),llGetSubString(m,5,5)]) != []) + (llSubStringIndex(m,llGetSubString(m,6,6)) < 6)) >> 1; } #ifdef NICENAMES #undef a #undef m #endif #endif #endif #ifdef TIGHTLISTTYPEENTRYTYPE #ifdef NICENAMES #define a input #define b index #define m seperators #endif integer TightListTypeEntryType(string a, integer b) { string m = llGetSubString(a,ZERO_INTEGER,6); return llSubStringIndex(m, a) + ((a = llList2String(TightListTypeQuickRead(a, m), b << 1)) != ""); } #ifdef NICENAMES #undef a #undef b #undef m #endif #endif #ifdef TIGHTLISTTYPEPARSE #ifdef NICENAMES #define a input #define b partial #define c pos #define d type #define e sub_pos #define f current #define m seperators #endif list TightListTypeParse(string a) { list b; if(llStringLength(a) > 6) { string m = llGetSubString(a,ZERO_INTEGER,6); integer c = ([] != (b = TightListTypeQuickRead(a, m))); integer d = ZERO_INTEGER; integer e = ZERO_INTEGER; do { list f = (list)(a = llList2String(b, e= -~c));//TYPE_STRING || TYPE_INVALID (though we don't care about invalid) if(!(d = llSubStringIndex(m, llList2String(b,c))))//TYPE_INTEGER f = (list)((integer)a); else if(d == 1)//TYPE_FLOAT f = (list)((float)a); else if(d == 3)//TYPE_KEY f = (list)((key)a); else if(d == 4)//TYPE_VECTOR f = (list)((vector)a); else if(d == 5)//TYPE_ROTATION f = (list)((rotation)a); b = llListReplaceList(b, f, c, e); }while((c= -~e) & 0x80000000); } return b; } #ifdef NICENAMES #undef a #undef b #undef c #undef d #undef e #undef f #undef m #endif #endif #ifdef TIGHTLISTTYPEDUMP #ifdef NICENAMES #define a input #define b seperators #define c cumulator #define d counter #define e type #define f buffer #endif string TightListTypeDump(list a, string b) {//This function is dangerous b += TIGHTLISTDUMPCHARS; string c = TLD_TYPECAST_DUMPLIST2STRING(a); integer d = ZERO_INTEGER; do if(~llSubStringIndex(c,llGetSubString(b,d,d))) b = llDeleteSubString(b,d,d); else d = -~d; while(d<6); b = llGetSubString(b,ZERO_INTEGER,5); #ifndef PURE_ALL_TLTD #if defined(ALL_TLTD) || defined(TLTDV) || defined(TLTDR) || defined(TLTDF) || defined(TLTDI) || defined(TLTDS) || defined(TLTDK) string f = c = ""; #else c = ""; #ifdef f #undef f #endif #define f llList2String(a,d) #endif #else #ifdef f #undef f #endif #define f PURE_ALL_TLTD(a,d) #endif if((d = (a != []))) { do { integer e = ~-llGetListEntryType(a, d = ~-d); #ifndef PURE_ALL_TLTD #if defined(ALL_TLTD) || defined(TLTDV) || defined(TLTDR) || defined(TLTDF) || defined(TLTDI) || defined(TLTDS) || defined(TLTDK) #if defined(TLTDV) && defined(TLTDR) && defined(TLTDF) && defined(TLTDI) && defined(TLTDS) && defined(TLTDK) #ifdef ALL_TLTD //you use this like #define ALL_TLTD(type, data, pos, out) if(type == TYPE_*){ out = your code( llList2*(data, pos)); } ALL_TLTD(e, a, d, f); else #endif if(e == 4)//TYPE_VECTOR - 1 f = TLTDV(a,d); else if(e == 5)//TYPE_ROTATION - 1 f = TLTDR(a,d); else if(e == 1)//TYPE_FLOAT - 1 f = TLTDF(a,d); else if(e == 0)//TYPE_INTEGER - 1 f = TLTDI(a,d); else if(e == 3)//TYPE_KEY - 1 f = TLTDK(a,d); else if(e == 2)//TYPE_STRING - 1 f = TLTDS(a,d); #// //} #else //{ #ifdef ALL_TLTD //you use this like #define ALL_TLTD(type, data, pos, out) if(type == TYPE_*){ out = your code( llList2*(data, pos)); } ALL_TLTD(e, a, d, f); else #endif #ifdef TLTDV //you use this like #define TLTDV(data, pos) your_func(llList2Vector(data, pos)); if(e == 4)//TYPE_VECTOR - 1 f = TLTDV(a,d); else #endif #ifdef TLTDR //you use this like #define TLTDR(data, pos) your_func(llList2Rotation(data, pos)); if(e == 5)//TYPE_ROTATION - 1 f = TLTDR(a,d); else #endif #ifdef TLTDF //you use this like #define TLTDF(data, pos) your_func(llList2Float(data, pos)); if(e == 1)//TYPE_FLOAT - 1 f = TLTDF(a,d); else #endif #ifdef TLTDI //you use this like #define TLTDI(data, pos) your_func(llList2Integer(data, pos)); if(e == 0)//TYPE_INTEGER - 1 f = TLTDI(a,d); else #endif #ifdef TLTDK //you use this like #define TLTDK(data, pos) your_func(llList2Key(data, pos)); if(e == 3)//TYPE_KEY - 1 f = TLTDK(a,d); else #endif #ifdef TLTDS //you use this like #define TLTDS(data, pos) your_func(llList2Key(data, pos)); if(e == 2)//TYPE_STRING - 1 f = TLTDS(a,d); else #endif f = llList2String(a,d); #endif #endif #endif c = (c = llGetSubString(b,e,e)) + f + c; }while(d); } return b + c; } #ifdef f #undef f #endif #ifdef NICENAMES #undef a #undef b #undef c #undef d #undef e #endif #endif #ifdef REMOVE_ZERO_INTEGER #undef REMOVE_ZERO_INTEGER #undef ZERO_INTEGER #endif