Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Optimize moveos_stdlib::table #908

Merged
merged 5 commits into from
Oct 4, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 11 additions & 5 deletions crates/rooch-framework-tests/tests/cases/table/test_destroy.exp
Original file line number Diff line number Diff line change
@@ -1,13 +1,19 @@
processed 5 tasks
processed 7 tasks

task 1 'publish'. lines 3-73:
task 1 'publish'. lines 3-85:
status EXECUTED

task 2 'run'. lines 75-88:
task 2 'run'. lines 87-101:
status EXECUTED

task 3 'run'. lines 89-104:
task 3 'run'. lines 102-117:
status EXECUTED

task 4 'run'. lines 105-115:
task 4 'run'. lines 118-133:
status EXECUTED

task 5 'run'. lines 134-146:
status ABORTED with code 3 in 0000000000000000000000000000000000000000000000000000000000000002::raw_table

task 6 'run'. lines 147-160:
status EXECUTED
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ module test::m {
table::add(&mut store.table, key, value);
}

public fun remove(store: &mut KVStore, key: String): vector<u8> {
table::remove(&mut store.table, key)
}

public fun contains(store: &KVStore, key: String): bool {
table::contains(&store.table, key)
}
Expand Down Expand Up @@ -66,6 +70,14 @@ module test::m {
account_storage::global_move_from(ctx, account)
}

public fun length(kv: &KVStore): u64 {
table::length(&kv.table)
}

public fun is_empty(kv: &KVStore): bool {
table::length(&kv.table) == 0
}

