first eithers impl

This commit is contained in:
BlackMATov
2026-01-11 20:33:17 +07:00
parent c9f4a74518
commit a5319351c1
2 changed files with 211 additions and 64 deletions

View File

@@ -28,6 +28,20 @@ end
---@param query evolved.query
local function generate_query(query)
local either_set = {}
local either_list = {}
local either_count = 0
for _ = 1, math.random(0, #all_fragment_list) do
local either = all_fragment_list[math.random(1, #all_fragment_list)]
if not either_set[either] then
either_count = either_count + 1
either_set[either] = either_count
either_list[either_count] = either
end
end
local include_set = {}
local include_list = {}
local include_count = 0
@@ -56,6 +70,10 @@ local function generate_query(query)
end
end
if either_count > 0 then
evo.set(query, evo.EITHERS, either_list)
end
if include_count > 0 then
evo.set(query, evo.INCLUDES, include_list)
end
@@ -171,9 +189,19 @@ local function execute_query(query)
local query_chunk_set = {}
local query_entity_set = {}
local query_either_list = evo.get(query, evo.EITHERS) or {}
local query_include_list = evo.get(query, evo.INCLUDES) or {}
local query_exclude_list = evo.get(query, evo.EXCLUDES) or {}
local query_either_count = #query_either_list
local query_include_count = #query_include_list
local query_exclude_count = #query_exclude_list
local query_either_set = {}
for _, either in ipairs(query_either_list) do
query_either_set[either] = true
end
local query_include_set = {}
for _, include in ipairs(query_include_list) do
query_include_set[include] = true
@@ -189,19 +217,29 @@ local function execute_query(query)
query_entity_set[entity] = true
end
assert(chunk:has_all(__table_unpack(query_include_list)))
assert(not chunk:has_any(__table_unpack(query_exclude_list)))
if query_either_count > 0 then
assert(chunk:has_any(__table_unpack(query_either_list)))
end
if query_include_count > 0 then
assert(chunk:has_all(__table_unpack(query_include_list)))
end
if query_exclude_count > 0 then
assert(not chunk:has_any(__table_unpack(query_exclude_list)))
end
end
for i = 1, all_entity_count do
local entity = all_entity_list[i]
local is_entity_matched =
evo.has_all(entity, __table_unpack(query_include_list))
and not evo.has_any(entity, __table_unpack(query_exclude_list))
(query_either_count == 0 or evo.has_any(entity, __table_unpack(query_either_list))) and
(query_include_count == 0 or evo.has_all(entity, __table_unpack(query_include_list))) and
(query_exclude_count == 0 or not evo.has_any(entity, __table_unpack(query_exclude_list)))
for fragment in evo.each(entity) do
if evo.has(fragment, evo.EXPLICIT) and not query_include_set[fragment] then
if evo.has(fragment, evo.EXPLICIT) and not query_either_set[fragment] and not query_include_set[fragment] then
is_entity_matched = false
end
end
@@ -236,7 +274,10 @@ for _ = 1, math.random(1, 5) do
if math.random(1, 2) == 1 then
generate_query(query)
else
if math.random(1, 2) == 1 then
local r = math.random(1, 3)
if r == 1 then
evo.remove(query, evo.EITHERS)
elseif r == 2 then
evo.remove(query, evo.INCLUDES)
else
evo.remove(query, evo.EXCLUDES)

View File

@@ -81,7 +81,9 @@ local evolved = {
---@field package [1] integer structural_changes
---@field package [2] evolved.chunk[] chunk_stack
---@field package [3] integer chunk_stack_size
---@field package [4] table<evolved.fragment, integer>? exclude_set
---@field package [4] table<evolved.fragment, integer>? either_set
---@field package [5] table<evolved.fragment, integer>? include_set
---@field package [6] table<evolved.fragment, integer>? exclude_set
---@alias evolved.each_iterator fun(
--- state: evolved.each_state?):
@@ -1108,6 +1110,9 @@ local __trace_minor_chunks
local __cache_query_chunks
local __reset_query_chunks
local __query_major_matches
local __query_minor_matches
local __update_major_chunks
local __update_major_chunks_trace
@@ -1117,7 +1122,6 @@ local __chunk_without_fragment
local __chunk_without_fragments
local __chunk_without_unique_fragments
local __chunk_matches
local __chunk_requires
local __chunk_fragments
local __chunk_components
@@ -1399,7 +1403,7 @@ function __update_chunk_queries(chunk)
local major_query_chunks = __query_chunks[major_query]
if major_query_chunks then
if __chunk_matches(chunk, major_query) then
if __query_major_matches(chunk, major_query) then
__assoc_list_insert(major_query_chunks, chunk)
else
__assoc_list_remove(major_query_chunks, chunk)
@@ -1572,20 +1576,37 @@ end
function __cache_query_chunks(query)
__reset_query_chunks(query)
local query_eithers = __sorted_eithers[query]
local query_either_list = query_eithers and query_eithers.__item_list
local query_either_count = query_eithers and query_eithers.__item_count or 0
local query_includes = __sorted_includes[query]
local query_include_list = query_includes and query_includes.__item_list
local query_include_count = query_includes and query_includes.__item_count or 0
if query_include_count == 0 then
__error_fmt('the query (%s) has no include fragments and cannot be cached',
__id_name(query))
end
---@type evolved.assoc_list<evolved.chunk>
local query_chunks = __assoc_list_new(4)
__query_chunks[query] = query_chunks
do
for query_either_index = 1, query_either_count do
local query_either = query_either_list[query_either_index]
if query_include_count == 0 or query_either > query_include_list[query_include_count] then
local major_chunks = __major_chunks[query_either]
local major_chunk_list = major_chunks and major_chunks.__item_list
local major_chunk_count = major_chunks and major_chunks.__item_count or 0
for major_chunk_index = 1, major_chunk_count do
local major_chunk = major_chunk_list[major_chunk_index]
if __query_major_matches(major_chunk, query) then
__assoc_list_insert(query_chunks, major_chunk)
end
end
end
end
if query_include_count > 0 then
local query_major = query_include_list[query_include_count]
local major_chunks = __major_chunks[query_major]
@@ -1595,7 +1616,7 @@ function __cache_query_chunks(query)
for major_chunk_index = 1, major_chunk_count do
local major_chunk = major_chunk_list[major_chunk_index]
if __chunk_matches(major_chunk, query) then
if __query_major_matches(major_chunk, query) then
__assoc_list_insert(query_chunks, major_chunk)
end
end
@@ -1609,6 +1630,87 @@ function __reset_query_chunks(query)
__query_chunks[query] = nil
end
---@param chunk evolved.chunk
---@param query evolved.query
---@return boolean
---@nodiscard
function __query_major_matches(chunk, query)
local query_eithers = __sorted_eithers[query]
local query_either_set = query_eithers and query_eithers.__item_set
local query_either_list = query_eithers and query_eithers.__item_list
local query_either_count = query_eithers and query_eithers.__item_count or 0
local query_includes = __sorted_includes[query]
local query_include_set = query_includes and query_includes.__item_set
local query_include_count = query_includes and query_includes.__item_count or 0
local query_either_index = query_either_count > 0 and query_either_set[chunk.__fragment] or nil
local query_include_index = query_include_count > 0 and query_include_set[chunk.__fragment] or nil
return (
(query_include_index ~= nil and query_include_index == query_include_count) or
(query_either_index ~= nil and not __chunk_has_any_fragment_list(chunk, query_either_list, query_either_index - 1))
) and __query_minor_matches(chunk, query)
end
---@param chunk evolved.chunk
---@param query evolved.query
---@return boolean
---@nodiscard
function __query_minor_matches(chunk, query)
local query_eithers = __sorted_eithers[query]
local query_either_set = query_eithers and query_eithers.__item_set
local query_either_list = query_eithers and query_eithers.__item_list
local query_either_count = query_eithers and query_eithers.__item_count or 0
if query_either_count > 0 then
if not __chunk_has_any_fragment_list(chunk, query_either_list, query_either_count) then
return false
end
end
local query_includes = __sorted_includes[query]
local query_include_set = query_includes and query_includes.__item_set
local query_include_list = query_includes and query_includes.__item_list
local query_include_count = query_includes and query_includes.__item_count or 0
if query_include_count > 0 then
if not __chunk_has_all_fragment_list(chunk, query_include_list, query_include_count) then
return false
end
end
local query_excludes = __sorted_excludes[query]
local query_exclude_list = query_excludes and query_excludes.__item_list
local query_exclude_count = query_excludes and query_excludes.__item_count or 0
if query_exclude_count > 0 then
if __chunk_has_any_fragment_list(chunk, query_exclude_list, query_exclude_count) then
return false
end
end
if chunk.__has_explicit_fragments then
local chunk_fragment_list = chunk.__fragment_list
local chunk_fragment_count = chunk.__fragment_count
for chunk_fragment_index = 1, chunk_fragment_count do
local chunk_fragment = chunk_fragment_list[chunk_fragment_index]
local is_chunk_fragment_matched =
(not __evolved_has(chunk_fragment, __EXPLICIT)) or
(query_either_count > 0 and query_either_set[chunk_fragment]) or
(query_include_count > 0 and query_include_set[chunk_fragment])
if not is_chunk_fragment_matched then
return false
end
end
end
return true
end
---@param major evolved.fragment
function __update_major_chunks(major)
if __defer_depth > 0 then
@@ -1789,50 +1891,6 @@ function __chunk_without_unique_fragments(chunk)
return sib_chunk
end
---@param chunk evolved.chunk
---@param query evolved.query
---@return boolean
---@nodiscard
function __chunk_matches(chunk, query)
local query_includes = __sorted_includes[query]
local query_include_set = query_includes and query_includes.__item_set
local query_include_list = query_includes and query_includes.__item_list
local query_include_count = query_includes and query_includes.__item_count or 0
if query_include_count > 0 then
if not __chunk_has_all_fragment_list(chunk, query_include_list, query_include_count) then
return false
end
elseif chunk.__has_explicit_fragments then
return false
end
local query_excludes = __sorted_excludes[query]
local query_exclude_list = query_excludes and query_excludes.__item_list
local query_exclude_count = query_excludes and query_excludes.__item_count or 0
if query_exclude_count > 0 then
if __chunk_has_any_fragment_list(chunk, query_exclude_list, query_exclude_count) then
return false
end
end
if chunk.__has_explicit_fragments then
local chunk_fragment_list = chunk.__fragment_list
local chunk_fragment_count = chunk.__fragment_count
for chunk_fragment_index = 1, chunk_fragment_count do
local chunk_fragment = chunk_fragment_list[chunk_fragment_index]
if not query_include_set[chunk_fragment] and __evolved_has(chunk_fragment, __EXPLICIT) then
return false
end
end
end
return true
end
---@param chunk evolved.chunk
---@return evolved.chunk
---@nodiscard
@@ -3864,7 +3922,9 @@ function __iterator_fns.__execute_iterator(execute_state)
local structural_changes = execute_state[1]
local chunk_stack = execute_state[2]
local chunk_stack_size = execute_state[3]
local exclude_set = execute_state[4]
local either_set = execute_state[4]
local include_set = execute_state[5]
local exclude_set = execute_state[6]
if structural_changes ~= __structural_changes then
__error_fmt('structural changes are prohibited during iteration')
@@ -3884,7 +3944,9 @@ function __iterator_fns.__execute_iterator(execute_state)
local chunk_child_fragment = chunk_child.__fragment
local is_chunk_child_matched =
(not chunk_child.__has_explicit_major) and
(not chunk_child.__has_explicit_major or (
(either_set and either_set[chunk_child_fragment]) or
(include_set and include_set[chunk_child_fragment]))) and
(not exclude_set or not exclude_set[chunk_child_fragment])
if is_chunk_child_matched then
@@ -5233,14 +5295,19 @@ function __evolved_execute(query)
local chunk_stack = __acquire_table(__table_pool_tag.chunk_list)
local chunk_stack_size = 0
local query_eithers = __sorted_eithers[query]
local query_either_set = query_eithers and query_eithers.__item_set
local query_either_count = query_eithers and query_eithers.__item_count or 0
local query_includes = __sorted_includes[query]
local query_include_set = query_includes and query_includes.__item_set
local query_include_count = query_includes and query_includes.__item_count or 0
local query_excludes = __sorted_excludes[query]
local query_exclude_set = query_excludes and query_excludes.__item_set
local query_exclude_count = query_excludes and query_excludes.__item_count or 0
if query_include_count > 0 then
if query_either_count > 0 or query_include_count > 0 then
local query_chunks = __query_chunks[query] or __cache_query_chunks(query)
local query_chunk_list = query_chunks and query_chunks.__item_list
local query_chunk_count = query_chunks and query_chunks.__item_count or 0
@@ -5281,7 +5348,9 @@ function __evolved_execute(query)
execute_state[1] = __structural_changes
execute_state[2] = chunk_stack
execute_state[3] = chunk_stack_size
execute_state[4] = query_exclude_set
execute_state[4] = query_either_set
execute_state[5] = query_include_set
execute_state[6] = query_exclude_set
return __iterator_fns.__execute_iterator, execute_state
end
@@ -6325,15 +6394,36 @@ __evolved_set(__ON_REMOVE, __UNIQUE)
---@param query evolved.query
local function __insert_query(query)
local query_eithers = __sorted_eithers[query]
local query_either_list = query_eithers and query_eithers.__item_list
local query_either_count = query_eithers and query_eithers.__item_count or 0
local query_includes = __sorted_includes[query]
local query_include_list = query_includes and query_includes.__item_list
local query_include_count = query_includes and query_includes.__item_count or 0
for query_either_index = 1, query_either_count do
local query_either = query_either_list[query_either_index]
if query_include_count == 0 or query_either > query_include_list[query_include_count] then
local major_queries = __major_queries[query_either]
if not major_queries then
---@type evolved.assoc_list<evolved.query>
major_queries = __assoc_list_new(4)
__major_queries[query_either] = major_queries
end
__assoc_list_insert(major_queries, query)
end
end
if query_include_count > 0 then
local query_major = query_include_list[query_include_count]
local major_queries = __major_queries[query_major]
if not major_queries then
---@type evolved.assoc_list<evolved.query>
major_queries = __assoc_list_new(4)
__major_queries[query_major] = major_queries
end
@@ -6344,10 +6434,26 @@ end
---@param query evolved.query
local function __remove_query(query)
local query_eithers = __sorted_eithers[query]
local query_either_list = query_eithers and query_eithers.__item_list
local query_either_count = query_eithers and query_eithers.__item_count or 0
local query_includes = __sorted_includes[query]
local query_include_list = query_includes and query_includes.__item_list
local query_include_count = query_includes and query_includes.__item_count or 0
for query_either_index = 1, query_either_count do
local query_either = query_either_list[query_either_index]
if query_include_count == 0 or query_either > query_include_list[query_include_count] then
local major_queries = __major_queries[query_either]
if major_queries and __assoc_list_remove(major_queries, query) == 0 then
__major_queries[query_either] = nil
end
end
end
if query_include_count > 0 then
local query_major = query_include_list[query_include_count]
local major_queries = __major_queries[query_major]