diff --git a/dev/tests/bytearray.lua b/dev/tests/bytearray.lua
index 415768c65..cbd537100 100644
--- a/dev/tests/bytearray.lua
+++ b/dev/tests/bytearray.lua
@@ -34,3 +34,70 @@ arr:insert(2, {5, 6})
assert(arr[#arr] == 25)
assert(arr[2] == 5)
assert(arr[3] == 6)
+
+-- =============================
+
+arr = Bytearray({254, 255, 255, 255, 255, 255, 255, 255})
+--assert(U64view(arr)[1] == 2 ^ 64 - 2)
+assert(I64view(arr)[1] == -2)
+assert(U32view(arr)[1] == 2 ^ 32 - 2)
+assert(I32view(arr)[1] == -2)
+assert(U16view(arr)[1] == 2 ^ 16 - 2)
+assert(I16view(arr)[1] == -2)
+assert(I8view(arr)[1] == -2)
+
+assert(I8view(arr)[2] == -1)
+assert(I32view(arr)[2] == -1)
+
+arr = Bytearray({0xFF, 0x7F})
+local arri16 = I16view(arr)
+local arru16 = U16view(arr)
+assert(arri16[1] == 32767)
+
+arru16[1] = arru16[1] + 1
+assert(arri16[1] == -32768)
+assert(arru16[1] == 32768)
+
+arr = Bytearray({0x9A, 0x99, 0x59, 0x40}) -- approx. equal 3.4 in hexadecimal representation of single-precision float number.
+local arrflt = FLTview(arr)
+local arru32 = U32view(arr)
+assert(arrflt[1] == 3.4000000953674316)
+assert(arru32[1] == 0x4059999A)
+
+arrflt[1] = arrflt[1] + 0.1
+assert(arrflt[1] == 3.5)
+assert(arru32[1] == 0x40600000) -- hexadecimal representation of 3.5 single-precision float number.
+
+arr = Bytearray({0x18, 0x2D, 0x44, 0x54, 0xFB, 0x21, 9, 64}) -- hexadecimal representation of 3.141592653589793 double-precision float number.
+assert(DBLview(arr)[1] == 3.141592653589793)
+
+-- =============================================
+
+function barrtostr(barr)
+ local outstr = ""
+ for i = 1, #barr do
+ local lastfmt = ""
+ if i ~= #barr then lastfmt = "|" end
+ outstr = outstr .. barr[i] .. lastfmt
+ end
+ return outstr
+end
+
+arr = Bytearray({0, 5, 34, 87, 21, 0, 210})
+assert(barrtostr(arr) == "0|5|34|87|21|0|210")
+arr:move(2, 3, 4)
+assert(barrtostr(arr) == "0|5|5|34|87|21|210")
+
+local arr2 = Bytearray({1, 2, 3, 4, 6, 7, 8})
+assert(barrtostr(arr2) == "1|2|3|4|6|7|8")
+arr:copy(4, arr2, 2, 3)
+assert(barrtostr(arr2) == "1|34|87|21|6|7|8")
+
+arr:fill(nil, nil, 192)
+assert(barrtostr(arr) == "192|192|192|192|192|192|192")
+arr:fill(nil, 3, 233)
+assert(barrtostr(arr) == "233|233|233|233|233|233|233")
+arr:fill(2, nil, 254)
+assert(barrtostr(arr) == "254|254|254|254|254|254|254")
+arr:fill(2, 2, 66)
+assert(barrtostr(arr) == "254|66|66|254|254|254|254")
\ No newline at end of file
diff --git a/doc/ru/scripting/bytearray.md b/doc/ru/scripting/bytearray.md
index c90b6d67e..d15cd6a58 100644
--- a/doc/ru/scripting/bytearray.md
+++ b/doc/ru/scripting/bytearray.md
@@ -2,6 +2,8 @@
*Bytearray* - динамический байтовый массив, реализованный через LuaJIT FFI. По нему можно итерироваться стандартными функциями **pairs** и **ipairs**
+Примечание касаемо условных обозначений в документации: псведотип `posint` означает натуральное (положительное ненулевое целое) число.
+
## Основное
### Создание массива
```lua
@@ -22,29 +24,64 @@ bytes:insert(index: int, value: int)
-- Удаляет элемент(ы), если передан `count`, удалит `count` кол-во элементов с выбранного индекса.
bytes:remove(index: int, [опционально] count: int)
--- Очищает массив
+-- Очищает массив.
bytes:clear()
+--[[
+Заполняет массив определённым байтом полностью либо в определённом диапазоне.
+Логика работы следующая: если параметр size или index равны nil, то указанным байтом будет заполнен весь массив,
+ иначе будет заполнен диапазон внутри массива начиная с индекса index и размером size.
+В случае выхода заполняемого блока за границы массива генерирует соответствующую ошибку.
+]]
+bytes:fill(index: posint/nil, size: posint/nil, byte: uint8)
+
-- Создаёт новый Bytearray, содержащий копию части данных с offset до offset+length
bytes:slice(offset: int, length: int) -> Bytearray
+
+--[[
+Копирует блок байт из исходного массива, начиная с индекса srcindex, в массив dst, начиная в нём с индекса dstindex.
+В случае выхода за границы исходного, конечного массива, либо при некорректном значении параметра size генерирует соответствующую ошибку.
+]]
+bytes:copy(srcindex: posint, dst: Bytearray, dstindex: posint, size: posint)
+
+--[[
+Безопасно копирует перекрывающиеся области памяти (байт) в пределах одного массива.
+В случае выхода за пределы массива начального или конечного диапазона байт генерирует соответствующую ошибку.
+]]
+bytes:move(fromindex: posint, toindex: posint, size: posint)
```
## View
Это "вьюшки" поверх Bytearray, которые интерпретируют его байты как массив чисел другого размера - **без копирования данных**.
-Не имеет своих методов, является просто объектной ссылкой на оригинальный Bytearray.
+Практически не имеют своих методов, являются простыми объектными ссылками на оригинальный Bytearray.
-| Класс | Тип элементов | Размер |
-| ------------ | ------------------ | ------- |
-| `I16view` | `int16_t` | 2 байта |
-| `U16view` | `uint16_t` | 2 байта |
-| `I32view` | `int32_t` | 4 байта |
-| `U32view` | `uint32_t` | 4 байта |
+| Класс | Тип элементов | Размер |
+| ------------ | ------------------ | --------- |
+| `I8view` | `int8_t` | 1 байт |
+| `I16view` | `int16_t` | 2 байта |
+| `U16view` | `uint16_t` | 2 байта |
+| `I32view` | `int32_t` | 4 байта |
+| `U32view` | `uint32_t` | 4 байта |
+| `I64view` | `int64_t` | 8 байт |
+| `U64view` | `uint64_t` | 8 байт |
+| `FLTview` | `float` | 4 байта* |
+| `DBLview` | `double` | 8 байт* |
----
+* Размеры "оригинальных" C-шных типов являются платформозависимыми и их точный размер не описан в стандарте языка. В данной таблице предоставлены наиболее распространённые размеры типов.
-### Пример использования
+### Специфические методы "вьюшек"
+
+```lua
+--[[
+Возвращает размер типа вьюшки. Результат тот же, что и от применения оператора sizeof() из языка C для данного типа.
+ Полезно для вьюшек, использующих C-шные типы нефиксированного размера, например те же float или double.
+]]
+view:typesize() -> posint
+```
+
+## Пример использования
```lua
local bytes = Bytearray({1, 0, 2, 0, 250, 255})
@@ -59,4 +96,4 @@ print(i16[3]) -- -6 (signed вьюшка)
for _, num in ipairs(i16) do
print(num)
end -- 1; 2; -6
-```
\ No newline at end of file
+```
diff --git a/res/modules/internal/bytearray.lua b/res/modules/internal/bytearray.lua
index 58b33a520..0908b8c9a 100644
--- a/res/modules/internal/bytearray.lua
+++ b/res/modules/internal/bytearray.lua
@@ -4,12 +4,14 @@ local FFI = ffi
FFI.cdef[[
void* malloc(size_t);
+ void* realloc(void*, size_t);
void free(void*);
typedef struct {
unsigned char* bytes;
int size;
int capacity;
} bytearray_t;
+ void* memmove(void*, const void*, size_t);
]]
local free = FFI.C.free
@@ -22,12 +24,33 @@ local GC_THRESHOLD = 50 * 1024 * 1024
local function malloc(size)
local raw = FFI.C.malloc(size)
- if raw == nil then
- return nil
+ if not raw then
+ error("failed memory allocation of " .. size .. " bytes")
end
allocated_bytes = allocated_bytes + size
+ if allocated_bytes >= GC_THRESHOLD then
+ collectgarbage("step", 200)
+ allocated_bytes = 0
+ end
+ return raw
+end
+
+local function realloc(buff, size, throwerror)
+ if throwerror == nil then throwerror = true end
+
+ if size <= 0 then
+ free(buff)
+ return
+ end
+
+ local raw = FFI.C.realloc(buff, size)
+ if not raw and throwerror then
+ error("failed memory reallocation of block " .. buff .. " to new size " .. size)
+ end
+
+ allocated_bytes = allocated_bytes + size
if allocated_bytes >= GC_THRESHOLD then
collectgarbage("step", 200)
allocated_bytes = 0
@@ -38,23 +61,17 @@ end
local function grow_buffer(self, elems)
local new_capacity = math.ceil(self.capacity / 0.75 + elems)
- local prev = self.bytes
- self.bytes = malloc(new_capacity)
- FFI.copy(self.bytes, prev, self.size)
+ self.bytes = realloc(self.bytes, new_capacity)
self.capacity = new_capacity
- free(prev)
end
local function trim_buffer(self)
- if self.size == self.capacity then
- return
+ if self.size == self.capacity then return end
+ local new_bytes = realloc(self.bytes, self.size, false)
+ if new_bytes then
+ self.bytes = new_bytes
+ self.capacity = self.size
end
- local size = self.size
- local prev = self.bytes
- self.bytes = malloc(size)
- FFI.copy(self.bytes, prev, self.size)
- self.capacity = size
- free(prev)
end
local function count_elements(b)
@@ -125,15 +142,21 @@ local function clear(self)
self.size = 0
end
-local function reserve(self, new_capacity)
- if new_capacity <= self.capacity then
- return
+local function fill(self, index, size, byte)
+ if index ~= nil and size ~= nil and size > 0 then
+ if index < 1 or index + size - 1 > self.size then
+ error("selected fill range is out of array bounds")
+ end
+ FFI.fill(self.bytes + index - 1, size, byte)
+ else
+ FFI.fill(self.bytes, self.size, byte)
end
- local prev = self.bytes
- self.bytes = malloc(new_capacity)
- FFI.copy(self.bytes, prev, self.size)
+end
+
+local function reserve(self, new_capacity)
+ if new_capacity <= self.capacity then return end
+ self.bytes = realloc(self.bytes, new_capacity)
self.capacity = new_capacity
- free(prev)
end
local function get_capacity(self)
@@ -150,22 +173,46 @@ local function slice(self, offset, length)
length = self.size - offset + 1
end
local buffer = malloc(length)
- if not buffer then
- error("malloc(" .. length .. ") returned NULL")
- end
FFI.copy(buffer, self.bytes + (offset - 1), length)
return bytearray_type(buffer, length, length)
end
+local function copy(self, srcindex, dst, dstindex, size)
+ if size <= 0 then error("size of byte range must be positive non-zero integer") end
+ if srcindex < 1 or srcindex + size - 1 > self.size then
+ error("specified source byte range is out of source array bounds")
+ end
+ if dstindex < 1 or dstindex + size - 1 > dst.size then
+ error("specified destination byte range is out of destination array bounds")
+ end
+
+ FFI.copy(dst.bytes + dstindex - 1, self.bytes + srcindex - 1, size)
+end
+
+local function move(self, fromindex, toindex, size)
+ if size <= 0 then error("size of byte range must be positive non-zero integer") end
+ if fromindex < 1 or fromindex + size - 1 > self.size then
+ error("specified source byte range is out of array bounds")
+ end
+ if toindex < 1 or toindex + size - 1 > self.size then
+ error("specified destination byte range is out of array bounds")
+ end
+
+ FFI.C.memmove(self.bytes + toindex - 1, self.bytes + fromindex - 1, size)
+end
+
local bytearray_methods = {
- append=append,
- insert=insert,
- remove=remove,
- trim=trim_buffer,
- clear=clear,
- reserve=reserve,
- get_capacity=get_capacity,
- slice=slice,
+ append = append,
+ insert = insert,
+ remove = remove,
+ trim = trim_buffer,
+ clear = clear,
+ reserve = reserve,
+ get_capacity = get_capacity,
+ slice = slice,
+ fill = fill,
+ copy = copy,
+ move = move
}
local bytearray_mt = {
@@ -256,7 +303,8 @@ local function FFIBytearray_as_string(bytes)
end
end
-local function create_FFIview_class(name, typename, typesize)
+local function create_FFIview_class(name, typename)
+ local typesize = FFI.sizeof(typename)
local ptrtype = typename .. "*"
local FFIview_mt = {
__index = function(self, key)
@@ -291,6 +339,9 @@ local function create_FFIview_class(name, typename, typesize)
return i, ptr[i - 1]
end
end
+ end,
+ typesize = function()
+ return typesize
end
}
return function (bytes)
@@ -301,17 +352,27 @@ local function create_FFIview_class(name, typename, typesize)
end
end
-local FFII16view = create_FFIview_class("FFII16view", "int16_t", 2)
-local FFIU16view = create_FFIview_class("FFIU16view", "uint16_t", 2)
-local FFII32view = create_FFIview_class("FFII32view", "int32_t", 4)
-local FFIU32view = create_FFIview_class("FFIU32view", "uint32_t", 4)
+local FFII8view = create_FFIview_class("FFII8view", "int8_t")
+local FFII16view = create_FFIview_class("FFII16view", "int16_t")
+local FFIU16view = create_FFIview_class("FFIU16view", "uint16_t")
+local FFII32view = create_FFIview_class("FFII32view", "int32_t")
+local FFIU32view = create_FFIview_class("FFIU32view", "uint32_t")
+local FFII64view = create_FFIview_class("FFII64view", "int64_t")
+local FFIU64view = create_FFIview_class("FFIU64view", "uint64_t")
+local FFIFLTview = create_FFIview_class("FFIFLTview", "float")
+local FFIDBLview = create_FFIview_class("FFIDBLview", "double")
return {
FFIBytearray = setmetatable(FFIBytearray, FFIBytearray),
FFIBytearray_as_string = FFIBytearray_as_string,
FFIBytearray_as_ptr = FFIBytearray_as_ptr,
+ FFII8view = FFII8view,
FFIU16view = FFIU16view,
FFII16view = FFII16view,
FFIU32view = FFIU32view,
FFII32view = FFII32view,
+ FFIU64view = FFIU64view,
+ FFII64view = FFII64view,
+ FFIFLTview = FFIFLTview,
+ FFIDBLview = FFIDBLview,
}
diff --git a/res/scripts/stdmin.lua b/res/scripts/stdmin.lua
index e341f68a1..196bf8942 100644
--- a/res/scripts/stdmin.lua
+++ b/res/scripts/stdmin.lua
@@ -311,10 +311,15 @@ local bytearray = require "core:internal/bytearray"
Bytearray = bytearray.FFIBytearray
Bytearray_as_string = bytearray.FFIBytearray_as_string
Bytearray_as_ptr = bytearray.FFIBytearray_as_ptr
+I8view = bytearray.FFII8view
U16view = bytearray.FFIU16view
I16view = bytearray.FFII16view
U32view = bytearray.FFIU32view
I32view = bytearray.FFII32view
+U64view = bytearray.FFIU64view
+I64view = bytearray.FFII64view
+FLTview = bytearray.FFIFLTview
+DBLview = bytearray.FFIDBLview
Bytearray_construct = function(...) return Bytearray(...) end
bit.compile = require "core:bitwise/compiler"
diff --git a/src/logic/scripting/lua/usertypes/lua_type_canvas.cpp b/src/logic/scripting/lua/usertypes/lua_type_canvas.cpp
index c670e23df..928515e74 100644
--- a/src/logic/scripting/lua/usertypes/lua_type_canvas.cpp
+++ b/src/logic/scripting/lua/usertypes/lua_type_canvas.cpp
@@ -161,17 +161,13 @@ static LuaCanvas& require_canvas(State* L, int idx) {
static int l_clear(State* L) {
auto& canvas = require_canvas(L, 1);
auto& image = canvas.getData();
- ubyte* data = image.getData();
- RGBA rgba {};
+ size_t pixelscount = image.getWidth() * image.getHeight();
+ uint32_t* data = reinterpret_cast(image.getData());
+
if (gettop(L) == 1) {
- std::fill(data, data + image.getDataSize(), 0);
- return 0;
- }
- rgba = get_rgba(L, 2);
- size_t pixels = image.getWidth() * image.getHeight();
- const size_t channels = 4;
- for (size_t i = 0; i < pixels * channels; i++) {
- data[i] = rgba.arr[i % channels];
+ std::fill(data, data + pixelscount, 0);
+ } else {
+ std::fill(data, data + pixelscount, get_rgba(L, 2).rgba);
}
return 0;
}