Skip to main content

Packo

Struct Packo 

Source
pub struct Packo<const S: IDX, T, const C: CATS = 1> { /* private fields */ }
Expand description

Create a packed structure Packo (array of Ts) with self referential indexes.

T will be always be zero initialized in memory without calling any default or constructor. T must be safe to be zero-initializable.

The total size of the Packo is scaled by S + 1 + the used array: S must be greater than 1 as the 0th element is used as an always zeroed sentinel value. And the extra element is used for dirty writes when indexing out of the valid elements.

Each element will also have a few indexes, at least 4: next, prev, child, parent. In case of C > 1 then the child index will repeat C times for each category. These indexes will add to the size of each node. Each index is of size of IDX.

When indexing the elements, the first element (Packo[0]) will always be a nil value (zeroed T). The 0th element is an invalid element and used as sentinel value. Writing to the 0th element will have also no effect.

If the index is out of the Packo bounds the 0th element will be returned.

Every item in Packo can connect as a list and/or a tree depending of the internal indexing capability. Because the internal indexing follows linked list principles, iterating into sub-lists will be linear time, but accessing a single item by index (id) will be constant instead.

Types:

  • S: Size. How many elements are preallocated. One element is reserved for the sentinel value, S must be > 1.
  • C: Categories. How many nesting categories are available per node. Each category will have its own separate child+parent indexing allowing for tree data structures based on the category index. Categories is defaulted to 1 and only if used the variant methods with suffix ‘c’ will select a specific category.
  • T: The type contained per each node.

After calling Packo::insert a few times for 1, 2, 3, 4, the collection will contain a number of disconnected root nodes that can be iterated through Packo::roots:

use packo::*;

let mut p = Packo::<20, u8>::default();
p.insert(1);
p.insert(2);
p.insert(3);
p.insert(4);
V IDX  CHILD  PARENT  PREV  NEXT
1   1      0       0     0     0
2   2      0       0     0     0
3   3      0       0     0     0
4   4      0       0     0     0
|1|  |2|  |3|  |4|

If called differently by appending to each returned ixd, then the elements will be connected together in a linked list that later can be queried with Packo::next and iterated with Packo::siblings given the starting point (linked lists are circular).

use packo::*;

let mut p = Packo::<20, u8>::default();
let p1 = p.insert(1);
let p2 = p.append(p1,2);
let p3 = p.append(p2, 3);
p.append(p3, 4);
V IDX  CHILD  PARENT  PREV  NEXT
1   1      0       0     4     2
2   2      0       0     1     3
3   3      0       0     2     4
4   4      0       0     3     1
|1| <> |2| <> |3| <> |4|

In this other example 1, 2 are roots and 3, 4 are a sublist of 1. Using Packo::nest the first element that calls that will spawn under the idx as a tree child. Children can later be checked with Packo::child to get the first child idx of a parent, or Packo::childs to iterate on all the childs of that parent.

use packo::*;

let mut p = Packo::<20, u8>::default();
let p1 = p.insert(1);
let p2 = p.append(p1, 2);
let p3 = p.nest(p1, 3);
p.append(p3, 4);
V IDX  CHILD  PARENT  PREV  NEXT
1   1      3       0     2     2
2   2      0       0     1     1
3   3      0       1     4     4
4   4      0       1     3     3
|1| <> |2|
 |
|3| <> |4|

When declaring Packo with a generic C for a number of categories greater than the default (1), then the children can be split into subtree by each category. The method variants ending with ‘c’ will use the cateogry identifier to select for which category to nest into or check childs for.

use packo::*;

let mut p = Packo::<20, u8, 2>::default();
let p1 = p.insert(1);
let p2 = p.append(p1, 2);
let p3 = p.nestc(p1, 3, 0);
p.append(p3, 4);
let p5 = p.nestc(p1, 5, 1);
p.append(p5, 6);
V IDX  CHILD[0] CHILD[1]  PARENT  PREV  NEXT
1   1        3        5        0     2     2
2   2        0        0        0     1     1
3   3        0        0        1     4     4
4   4        0        0        1     3     3
5   5        0        0        1     6     6
6   6        0        0        1     5     5
   |1| <> |2|
   / \
  /   \
 |     | (via C=1)
 |     |
 |    |5| <> |6|
 |
 | (via C=0)
 |
|3| <> |4|  

Implementations§

Source§

impl<const S: IDX, T, const C: CATS> Packo<S, T, C>

Source

pub fn reset(&mut self)

Delete all items, sets all to unused.

Examples found in repository?
examples/entities.rs (line 145)
144fn game_reset(e: &mut Entities) -> IDX {
145    e.reset();
146
147    let p = e.insert(Entity {
148        components: Component::Player + Component::Spatial + Component::Life.into(),
149        life: 100,
150        ..Default::default()
151    });
152    e[p].id = p;
153
154    let potion = e.nestc(
155        p,
156        CAT_INVENTORY,
157        Entity {
158            components: Component::Life + Component::Consume,
159            life: 50,
160            ..Default::default()
161        },
162    );
163    e[potion].id = potion;
164
165    let firebolt = e.nestc(
166        p,
167        CAT_SPELLS,
168        Entity {
169            components: Component::Damage + Component::Cost,
170            damage: 10,
171            cost: 5,
172            ..Default::default()
173        },
174    );
175    e[firebolt].id = firebolt;
176
177    let heal = e.nestc(
178        p,
179        CAT_SPELLS,
180        Entity {
181            components: Component::Life + Component::Cost,
182            life: 10,
183            cost: 5,
184            ..Default::default()
185        },
186    );
187    e[heal].id = heal;
188
189    p
190}
Source

