Datatypes
LSL integer
Used as a boolean value, with constants TRUE and FALSE is an SLua type boolean, with values true and false.
Used as an integer is an SLua type number (which has integer and float values all in the same datatype).
|
|
The constants TRUE and FALSE doesn’t exist in SLua. Remember to not use them!
LSL float
It is an SLua type number (floats and integers all go in the datatype number). SLua numbers are stored as 64 bit floats.
|
|
LSL string
It is an SLua type string.
|
|
More about strings here: Strings.
LSL key
SLua adds the type uuid, which is the same than the LSL key.The change of name is to avoid confusion with the key of a table.
- We get a uuid using
myId = uuid("0f16c0e1-384e-4b5f-b7ce-886dda3bce41"). - Or with
myId = uuid.create("0f16c0e1-384e-4b5f-b7ce-886dda3bce41"). - Or with
myId = touuid("0f16c0e1-384e-4b5f-b7ce-886dda3bce41").
uuid(), uuid.create() and touuid() are the same, we can use any of them.
|
|
They return nil if the string has not a valid uuid format.
This is different to LSL, where we can store any string in a variable of type key.
More info on the use of uuid’s in linked messages here: Linked messages and uuid’s.
They can take a buffer of 16 or more bytes and get the uuid in numeric format from the first 16 bytes.
The variables that contain an uuid have these properties:
- istruthy : returns true if the variable has an uuid, false if it is “” or NULL_KEY. It’s mostly used with an if command:
if someUuid.istruthy then
- bytes : returns a string with the uuid in numeric format (16 characters):
print(someUuid.bytes) -- > ??8NK_?Έm?;?A
Example converting an uuid to numeric format and back. Useful to store them in 16 bytes instead of 36:
-- uuid's to string16
local me = ll.GetOwner()
local meStr16 = me.bytes
print(#meStr16) -- > 16
local meBack = uuid(buffer.fromstring(meStr16))
print(me == meBack) -- > true
To store in linkset data we need to avoid characters ascii 0 and other special characters. We can store them in 24 byes instead of 36:
-- uuid's to string24 for linkset data
local me = ll.GetOwner()
ll.LinksetDataWrite("test",llbase64.encode(me.bytes))
print(#ll.LinksetDataRead("test")) -- > 24
local meBack = uuid(llbase64.decode(ll.LinksetDataRead("test"), true))
print(me == meBack) -- > true
LSL vector
It is an SLua type vector. It uses the Luau library vector.
- We get a vector using
myVec = vector(50, 50, 0). - It’s not possible to assign a value to a component. We need to create a new vector.
- We can get a component from the return value of a function, not only from a variable.
- Components are stored in 32 bits (same as LSL).
|
|
More about vectors here: Vectors.
LSL rotation
SLua adds the types rotation and quaternion. They are synonims, internally they are the same datatype quaternion.
- We get a rotation using
myRot = rotation(1, 1, 1, 0)ormyRot = quaternion(1, 1, 1, 0). - It’s not possible to assign a value to a component. We need to create a new rotation.
- We can get a component from the return value of a function, not only from a variable.
- Components are stored in 32 bits (same as LSL).
- Vectors can use uppercase components (X, Y, Z) but rotations can’t. Let’s use always lowercase components.
|
|
More about rotations here: Rotations.
LSL list
Lists are tables.
|
|
More about tables here: Tables.
Typecasting
- to boolean
- from number or integer:
myBool = (myNum ~= 0)
- from number or integer:
- to number
- from string:
- if the string is fully numeric:
myNum = tonumber("123") -- > 123ormyNum = tonumber("1.75") -- > 1.75- but
tonumber("123abc") -- > nil
- if the string starts with an integer, to typecast in LSL-style:
tonumber(string.match("123abc", "^%s*([-+]?%d+)" )) or 0 -- > 123tonumber(string.match("aaa", "^%s*([-+]?%d+)" )) or 0 -- > 0
- if the string is fully numeric:
- from boolean:
if myBool then 1 else 0
- from string:
- to string
- from any type:
myStr = tostring(myVar)
- from any type:
- to uuid
- from string:
myUuid = uuid("0f16c0e1-384e-4b5f-b7ce-886dda3bce41")- or with touuid() that is the same
- from string:
- to vector
- from string:
myVec = tovector("<50, 50, 20>")
- from string:
- to rotation/quaternion
- from string:
myRot = torotation("<1, 1, 1, 0>")myRot = toquaternion("<1, 1, 1, 0>")
- from string:
type() and typeof()
type( myVar ) returns the Lua base type of the variable. All the types added by SLua return “userdata”, which is an internal datatype used to define new types in the language itself. We can’t use “userdata”.
typeof( myVar ) returns the type of the variable, including the new types:
typeof( vector( 1, 2, 3 ) ) -- > vectortypeof( uuid( "0f16c0e1-384e-4b5f-b7ce-886dda3bce41" ) ) -- > uuidtypeof ( rotation ( 1, 2, 3, 4) ) -- > quaternion
We have the datatypes rotation and quaternion and the functions torotation() and toquaternion() to cast from string, but internally only exists the type quaternion, rotation is just an alias.
-- typeof() (SLua)
if typeof(myVar) == "quaternion" then -- NOT "rotation", it would never happen!
-- do rotations stuff
end
typeof( ZERO_ROTATION ) -- > quaternion. There is no constant ZERO_QUATERNION.
Types in LL constants and functions
In SLua, LL constants, function return values, and the elements of lists returned by LL functions have the type number if their LSL type is integer or float.
For LL function parameters that are integer or float in LSL, SLua accepts both number and integer types.
- If a number with decimals is passed to a parameter expecting an integer, the decimal part is truncated (not rounded).
- Many of these functions also accept a boolean type, which is internally cast to an integer.
In SLua, LL constants that contain a uuid have type uuid. In LSL they have type string.
Use of memory
Every variable or literal value, of any type, is stored as a 16 bytes tagged value (TValue) that includes the type identifier.
- Primitive types (boolean, number, vector and nil) have their value in the TValue.
-
Reference types (string, table, function, thread, buffer and userdata) have a pointer to the heap.
-
SLua vector is derived from Luau vector and is a primitive type.
- SLua quaternion and uuid are derived from userdata and are reference types.
- rotation is an alternative name for quaternion, internally there is only the type quaternion.
The format of the TValue is:
- 8 bytes : value (for primitive types) or pointer (for reference types)
- 4 bytes : extra (used for Luau vectors to store their 3rd component)
- 4 bytes : type identifier
When used as a key in a table, it changes to a Tkey, with this format:
- 8 bytes : value
- 4 bytes : extra
- 4 bits : type identifier
- 28 bits : link to the next node in the table
Each node in a dictionary table has a TKey and a TValue. Array tables are very optimized and in the best case only need to store the TValues (depending on how the elements are added, preallocating the array with table.create() when the number of elements is known gives the best optimization).
Reference types have their data stored in the heap (pointed by the TValue) with a header with internal metadata:
- string has its length (in bytes) and data for string interning (explained here string interning).
- table has the length of the array part, a pointer to its metatable, the read-only parameter and data to optimize search.
- userdata has the length and a pointer to its internal metatable.
Memory used for each datatype:
| Datatype | Bytes | Comments |
|---|---|---|
| nil | 16 | |
| boolean | 16 | |
| number | 16 | |
| vector | 16 | Luau vector |
| quaternion | 48 | userdata, rotations are quaternions |
| string | 37 + string length | uses string interning |
| uuid | 61 | userdata, stored in numeric format |
| local variable | 24 | without TValue, has an index to it in the stack (or heap for closures) |
| table | 52 (empty) | |
| function | 36 (empty) | |
| buffer | 32 + buffer length | more exactly: 24 + buffer length with a minimum of 32 |
| thread | 1024 (empty) | coroutine |
Strings and string uuids are stored as UTF-8. The characters ASCII 0-127 use 1 byte (instead of 2 bytes in LSL):
| Bytes | Unicode Range | Character Types |
|---|---|---|
| 1 | U+0000 to U+007F | ASCII characters (basic English letters, digits, etc.) |
| 2 | U+0080 to U+07FF | Extended Latin, Greek, Cyrillic, Hebrew, Arabic |
| 3 | U+0800 to U+FFFF | Chinese, Japanese, Korean, symbols, most emojis |
| 4 | U+10000 to U+10FFFF | Supplementary characters, rare scripts, more emojis |
Details about memory allocation for tables here: Memory allocation for tables.