removed vtree, contracts on mtree

This commit is contained in:
Alessandro Mauri 2025-10-04 19:58:34 +02:00
parent 01c2fa3367
commit d7cab085f7
5 changed files with 36 additions and 369 deletions

View File

@ -186,7 +186,7 @@ fn PElemTuple? Ctx.get_elem(&ctx, Id id, ElemType type)
elem.type = type; elem.type = type;
} }
elem.z_index = parent.z_index; elem.z_index = parent.z_index;
elem.tree_idx = ctx.tree.add(ctx.active_div, id); elem.tree_idx = ctx.tree.add(ctx.active_div, id)!;
return {elem, parent}; return {elem, parent};
} }

View File

@ -88,6 +88,7 @@ struct MTree {
} }
<* @param [&inout] tree *>
fn void MTree.init(&tree, usz size, Allocator allocator = mem) fn void MTree.init(&tree, usz size, Allocator allocator = mem)
{ {
// round size to the nearest multiple of BITS // round size to the nearest multiple of BITS
@ -106,6 +107,7 @@ fn void MTree.init(&tree, usz size, Allocator allocator = mem)
} }
<* @param [&inout] tree *>
fn void MTree.free(&tree) fn void MTree.free(&tree)
{ {
tree.elements = 0; tree.elements = 0;
@ -116,7 +118,8 @@ fn void MTree.free(&tree)
} }
fn int MTree.get_free_spot(&tree) <* @param [&inout] tree *>
fn int? MTree.get_free_spot(&tree)
{ {
foreach (idx, d: tree.used) { foreach (idx, d: tree.used) {
if (d != $typeof(d).max) { if (d != $typeof(d).max) {
@ -124,7 +127,7 @@ fn int MTree.get_free_spot(&tree)
return spot; return spot;
} }
} }
unreachable("no free spots left"); return CAPACITY_EXCEEDED?;
} }
<* @require idx >= 0 *> <* @require idx >= 0 *>
@ -163,10 +166,13 @@ fn int MTree.last_node(&tree, int parent)
} }
<* @require tree.elements == 0 || tree.is_used(parent) == true *> <*
fn int MTree.add(&tree, int parent, Type t) @require tree.elements == 0 || tree.is_used(parent) == true
@param [&inout] tree
*>
fn int? MTree.add(&tree, int parent, Type t)
{ {
int idx = tree.get_free_spot(); int idx = tree.get_free_spot()!;
int subtree = idx / BITS; int subtree = idx / BITS;
tree.set_used(idx); tree.set_used(idx);
@ -197,11 +203,11 @@ fn int MTree.add(&tree, int parent, Type t)
} }
// on fail we need to create another parent node // on fail we need to create another parent node
if (!done) { if (!done) {
int new_next = tree.get_free_spot(); int new_next = tree.get_free_spot()!;
// if the new node does not land in the same subtree as the child we cannot do // if the new node does not land in the same subtree as the child we cannot do
// anything since the references are immutable // anything since the references are immutable
if (new_next/BITS != subtree) { if (new_next/BITS != subtree) {
unreachable("cannot allocate new child for parent"); return CAPACITY_EXCEEDED?;
} }
tree.set_used(new_next); tree.set_used(new_next);
tree.elements++; tree.elements++;
@ -220,6 +226,7 @@ fn int MTree.add(&tree, int parent, Type t)
// get the index of the n-th children of parent, -1 otherwise // get the index of the n-th children of parent, -1 otherwise
// usage: for (int i, c; (c = tree.children_it(parent, i)) >= 0; i++) { ... } // usage: for (int i, c; (c = tree.children_it(parent, i)) >= 0; i++) { ... }
<* @param [&in] tree *>
fn int MTree.children_it(&tree, int parent, int n) fn int MTree.children_it(&tree, int parent, int n)
{ {
int tot_children; int tot_children;
@ -247,6 +254,7 @@ fn int MTree.children_it(&tree, int parent, int n)
return -1; return -1;
} }
<* @param [&in] tree *>
fn int MTree.children_num(&tree, int parent) fn int MTree.children_num(&tree, int parent)
{ {
int n; int n;
@ -256,6 +264,7 @@ fn int MTree.children_num(&tree, int parent)
return n; return n;
} }
<* @param [&in] tree *>
fn int MTree.subtree_size(&tree, int parent) fn int MTree.subtree_size(&tree, int parent)
{ {
int x = tree.children_num(parent); int x = tree.children_num(parent);
@ -266,8 +275,7 @@ fn int MTree.subtree_size(&tree, int parent)
return x; return x;
} }
<* @param [&inout] tree *>
fn int MTree.level_order_it(&tree, int parent, int i) fn int MTree.level_order_it(&tree, int parent, int i)
{ {
if (i == 0) { if (i == 0) {
@ -285,8 +293,11 @@ fn int MTree.level_order_it(&tree, int parent, int i)
return p; return p;
} }
<* @param [&inout] tree *>
fn void MTree.prune(&tree, int parent) fn void MTree.prune(&tree, int parent)
{ {
if (!tree.is_used(parent)) return;
int c; int c;
for (int i = 0; (c = tree.children_it(parent, i)) >= 0; i++) { for (int i = 0; (c = tree.children_it(parent, i)) >= 0; i++) {
tree.prune(c); // prune the subtree tree.prune(c); // prune the subtree
@ -312,16 +323,24 @@ fn void MTree.prune(&tree, int parent)
} }
<* @require ref >= 0 , ref < tree.elem_vec.len *> <*
@require ref >= 0 , ref < tree.elem_vec.len
@param [&inout] tree
*>
fn Type? MTree.get(&tree, int ref) @operator([]) fn Type? MTree.get(&tree, int ref) @operator([])
{ {
if (tree.is_used(ref)) return tree.elem_vec[ref]; if (tree.is_used(ref)) return tree.elem_vec[ref];
return NOT_FOUND?; return NOT_FOUND?;
} }
<* @require tree.is_used(ref) *> <* @param [&in] tree *>
fn Type MTree.parentof(&tree, int ref) => tree.refs_vec[ref].parent; fn Type? MTree.parentof(&tree, int ref)
{
if (!tree.is_used(ref)) return NOT_FOUND?;
return tree.refs_vec[ref].parent;
}
<* @param [&inout] tree *>
fn void MTree.nuke(&tree) fn void MTree.nuke(&tree)
{ {
foreach (idx, &b: tree.used) { foreach (idx, &b: tree.used) {
@ -331,9 +350,10 @@ fn void MTree.nuke(&tree)
tree.elements = 0; tree.elements = 0;
} }
<* @param [&in] t *>
macro bool MTree.is_root(&t, int i) => t.is_used(i) && t.refs_vec[i].parent == -1;
macro bool MTree.is_root(&t, int i) => t.refs_vec[i].parent == -1; <* @param [&in] tree *>
fn void MTree.print(&tree) fn void MTree.print(&tree)
{ {
foreach (idx, c: tree.elem_vec) { foreach (idx, c: tree.elem_vec) {

View File

@ -1,352 +0,0 @@
module vtree::faults;
faultdef CANNOT_SHRINK, INVALID_REFERENCE, TREE_FULL, REFERENCE_NOT_PRESENT, INVALID_ARGUMENT;
module vtree{ElemType};
import std::core::mem;
import std::core::mem::allocator;
import std::io;
struct VTree {
Allocator allocator;
usz elements;
ElemType[] vector; // vector of element ids
isz[] refs, ordered_refs;
}
macro VTree.ref_is_valid(&tree, isz ref) => (ref >= 0 && ref < tree.refs.len);
macro VTree.ref_is_present(&tree, isz ref) => tree.refs[ref] >= 0;
macro VTree.size(&tree) => tree.refs.len;
// macro to zero an element
macro @zero()
{
$if @assignable_to(0, ElemType):
return 0;
$endif
$if @assignable_to(null, ElemType):
return null;
$endif
$if @assignable_to({}, ElemType):
return {};
$endif
//$assert true == false : ElemType.nameof +++ " is not assignable to zero or equivalent";
}
fn void? VTree.init(&tree, usz size, Allocator allocator)
{
tree.allocator = allocator;
tree.vector = allocator::new_array(tree.allocator, ElemType, size);
defer catch { (void)allocator::free(tree.allocator, tree.vector); }
tree.refs = allocator::new_array(tree.allocator, isz, size);
defer catch { (void)allocator::free(tree.allocator, tree.refs); }
tree.ordered_refs = allocator::new_array(tree.allocator, isz, size);
defer catch { (void)allocator::free(tree.allocator, tree.ordered_refs); }
// set all refs to -1, meaning invalid (free) element
tree.refs[..] = -1;
tree.elements = 0;
}
fn void VTree.free(&tree)
{
(void)allocator::free(tree.allocator, tree.vector);
(void)allocator::free(tree.allocator, tree.refs);
(void)allocator::free(tree.allocator, tree.ordered_refs);
}
fn void VTree.pack(&tree)
{
// TODO: add a PACKED flag to skip this
isz free_spot = -1;
for (usz i = 0; i < tree.size(); i++) {
if (tree.refs[i] == -1) {
free_spot = i;
continue;
}
// find a item that can be packed
if (free_spot >= 0 && tree.refs[i] >= 0) {
isz old_ref = i;
// move the item
tree.vector[free_spot] = tree.vector[i];
tree.refs[free_spot] = tree.refs[i];
tree.vector[i] = {};
tree.refs[i] = -1;
// and move all references
for (usz j = 0; j < tree.size(); j++) {
if (tree.refs[j] == old_ref) {
tree.refs[j] = free_spot;
}
}
// mark the free spot as used
free_spot = -1;
}
}
}
fn void? VTree.resize(&tree, usz newsize)
{
// return error when shrinking with too many elements
if (newsize < tree.elements) {
return vtree::faults::CANNOT_SHRINK?;
}
// pack the vector when shrinking to avoid data loss
if ((int)newsize < tree.size()) {
// FIXME: packing destroys all references to elements of vec
// so shrinking may cause dangling pointers
return vtree::faults::CANNOT_SHRINK?;
}
usz old_size = tree.size();
tree.vector = ((ElemType*)allocator::realloc(tree.allocator, tree.vector, newsize*ElemType.sizeof))[:newsize];
defer catch { (void)allocator::free(tree.allocator, tree.vector); }
tree.refs = ((isz*)allocator::realloc(tree.allocator, tree.refs, newsize*isz.sizeof))[:newsize];
defer catch { (void)allocator::free(tree.allocator, tree.refs); }
tree.ordered_refs = ((isz*)allocator::realloc(tree.allocator, tree.ordered_refs, newsize*isz.sizeof))[:newsize];
defer catch { (void)allocator::free(tree.allocator, tree.ordered_refs); }
if (newsize > tree.size()) {
tree.vector[old_size..newsize-1] = @zero();
tree.refs[old_size..newsize-1] = -1;
}
}
// add an element to the tree, return it's ref
fn isz? VTree.add(&tree, ElemType elem, isz parent)
{
// invalid parent
if (!tree.ref_is_valid(parent)) {
return vtree::faults::INVALID_REFERENCE?;
}
// no space left
if (tree.elements >= tree.size()) {
return vtree::faults::TREE_FULL?;
}
// check if the parent exists
// if there are no elements in the tree the first add will set the root
if (!tree.ref_is_present(parent) && tree.elements != 0) {
return vtree::faults::REFERENCE_NOT_PRESENT?;
}
// get the first free spot
isz free_spot = -1;
for (usz i = 0; i < tree.size(); i++) {
if (tree.refs[i] == -1) {
free_spot = i;
break;
}
}
if (free_spot < 0) {
return vtree::faults::TREE_FULL?;
}
// finally add the element
tree.vector[free_spot] = elem;
tree.refs[free_spot] = parent;
tree.elements++;
return free_spot;
}
// prune the tree starting from the ref
// returns the number of pruned elements
fn usz? VTree.prune(&tree, isz ref)
{
if (!tree.ref_is_valid(ref)) {
return vtree::faults::INVALID_REFERENCE?;
}
if (!tree.ref_is_present(ref)) {
return 0;
}
tree.vector[ref] = @zero();
tree.refs[ref] = -1;
tree.elements--;
usz count = 1;
for (usz i = 0; tree.elements > 0 && i < tree.size(); i++) {
if (tree.refs[i] == ref) {
count += tree.prune(i)!;
}
}
return count;
}
fn usz VTree.nuke(&tree)
{
tree.vector[0..] = @zero();
tree.refs[0..] = -1;
usz x = tree.elements;
tree.elements = 0;
return x;
}
// find the size of the subtree starting from ref
fn usz? VTree.subtree_size(&tree, isz ref)
{
if (!tree.ref_is_valid(ref)) {
return vtree::faults::INVALID_REFERENCE?;
}
if (!tree.ref_is_present(ref)) {
return 0;
}
usz count = 1;
for (usz i = 0; i < tree.size(); i++) {
// only root has the reference to itself
if (tree.refs[i] == ref && ref != i) {
count += tree.subtree_size(i)!;
}
}
return count;
}
fn bool? VTree.is_root(&tree, isz node) => node == tree.parentof(node)!;
// iterate through the first level children, use a cursor like strtok_r
fn isz? VTree.children_it(&tree, isz parent, isz *cursor)
{
if (cursor == null) {
return vtree::faults::INVALID_ARGUMENT?;
}
// if the cursor is out of bounds then we are done for sure
if (!tree.ref_is_valid(*cursor)) {
return vtree::faults::INVALID_REFERENCE?;
}
// same for the parent, if it's invalid it can't have children
if (!tree.ref_is_valid(parent) || !tree.ref_is_present(parent)) {
return vtree::faults::INVALID_REFERENCE?;
}
// find the first child, update the cursor and return the ref
for (isz i = *cursor; i < tree.size(); i++) {
if (tree.refs[i] == parent) {
*cursor = i + 1;
return i;
}
}
// if no children are found return -1
*cursor = -1;
return -1;
}
/* iterates trough every leaf of the subtree in the following manner
* node [x], x: visit order
* [0]
* / | \
* / [2] [3]
* [1] |
* / \ [6]
* [4] [5]
*/
fn isz? VTree.level_order_it(&tree, isz ref, isz *cursor)
{
if (cursor == null) {
return vtree::faults::INVALID_ARGUMENT?;
}
isz[] queue = tree.ordered_refs;
// TODO: this could also be done when adding or removing elements
// first call, create a ref array ordered like we desire
if (*cursor == -1) {
*cursor = 0;
queue[..] = -1;
// iterate through the queue appending found children
isz pos, off;
do {
// printf ("ref=%d\n", ref);
for (isz i = 0; i < tree.size(); i++) {
if (tree.refs[i] == ref) {
queue[pos++] = i;
}
}
for (; ref == queue[off] && off < tree.size(); off++);
ref = queue[off];
} while (tree.ref_is_valid(ref));
// This line is why tree.ordered_refs has to be size+1
queue[off + 1] = -1;
}
// PRINT_ARR(queue, tree.size());
// return -1;
// on successive calls just iterate through the queue until we find an
// invalid ref, if the user set the cursor to -1 it means it has found what
// he needed, so free
if (*cursor < 0) {
return -1;
} else if (tree.ref_is_valid(*cursor)) {
return queue[(*cursor)++];
}
return -1;
}
fn isz? VTree.parentof(&tree, isz ref)
{
if (!tree.ref_is_valid(ref)) {
return vtree::faults::INVALID_REFERENCE?;
}
if (!tree.ref_is_present(ref)) {
return vtree::faults::REFERENCE_NOT_PRESENT?;
}
return tree.refs[ref];
}
fn ElemType? VTree.get(&tree, isz ref)
{
if (!tree.ref_is_valid(ref)) {
return vtree::faults::INVALID_REFERENCE?;
}
if (!tree.ref_is_present(ref)) {
return vtree::faults::REFERENCE_NOT_PRESENT?;
}
return tree.vector[ref];
}
fn void VTree.print(&tree)
{
for (isz i = 0; i < tree.size(); i++) {
if (tree.refs[i] == -1) {
continue;
}
io::printf("[%d] {parent=%d, data=", i, tree.refs[i]);
io::print(tree.vector[i]);
io::printn("}");
}
}

View File

@ -156,7 +156,7 @@ fn Id? Ctx.div_end(&ctx)
} }
// the active_div returns to the parent of the current one // the active_div returns to the parent of the current one
ctx.active_div = ctx.tree.parentof(ctx.active_div); ctx.active_div = ctx.tree.parentof(ctx.active_div)!;
Elem* parent = ctx.get_parent()!; Elem* parent = ctx.get_parent()!;
ctx.div_scissor = parent.bounds.pad(parent.layout.content_offset); ctx.div_scissor = parent.bounds.pad(parent.layout.content_offset);
ctx.reset_scissor(elem.z_index)!; ctx.reset_scissor(elem.z_index)!;

View File

@ -1,5 +1,4 @@
import std::io; import std::io;
import vtree;
import cache; import cache;
import ugui; import ugui;
import std::time; import std::time;