diff options
author | Sean Dague | 2008-02-28 21:25:28 +0000 |
---|---|---|
committer | Sean Dague | 2008-02-28 21:25:28 +0000 |
commit | de1024adf71b05c9f2f4f89fbda891211de03a42 (patch) | |
tree | f6bb06b8272b26e11cb92d20b3631fdaa8a2e4de | |
parent | sample change to see if rev actually changes on prebuild when I do this (diff) | |
download | opensim-SC-de1024adf71b05c9f2f4f89fbda891211de03a42.zip opensim-SC-de1024adf71b05c9f2f4f89fbda891211de03a42.tar.gz opensim-SC-de1024adf71b05c9f2f4f89fbda891211de03a42.tar.bz2 opensim-SC-de1024adf71b05c9f2f4f89fbda891211de03a42.tar.xz |
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.
-rw-r--r-- | OpenSim/Region/ScriptEngine/Common/LSL_BuiltIn_Commands.cs | 427 |
1 files changed, 418 insertions, 9 deletions
diff --git a/OpenSim/Region/ScriptEngine/Common/LSL_BuiltIn_Commands.cs b/OpenSim/Region/ScriptEngine/Common/LSL_BuiltIn_Commands.cs index 7d4219b..7b59d73 100644 --- a/OpenSim/Region/ScriptEngine/Common/LSL_BuiltIn_Commands.cs +++ b/OpenSim/Region/ScriptEngine/Common/LSL_BuiltIn_Commands.cs | |||
@@ -3244,18 +3244,259 @@ namespace OpenSim.Region.ScriptEngine.Common | |||
3244 | NotImplemented("llGetPrimitiveParams"); | 3244 | NotImplemented("llGetPrimitiveParams"); |
3245 | } | 3245 | } |
3246 | 3246 | ||
3247 | // <remarks> | ||
3248 | // <para> | ||
3249 | // The .NET definition of base 64 is: | ||
3250 | // <list> | ||
3251 | // <item> | ||
3252 | // Significant: A-Z a-z 0-9 + - | ||
3253 | // </item> | ||
3254 | // <item> | ||
3255 | // Whitespace: \t \n \r ' ' | ||
3256 | // </item> | ||
3257 | // <item> | ||
3258 | // Valueless: = | ||
3259 | // </item> | ||
3260 | // <item> | ||
3261 | // End-of-string: \0 or '==' | ||
3262 | // </item> | ||
3263 | // </list> | ||
3264 | // </para> | ||
3265 | // <para> | ||
3266 | // Each point in a base-64 string represents | ||
3267 | // a 6 bit value. A 32-bit integer can be | ||
3268 | // represented using 6 characters (with some | ||
3269 | // redundancy). | ||
3270 | // </para> | ||
3271 | // <para> | ||
3272 | // LSL requires a base64 string to be 8 | ||
3273 | // characters in length. LSL also uses '/' | ||
3274 | // rather than '-' (MIME compliant). | ||
3275 | // </para> | ||
3276 | // <para> | ||
3277 | // RFC 1341 used as a reference (as specified | ||
3278 | // by the SecondLife Wiki). | ||
3279 | // </para> | ||
3280 | // <para> | ||
3281 | // SL do not record any kind of exception for | ||
3282 | // these functions, so the string to integer | ||
3283 | // conversion returns '0' if an invalid | ||
3284 | // character is encountered during conversion. | ||
3285 | // </para> | ||
3286 | // <para> | ||
3287 | // References | ||
3288 | // <list> | ||
3289 | // <item> | ||
3290 | // http://lslwiki.net/lslwiki/wakka.php?wakka=Base64 | ||
3291 | // </item> | ||
3292 | // <item> | ||
3293 | // </item> | ||
3294 | // </list> | ||
3295 | // </para> | ||
3296 | // </remarks> | ||
3297 | |||
3298 | // <summary> | ||
3299 | // Table for converting 6-bit integers into | ||
3300 | // base-64 characters | ||
3301 | // </summary> | ||
3302 | |||
3303 | private static readonly char[] i2ctable = | ||
3304 | { | ||
3305 | 'A','B','C','D','E','F','G','H', | ||
3306 | 'I','J','K','L','M','N','O','P', | ||
3307 | 'Q','R','S','T','U','V','W','X', | ||
3308 | 'Y','Z', | ||
3309 | 'a','b','c','d','e','f','g','h', | ||
3310 | 'i','j','k','l','m','n','o','p', | ||
3311 | 'q','r','s','t','u','v','w','x', | ||
3312 | 'y','z', | ||
3313 | '0','1','2','3','4','5','6','7', | ||
3314 | '8','9', | ||
3315 | '+','/' | ||
3316 | }; | ||
3317 | |||
3318 | // <summary> | ||
3319 | // Table for converting base-64 characters | ||
3320 | // into 6-bit integers. | ||
3321 | // </summary> | ||
3322 | |||
3323 | private static readonly int[] c2itable = | ||
3324 | { | ||
3325 | -1,-1,-1,-1,-1,-1,-1,-1, // 0x | ||
3326 | -1,-1,-1,-1,-1,-1,-1,-1, | ||
3327 | -1,-1,-1,-1,-1,-1,-1,-1, // 1x | ||
3328 | -1,-1,-1,-1,-1,-1,-1,-1, | ||
3329 | -1,-1,-1,-1,-1,-1,-1,-1, // 2x | ||
3330 | -1,-1,-1,63,-1,-1,-1,64, | ||
3331 | 53,54,55,56,57,58,59,60, // 3x | ||
3332 | 61,62,-1,-1,-1,0,-1,-1, | ||
3333 | -1,1,2,3,4,5,6,7, // 4x | ||
3334 | 8,9,10,11,12,13,14,15, | ||
3335 | 16,17,18,19,20,21,22,23, // 5x | ||
3336 | 24,25,26,-1,-1,-1,-1,-1, | ||
3337 | -1,27,28,29,30,31,32,33, // 6x | ||
3338 | 34,35,36,37,38,39,40,41, | ||
3339 | 42,43,44,45,46,47,48,49, // 7x | ||
3340 | 50,51,52,-1,-1,-1,-1,-1, | ||
3341 | -1,-1,-1,-1,-1,-1,-1,-1, // 8x | ||
3342 | -1,-1,-1,-1,-1,-1,-1,-1, | ||
3343 | -1,-1,-1,-1,-1,-1,-1,-1, // 9x | ||
3344 | -1,-1,-1,-1,-1,-1,-1,-1, | ||
3345 | -1,-1,-1,-1,-1,-1,-1,-1, // Ax | ||
3346 | -1,-1,-1,-1,-1,-1,-1,-1, | ||
3347 | -1,-1,-1,-1,-1,-1,-1,-1, // Bx | ||
3348 | -1,-1,-1,-1,-1,-1,-1,-1, | ||
3349 | -1,-1,-1,-1,-1,-1,-1,-1, // Cx | ||
3350 | -1,-1,-1,-1,-1,-1,-1,-1, | ||
3351 | -1,-1,-1,-1,-1,-1,-1,-1, // Dx | ||
3352 | -1,-1,-1,-1,-1,-1,-1,-1, | ||
3353 | -1,-1,-1,-1,-1,-1,-1,-1, // Ex | ||
3354 | -1,-1,-1,-1,-1,-1,-1,-1, | ||
3355 | -1,-1,-1,-1,-1,-1,-1,-1, // Fx | ||
3356 | -1,-1,-1,-1,-1,-1,-1,-1 | ||
3357 | }; | ||
3358 | |||
3359 | // <summary> | ||
3360 | // Converts a 32-bit integer into a Base64 | ||
3361 | // character string. Base64 character strings | ||
3362 | // are always 8 characters long. All iinteger | ||
3363 | // values are acceptable. | ||
3364 | // </summary> | ||
3365 | // <param name="number"> | ||
3366 | // 32-bit integer to be converted. | ||
3367 | // </param> | ||
3368 | // <returns> | ||
3369 | // 8 character string. The 1st six characters | ||
3370 | // contain the encoded number, the last two | ||
3371 | // characters are padded with "=". | ||
3372 | // </returns> | ||
3373 | |||
3247 | public string llIntegerToBase64(int number) | 3374 | public string llIntegerToBase64(int number) |
3248 | { | 3375 | { |
3249 | m_host.AddScriptLPS(1); | 3376 | |
3250 | NotImplemented("llIntegerToBase64"); | 3377 | // uninitialized string |
3251 | return String.Empty; | 3378 | |
3252 | } | 3379 | char[] imdt = new char[8]; |
3380 | |||
3381 | m_host.AddScriptLPS(1); | ||
3382 | |||
3383 | // Manually unroll the loop | ||
3384 | |||
3385 | imdt[7] = '='; | ||
3386 | imdt[6] = '='; | ||
3387 | imdt[5] = i2ctable[number<<4 & 0x3F]; | ||
3388 | imdt[4] = i2ctable[number>>2 & 0x3F]; | ||
3389 | imdt[3] = i2ctable[number>>8 & 0x3F]; | ||
3390 | imdt[2] = i2ctable[number>>14 & 0x3F]; | ||
3391 | imdt[1] = i2ctable[number>>20 & 0x3F]; | ||
3392 | imdt[0] = i2ctable[number>>26 & 0x3F]; | ||
3393 | |||
3394 | return new string(imdt); | ||
3395 | |||
3396 | } | ||
3397 | |||
3398 | // <summary> | ||
3399 | // Converts an eight character base-64 string | ||
3400 | // into a 32-bit integer. | ||
3401 | // </summary> | ||
3402 | // <param name="str"> | ||
3403 | // 8 characters string to be converted. Other | ||
3404 | // length strings return zero. | ||
3405 | // </param> | ||
3406 | // <returns> | ||
3407 | // Returns an integer representing the | ||
3408 | // encoded value providedint he 1st 6 | ||
3409 | // characters of the string. | ||
3410 | // </returns> | ||
3411 | // <remarks> | ||
3412 | // This is coded to behave like LSL's | ||
3413 | // implementation (I think), based upon the | ||
3414 | // information available at the Wiki. | ||
3415 | // If more than 8 characters are supplied, | ||
3416 | // zero is returned. | ||
3417 | // If a NULL string is supplied, zero will | ||
3418 | // be returned. | ||
3419 | // If fewer than 6 characters are supplied, then | ||
3420 | // the answer will reflect a partial | ||
3421 | // accumulation. | ||
3422 | // <para> | ||
3423 | // The 6-bit segments are | ||
3424 | // extracted left-to-right in big-endian mode, | ||
3425 | // which means that segment 6 only contains the | ||
3426 | // two low-order bits of the 32 bit integer as | ||
3427 | // its high order 2 bits. A short string therefore | ||
3428 | // means loss of low-order information. E.g. | ||
3429 | // | ||
3430 | // |<---------------------- 32-bit integer ----------------------->|<-Pad->| | ||
3431 | // |<--Byte 0----->|<--Byte 1----->|<--Byte 2----->|<--Byte 3----->|<-Pad->| | ||
3432 | // |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| | ||
3433 | // |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| | ||
3434 | // | str[0] | str[1] | str[2] | str[3] | str[4] | str[6] | | ||
3435 | // | ||
3436 | // </para> | ||
3437 | // </remarks> | ||
3253 | 3438 | ||
3254 | public int llBase64ToInteger(string str) | 3439 | public int llBase64ToInteger(string str) |
3255 | { | 3440 | { |
3441 | |||
3442 | int number = 0; | ||
3443 | int digit; | ||
3444 | int baddigit = 0; | ||
3445 | |||
3256 | m_host.AddScriptLPS(1); | 3446 | m_host.AddScriptLPS(1); |
3257 | NotImplemented("llBase64ToInteger"); | 3447 | |
3258 | return 0; | 3448 | // Require a well-fromed base64 string |
3449 | |||
3450 | if(str.Length > 8) | ||
3451 | return 0; | ||
3452 | |||
3453 | // The loop is unrolled in the interests | ||
3454 | // of performance and simple necessity. | ||
3455 | // | ||
3456 | // MUST find 6 digits to be well formed | ||
3457 | // -1 == invalid | ||
3458 | // 0 == padding | ||
3459 | |||
3460 | if((digit=c2itable[str[0]])<=0) | ||
3461 | { | ||
3462 | return digit<0?(int)0:number; | ||
3463 | } | ||
3464 | number += --digit<<26; | ||
3465 | |||
3466 | if((digit=c2itable[str[1]])<=0) | ||
3467 | { | ||
3468 | return digit<0?(int)0:number; | ||
3469 | } | ||
3470 | number += --digit<<20; | ||
3471 | |||
3472 | if((digit=c2itable[str[2]])<=0) | ||
3473 | { | ||
3474 | return digit<0?(int)0:number; | ||
3475 | } | ||
3476 | number += --digit<<14; | ||
3477 | |||
3478 | if((digit=c2itable[str[3]])<=0) | ||
3479 | { | ||
3480 | return digit<0?(int)0:number; | ||
3481 | } | ||
3482 | number += --digit<<8; | ||
3483 | |||
3484 | if((digit=c2itable[str[4]])<=0) | ||
3485 | { | ||
3486 | return digit<0?(int)0:number; | ||
3487 | } | ||
3488 | number += --digit<<2; | ||
3489 | |||
3490 | if((digit=c2itable[str[5]])<=0) | ||
3491 | { | ||
3492 | return digit<0?(int)0:number; | ||
3493 | } | ||
3494 | number += --digit>>4; | ||
3495 | |||
3496 | // ignore trailing padding | ||
3497 | |||
3498 | return number; | ||
3499 | |||
3259 | } | 3500 | } |
3260 | 3501 | ||
3261 | public double llGetGMTclock() | 3502 | public double llGetGMTclock() |
@@ -3276,11 +3517,179 @@ namespace OpenSim.Region.ScriptEngine.Common | |||
3276 | m_host.RotationOffset = new LLQuaternion((float)rot.x, (float)rot.y, (float)rot.z, (float)rot.s); | 3517 | m_host.RotationOffset = new LLQuaternion((float)rot.x, (float)rot.y, (float)rot.z, (float)rot.s); |
3277 | } | 3518 | } |
3278 | 3519 | ||
3279 | public LSL_Types.list llParseStringKeepNulls(string src, LSL_Types.list seperators, LSL_Types.list spacers) | 3520 | // <summary> |
3521 | // Scan the string supplied in 'src' and | ||
3522 | // tokenize it based upon two sets of | ||
3523 | // tokenizers provided in two lists, | ||
3524 | // separators and spacers. | ||
3525 | // </summary> | ||
3526 | // | ||
3527 | // <remarks> | ||
3528 | // Separators demarcate tokens and are | ||
3529 | // elided as they are encountered. Spacers | ||
3530 | // also demarcate tokens, but are themselves | ||
3531 | // retained as tokens. | ||
3532 | // | ||
3533 | // Both separators and spacers may be arbitrarily | ||
3534 | // long strings. i.e. ":::". | ||
3535 | // | ||
3536 | // The function returns an ordered list | ||
3537 | // representing the tokens found in the supplied | ||
3538 | // sources string. If two successive tokenizers | ||
3539 | // are encountered, then a NULL entry is added | ||
3540 | // to the list. | ||
3541 | // | ||
3542 | // It is a precondition that the source and | ||
3543 | // toekizer lisst are non-null. If they are null, | ||
3544 | // then a null pointer exception will be thrown | ||
3545 | // while their lengths are being determined. | ||
3546 | // | ||
3547 | // A small amount of working memoryis required | ||
3548 | // of approximately 8*#tokenizers. | ||
3549 | // | ||
3550 | // There are many ways in which this function | ||
3551 | // can be implemented, this implementation is | ||
3552 | // fairly naive and assumes that when the | ||
3553 | // function is invooked with a short source | ||
3554 | // string and/or short lists of tokenizers, then | ||
3555 | // performance will not be an issue. | ||
3556 | // | ||
3557 | // In order to minimize the perofrmance | ||
3558 | // effects of long strings, or large numbers | ||
3559 | // of tokeizers, the function skips as far as | ||
3560 | // possible whenever a toekenizer is found, | ||
3561 | // and eliminates redundant tokenizers as soon | ||
3562 | // as is possible. | ||
3563 | // | ||
3564 | // The implementation tries to avoid any copying | ||
3565 | // of arrays or other objects. | ||
3566 | // </remarks> | ||
3567 | |||
3568 | public LSL_Types.list llParseStringKeepNulls(string src, LSL_Types.list separators, LSL_Types.list spacers) | ||
3280 | { | 3569 | { |
3570 | |||
3571 | int beginning = 0; | ||
3572 | int srclen = src.Length; | ||
3573 | int seplen = separators.Length; | ||
3574 | object[] separray = separators.Data; | ||
3575 | int spclen = spacers.Length; | ||
3576 | object[] spcarray = spacers.Data; | ||
3577 | int mlen = seplen+spclen; | ||
3578 | |||
3579 | int[] offset = new int[mlen+1]; | ||
3580 | bool[] active = new bool[mlen]; | ||
3581 | |||
3582 | int best; | ||
3583 | int j; | ||
3584 | |||
3585 | // Initial capacity reduces resize cost | ||
3586 | |||
3587 | LSL_Types.list tokens = new LSL_Types.list(); | ||
3588 | |||
3281 | m_host.AddScriptLPS(1); | 3589 | m_host.AddScriptLPS(1); |
3282 | NotImplemented("llParseStringKeepNulls"); | 3590 | |
3283 | return new LSL_Types.list(); | 3591 | // All entries are initially valid |
3592 | |||
3593 | for(int i=0; i<mlen; i++) active[i] = true; | ||
3594 | |||
3595 | offset[mlen] = srclen; | ||
3596 | |||
3597 | while(beginning<srclen) | ||
3598 | { | ||
3599 | |||
3600 | best = mlen; // as bad as it gets | ||
3601 | |||
3602 | // Scan for separators | ||
3603 | |||
3604 | for(j=0; j<seplen; j++) | ||
3605 | { | ||
3606 | if(active[j]) | ||
3607 | { | ||
3608 | // scan all of the markers | ||
3609 | if((offset[j] = src.IndexOf((string)separray[j],beginning)) == -1) | ||
3610 | { | ||
3611 | // not present at all | ||
3612 | active[j] = false; | ||
3613 | } else | ||
3614 | { | ||
3615 | // present and correct | ||
3616 | if(offset[j] < offset[best]) | ||
3617 | { | ||
3618 | // closest so far | ||
3619 | best = j; | ||
3620 | if(offset[best] == beginning) | ||
3621 | break; | ||
3622 | } | ||
3623 | } | ||
3624 | } | ||
3625 | } | ||
3626 | |||
3627 | // Scan for spacers | ||
3628 | |||
3629 | if(offset[best] != beginning) | ||
3630 | { | ||
3631 | for(j=seplen; (j<mlen) && (offset[best] > beginning); j++) | ||
3632 | { | ||
3633 | if(active[j]) | ||
3634 | { | ||
3635 | // scan all of the markers | ||
3636 | if((offset[j] = src.IndexOf((string)spcarray[j-seplen],beginning)) == -1) | ||
3637 | { | ||
3638 | // not present at all | ||
3639 | active[j] = false; | ||
3640 | } else | ||
3641 | { | ||
3642 | // present and correct | ||
3643 | if(offset[j] < offset[best]) | ||
3644 | { | ||
3645 | // closest so far | ||
3646 | best = j; | ||
3647 | } | ||
3648 | } | ||
3649 | } | ||
3650 | } | ||
3651 | } | ||
3652 | |||
3653 | // This is the normal exit from the scanning loop | ||
3654 | |||
3655 | if(best == mlen) | ||
3656 | { | ||
3657 | // no markers were found on this pass | ||
3658 | // so we're pretty much done | ||
3659 | tokens.Add(src.Substring(beginning, srclen-beginning)); | ||
3660 | break; | ||
3661 | } | ||
3662 | |||
3663 | // Otherwise we just add the newly delimited token | ||
3664 | // and recalculate where the search should continue. | ||
3665 | |||
3666 | tokens.Add(src.Substring(beginning,offset[best]-beginning)); | ||
3667 | |||
3668 | if(best<seplen) | ||
3669 | { | ||
3670 | beginning = offset[best]+((string)separray[best]).Length; | ||
3671 | } else | ||
3672 | { | ||
3673 | beginning = offset[best]+((string)spcarray[best-seplen]).Length; | ||
3674 | tokens.Add(spcarray[best-seplen]); | ||
3675 | } | ||
3676 | |||
3677 | } | ||
3678 | |||
3679 | // This an awkward an not very intuitive boundary case. If the | ||
3680 | // last substring is a tokenizer, then there is an implied trailing | ||
3681 | // null list entry. Hopefully the single comparison will not be too | ||
3682 | // arduous. Alternatively the 'break' could be replced with a return | ||
3683 | // but that's shabby programming. | ||
3684 | |||
3685 | if(beginning == srclen) | ||
3686 | { | ||
3687 | if(srclen != 0) | ||
3688 | tokens.Add(""); | ||
3689 | } | ||
3690 | |||
3691 | return tokens; | ||
3692 | |||
3284 | } | 3693 | } |
3285 | 3694 | ||
3286 | public void llRezAtRoot(string inventory, LSL_Types.Vector3 position, LSL_Types.Vector3 velocity, | 3695 | public void llRezAtRoot(string inventory, LSL_Types.Vector3 position, LSL_Types.Vector3 velocity, |