pub fn next(&self, i: IDX) -> IDX

Gets the next index, if this item is part of a list. Failure returns idx 0.

Examples found in repository?
examples/html.rs (line 29)
23fn print(d: &Document, i: IDX) {
24    print!("<{}>", d[i].name);
25    let s = d.child(i);
26    let mut c = s;
27    while c != 0 {
28        print(d, c);
29        c = d.next(c);
30        if c == s {
31            break;
32        }
33    }
34    print!("{}", d[i].text);
35    print!("</{}>", d[i].name);
36}
Source

pub fn prev(&self, i: IDX) -> IDX

Gets the previous index, if this item is part of a list. Failure returns idx 0.

Source

pub fn parent(&self, i: IDX) -> IDX

Gets the index of the parent of the given i. Failure returns idx 0.

Source

pub fn child(&self, i: IDX) -> IDX

Gets the index of the first child, or 0 if no childs are present. Failure returns idx 0.

Examples found in repository?
examples/html.rs (line 25)
23fn print(d: &Document, i: IDX) {
24    print!("<{}>", d[i].name);
25    let s = d.child(i);
26    let mut c = s;
27    while c != 0 {
28        print(d, c);
29        c = d.next(c);
30        if c == s {
31            break;
32        }
33    }
34    print!("{}", d[i].text);
35    print!("</{}>", d[i].name);
36}
Source

pub fn childc(&self, i: IDX, c: CATS) -> IDX

Gets the index of the first child, or 0 if no childs are present. This retrieves the children for the category c if categories C>1, otherwise use the basic variant Packo::child. Failure returns idx 0.

Source

pub fn insert(&mut self, t: T) -> IDX

Adds the T as a standalone node in the items. This element will only be found when using the basic iterator. It has no childs and no parent. Failure returns idx 0.

Examples found in repository?
examples/html.rs (line 14)
11fn main() {
12    let mut doc = Document::default();
13
14    let html = doc.insert(Tag { name: "html", text: "" });
15    let body = doc.nest(html, Tag{ name: "body", text: "" });
16    let p1 = doc.nest(body, Tag{ name: "p", text: "first line" }); 
17    let _p2 = doc.append(p1, Tag{ name: "p", text: "second line" }); 
18
19    print(&doc, html);
20    println!();
21}
More examples
Hide additional examples
examples/entities.rs (lines 147-151)
144fn game_reset(e: &mut Entities) -> IDX {
145    e.reset();
146
147    let p = e.insert(Entity {
148        components: Component::Player + Component::Spatial + Component::Life.into(),
149        life: 100,
150        ..Default::default()
151    });
152    e[p].id = p;
153
154    let potion = e.nestc(
155        p,
156        CAT_INVENTORY,
157        Entity {
158            components: Component::Life + Component::Consume,
159            life: 50,
160            ..Default::default()
161        },
162    );
163    e[potion].id = potion;
164
165    let firebolt = e.nestc(
166        p,
167        CAT_SPELLS,
168        Entity {
169            components: Component::Damage + Component::Cost,
170            damage: 10,
171            cost: 5,
172            ..Default::default()
173        },
174    );
175    e[firebolt].id = firebolt;
176
177    let heal = e.nestc(
178        p,
179        CAT_SPELLS,
180        Entity {
181            components: Component::Life + Component::Cost,
182            life: 10,
183            cost: 5,
184            ..Default::default()
185        },
186    );
187    e[heal].id = heal;
188
189    p
190}
Source

pub fn nest(&mut self, i: IDX, t: T) -> IDX

Appends this item as a child of the given i. If there were already children, append to the last of them, otherwise this new element will be the only children. Failure returns idx 0.

Examples found in repository?
examples/html.rs (line 15)
11fn main() {
12    let mut doc = Document::default();
13
14    let html = doc.insert(Tag { name: "html", text: "" });
15    let body = doc.nest(html, Tag{ name: "body", text: "" });
16    let p1 = doc.nest(body, Tag{ name: "p", text: "first line" }); 
17    let _p2 = doc.append(p1, Tag{ name: "p", text: "second line" }); 
18
19    print(&doc, html);
20    println!();
21}
Source

pub fn nestc(&mut self, i: IDX, c: CATS, t: T) -> IDX

Nests based on category index. If default C=1 of category sizes, then use Packo::nest. Failure returns idx 0.

