module aptos_std::iterable_table { use std::option::{Self, Option}; use aptos_std::table_with_length::{Self, TableWithLength}; /// The iterable wrapper around value, points to previous and next key if any. struct IterableValue has store { val: V, prev: Option, next: Option, } /// An iterable table implementation based on double linked list. struct IterableTable has store { inner: TableWithLength>, head: Option, tail: Option, } /// Regular table API. /// Create an empty table. public fun new(): IterableTable { IterableTable { inner: table_with_length::new(), head: option::none(), tail: option::none(), } } /// Destroy a table. The table must be empty to succeed. public fun destroy_empty(table: IterableTable) { assert!(empty(&table), 0); assert!(option::is_none(&table.head), 0); assert!(option::is_none(&table.tail), 0); let IterableTable {inner, head: _, tail: _} = table; table_with_length::destroy_empty(inner); } /// Add a new entry to the table. Aborts if an entry for this /// key already exists. public fun add(table: &mut IterableTable, key: K, val: V) { let wrapped_value = IterableValue { val, prev: table.tail, next: option::none(), }; table_with_length::add(&mut table.inner, key, wrapped_value); if (option::is_some(&table.tail)) { let k = option::borrow(&table.tail); table_with_length::borrow_mut(&mut table.inner, *k).next = option::some(key); } else { table.head = option::some(key); }; table.tail = option::some(key); } /// Remove from `table` and return the value which `key` maps to. /// Aborts if there is no entry for `key`. public fun remove(table: &mut IterableTable, key: K): V { let (val, _, _) = remove_iter(table, key); val } /// Acquire an immutable reference to the value which `key` maps to. /// Aborts if there is no entry for `key`. public fun borrow(table: &IterableTable, key: K): &V { &table_with_length::borrow(&table.inner, key).val } /// Acquire a mutable reference to the value which `key` maps to. /// Aborts if there is no entry for `key`. public fun borrow_mut(table: &mut IterableTable, key: K): &mut V { &mut table_with_length::borrow_mut(&mut table.inner, key).val } /// Acquire a mutable reference to the value which `key` maps to. /// Insert the pair (`key`, `default`) first if there is no entry for `key`. public fun borrow_mut_with_default(table: &mut IterableTable, key: K, default: V): &mut V { if (!contains(table, key)) { add(table, key, default) }; borrow_mut(table, key) } /// Returns the length of the table, i.e. the number of entries. public fun length(table: &IterableTable): u64 { table_with_length::length(&table.inner) } /// Returns true if this table is empty. public fun empty(table: &IterableTable): bool { table_with_length::empty(&table.inner) } /// Returns true iff `table` contains an entry for `key`. public fun contains(table: &IterableTable, key: K): bool { table_with_length::contains(&table.inner, key) } /// Iterable API. /// Returns the key of the head for iteration. public fun head_key(table: &IterableTable): Option { table.head } /// Returns the key of the tail for iteration. public fun tail_key(table: &IterableTable): Option { table.tail } /// Acquire an immutable reference to the IterableValue which `key` maps to. /// Aborts if there is no entry for `key`. public fun borrow_iter(table: &IterableTable, key: K): (&V, Option, Option) { let v = table_with_length::borrow(&table.inner, key); (&v.val, v.prev, v.next) } /// Acquire a mutable reference to the value and previous/next key which `key` maps to. /// Aborts if there is no entry for `key`. public fun borrow_iter_mut(table: &mut IterableTable, key: K): (&mut V, Option, Option) { let v = table_with_length::borrow_mut(&mut table.inner, key); (&mut v.val, v.prev, v.next) } /// Remove from `table` and return the value and previous/next key which `key` maps to. /// Aborts if there is no entry for `key`. public fun remove_iter(table: &mut IterableTable, key: K): (V, Option, Option) { let val = table_with_length::remove(&mut table.inner, copy key); if (option::contains(&table.tail, &key)) { table.tail = val.prev; }; if (option::contains(&table.head, &key)) { table.head = val.next; }; if (option::is_some(&val.prev)) { let key = option::borrow(&val.prev); table_with_length::borrow_mut(&mut table.inner, *key).next = val.next; }; if (option::is_some(&val.next)) { let key = option::borrow(&val.next); table_with_length::borrow_mut(&mut table.inner, *key).prev = val.prev; }; let IterableValue {val, prev, next} = val; (val, prev, next) } /// Remove all items from v2 and append to v1. public fun append(v1: &mut IterableTable, v2: &mut IterableTable) { let key = head_key(v2); while (option::is_some(&key)) { let (val, _, next) = remove_iter(v2, *option::borrow(&key)); add(v1, *option::borrow(&key), val); key = next; }; } #[test] fun iterable_table_test() { let table = new(); let i = 0; while (i < 100) { add(&mut table, i, i); i = i + 1; }; assert!(length(&table) == 100, 0); i = 0; while (i < 100) { assert!(remove(&mut table, i) == i, 0); i = i + 2; }; assert!(!empty(&table), 0); let key = head_key(&table); i = 1; while (option::is_some(&key)) { let (val, _, next) = borrow_iter(&table, *option::borrow(&key)); assert!(*val == i, 0); key = next; i = i + 2; }; assert!(i == 101, 0); let table2 = new(); append(&mut table2, &mut table); destroy_empty(table); let key = tail_key(&table2); while (option::is_some(&key)) { let (val, prev, _) = remove_iter(&mut table2, *option::borrow(&key)); assert!(val == *option::borrow(&key), 0); key = prev; }; destroy_empty(table2); } }