From: Alan M Webb <awebb@vnet.ibm.com>
This patch is intended to implement the following functions: llIntegerToBase64 llBase64ToInteger llParseStringKeepNulls None of these functions are dependent upon state elsewhere in the SIM, so they are appropriately self-contained. I've tested them out of context, and from a script attached to an object in my test region.0.6.0-stable
parent
2d0db170a3
commit
de1024adf7
|
@ -3244,18 +3244,259 @@ namespace OpenSim.Region.ScriptEngine.Common
|
||||||
NotImplemented("llGetPrimitiveParams");
|
NotImplemented("llGetPrimitiveParams");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// <remarks>
|
||||||
|
// <para>
|
||||||
|
// The .NET definition of base 64 is:
|
||||||
|
// <list>
|
||||||
|
// <item>
|
||||||
|
// Significant: A-Z a-z 0-9 + -
|
||||||
|
// </item>
|
||||||
|
// <item>
|
||||||
|
// Whitespace: \t \n \r ' '
|
||||||
|
// </item>
|
||||||
|
// <item>
|
||||||
|
// Valueless: =
|
||||||
|
// </item>
|
||||||
|
// <item>
|
||||||
|
// End-of-string: \0 or '=='
|
||||||
|
// </item>
|
||||||
|
// </list>
|
||||||
|
// </para>
|
||||||
|
// <para>
|
||||||
|
// Each point in a base-64 string represents
|
||||||
|
// a 6 bit value. A 32-bit integer can be
|
||||||
|
// represented using 6 characters (with some
|
||||||
|
// redundancy).
|
||||||
|
// </para>
|
||||||
|
// <para>
|
||||||
|
// LSL requires a base64 string to be 8
|
||||||
|
// characters in length. LSL also uses '/'
|
||||||
|
// rather than '-' (MIME compliant).
|
||||||
|
// </para>
|
||||||
|
// <para>
|
||||||
|
// RFC 1341 used as a reference (as specified
|
||||||
|
// by the SecondLife Wiki).
|
||||||
|
// </para>
|
||||||
|
// <para>
|
||||||
|
// SL do not record any kind of exception for
|
||||||
|
// these functions, so the string to integer
|
||||||
|
// conversion returns '0' if an invalid
|
||||||
|
// character is encountered during conversion.
|
||||||
|
// </para>
|
||||||
|
// <para>
|
||||||
|
// References
|
||||||
|
// <list>
|
||||||
|
// <item>
|
||||||
|
// http://lslwiki.net/lslwiki/wakka.php?wakka=Base64
|
||||||
|
// </item>
|
||||||
|
// <item>
|
||||||
|
// </item>
|
||||||
|
// </list>
|
||||||
|
// </para>
|
||||||
|
// </remarks>
|
||||||
|
|
||||||
|
// <summary>
|
||||||
|
// Table for converting 6-bit integers into
|
||||||
|
// base-64 characters
|
||||||
|
// </summary>
|
||||||
|
|
||||||
|
private static readonly char[] i2ctable =
|
||||||
|
{
|
||||||
|
'A','B','C','D','E','F','G','H',
|
||||||
|
'I','J','K','L','M','N','O','P',
|
||||||
|
'Q','R','S','T','U','V','W','X',
|
||||||
|
'Y','Z',
|
||||||
|
'a','b','c','d','e','f','g','h',
|
||||||
|
'i','j','k','l','m','n','o','p',
|
||||||
|
'q','r','s','t','u','v','w','x',
|
||||||
|
'y','z',
|
||||||
|
'0','1','2','3','4','5','6','7',
|
||||||
|
'8','9',
|
||||||
|
'+','/'
|
||||||
|
};
|
||||||
|
|
||||||
|
// <summary>
|
||||||
|
// Table for converting base-64 characters
|
||||||
|
// into 6-bit integers.
|
||||||
|
// </summary>
|
||||||
|
|
||||||
|
private static readonly int[] c2itable =
|
||||||
|
{
|
||||||
|
-1,-1,-1,-1,-1,-1,-1,-1, // 0x
|
||||||
|
-1,-1,-1,-1,-1,-1,-1,-1,
|
||||||
|
-1,-1,-1,-1,-1,-1,-1,-1, // 1x
|
||||||
|
-1,-1,-1,-1,-1,-1,-1,-1,
|
||||||
|
-1,-1,-1,-1,-1,-1,-1,-1, // 2x
|
||||||
|
-1,-1,-1,63,-1,-1,-1,64,
|
||||||
|
53,54,55,56,57,58,59,60, // 3x
|
||||||
|
61,62,-1,-1,-1,0,-1,-1,
|
||||||
|
-1,1,2,3,4,5,6,7, // 4x
|
||||||
|
8,9,10,11,12,13,14,15,
|
||||||
|
16,17,18,19,20,21,22,23, // 5x
|
||||||
|
24,25,26,-1,-1,-1,-1,-1,
|
||||||
|
-1,27,28,29,30,31,32,33, // 6x
|
||||||
|
34,35,36,37,38,39,40,41,
|
||||||
|
42,43,44,45,46,47,48,49, // 7x
|
||||||
|
50,51,52,-1,-1,-1,-1,-1,
|
||||||
|
-1,-1,-1,-1,-1,-1,-1,-1, // 8x
|
||||||
|
-1,-1,-1,-1,-1,-1,-1,-1,
|
||||||
|
-1,-1,-1,-1,-1,-1,-1,-1, // 9x
|
||||||
|
-1,-1,-1,-1,-1,-1,-1,-1,
|
||||||
|
-1,-1,-1,-1,-1,-1,-1,-1, // Ax
|
||||||
|
-1,-1,-1,-1,-1,-1,-1,-1,
|
||||||
|
-1,-1,-1,-1,-1,-1,-1,-1, // Bx
|
||||||
|
-1,-1,-1,-1,-1,-1,-1,-1,
|
||||||
|
-1,-1,-1,-1,-1,-1,-1,-1, // Cx
|
||||||
|
-1,-1,-1,-1,-1,-1,-1,-1,
|
||||||
|
-1,-1,-1,-1,-1,-1,-1,-1, // Dx
|
||||||
|
-1,-1,-1,-1,-1,-1,-1,-1,
|
||||||
|
-1,-1,-1,-1,-1,-1,-1,-1, // Ex
|
||||||
|
-1,-1,-1,-1,-1,-1,-1,-1,
|
||||||
|
-1,-1,-1,-1,-1,-1,-1,-1, // Fx
|
||||||
|
-1,-1,-1,-1,-1,-1,-1,-1
|
||||||
|
};
|
||||||
|
|
||||||
|
// <summary>
|
||||||
|
// Converts a 32-bit integer into a Base64
|
||||||
|
// character string. Base64 character strings
|
||||||
|
// are always 8 characters long. All iinteger
|
||||||
|
// values are acceptable.
|
||||||
|
// </summary>
|
||||||
|
// <param name="number">
|
||||||
|
// 32-bit integer to be converted.
|
||||||
|
// </param>
|
||||||
|
// <returns>
|
||||||
|
// 8 character string. The 1st six characters
|
||||||
|
// contain the encoded number, the last two
|
||||||
|
// characters are padded with "=".
|
||||||
|
// </returns>
|
||||||
|
|
||||||
public string llIntegerToBase64(int number)
|
public string llIntegerToBase64(int number)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
// uninitialized string
|
||||||
|
|
||||||
|
char[] imdt = new char[8];
|
||||||
|
|
||||||
m_host.AddScriptLPS(1);
|
m_host.AddScriptLPS(1);
|
||||||
NotImplemented("llIntegerToBase64");
|
|
||||||
return String.Empty;
|
// Manually unroll the loop
|
||||||
|
|
||||||
|
imdt[7] = '=';
|
||||||
|
imdt[6] = '=';
|
||||||
|
imdt[5] = i2ctable[number<<4 & 0x3F];
|
||||||
|
imdt[4] = i2ctable[number>>2 & 0x3F];
|
||||||
|
imdt[3] = i2ctable[number>>8 & 0x3F];
|
||||||
|
imdt[2] = i2ctable[number>>14 & 0x3F];
|
||||||
|
imdt[1] = i2ctable[number>>20 & 0x3F];
|
||||||
|
imdt[0] = i2ctable[number>>26 & 0x3F];
|
||||||
|
|
||||||
|
return new string(imdt);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// <summary>
|
||||||
|
// Converts an eight character base-64 string
|
||||||
|
// into a 32-bit integer.
|
||||||
|
// </summary>
|
||||||
|
// <param name="str">
|
||||||
|
// 8 characters string to be converted. Other
|
||||||
|
// length strings return zero.
|
||||||
|
// </param>
|
||||||
|
// <returns>
|
||||||
|
// Returns an integer representing the
|
||||||
|
// encoded value providedint he 1st 6
|
||||||
|
// characters of the string.
|
||||||
|
// </returns>
|
||||||
|
// <remarks>
|
||||||
|
// This is coded to behave like LSL's
|
||||||
|
// implementation (I think), based upon the
|
||||||
|
// information available at the Wiki.
|
||||||
|
// If more than 8 characters are supplied,
|
||||||
|
// zero is returned.
|
||||||
|
// If a NULL string is supplied, zero will
|
||||||
|
// be returned.
|
||||||
|
// If fewer than 6 characters are supplied, then
|
||||||
|
// the answer will reflect a partial
|
||||||
|
// accumulation.
|
||||||
|
// <para>
|
||||||
|
// The 6-bit segments are
|
||||||
|
// extracted left-to-right in big-endian mode,
|
||||||
|
// which means that segment 6 only contains the
|
||||||
|
// two low-order bits of the 32 bit integer as
|
||||||
|
// its high order 2 bits. A short string therefore
|
||||||
|
// means loss of low-order information. E.g.
|
||||||
|
//
|
||||||
|
// |<---------------------- 32-bit integer ----------------------->|<-Pad->|
|
||||||
|
// |<--Byte 0----->|<--Byte 1----->|<--Byte 2----->|<--Byte 3----->|<-Pad->|
|
||||||
|
// |3|3|2|2|2|2|2|2|2|2|2|2|1|1|1|1|1|1|1|1|1|1| | | | | | | | | | |P|P|P|P|
|
||||||
|
// |1|0|9|8|7|6|5|4|3|2|1|0|9|8|7|6|5|4|3|2|1|0|9|8|7|6|5|4|3|2|1|0|P|P|P|P|
|
||||||
|
// | str[0] | str[1] | str[2] | str[3] | str[4] | str[6] |
|
||||||
|
//
|
||||||
|
// </para>
|
||||||
|
// </remarks>
|
||||||
|
|
||||||
public int llBase64ToInteger(string str)
|
public int llBase64ToInteger(string str)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
int number = 0;
|
||||||
|
int digit;
|
||||||
|
int baddigit = 0;
|
||||||
|
|
||||||
m_host.AddScriptLPS(1);
|
m_host.AddScriptLPS(1);
|
||||||
NotImplemented("llBase64ToInteger");
|
|
||||||
return 0;
|
// Require a well-fromed base64 string
|
||||||
|
|
||||||
|
if(str.Length > 8)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
// The loop is unrolled in the interests
|
||||||
|
// of performance and simple necessity.
|
||||||
|
//
|
||||||
|
// MUST find 6 digits to be well formed
|
||||||
|
// -1 == invalid
|
||||||
|
// 0 == padding
|
||||||
|
|
||||||
|
if((digit=c2itable[str[0]])<=0)
|
||||||
|
{
|
||||||
|
return digit<0?(int)0:number;
|
||||||
|
}
|
||||||
|
number += --digit<<26;
|
||||||
|
|
||||||
|
if((digit=c2itable[str[1]])<=0)
|
||||||
|
{
|
||||||
|
return digit<0?(int)0:number;
|
||||||
|
}
|
||||||
|
number += --digit<<20;
|
||||||
|
|
||||||
|
if((digit=c2itable[str[2]])<=0)
|
||||||
|
{
|
||||||
|
return digit<0?(int)0:number;
|
||||||
|
}
|
||||||
|
number += --digit<<14;
|
||||||
|
|
||||||
|
if((digit=c2itable[str[3]])<=0)
|
||||||
|
{
|
||||||
|
return digit<0?(int)0:number;
|
||||||
|
}
|
||||||
|
number += --digit<<8;
|
||||||
|
|
||||||
|
if((digit=c2itable[str[4]])<=0)
|
||||||
|
{
|
||||||
|
return digit<0?(int)0:number;
|
||||||
|
}
|
||||||
|
number += --digit<<2;
|
||||||
|
|
||||||
|
if((digit=c2itable[str[5]])<=0)
|
||||||
|
{
|
||||||
|
return digit<0?(int)0:number;
|
||||||
|
}
|
||||||
|
number += --digit>>4;
|
||||||
|
|
||||||
|
// ignore trailing padding
|
||||||
|
|
||||||
|
return number;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public double llGetGMTclock()
|
public double llGetGMTclock()
|
||||||
|
@ -3276,11 +3517,179 @@ namespace OpenSim.Region.ScriptEngine.Common
|
||||||
m_host.RotationOffset = new LLQuaternion((float)rot.x, (float)rot.y, (float)rot.z, (float)rot.s);
|
m_host.RotationOffset = new LLQuaternion((float)rot.x, (float)rot.y, (float)rot.z, (float)rot.s);
|
||||||
}
|
}
|
||||||
|
|
||||||
public LSL_Types.list llParseStringKeepNulls(string src, LSL_Types.list seperators, LSL_Types.list spacers)
|
// <summary>
|
||||||
|
// Scan the string supplied in 'src' and
|
||||||
|
// tokenize it based upon two sets of
|
||||||
|
// tokenizers provided in two lists,
|
||||||
|
// separators and spacers.
|
||||||
|
// </summary>
|
||||||
|
//
|
||||||
|
// <remarks>
|
||||||
|
// Separators demarcate tokens and are
|
||||||
|
// elided as they are encountered. Spacers
|
||||||
|
// also demarcate tokens, but are themselves
|
||||||
|
// retained as tokens.
|
||||||
|
//
|
||||||
|
// Both separators and spacers may be arbitrarily
|
||||||
|
// long strings. i.e. ":::".
|
||||||
|
//
|
||||||
|
// The function returns an ordered list
|
||||||
|
// representing the tokens found in the supplied
|
||||||
|
// sources string. If two successive tokenizers
|
||||||
|
// are encountered, then a NULL entry is added
|
||||||
|
// to the list.
|
||||||
|
//
|
||||||
|
// It is a precondition that the source and
|
||||||
|
// toekizer lisst are non-null. If they are null,
|
||||||
|
// then a null pointer exception will be thrown
|
||||||
|
// while their lengths are being determined.
|
||||||
|
//
|
||||||
|
// A small amount of working memoryis required
|
||||||
|
// of approximately 8*#tokenizers.
|
||||||
|
//
|
||||||
|
// There are many ways in which this function
|
||||||
|
// can be implemented, this implementation is
|
||||||
|
// fairly naive and assumes that when the
|
||||||
|
// function is invooked with a short source
|
||||||
|
// string and/or short lists of tokenizers, then
|
||||||
|
// performance will not be an issue.
|
||||||
|
//
|
||||||
|
// In order to minimize the perofrmance
|
||||||
|
// effects of long strings, or large numbers
|
||||||
|
// of tokeizers, the function skips as far as
|
||||||
|
// possible whenever a toekenizer is found,
|
||||||
|
// and eliminates redundant tokenizers as soon
|
||||||
|
// as is possible.
|
||||||
|
//
|
||||||
|
// The implementation tries to avoid any copying
|
||||||
|
// of arrays or other objects.
|
||||||
|
// </remarks>
|
||||||
|
|
||||||
|
public LSL_Types.list llParseStringKeepNulls(string src, LSL_Types.list separators, LSL_Types.list spacers)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
int beginning = 0;
|
||||||
|
int srclen = src.Length;
|
||||||
|
int seplen = separators.Length;
|
||||||
|
object[] separray = separators.Data;
|
||||||
|
int spclen = spacers.Length;
|
||||||
|
object[] spcarray = spacers.Data;
|
||||||
|
int mlen = seplen+spclen;
|
||||||
|
|
||||||
|
int[] offset = new int[mlen+1];
|
||||||
|
bool[] active = new bool[mlen];
|
||||||
|
|
||||||
|
int best;
|
||||||
|
int j;
|
||||||
|
|
||||||
|
// Initial capacity reduces resize cost
|
||||||
|
|
||||||
|
LSL_Types.list tokens = new LSL_Types.list();
|
||||||
|
|
||||||
m_host.AddScriptLPS(1);
|
m_host.AddScriptLPS(1);
|
||||||
NotImplemented("llParseStringKeepNulls");
|
|
||||||
return new LSL_Types.list();
|
// All entries are initially valid
|
||||||
|
|
||||||
|
for(int i=0; i<mlen; i++) active[i] = true;
|
||||||
|
|
||||||
|
offset[mlen] = srclen;
|
||||||
|
|
||||||
|
while(beginning<srclen)
|
||||||
|
{
|
||||||
|
|
||||||
|
best = mlen; // as bad as it gets
|
||||||
|
|
||||||
|
// Scan for separators
|
||||||
|
|
||||||
|
for(j=0; j<seplen; j++)
|
||||||
|
{
|
||||||
|
if(active[j])
|
||||||
|
{
|
||||||
|
// scan all of the markers
|
||||||
|
if((offset[j] = src.IndexOf((string)separray[j],beginning)) == -1)
|
||||||
|
{
|
||||||
|
// not present at all
|
||||||
|
active[j] = false;
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
// present and correct
|
||||||
|
if(offset[j] < offset[best])
|
||||||
|
{
|
||||||
|
// closest so far
|
||||||
|
best = j;
|
||||||
|
if(offset[best] == beginning)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scan for spacers
|
||||||
|
|
||||||
|
if(offset[best] != beginning)
|
||||||
|
{
|
||||||
|
for(j=seplen; (j<mlen) && (offset[best] > beginning); j++)
|
||||||
|
{
|
||||||
|
if(active[j])
|
||||||
|
{
|
||||||
|
// scan all of the markers
|
||||||
|
if((offset[j] = src.IndexOf((string)spcarray[j-seplen],beginning)) == -1)
|
||||||
|
{
|
||||||
|
// not present at all
|
||||||
|
active[j] = false;
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
// present and correct
|
||||||
|
if(offset[j] < offset[best])
|
||||||
|
{
|
||||||
|
// closest so far
|
||||||
|
best = j;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is the normal exit from the scanning loop
|
||||||
|
|
||||||
|
if(best == mlen)
|
||||||
|
{
|
||||||
|
// no markers were found on this pass
|
||||||
|
// so we're pretty much done
|
||||||
|
tokens.Add(src.Substring(beginning, srclen-beginning));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise we just add the newly delimited token
|
||||||
|
// and recalculate where the search should continue.
|
||||||
|
|
||||||
|
tokens.Add(src.Substring(beginning,offset[best]-beginning));
|
||||||
|
|
||||||
|
if(best<seplen)
|
||||||
|
{
|
||||||
|
beginning = offset[best]+((string)separray[best]).Length;
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
beginning = offset[best]+((string)spcarray[best-seplen]).Length;
|
||||||
|
tokens.Add(spcarray[best-seplen]);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// This an awkward an not very intuitive boundary case. If the
|
||||||
|
// last substring is a tokenizer, then there is an implied trailing
|
||||||
|
// null list entry. Hopefully the single comparison will not be too
|
||||||
|
// arduous. Alternatively the 'break' could be replced with a return
|
||||||
|
// but that's shabby programming.
|
||||||
|
|
||||||
|
if(beginning == srclen)
|
||||||
|
{
|
||||||
|
if(srclen != 0)
|
||||||
|
tokens.Add("");
|
||||||
|
}
|
||||||
|
|
||||||
|
return tokens;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void llRezAtRoot(string inventory, LSL_Types.Vector3 position, LSL_Types.Vector3 velocity,
|
public void llRezAtRoot(string inventory, LSL_Types.Vector3 position, LSL_Types.Vector3 velocity,
|
||||||
|
|
Loading…
Reference in New Issue