Examples found in repository?
examples/entities.rs (lines 154-162)
144fn game_reset(e: &mut Entities) -> IDX {
145    e.reset();
146
147    let p = e.insert(Entity {
148        components: Component::Player + Component::Spatial + Component::Life.into(),
149        life: 100,
150        ..Default::default()
151    });
152    e[p].id = p;
153
154    let potion = e.nestc(
155        p,
156        CAT_INVENTORY,
157        Entity {
158            components: Component::Life + Component::Consume,
159            life: 50,
160            ..Default::default()
161        },
162    );
163    e[potion].id = potion;
164
165    let firebolt = e.nestc(
166        p,
167        CAT_SPELLS,
168        Entity {
169            components: Component::Damage + Component::Cost,
170            damage: 10,
171            cost: 5,
172            ..Default::default()
173        },
174    );
175    e[firebolt].id = firebolt;
176
177    let heal = e.nestc(
178        p,
179        CAT_SPELLS,
180        Entity {
181            components: Component::Life + Component::Cost,
182            life: 10,
183            cost: 5,
184            ..Default::default()
185        },
186    );
187    e[heal].id = heal;
188
189    p
190}
Source

pub fn append(&mut self, i: IDX, t: T) -> IDX

Appends this element as a sibling after the given i. i can be at any hierarchy but must be a valid used node. Failure returns idx 0.

Examples found in repository?
examples/html.rs (line 17)
11fn main() {
12    let mut doc = Document::default();
13
14    let html = doc.insert(Tag { name: "html", text: "" });
15    let body = doc.nest(html, Tag{ name: "body", text: "" });
16    let p1 = doc.nest(body, Tag{ name: "p", text: "first line" }); 
17    let _p2 = doc.append(p1, Tag{ name: "p", text: "second line" }); 
18
19    print(&doc, html);
20    println!();
21}
Source

pub fn remove(&mut self, i: IDX) -> T

Remove this item from the list (sets it to unused). If an item has this one as a parent, its parent becomes 0/nil. If this item was part of a list, cut out its circular refs.

Source

pub fn all(&self) -> impl Iterator<Item = &T>

Iterates on all the elements regardless of their relation or hierarchy. Only used elements are returned.

Source

pub fn roots(&self) -> impl Iterator<Item = &T>

Iterates only on the root nodes, nodes that have no parent.

Source

pub fn siblings(&self, i: IDX) -> impl Iterator<Item = &T>

Iterates on the linked list which this item is the first element of

Source

pub fn childs(&self, i: IDX) -> impl Iterator<Item = &T>

Iterates on the siblings childs of this node. Basically only one level of the tree given i being the parent idx.

Source

pub fn childsc(&self, i: IDX, c: CATS) -> impl Iterator<Item = &T>

Iterates on the siblings childs of this node. Basically only one level of the tree given i being the parent idx. This variant uses the category selector. For a simple version of C=1 use Packo::childs.

Examples found in repository?
examples/entities.rs (line 197)
192fn main() {
193    let mut e = Entities::default();
194    let p = game_reset(&mut e);
195
196    println!("Player: {}", e[p]);
197    for e in e.childsc(p, CAT_INVENTORY) {
198        println!("Item: {e}");
199    }
200    for e in e.childsc(p, CAT_SPELLS) {
201        println!("Spell: {e}");
202    }
203}

Trait Implementations§

Source§

impl<const S: IDX, T, const C: CATS> Default for Packo<S, T, C>

Source§

fn default() -> Self

Returns the “default value” for a type. Read more
Source§

impl<const S: IDX, T, const C: CATS> Index<usize> for Packo<S, T, C>

Source§

type Output = T

The returned type after indexing.
Source§

fn index(&self, i: IDX) -> &Self::Output

Performs the indexing (container[index]) operation. Read more
Source§

impl<const S: IDX, T, const C: CATS> IndexMut<usize> for Packo<S, T, C>

Source§

fn index_mut(&mut self, i: IDX) -> &mut Self::Output

Performs the mutable indexing (container[index]) operation. Read more
Source§

impl<'a, const S: IDX, T, const C: CATS> IntoIterator for &'a Packo<S, T, C>

Source§

type Item = &'a T

The type of the elements being iterated over.
Source§

type IntoIter = PackoIterator<'a, S, T, C>

Which kind of iterator are we turning this into?
Source§

fn into_iter(self) -> Self::IntoIter

Creates an iterator from a value. Read more

Auto Trait Implementations§

§

impl<const S: usize, T, const C: usize> Freeze for Packo<S, T, C>
where T: Freeze,

§

impl<const S: usize, T, const C: usize> RefUnwindSafe for Packo<S, T, C>
where T: RefUnwindSafe,

§

impl<const S: usize, T, const C: usize> Send for Packo<S, T, C>
where T: Send,

§

impl<const S: usize, T, const C: usize> Sync for Packo<S, T, C>
where T: Sync,

§

impl<const S: usize, T, const C: usize> Unpin for Packo<S, T, C>
where T: Unpin,

§

impl<const S: usize, T, const C: usize> UnsafeUnpin for Packo<S, T, C>
where T: UnsafeUnpin,

§

impl<const S: usize, T, const C: usize> UnwindSafe for Packo<S, T, C>
where T: UnwindSafe,

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.