public fun destroy(kv: KVStore){
let KVStore{table} = kv;
table::destroy_empty(table);
Expand All @@ -81,6 +93,7 @@ script {
fun main(ctx: &mut StorageContext, sender: signer) {
let kv = m::make_kv_store(ctx);
m::add(&mut kv, string::utf8(b"test"), b"value");
assert!(m::length(&kv) == 1, 1000); // check length is correct when data in table cache
m::save_to_account_storage(ctx, &sender, kv);
}
}
Expand All @@ -95,21 +108,53 @@ script {
fun main(ctx: &mut StorageContext) {
let sender = storage_context::sender(ctx);
let kv = m::borrow_from_account_storage(ctx, sender);
assert!(m::contains(kv, string::utf8(b"test")), 1000);
assert!(m::contains(kv, string::utf8(b"test")), 1001);
let v = m::borrow(kv, string::utf8(b"test"));
assert!(v == &b"value", 1001);
assert!(v == &b"value", 1002);
}
}

//FIXME destroy no empty table, should failed.
// check length when data is in both remote storage and cache
//# run --signers test
script {
use std::string;
use moveos_std::storage_context::{Self, StorageContext};
use test::m;

fun main(ctx: &mut StorageContext) {
let sender = storage_context::sender(ctx);
let kv = m::borrow_mut_from_account_storage(ctx, sender);
m::add(kv, string::utf8(b"test1"), b"value1");
assert!(m::length(kv) == 2, 1003);
let _value = m::remove(kv, string::utf8(b"test1"));
}
}

// destroy none empty table, should failed.
//# run --signers test
script {
use moveos_std::storage_context::{Self, StorageContext};
use test::m;

fun main(ctx: &mut StorageContext) {
let sender = storage_context::sender(ctx);
let kv = m::move_from_account_storage(ctx, sender);
m::destroy(kv);
}
}

// destroy empty table, should success.
//# run --signers test
script {
use std::string;
use moveos_std::storage_context::{Self, StorageContext};
use test::m;

fun main(ctx: &mut StorageContext) {
let sender = storage_context::sender(ctx);
let kv = m::move_from_account_storage(ctx, sender);
let v = m::remove(&mut kv, string::utf8(b"test"));
assert!(v == b"value", 1004);
m::destroy(kv);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,6 @@ crate::natives::gas_parameter::native::define_gas_parameters_for_natives!(GasPar
[.contains_box.per_byte_serialized, "contains_box.per_byte_serialized", (5 + 1) * MUL],
[.remove_box.base, "remove_box.base", (5 + 1) * MUL],
[.remove_box.per_byte_serialized, "remove_box.per_byte_serialized", (5 + 1) * MUL],
[.destroy_empty_box.base, "destroy_empty_box.base", (5 + 1) * MUL],
[.drop_unchecked_box.base, "drop_unchecked_box.base", (5 + 1) * MUL],
[.box_length.base, "box_length.base", (5 + 1) * MUL],
]);
89 changes: 87 additions & 2 deletions moveos/moveos-stdlib/moveos-stdlib/doc/raw_table.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ This type table if for design internal global storage, so all functions are frie
- [Function `upsert`](#0x2_raw_table_upsert)
- [Function `remove`](#0x2_raw_table_remove)
- [Function `contains`](#0x2_raw_table_contains)
- [Function `length`](#0x2_raw_table_length)
- [Function `is_empty`](#0x2_raw_table_is_empty)
- [Function `drop_unchecked`](#0x2_raw_table_drop_unchecked)
- [Function `destroy_empty`](#0x2_raw_table_destroy_empty)
- [Function `new_table_handle`](#0x2_raw_table_new_table_handle)

Expand Down Expand Up @@ -50,6 +53,12 @@ This type table if for design internal global storage, so all functions are frie
</dt>
<dd>

</dd>
<dt>
<code>size: u64</code>
</dt>
<dd>

</dd>
</dl>

Expand Down Expand Up @@ -339,13 +348,88 @@ Returns true if <code><a href="table.md#0x2_table">table</a></code> contains an



</details>

<a name="0x2_raw_table_length"></a>

## Function `length`

Returns the size of the table, the number of key-value pairs


<pre><code><b>public</b>(<b>friend</b>) <b>fun</b> <a href="raw_table.md#0x2_raw_table_length">length</a>(table_handle: &<a href="object_id.md#0x2_object_id_ObjectID">object_id::ObjectID</a>): u64
</code></pre>



<details>
<summary>Implementation</summary>


<pre><code><b>public</b>(<b>friend</b>) <b>fun</b> <a href="raw_table.md#0x2_raw_table_length">length</a>(table_handle: &ObjectID): u64 {
<a href="raw_table.md#0x2_raw_table_box_length">box_length</a>(table_handle)
}
</code></pre>



</details>

<a name="0x2_raw_table_is_empty"></a>

## Function `is_empty`

Returns true iff the table is empty (if <code>length</code> returns <code>0</code>)


<pre><code><b>public</b>(<b>friend</b>) <b>fun</b> <a href="raw_table.md#0x2_raw_table_is_empty">is_empty</a>(table_handle: &<a href="object_id.md#0x2_object_id_ObjectID">object_id::ObjectID</a>): bool
</code></pre>



<details>
<summary>Implementation</summary>


<pre><code><b>public</b>(<b>friend</b>) <b>fun</b> <a href="raw_table.md#0x2_raw_table_is_empty">is_empty</a>(table_handle: &ObjectID): bool {
<a href="raw_table.md#0x2_raw_table_length">length</a>(table_handle) == 0
}
</code></pre>



</details>

<a name="0x2_raw_table_drop_unchecked"></a>

## Function `drop_unchecked`

Testing only: allows to drop a table even if it is not empty.


<pre><code><b>public</b>(<b>friend</b>) <b>fun</b> <a href="raw_table.md#0x2_raw_table_drop_unchecked">drop_unchecked</a>(table_handle: &<a href="object_id.md#0x2_object_id_ObjectID">object_id::ObjectID</a>)
</code></pre>



<details>
<summary>Implementation</summary>


<pre><code><b>public</b>(<b>friend</b>) <b>fun</b> <a href="raw_table.md#0x2_raw_table_drop_unchecked">drop_unchecked</a>(table_handle: &ObjectID) {
<a href="raw_table.md#0x2_raw_table_drop_unchecked_box">drop_unchecked_box</a>(table_handle)
}
</code></pre>



</details>

<a name="0x2_raw_table_destroy_empty"></a>

## Function `destroy_empty`

Destroy a table. The table must be empty to succeed.
Destroy a table. Aborts if the table is not empty


<pre><code><b>public</b>(<b>friend</b>) <b>fun</b> <a href="raw_table.md#0x2_raw_table_destroy_empty">destroy_empty</a>(table_handle: &<a href="object_id.md#0x2_object_id_ObjectID">object_id::ObjectID</a>)
Expand All @@ -358,7 +442,8 @@ Destroy a table. The table must be empty to succeed.


<pre><code><b>public</b>(<b>friend</b>) <b>fun</b> <a href="raw_table.md#0x2_raw_table_destroy_empty">destroy_empty</a>(table_handle: &ObjectID) {
<a href="raw_table.md#0x2_raw_table_destroy_empty_box">destroy_empty_box</a>(table_handle)
<b>assert</b>!(<a href="raw_table.md#0x2_raw_table_is_empty">is_empty</a>(table_handle), <a href="raw_table.md#0x2_raw_table_ErrorNotEmpty">ErrorNotEmpty</a>);
<a href="raw_table.md#0x2_raw_table_drop_unchecked_box">drop_unchecked_box</a>(table_handle)
}
</code></pre>

Expand Down
80 changes: 80 additions & 0 deletions moveos/moveos-stdlib/moveos-stdlib/doc/table.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ struct itself, while the operations are implemented as native functions. No trav
- [Function `remove`](#0x2_table_remove)
- [Function `contains`](#0x2_table_contains)
- [Function `destroy_empty`](#0x2_table_destroy_empty)
- [Function `length`](#0x2_table_length)
- [Function `is_empty`](#0x2_table_is_empty)
- [Function `drop`](#0x2_table_drop)


<pre><code><b>use</b> <a href="object_id.md#0x2_object_id">0x2::object_id</a>;
Expand Down Expand Up @@ -346,4 +349,81 @@ Destroy a table. The table must be empty to succeed.



</details>

<a name="0x2_table_length"></a>

## Function `length`

Returns the size of the table, the number of key-value pairs


<pre><code><b>public</b> <b>fun</b> <a href="table.md#0x2_table_length">length</a>&lt;K: <b>copy</b>, drop, V&gt;(<a href="table.md#0x2_table">table</a>: &<a href="table.md#0x2_table_Table">table::Table</a>&lt;K, V&gt;): u64
</code></pre>



<details>
<summary>Implementation</summary>


<pre><code><b>public</b> <b>fun</b> <a href="table.md#0x2_table_length">length</a>&lt;K: <b>copy</b> + drop, V&gt;(<a href="table.md#0x2_table">table</a>: &<a href="table.md#0x2_table_Table">Table</a>&lt;K, V&gt;): u64 {
<a href="raw_table.md#0x2_raw_table_length">raw_table::length</a>(&<a href="table.md#0x2_table">table</a>.handle)
}
</code></pre>



</details>

<a name="0x2_table_is_empty"></a>

## Function `is_empty`

Returns true iff the table is empty (if <code>length</code> returns <code>0</code>)


<pre><code><b>public</b> <b>fun</b> <a href="table.md#0x2_table_is_empty">is_empty</a>&lt;K: <b>copy</b>, drop, V&gt;(<a href="table.md#0x2_table">table</a>: &<a href="table.md#0x2_table_Table">table::Table</a>&lt;K, V&gt;): bool
</code></pre>



<details>
<summary>Implementation</summary>


<pre><code><b>public</b> <b>fun</b> <a href="table.md#0x2_table_is_empty">is_empty</a>&lt;K: <b>copy</b> + drop, V&gt;(<a href="table.md#0x2_table">table</a>: &<a href="table.md#0x2_table_Table">Table</a>&lt;K, V&gt;): bool {
<a href="raw_table.md#0x2_raw_table_length">raw_table::length</a>(&<a href="table.md#0x2_table">table</a>.handle) == 0
}
</code></pre>



</details>

<a name="0x2_table_drop"></a>

## Function `drop`

Drop a possibly non-empty table.
Usable only if the value type <code>V</code> has the <code>drop</code> ability


<pre><code><b>public</b> <b>fun</b> <a href="table.md#0x2_table_drop">drop</a>&lt;K: <b>copy</b>, drop, V: drop&gt;(<a href="table.md#0x2_table">table</a>: <a href="table.md#0x2_table_Table">table::Table</a>&lt;K, V&gt;)
</code></pre>



<details>
<summary>Implementation</summary>


<pre><code><b>public</b> <b>fun</b> <a href="table.md#0x2_table_drop">drop</a>&lt;K: <b>copy</b> + drop , V: drop&gt;(<a href="table.md#0x2_table">table</a>: <a href="table.md#0x2_table_Table">Table</a>&lt;K, V&gt;) {
<b>let</b> <a href="table.md#0x2_table_Table">Table</a> { handle } = <a href="table.md#0x2_table">table</a>;
<a href="raw_table.md#0x2_raw_table_drop_unchecked">raw_table::drop_unchecked</a>(&handle)
}
</code></pre>



</details>
22 changes: 17 additions & 5 deletions moveos/moveos-stdlib/moveos-stdlib/sources/raw_table.move
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ module moveos_std::raw_table {
struct TableInfo has key {
// Table SMT root
state_root: address,
// Table size, number of items
size: u64,
}

/// Add a new entry to the table. Aborts if an entry for this
Expand Down Expand Up @@ -84,15 +86,25 @@ module moveos_std::raw_table {
contains_box<K>(table_handle, key)
}

#[test_only]
/// Returns the size of the table, the number of key-value pairs
public(friend) fun length(table_handle: &ObjectID): u64 {
box_length(table_handle)
}

/// Returns true iff the table is empty (if `length` returns `0`)
public(friend) fun is_empty(table_handle: &ObjectID): bool {
length(table_handle) == 0
}

/// Testing only: allows to drop a table even if it is not empty.
public(friend) fun drop_unchecked(table_handle: &ObjectID) {
drop_unchecked_box(table_handle)
}

/// Destroy a table. The table must be empty to succeed.
/// Destroy a table. Aborts if the table is not empty
public(friend) fun destroy_empty(table_handle: &ObjectID) {
destroy_empty_box(table_handle)
assert!(is_empty(table_handle), ErrorNotEmpty);
drop_unchecked_box(table_handle)
}

// ======================================================================================================
Expand All @@ -118,7 +130,7 @@ module moveos_std::raw_table {

native fun remove_box<K: copy + drop, V, B>(table_handle: &ObjectID, key: K): Box<V>;

native fun destroy_empty_box(table_handle: &ObjectID);

native fun drop_unchecked_box(table_handle: &ObjectID);

native fun box_length(table_handle: &ObjectID): u64;
}
Loading