mirror of
https://github.com/centau/vide.git
synced 2026-08-20 14:41:37 +00:00
This commit is contained in:
parent
0d542c66a6
commit
7dead44ac5
9 changed files with 207 additions and 173 deletions
|
|
@ -17,7 +17,7 @@ local function action(callback: (Instance) -> (), priority: number?): Action
|
|||
|
||||
setmetatable(t :: any, ActionMT)
|
||||
|
||||
return t
|
||||
return table.freeze(t)
|
||||
end
|
||||
|
||||
return function()
|
||||
|
|
|
|||
|
|
@ -10,11 +10,16 @@ local _, is_action = require(script.Parent.action)()
|
|||
local graph = require(script.Parent.graph)
|
||||
type Node<T> = graph.Node<T>
|
||||
|
||||
type Array<V> = { V }
|
||||
type Map<K, V> = { [K]: V }
|
||||
|
||||
-- buffer of event -> callback to connect after properties are set
|
||||
local event_buffer: { [string]: () -> () } = {}
|
||||
local event_buffer = {} :: Map<string, () -> ()>
|
||||
|
||||
-- buffer of priority -> callback to run after events are connected
|
||||
local action_buffers = {} :: { { (Instance) -> () } }
|
||||
local action_buffers = {} :: Map<number, Array<(Instance) -> ()>>
|
||||
|
||||
-- lazily create buffers on nil index
|
||||
setmetatable(action_buffers :: any, {
|
||||
__index = function(_, i: number)
|
||||
action_buffers[i] = {}
|
||||
|
|
@ -22,8 +27,9 @@ setmetatable(action_buffers :: any, {
|
|||
end
|
||||
})
|
||||
|
||||
-- cache used in strict mode to detect duplicate property sets at same nesting levels
|
||||
local nested_debug_cache: { [number]: { [string]: true } } = {}
|
||||
-- cache in strict mode to detect duplicate property set at same nesting level
|
||||
local nested_debug_cache = {} :: Map<number, Map<string, true>>
|
||||
|
||||
setmetatable(nested_debug_cache :: any, {
|
||||
__index = function(_, i: number)
|
||||
nested_debug_cache[i] = {}
|
||||
|
|
@ -31,28 +37,30 @@ setmetatable(nested_debug_cache :: any, {
|
|||
end
|
||||
})
|
||||
|
||||
-- a stack used in place of a recursive function to process nesting layers one at a time
|
||||
-- enforces the behavior of deeper-nested properties taking precedence of lesser-nested ones
|
||||
-- each nested table occupies two indexes, reference to table itself and the depth number
|
||||
-- e.g. props = { t1 = { t3 = {} }, t2 = {} } -> { t1, 1, t2, 1, t3, 2 }
|
||||
-- use stack instead of recursive function to process nested layers one at time
|
||||
-- deeper-nested properties take precedence over shallower-nested ones
|
||||
-- each nested layer occupies two indexes: 1. table ref 2. nested depth
|
||||
-- e.g. { t1 = { t3 = {} }, t2 = {} } -> { t1, 1, t2, 1, t3, 2 }
|
||||
local nested_stack = {} :: { {} | number }
|
||||
|
||||
-- todo: solution without manual updating of this table
|
||||
-- map of datatype names to class default constructor for aggregate initialization
|
||||
-- map of datatype names to class default constructor for aggregate init
|
||||
local aggregates = {}
|
||||
|
||||
for i, v in next, {
|
||||
Vector2 = Vector2,
|
||||
UDim2 = UDim2,
|
||||
CFrame = CFrame,
|
||||
Color3 = Color3,
|
||||
UDim = UDim,
|
||||
Rect = Rect,
|
||||
Color3 = Color3
|
||||
UDim2 = UDim2,
|
||||
Vector2 = Vector2,
|
||||
Vector3 = Vector3,
|
||||
Rect = Rect
|
||||
} do
|
||||
aggregates[i] = v.new
|
||||
end
|
||||
|
||||
-- processes a potentially nested table of values to assign to an instance
|
||||
local function process_nested(instance: Instance, properties: { [unknown]: unknown })
|
||||
local function process_props(instance: Instance, properties: Map<unknown, unknown>)
|
||||
local strict = flags.strict
|
||||
|
||||
table.clear(nested_stack)
|
||||
|
|
@ -73,27 +81,27 @@ local function process_nested(instance: Instance, properties: { [unknown]: unkno
|
|||
if type(value) == "table" then -- attempt aggregate init
|
||||
local ctor = aggregates[typeof((instance :: any)[property])]
|
||||
if ctor == nil then
|
||||
throw(`cannot aggregate construct type {typeof(value)} for property {property}`)
|
||||
throw(`cannot aggregate type {typeof(value)} for property {property}`)
|
||||
end
|
||||
(instance :: any)[property] = ctor(unpack(value :: {}))
|
||||
elseif type(value) == "function" then
|
||||
if typeof((instance :: any)[property]) == "RBXScriptSignal" then
|
||||
event_buffer[property] = value :: () -> () -- add event to buffer
|
||||
else
|
||||
bind.property(instance, property, value :: () -> ()) -- bind source
|
||||
bind.property(instance, property, value :: () -> ()) -- bind property
|
||||
end
|
||||
else
|
||||
(instance :: any)[property] = value -- set property
|
||||
end
|
||||
elseif type(property) == "number" then
|
||||
if type(value) == "function" then
|
||||
bind.children(instance, value :: () -> { Instance }) -- bind children
|
||||
bind.children(instance, value :: () -> Instance | Array<Instance>) -- bind children
|
||||
elseif type(value) == "table" then
|
||||
if is_action(value) then
|
||||
table.insert(action_buffers[(value :: any).priority], (value :: any).callback :: () -> ()) -- add action to buffer
|
||||
else
|
||||
table.insert(nested_stack, depth + 1) -- push table to stack for later processing
|
||||
table.insert(nested_stack, value :: {})
|
||||
table.insert(nested_stack, depth + 1) -- push table to stack for later processing
|
||||
end
|
||||
else
|
||||
(value :: Instance).Parent = instance -- parent child
|
||||
|
|
@ -102,8 +110,8 @@ local function process_nested(instance: Instance, properties: { [unknown]: unkno
|
|||
end
|
||||
|
||||
-- pop next nested table off stack
|
||||
properties = table.remove(nested_stack) :: {}
|
||||
depth = table.remove(nested_stack) :: number
|
||||
properties = table.remove(nested_stack) :: {}
|
||||
|
||||
until not properties
|
||||
end
|
||||
|
|
@ -121,14 +129,14 @@ local function apply<T>(instance: T & Instance, properties: { [unknown]: unknown
|
|||
end
|
||||
|
||||
-- process all properties for immediate setting or buffering
|
||||
process_nested(instance, properties)
|
||||
process_props(instance, properties)
|
||||
|
||||
-- connect buffered events
|
||||
for event, fn in next, event_buffer do
|
||||
(instance :: any)[event]:Connect(fn)
|
||||
end
|
||||
|
||||
-- run buffered actions respecting their priorities
|
||||
-- run buffered actions
|
||||
for _, buffer in next, action_buffers do
|
||||
for _, callback in next, buffer do
|
||||
callback(instance)
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
if not game then script = require "test/relative-string" end
|
||||
local warn = game and warn or print :: never
|
||||
|
||||
local throw = require(script.Parent.throw)
|
||||
local flags = require(script.Parent.flags)
|
||||
|
|
@ -33,27 +32,38 @@ local function traceback(skips: number) -- ensures trace begins outside of any v
|
|||
return debug.traceback(nil, s)
|
||||
end
|
||||
|
||||
function create_binding<T>(updater: (T) -> T, binding_data: T)
|
||||
-- if flags.strict then
|
||||
-- -- wrap setter in function with stack inspection for better error msgs
|
||||
-- local fn = setter
|
||||
-- local bind_trace = traceback(0)
|
||||
-- setter = function(instance)
|
||||
-- local ok, err: string? = xpcall(fn, function(err: string)
|
||||
-- return err .. "\nsource updated at: " .. traceback(2)
|
||||
-- end, instance)
|
||||
-- if not ok then warn(`error occured updating {property}: {err}bound at: {bind_trace}`) end
|
||||
-- end
|
||||
-- end
|
||||
function create_binding<T>(updater: (T) -> T, binding: T)
|
||||
if flags.strict then
|
||||
-- track bind creation trace
|
||||
local fn = updater
|
||||
local bind_trace = traceback(0)
|
||||
updater = function(...)
|
||||
local ok, result = xpcall(fn, function(err: string)
|
||||
return err
|
||||
end, ...)
|
||||
|
||||
if not ok then
|
||||
local btype =
|
||||
if (binding :: any).property then (binding :: any).property
|
||||
elseif (binding :: any).parent then "Parent"
|
||||
else "children"
|
||||
error(`PROPERTY BINDING ERROR: Property {btype}\n{result}\nBIND CREATION TRACE:\n{bind_trace}`, 0)
|
||||
end
|
||||
|
||||
return result
|
||||
end
|
||||
end
|
||||
|
||||
local binding = create_node(binding_data, updater)
|
||||
|
||||
local owner = get_scope()
|
||||
if not owner then throw("cannot bind property in non-reactive scope") end
|
||||
assert(owner)
|
||||
if not owner then
|
||||
throw("cannot bind property in non-reactive scope")
|
||||
end; assert(owner)
|
||||
|
||||
set_owner(binding, owner)
|
||||
evaluate_node(binding)
|
||||
local node = create_node(binding, updater)
|
||||
|
||||
set_owner(node, owner)
|
||||
evaluate_node(node)
|
||||
end
|
||||
|
||||
type PropertyBinding = {
|
||||
|
|
@ -81,7 +91,7 @@ type ChildrenBinding = {
|
|||
instance: Instance,
|
||||
cur_children_set: { [Instance]: true },
|
||||
new_children_set: { [Instance]: true },
|
||||
children: () -> { Instance }
|
||||
children: () -> Instance | { Instance }
|
||||
}
|
||||
|
||||
local function update_children(p: ChildrenBinding)
|
||||
|
|
@ -95,7 +105,7 @@ local function update_children(p: ChildrenBinding)
|
|||
end
|
||||
|
||||
if new_children then
|
||||
for _, child in next, new_children do
|
||||
for _, child in next, new_children :: { Instance } do
|
||||
new_child_set[child] = true -- record child set from this update
|
||||
if not cur_children_set[child] then
|
||||
child.Parent = p.instance -- if child wasn't already parented then parent it
|
||||
|
|
|
|||
|
|
@ -10,8 +10,9 @@ local evaluate_node = graph.evaluate_node
|
|||
|
||||
local function derive<T>(fn: () -> T): () -> T
|
||||
local owner = get_scope()
|
||||
if not owner then throw("cannot derive in non-reactive scope") end
|
||||
assert(owner)
|
||||
if not owner then
|
||||
throw("cannot derive in non-reactive scope")
|
||||
end; assert(owner)
|
||||
|
||||
local node = create_node(false :: any, fn)
|
||||
|
||||
|
|
|
|||
|
|
@ -5,26 +5,24 @@ local flags = require(script.Parent.flags)
|
|||
|
||||
export type StartNode<T> = {
|
||||
cache: T,
|
||||
children: { Node<T> } | false
|
||||
[number]: Node<T>
|
||||
}
|
||||
|
||||
export type Node<T> = {
|
||||
cache: T,
|
||||
owner: Node<T> | false,
|
||||
parents: { StartNode<T> },
|
||||
children: { Node<T> } | false,
|
||||
effect: ((T) -> T) | false,
|
||||
|
||||
owner: Node<T> | false,
|
||||
cleanups: { () -> () } | false,
|
||||
|
||||
parents: { StartNode<T> },
|
||||
[number]: Node<T>
|
||||
}
|
||||
|
||||
local scopes = { n = 0 } :: { [number]: Node<any>, n: number }
|
||||
|
||||
local WEAK_VALUES = { __mode = "v" }
|
||||
local WEAK_KEYS = { __mode = "k" }
|
||||
local EVALUATION_ERR = "error while evaluating source:\n\n"
|
||||
|
||||
-- runs a given callback in a context that Luau does not allow yielding in
|
||||
local check_for_yield: <T...>(fn: (T...) -> unknown, T...) -> () do
|
||||
local check_for_yield: <T...>(fn: (T...) -> (boolean, string?), T...) -> () do
|
||||
local t = { __mode = "kv" }
|
||||
setmetatable(t, t)
|
||||
|
||||
|
|
@ -35,17 +33,13 @@ local check_for_yield: <T...>(fn: (T...) -> unknown, T...) -> () do
|
|||
fn(unpack(args))
|
||||
end
|
||||
|
||||
local ok, err = pcall(function()
|
||||
local ok, err: string? = pcall(function()
|
||||
local _ = -t
|
||||
end)
|
||||
|
||||
if not ok then
|
||||
if err == "attempt to yield across metamethod/C-call boundary" or err == "thread is not yieldable" then
|
||||
throw(EVALUATION_ERR .. "cannot yield when deriving node in effecter")
|
||||
else
|
||||
throw(EVALUATION_ERR .. err)
|
||||
end
|
||||
end
|
||||
return ok, if err == "attempt to yield across metamethod/C-call boundary"
|
||||
or err == "thread is not yieldable" then "yield occured"
|
||||
else err
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -57,24 +51,14 @@ local function get_scope(): Node<unknown>?
|
|||
return scopes[scopes.n]
|
||||
end
|
||||
|
||||
|
||||
local function add_child<T>(parent: StartNode<any>, child: Node<any>)
|
||||
if parent.children then
|
||||
table.insert(parent.children :: { Node<T> }, child)
|
||||
else
|
||||
parent.children = { child }
|
||||
end
|
||||
|
||||
table.insert(parent, child)
|
||||
table.insert(child.parents, parent)
|
||||
end
|
||||
|
||||
local function set_owner(node: Node<any>, owner: Node<any>)
|
||||
node.owner = owner
|
||||
if owner.children then
|
||||
table.insert(owner.children :: { Node<any> }, node)
|
||||
else
|
||||
owner.children = { node }
|
||||
end
|
||||
table.insert(owner, node)
|
||||
end
|
||||
|
||||
local function open_scope<T>(node: Node<T>)
|
||||
|
|
@ -108,12 +92,11 @@ local function run_cleanups<T>(node: Node<T>)
|
|||
end
|
||||
|
||||
local function remove_child<T>(parent: StartNode<T>, child: Node<T>)
|
||||
local children = parent.children :: {}
|
||||
local idx = table.find(children :: {}, child)
|
||||
|
||||
local n = #children
|
||||
children[idx] = children[n]
|
||||
children[n] = nil
|
||||
local idx = table.find(parent, child)
|
||||
assert(idx, "child not found")
|
||||
local n = #parent
|
||||
parent[idx] = parent[n]
|
||||
parent[n] = nil
|
||||
end
|
||||
|
||||
local function unparent<T>(node: Node<T>)
|
||||
|
|
@ -127,12 +110,13 @@ end
|
|||
local function destroy<T>(node: Node<T>)
|
||||
run_cleanups(node)
|
||||
unparent(node)
|
||||
if node.owner then remove_child(node.owner, node) end
|
||||
node.owner = false
|
||||
local children = node.children :: {}
|
||||
if children then
|
||||
while children[1] do destroy(children[1]) end
|
||||
|
||||
if node.owner then
|
||||
remove_child(node.owner, node)
|
||||
node.owner = false
|
||||
end
|
||||
|
||||
while node[1] do destroy(node[1]) end
|
||||
end
|
||||
|
||||
local function evaluate_node<T>(node: Node<T>)
|
||||
|
|
@ -147,7 +131,7 @@ local function evaluate_node<T>(node: Node<T>)
|
|||
close_scope()
|
||||
|
||||
if not ok then
|
||||
throw(`side-effect error\n{new_value}`)
|
||||
throw(`side-effect error from source update\n{new_value}`)
|
||||
end
|
||||
|
||||
node.cache = new_value
|
||||
|
|
@ -158,8 +142,7 @@ end
|
|||
local update_queue = {} :: { Node<any> }
|
||||
|
||||
local function update<T>(node: StartNode<T>)
|
||||
local children = node.children :: {}
|
||||
if not children then return end
|
||||
if not node[1] then return end
|
||||
|
||||
local n0 = #update_queue
|
||||
local first_update = n0 == 0
|
||||
|
|
@ -170,14 +153,14 @@ local function update<T>(node: StartNode<T>)
|
|||
end
|
||||
|
||||
do
|
||||
local child = children[1]
|
||||
local child = node[1]
|
||||
while child do -- todo: case where child in owner context
|
||||
unparent(child)
|
||||
|
||||
n += 1
|
||||
update_queue[n] = child
|
||||
|
||||
child = children[1]
|
||||
child = node[1]
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -216,15 +199,7 @@ local function create_node<T>(value: T, effect: false | (T) -> T): Node<T>
|
|||
end
|
||||
|
||||
local function get_children<T>(node: Node<T>): { Node<unknown> }
|
||||
if not node.children then return {} end
|
||||
|
||||
local children = {}
|
||||
|
||||
for _, child in node.children do
|
||||
table.insert(children, child)
|
||||
end
|
||||
|
||||
return children :: { Node<any> }
|
||||
return { unpack(node) } :: { Node<any> }
|
||||
end
|
||||
|
||||
local function create_start_node<T>(value: T): StartNode<T>
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ local update = graph.update
|
|||
local get_scope = graph.get_scope
|
||||
local open_scope = graph.open_scope
|
||||
local close_scope = graph.close_scope
|
||||
local evaluate_node = graph.evaluate_node
|
||||
local destroy = graph.destroy
|
||||
|
||||
type Map<K, V> = { [K]: V }
|
||||
|
|
@ -33,14 +34,17 @@ end
|
|||
-- todo: optimize output array
|
||||
local function indexes<K, VI, VO>(input: () -> Map<K, VI>, transform: (() -> VI, K) -> VO): () -> { VO }
|
||||
local owner = get_scope()
|
||||
if not owner then throw("cannot derive in non-reactive scope") end
|
||||
assert(owner)
|
||||
if not owner then
|
||||
throw("cannot derive in non-reactive scope")
|
||||
end; assert(owner)
|
||||
|
||||
local subowner = create_node(false, false)
|
||||
set_owner(subowner, owner)
|
||||
|
||||
local input_cache = {} :: Map<K, VI>
|
||||
local output_cache = {} :: Map<K, VO>
|
||||
local input_nodes = {} :: Map<K, StartNode<VI>>
|
||||
local remove_queue = {} :: { K }
|
||||
|
||||
local scopes = {} :: Map<K, Node<unknown>>
|
||||
|
||||
local function update_children(data)
|
||||
|
|
@ -63,7 +67,7 @@ local function indexes<K, VI, VO>(input: () -> Map<K, VI>, transform: (() -> VI,
|
|||
|
||||
table.clear(remove_queue)
|
||||
|
||||
open_scope(owner) -- todo: needed?
|
||||
open_scope(subowner)
|
||||
|
||||
-- process new or changed values
|
||||
for i, v in next, data do
|
||||
|
|
@ -71,10 +75,10 @@ local function indexes<K, VI, VO>(input: () -> Map<K, VI>, transform: (() -> VI,
|
|||
|
||||
if cv ~= v then
|
||||
if cv == nil then
|
||||
local scope = create_node(false)
|
||||
local scope = create_node(false, false)
|
||||
scopes[i] = scope :: Node<any>
|
||||
|
||||
set_owner(scope, owner)
|
||||
set_owner(scope, subowner)
|
||||
open_scope(scope)
|
||||
|
||||
local node = create_start_node(v)
|
||||
|
|
@ -125,15 +129,17 @@ end
|
|||
-- todo: optimize output array
|
||||
local function values<K, VI, VO>(input: () -> Map<K, VI>, transform: (VI, () -> K) -> VO): () -> { VO }
|
||||
local owner = get_scope()
|
||||
if not owner then throw("cannot derive in non-reactive scope") end
|
||||
assert(owner)
|
||||
if not owner then
|
||||
throw("cannot derive in non-reactive scope")
|
||||
end; assert(owner)
|
||||
|
||||
local subowner = create_node(false, false)
|
||||
set_owner(subowner, owner)
|
||||
|
||||
local cur_input_cache_up = {} :: Map<VI, K>
|
||||
local new_input_cache_up = {} :: Map<VI, K>
|
||||
|
||||
local output_cache = {} :: Map<VI, VO>
|
||||
local input_nodes = {} :: Map<VI, StartNode<K>>
|
||||
|
||||
local scopes = {} :: Map<VI, Node<unknown>>
|
||||
|
||||
local function update_children(data: Map<K, VI>)
|
||||
|
|
@ -149,7 +155,7 @@ local function values<K, VI, VO>(input: () -> Map<K, VI>, transform: (VI, () ->
|
|||
end
|
||||
end
|
||||
|
||||
open_scope(owner)
|
||||
open_scope(subowner)
|
||||
|
||||
-- process data
|
||||
for i, v in next, data do
|
||||
|
|
@ -158,10 +164,10 @@ local function values<K, VI, VO>(input: () -> Map<K, VI>, transform: (VI, () ->
|
|||
local cv = cur_input_cache[v]
|
||||
|
||||
if cv == nil then
|
||||
local scope = create_node(false)
|
||||
local scope = create_node(false, false)
|
||||
scopes[v] = scope :: Node<any>
|
||||
|
||||
set_owner(scope, owner)
|
||||
set_owner(scope, subowner)
|
||||
open_scope(scope)
|
||||
|
||||
local node = create_start_node(i)
|
||||
|
|
|
|||
|
|
@ -4,7 +4,9 @@ local vide = require "src/init"
|
|||
|
||||
local N = 2^18 -- 262144
|
||||
|
||||
BENCH("create state", function()
|
||||
-- todo: wide and deep graph benchmarks
|
||||
|
||||
BENCH("create source", function()
|
||||
local source = vide.source
|
||||
|
||||
local cache = table.create(N)
|
||||
|
|
@ -15,50 +17,50 @@ BENCH("create state", function()
|
|||
end)
|
||||
|
||||
BENCH("get value", function()
|
||||
local state = vide.source(1)
|
||||
local src = vide.source(1)
|
||||
|
||||
for i = 1, START(N) do
|
||||
state()
|
||||
src()
|
||||
end
|
||||
end)
|
||||
|
||||
BENCH("set value", function()
|
||||
local state = vide.source(1)
|
||||
local src = vide.source(1)
|
||||
|
||||
for i = 1, START(N) do
|
||||
state(i)
|
||||
src(i)
|
||||
end
|
||||
end)
|
||||
|
||||
BENCH("derive 1 state", function()
|
||||
BENCH("derive 1 source", function()
|
||||
local derive = vide.derive
|
||||
|
||||
local cache = table.create(N)
|
||||
local state = vide.source(1)
|
||||
local src = vide.source(1)
|
||||
|
||||
vide.root(function()
|
||||
for i = 1, START(N) do
|
||||
cache[i] = derive(function()
|
||||
return state()
|
||||
return src()
|
||||
end)
|
||||
end
|
||||
return nil
|
||||
end)
|
||||
end)
|
||||
|
||||
BENCH("derive 4 states", function()
|
||||
BENCH("derive 4 sources", function()
|
||||
local derive = vide.derive
|
||||
|
||||
local cache = table.create(N)
|
||||
local state = vide.source(1)
|
||||
local state2 = vide.source(2)
|
||||
local state3 = vide.source(3)
|
||||
local state4 = vide.source(4)
|
||||
local src = vide.source(1)
|
||||
local src2 = vide.source(2)
|
||||
local src3 = vide.source(3)
|
||||
local src4 = vide.source(4)
|
||||
|
||||
vide.root(function()
|
||||
for i = 1, START(N) do
|
||||
cache[i] = derive(function()
|
||||
return state() + state2() + state3() + state4()
|
||||
return src() + src2() + src3() + src4()
|
||||
end)
|
||||
end
|
||||
|
||||
|
|
@ -66,14 +68,15 @@ BENCH("derive 4 states", function()
|
|||
end)
|
||||
end)
|
||||
|
||||
-- todo: why is this so fast?
|
||||
BENCH("set derived value", function()
|
||||
local state = vide.source(1)
|
||||
local src = vide.source(1)
|
||||
|
||||
vide.root(function()
|
||||
local _derived = vide.derive(state)
|
||||
local _derived = vide.derive(src)
|
||||
|
||||
for i = 1, START(N) do
|
||||
state(i)
|
||||
src(i)
|
||||
end
|
||||
|
||||
return nil
|
||||
|
|
@ -95,14 +98,14 @@ BENCH("apply 8 properties", function()
|
|||
|
||||
for i = 1, START(N) do
|
||||
apply(instance, {
|
||||
Name = i,
|
||||
Name2 = i,
|
||||
Name3 = i,
|
||||
Name4 = i,
|
||||
Name5 = i,
|
||||
Name6 = i,
|
||||
Name7 = i,
|
||||
Name8 = i,
|
||||
Text = i,
|
||||
Text2 = i,
|
||||
Text3 = i,
|
||||
Text4 = i,
|
||||
Text5 = i,
|
||||
Text6 = i,
|
||||
Text7 = i,
|
||||
Text8 = i,
|
||||
})
|
||||
end
|
||||
end)
|
||||
|
|
@ -111,12 +114,12 @@ BENCH("bind source", function()
|
|||
local apply = require "src/apply"
|
||||
|
||||
local instance = vide.create("Frame") {}
|
||||
local state = vide.source(1)
|
||||
local src = vide.source(1)
|
||||
|
||||
vide.root(function()
|
||||
for i = 1, START(N) do
|
||||
apply(instance, {
|
||||
Name = state
|
||||
Text = src
|
||||
})
|
||||
end
|
||||
|
||||
|
|
@ -128,15 +131,15 @@ BENCH("update binding", function()
|
|||
local apply = require "src/apply"
|
||||
|
||||
local instance = vide.create("Frame") {}
|
||||
local state = vide.source(1)
|
||||
local src = vide.source(1)
|
||||
|
||||
vide.root(function()
|
||||
apply(instance, {
|
||||
Name = state
|
||||
Text = src
|
||||
})
|
||||
|
||||
for i = 1, START(N) do
|
||||
state(i)
|
||||
src(i)
|
||||
end
|
||||
|
||||
return nil
|
||||
|
|
@ -145,6 +148,26 @@ end)
|
|||
|
||||
N /= 1024
|
||||
|
||||
BENCH("indexes() all new", function()
|
||||
local data = {}
|
||||
|
||||
for i = 1, N do
|
||||
data[i] = i
|
||||
end
|
||||
|
||||
local src = vide.source(data)
|
||||
|
||||
vide.root(function()
|
||||
START(N)
|
||||
|
||||
local _list = vide.indexes(src, function(v, i)
|
||||
return {}
|
||||
end)
|
||||
|
||||
return nil
|
||||
end)
|
||||
end)
|
||||
|
||||
BENCH("indexes() no change", function()
|
||||
local data = {}
|
||||
|
||||
|
|
@ -152,18 +175,16 @@ BENCH("indexes() no change", function()
|
|||
data[i] = i
|
||||
end
|
||||
|
||||
local state = vide.source(data)
|
||||
local src = vide.source(data)
|
||||
|
||||
vide.root(function()
|
||||
local _list = vide.indexes(state, function(v, i)
|
||||
local _list = vide.indexes(src, function(v, i)
|
||||
return {}
|
||||
end)
|
||||
|
||||
--state(state()) -- fill double buffer
|
||||
|
||||
START(N)
|
||||
|
||||
state(data)
|
||||
src(data)
|
||||
|
||||
return nil
|
||||
end)
|
||||
|
|
@ -176,15 +197,15 @@ BENCH("indexes() all change", function()
|
|||
data[i] = i
|
||||
end
|
||||
|
||||
local state = vide.source(data)
|
||||
local src = vide.source(data)
|
||||
|
||||
|
||||
vide.root(function()
|
||||
local _list = vide.indexes(state, function(v, i)
|
||||
local _list = vide.indexes(src, function(v, i)
|
||||
return {}
|
||||
end)
|
||||
|
||||
--state(state()) -- fill double buffer
|
||||
--src(src()) -- fill double buffer
|
||||
|
||||
for i, v in data do
|
||||
data[i] = v + 1
|
||||
|
|
@ -192,7 +213,7 @@ BENCH("indexes() all change", function()
|
|||
|
||||
START(N)
|
||||
|
||||
state(data)
|
||||
src(data)
|
||||
|
||||
return nil
|
||||
end)
|
||||
|
|
@ -205,10 +226,10 @@ BENCH("indexes() all remove", function()
|
|||
data[i] = i
|
||||
end
|
||||
|
||||
local state = vide.source(data)
|
||||
local src = vide.source(data)
|
||||
|
||||
vide.root(function()
|
||||
local _list = vide.indexes(state, function(v, i)
|
||||
local _list = vide.indexes(src, function(v, i)
|
||||
return {}
|
||||
end)
|
||||
|
||||
|
|
@ -216,7 +237,27 @@ BENCH("indexes() all remove", function()
|
|||
|
||||
START(N)
|
||||
|
||||
state(data)
|
||||
src(data)
|
||||
|
||||
return nil
|
||||
end)
|
||||
end)
|
||||
|
||||
BENCH("values() all new", function()
|
||||
local data = {}
|
||||
|
||||
for i = 1, N do
|
||||
data[i] = {}
|
||||
end
|
||||
|
||||
local src = vide.source(data)
|
||||
|
||||
vide.root(function()
|
||||
START(N)
|
||||
|
||||
local _list = vide.values(src, function(v, i)
|
||||
return {}
|
||||
end)
|
||||
|
||||
return nil
|
||||
end)
|
||||
|
|
@ -229,18 +270,18 @@ BENCH("values() no change", function()
|
|||
data[i] = {}
|
||||
end
|
||||
|
||||
local state = vide.source(data)
|
||||
local src = vide.source(data)
|
||||
|
||||
vide.root(function()
|
||||
local _list = vide.values(state, function(v, i)
|
||||
local _list = vide.values(src, function(v, i)
|
||||
return {}
|
||||
end)
|
||||
|
||||
state(state()) -- fill double buffer
|
||||
src(src()) -- fill double buffer
|
||||
|
||||
START(N)
|
||||
|
||||
state(data)
|
||||
src(data)
|
||||
|
||||
return nil
|
||||
end)
|
||||
|
|
@ -253,14 +294,14 @@ BENCH("values() all change", function()
|
|||
data[i] = {}
|
||||
end
|
||||
|
||||
local state = vide.source(data)
|
||||
local src = vide.source(data)
|
||||
|
||||
vide.root(function()
|
||||
local _list = vide.values(state, function(v, i)
|
||||
local _list = vide.values(src, function(v, i)
|
||||
return {}
|
||||
end)
|
||||
|
||||
state(state()) -- fill double buffer
|
||||
src(src()) -- fill double buffer
|
||||
|
||||
for i = 1, N do
|
||||
local r = math.random(1, #data)
|
||||
|
|
@ -269,7 +310,7 @@ BENCH("values() all change", function()
|
|||
|
||||
START(N)
|
||||
|
||||
state(data)
|
||||
src(data)
|
||||
|
||||
return nil
|
||||
end)
|
||||
|
|
@ -282,10 +323,10 @@ BENCH("values() all remove", function()
|
|||
data[i] = {}
|
||||
end
|
||||
|
||||
local state = vide.source(data)
|
||||
local src = vide.source(data)
|
||||
|
||||
vide.root(function()
|
||||
local _list = vide.values(state, function(v, i)
|
||||
local _list = vide.values(src, function(v, i)
|
||||
return {}
|
||||
end)
|
||||
|
||||
|
|
@ -293,7 +334,7 @@ BENCH("values() all remove", function()
|
|||
|
||||
START(N)
|
||||
|
||||
state(data)
|
||||
src(data)
|
||||
|
||||
return nil
|
||||
end)
|
||||
|
|
|
|||
|
|
@ -113,6 +113,7 @@ local Instance = {} :: any do
|
|||
local function __newindex(userdata: userdata, property: string, value: unknown)
|
||||
local data = get_data(userdata)
|
||||
if property == "Name" then
|
||||
if type(value) ~= "string" then error("name must be a string", 2) end
|
||||
data.name = value :: string
|
||||
elseif property == "Parent" then
|
||||
assert(value == nil or is_instance(value), "attempt to set non-instance as parent")
|
||||
|
|
|
|||
|
|
@ -29,15 +29,7 @@ local function wrap_root(fn: () -> ())
|
|||
end
|
||||
end
|
||||
|
||||
local NIL = nil
|
||||
|
||||
-- vide.mount(function()
|
||||
-- local src = vide.source(0)
|
||||
|
||||
-- vide.effect(function()
|
||||
-- axasd += 1
|
||||
-- end)
|
||||
-- end)
|
||||
local NIL = nil :: any
|
||||
|
||||
TEST("graph", function()
|
||||
local create_node = graph.create_node
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue