We had too much precision. In LSL, llRound(0.49999997) gives 1, not 0, because the loss of precision after adding 0.5 to that makes the result 1. Fixed by converting to F32 prior to flooring.
LL's algorithm for llEuler2Rot involves calculating a rotation matrix from the Eulers, then transforming the rotation matrix to quaternion. This generates sign discrepancies with our direct quaternion calculation algorithm.
Fixed by making only the necessary calculations to find the correct sign. Unfortunately, they are still heavy (notably, six more trig functions).
Mainly, the input quaternion wasn't being normalized prior to using it for calculations, and that broke compatibility. But even then, sometimes the arc sine of a value greater than 1 could be calculated, so we clamp it.
For all zeros input, the result was <1,0,0,0> but we were producing <0,0,0,.5> because the first branch was taken. Fixed.
We forgot to take the square root when calculating the magnitude of the quaternion while normalizing. Fixed.
Use qnz instead of special casing.
LSL behaves as if it was ZERO_ROTATION instead, so we fix it by creating a function that returns ZERO_ROTATION when given <0,0,0,0>. Use it for llRot2{Fwd,Left,Up} as well.
llAbs(-2147483648) raises this:
System.OverflowException: Value is too small.
at System.Math.Abs (Int32 value) [0x00000] in <filename unknown>:0
at LindenLab.SecondLife.Library.llAbs (Int32 i) [0x00000] in <filename unknown>:0
So it's actually not computable. In LSO it returns -2147483648, though.
llGetSubString and llList2List could produce output longer than the input for some params. Fix that, and join the functions into one unique function for uniformity. Also, get rid of the special case of empty elements, because it can be treated properly by the general case, and it's not so common as to merit special attention.
When the input was of the form e.g. "%4%40", the second "%" was erroneously starting another quoted character. LSL doesn't behave that way: parsing resumes without starting another quoted character. Disturbingly, the expected result in the corresponding test was wrong. Fixed both the test case and the code to match actual LSL behaviour.
llBase64ToString hid another surprise: characters in range from U+0000 to U+001F are substituted by "?" except for tabs (\x09), form feeds (\x0A), shift ins (\x0F) and unit separators (\x1F), which were kept verbatim. So, mimic this behaviour.
The reason is they have an embedded delay. A script might rely on it, therefore substituting the call with its value is not equivalent to leaving the call. They were both already excluded from the SEF table for the same reason.
Apparently, under Windows, Python does a UTF-16 word-by-word comparison when comparing two strings:
>>> u'\ud700' < u'\U0001d41a'
True
>>> u'\ue000' < u'\U0001d41a'
False
Fix it by encoding as UTF-32 big endian before comparison, when that happens.
- Remove it from lslextrafuncs, and move all the code to lslbasefuncs.
- Make it behave like SL's more accurately. Denormals return 0 always in SL.
- Use int() for truncation rather then floor/ceil.
- Add test cases.
inf and nan already did the right thing in Python, but just in case that doesn't happen in all platforms, we handle them explicitly. Also, that will make it more immune to bugs in future.
We had a big chaos with type conversion. That caused a bug where passing a key to a function that required a string, or vice versa, crashed the script.
Diminish the chaos by modifying the parameters just prior to invocation (in lslfoldconst). We also remove the, now unnecessary, calls to force floats, either alone or within vectors or quaternions.
The previous commit didn't work as expected. "from module import var" freezes the value at load time; changing it later has no effect. A reference to the module needs to be used.
Fix that and the similar problem with LSO. Also revert some "from lslcommon import *" introduced earlier.
That also revealed another bug about missing 'cond' in the import list of lslextrafuncs. This should fix all functions that return values on null key input.
llBase64ToString doesn't behave like llUnescapeURL wrt invalid UTF-8. First, the last NUL if any is removed, and the remaining NULs are converted to "?". Second, all overlong sequences are converted to a single "?", *including the 5- and 6-byte UTF-8*.
Implement this behavour and the corresponding unit tests.
LSO strings are byte arrays, but our strings are made for Mono which uses Unicode, and invalid UTF-8 sequences can't be stored in Unicode without using a custom representation.
One possible representation is to only use the codepoints 0-255 in the Unicode string, to avoid supporting multiple types for strings. Something to study in future.
Our UTF-8 validity checker failed to recognize that characters in the surrogate range (D800-DFFF) were invalid. Fortunately, Python 2 is happy about that, therefore it doesn't crash (Python 3 fixed that range too). Unfortunately, SL isn't, therefore we fix it.
Added corresponding unit tests.
All three functions have in SL the Intel problem of inaccuracy with high values. Since we work in single precision, it's barely detectable usually, but for large input numbers the difference between the imprecise results of SL and the more accurate results of recent glibc become more obvious.
This change brings back the Intel inaccuracy, even across systems (different versions of Python/C might behave differently otherwise).
Reference:
https://randomascii.wordpress.com/2014/10/09/intel-underestimates-error-bounds-by-1-3-quintillion/