/// Type of large-scale storage tables. module extensions::table { use std::errors; // TODO: native code should not use reasons to signal logical type of error. Instead, // use Errors::ALREADY_PUBLISHED and Errors::NOT_PUBLISHED. const EALREADY_EXISTS: u64 = 100; // native code raises this with Errors::invalid_arguments() const ENOT_FOUND: u64 = 101; const ENOT_EMPTY: u64 = 102; /// Type of tables struct Table has store { handle: u128, length: u64, } /// Create a new Table. public fun new(): Table { Table{ handle: new_table_handle(), length: 0, } } /// Destroy a table. The table must be empty to succeed. public fun destroy_empty(table: Table) { assert!(table.length == 0, errors::invalid_state(ENOT_EMPTY)); destroy_empty_box>(&table); drop_unchecked_box>(table) } /// Add a new entry to the table. Aborts if an entry for this /// key already exists. The entry itself is not stored in the /// table, and cannot be discovered from it. public fun add(table: &mut Table, key: K, val: V) { add_box>(table, key, Box{val}); table.length = table.length + 1 } /// Acquire an immutable reference to the value which `key` maps to. /// Aborts if there is no entry for `key`. public fun borrow(table: &Table, key: K): &V { &borrow_box>(table, 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 Table, key: K): &mut V { &mut borrow_box_mut>(table, key).val } /// Returns the length of the table, i.e. the number of entries. public fun length(table: &Table): u64 { table.length } /// Returns true if this table is empty. public fun empty(table: &Table): bool { table.length == 0 } /// 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 Table, key: K, default: V): &mut V { if (!contains(table, copy key)) { add(table, copy key, default) }; borrow_mut(table, 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 Table, key: K): V { let Box{val} = remove_box>(table, key); table.length = table.length - 1; val } /// Returns true iff `table` contains an entry for `key`. public fun contains(table: &Table, key: K): bool { contains_box>(table, key) } #[test_only] /// Testing only: allows to drop a table even if it is not empty. public fun drop_unchecked(table: Table) { drop_unchecked_box>(table) } // ====================================================================================================== // Internal API /// Wrapper for values. Required for making values appear as resources in the implementation. struct Box has key, drop, store { val: V } // Primitives which take as an additional type parameter `Box`, so the implementation // can use this to determine serialization layout. native fun new_table_handle(): u128; native fun add_box(table: &mut Table, key: K, val: Box); native fun borrow_box(table: &Table, key: K): &Box; native fun borrow_box_mut(table: &mut Table, key: K): &mut Box; native fun contains_box(table: &Table, key: K): bool; native fun remove_box(table: &mut Table, key: K): Box; native fun destroy_empty_box(table: &Table); native fun drop_unchecked_box(table: Table); }