diff --git a/aptos-move/aptos-gas-schedule/src/gas_schedule/move_stdlib.rs b/aptos-move/aptos-gas-schedule/src/gas_schedule/move_stdlib.rs index ef2b19452885ec..486869ffafbf9a 100644 --- a/aptos-move/aptos-gas-schedule/src/gas_schedule/move_stdlib.rs +++ b/aptos-move/aptos-gas-schedule/src/gas_schedule/move_stdlib.rs @@ -7,7 +7,7 @@ use crate::{ gas_feature_versions::{RELEASE_V1_18, RELEASE_V1_22}, gas_schedule::NativeGasParameters, }; -use aptos_gas_algebra::{InternalGas, InternalGasPerArg, InternalGasPerByte}; +use aptos_gas_algebra::{InternalGas, InternalGasPerAbstractValueUnit, InternalGasPerArg, InternalGasPerByte}; crate::gas_schedule::macros::define_gas_parameters!( MoveStdlibGasParameters, diff --git a/aptos-move/framework/aptos-stdlib/doc/big_ordered_map.md b/aptos-move/framework/aptos-stdlib/doc/big_ordered_map.md index 9b5e23b32965b1..0ddc969eda9405 100644 --- a/aptos-move/framework/aptos-stdlib/doc/big_ordered_map.md +++ b/aptos-move/framework/aptos-stdlib/doc/big_ordered_map.md @@ -3,11 +3,22 @@ # Module `0x1::big_ordered_map` -Type of large-scale search trees. +This module provides an implementation for an big ordered map. +Big means that it is stored across multiple resources, and doesn't have an +upper limit on number of elements it can contain. -It internally uses BTree to organize the search tree data structure for keys. Comparing with -other common search trees like AVL or Red-black tree, a BTree node has more children, and packs -more metadata into one node, which is more disk friendly (and gas friendly). +Keys point to values, and each key in the map must be unique. + +Currently, one implementation is provided - BPlusTreeMap, backed by a B+Tree, +with each node being a separate resource, internally containing OrderedMap. + +BPlusTreeMap is chosen since the biggest (performance and gast) +costs are reading resources, and it: +* reduces number of resource accesses +* reduces number of rebalancing operations, and makes each rebalancing +operation touch only few resources +* it allows for parallelism for keys that are not close to each other, +once it contains enough keys - [Struct `Node`](#0x1_big_ordered_map_Node) @@ -20,21 +31,20 @@ more metadata into one node, which is more disk friendly (and gas friendly). - [Function `destroy_empty`](#0x1_big_ordered_map_destroy_empty) - [Function `add`](#0x1_big_ordered_map_add) - [Function `upsert`](#0x1_big_ordered_map_upsert) -- [Function `add_or_upsert_impl`](#0x1_big_ordered_map_add_or_upsert_impl) - [Function `remove`](#0x1_big_ordered_map_remove) -- [Function `is_begin_iter`](#0x1_big_ordered_map_is_begin_iter) -- [Function `is_end_iter`](#0x1_big_ordered_map_is_end_iter) -- [Function `iter_get_key`](#0x1_big_ordered_map_iter_get_key) - [Function `lower_bound`](#0x1_big_ordered_map_lower_bound) - [Function `find`](#0x1_big_ordered_map_find) - [Function `contains`](#0x1_big_ordered_map_contains) - [Function `borrow`](#0x1_big_ordered_map_borrow) -- [Function `borrow_mut`](#0x1_big_ordered_map_borrow_mut) - [Function `new_begin_iter`](#0x1_big_ordered_map_new_begin_iter) - [Function `new_end_iter`](#0x1_big_ordered_map_new_end_iter) -- [Function `next_iter`](#0x1_big_ordered_map_next_iter) -- [Function `prev_iter`](#0x1_big_ordered_map_prev_iter) -- [Function `init_max_degrees`](#0x1_big_ordered_map_init_max_degrees) +- [Function `iter_is_begin`](#0x1_big_ordered_map_iter_is_begin) +- [Function `iter_is_end`](#0x1_big_ordered_map_iter_is_end) +- [Function `iter_get_key`](#0x1_big_ordered_map_iter_get_key) +- [Function `iter_next`](#0x1_big_ordered_map_iter_next) +- [Function `iter_prev`](#0x1_big_ordered_map_iter_prev) +- [Function `add_or_upsert_impl`](#0x1_big_ordered_map_add_or_upsert_impl) +- [Function `validate_size_and_init_max_degrees`](#0x1_big_ordered_map_validate_size_and_init_max_degrees) - [Function `destroy_inner_child`](#0x1_big_ordered_map_destroy_inner_child) - [Function `destroy_empty_node`](#0x1_big_ordered_map_destroy_empty_node) - [Function `new_node`](#0x1_big_ordered_map_new_node) @@ -54,6 +64,7 @@ more metadata into one node, which is more disk friendly (and gas friendly).
use 0x1::bcs;
use 0x1::cmp;
+use 0x1::error;
use 0x1::math64;
use 0x1::option;
use 0x1::ordered_map;
@@ -68,6 +79,11 @@ more metadata into one node, which is more disk friendly (and gas friendly).
A node of the BigOrderedMap.
+Inner node will have all children be Child::Inner, pointing to the child nodes.
+Leaf node will have all children be Child::Leaf.
+Basically - Leaf node is a single-resource OrderedMap, containing as much keys as can fit.
+So Leaf node contains multiple values, not just one.
+
struct Node<K: store, V: store> has store
@@ -329,11 +345,31 @@ The BigOrderedMap data structure.
+
+
+
+
+const EITER_OUT_OF_BOUNDS: u64 = 3;
+
+
+
+
+Map key already exists
-const EKEY_ALREADY_EXISTS: u64 = 4;
+const EKEY_ALREADY_EXISTS: u64 = 1;
+
+
+
+
+
+
+Map key is not found
+
+
+const EKEY_NOT_FOUND: u64 = 2;
@@ -365,38 +401,38 @@ The BigOrderedMap data structure.
-
+
-const E_INTERNAL: u64 = 0;
+const EARGUMENT_BYTES_TOO_LARGE: u64 = 6;
-
+
-const E_INVALID_PARAMETER: u64 = 3;
+const EINTERNAL_INVARIANT_BROKEN: u64 = 7;
-
+
-const E_TREE_NOT_EMPTY: u64 = 1;
+const EINVALID_CONFIG_PARAMETER: u64 = 4;
-
+
-const E_TREE_TOO_BIG: u64 = 2;
+const EMAP_NOT_EMPTY: u64 = 5;
@@ -410,6 +446,15 @@ The BigOrderedMap data structure.
+
+
+
+
+const MAX_NODE_BYTES: u64 = 204800;
+
+
+
+
## Function `new`
@@ -453,12 +498,12 @@ If 0 is passed, then it is dynamically computed based on size of first key and v
public fun new_with_config<K: store, V: store>(inner_max_degree: u16, leaf_max_degree: u16, reuse_slots: bool, num_to_preallocate: u64): BigOrderedMap<K, V> {
- assert!(inner_max_degree == 0 || inner_max_degree >= DEFAULT_INNER_MIN_DEGREE, E_INVALID_PARAMETER);
- assert!(leaf_max_degree == 0 || leaf_max_degree >= DEFAULT_LEAF_MIN_DEGREE, E_INVALID_PARAMETER);
+ assert!(inner_max_degree == 0 || inner_max_degree >= DEFAULT_INNER_MIN_DEGREE, error::invalid_argument(EINVALID_CONFIG_PARAMETER));
+ assert!(leaf_max_degree == 0 || leaf_max_degree >= DEFAULT_LEAF_MIN_DEGREE, error::invalid_argument(EINVALID_CONFIG_PARAMETER));
let nodes = if (reuse_slots) {
storage_slots_allocator::new_reuse_storage_slots(num_to_preallocate)
} else {
- assert!(num_to_preallocate == 0, E_INVALID_PARAMETER);
+ assert!(num_to_preallocate == 0, error::invalid_argument(EINVALID_CONFIG_PARAMETER));
storage_slots_allocator::new_storage_slots()
};
let root_index = nodes.add(new_node(/*is_leaf=*/true, /*parent=*/NULL_INDEX));
@@ -481,7 +526,7 @@ If 0 is passed, then it is dynamically computed based on size of first key and v
## Function `destroy_empty`
-Destroys the tree if it's empty, otherwise aborts.
+Destroys the map if it's empty, otherwise aborts.
public fun destroy_empty<K: store, V: store>(self: big_ordered_map::BigOrderedMap<K, V>)
@@ -495,7 +540,6 @@ Destroys the tree if it's empty, otherwise aborts.
public fun destroy_empty<K: store, V: store>(self: BigOrderedMap<K, V>) {
let BigOrderedMap::BPlusTreeMap { nodes, root_index, min_leaf_index: _, max_leaf_index: _, inner_max_degree: _, leaf_max_degree: _ } = self;
- // aptos_std::debug::print(&nodes);
nodes.remove(root_index).destroy_empty_node();
nodes.destroy();
}
@@ -510,7 +554,7 @@ Destroys the tree if it's empty, otherwise aborts.
## Function `add`
Inserts the key/value into the BigOrderedMap.
-Aborts if the key is already in the tree.
+Aborts if the key is already in the map.
public fun add<K: copy, drop, store, V: store>(self: &mut big_ordered_map::BigOrderedMap<K, V>, key: K, value: V)
@@ -535,7 +579,7 @@ Aborts if the key is already in the tree.
## Function `upsert`
-If the key doesn't exist in the tree, inserts the key/value, and returns none.
+If the key doesn't exist in the map, inserts the key/value, and returns none.
Otherwise updates the value under the given key, and returns the old value.
@@ -564,49 +608,6 @@ Otherwise updates the value under the given key, and returns the old value.
-
-
-
-
-## Function `add_or_upsert_impl`
-
-
-
-fun add_or_upsert_impl<K: copy, drop, store, V: store>(self: &mut big_ordered_map::BigOrderedMap<K, V>, key: K, value: V, allow_overwrite: bool): option::Option<big_ordered_map::Child<V>>
-
-
-
-
-
-Implementation
-
-
-fun add_or_upsert_impl<K: drop + copy + store, V: store>(self: &mut BigOrderedMap<K, V>, key: K, value: V, allow_overwrite: bool): Option<Child<V>> {
- if (self.inner_max_degree == 0 || self.leaf_max_degree == 0) {
- self.init_max_degrees(&key, &value);
- };
-
- let leaf = self.find_leaf(&key);
-
- if (leaf == NULL_INDEX) {
- // In this case, the key is greater than all keys in the tree.
- leaf = self.max_leaf_index;
- let current = self.nodes.borrow(leaf).parent;
- while (current != NULL_INDEX) {
- let current_node = self.nodes.borrow_mut(current);
-
- let last_value = current_node.children.new_end_iter().iter_prev(¤t_node.children).iter_remove(&mut current_node.children);
- current_node.children.add(key, last_value);
- current = current_node.parent;
- }
- };
-
- self.add_at(leaf, key, new_leaf_child(value), allow_overwrite)
-}
-
-
-
-
@@ -628,7 +629,7 @@ Aborts if there is no entry for key
.
public fun remove<K: drop + copy + store, V: store>(self: &mut BigOrderedMap<K, V>, key: &K): V {
let iter = self.find(key);
- assert!(!is_end_iter(self, &iter), E_INTERNAL);
+ assert!(!iter.iter_is_end(self), error::invalid_argument(EKEY_NOT_FOUND));
let Child::Leaf {
value,
@@ -640,84 +641,6 @@ Aborts if there is no entry for key
.
-
-
-
-
-## Function `is_begin_iter`
-
-
-
-public fun is_begin_iter<K: store, V: store>(tree: &big_ordered_map::BigOrderedMap<K, V>, iter: &big_ordered_map::Iterator<K>): bool
-
-
-
-
-
-Implementation
-
-
-public fun is_begin_iter<K: store, V: store>(tree: &BigOrderedMap<K, V>, iter: &Iterator<K>): bool {
- if (iter is Iterator::End<K>) {
- tree.is_empty()
- } else {
- (iter.node_index == tree.min_leaf_index && iter.child_iter.iter_is_begin_from_non_empty())
- }
-}
-
-
-
-
-
-
-
-
-## Function `is_end_iter`
-
-
-
-public fun is_end_iter<K: store, V: store>(_tree: &big_ordered_map::BigOrderedMap<K, V>, iter: &big_ordered_map::Iterator<K>): bool
-
-
-
-
-
-Implementation
-
-
-public fun is_end_iter<K: store, V: store>(_tree: &BigOrderedMap<K, V>, iter: &Iterator<K>): bool {
- iter is Iterator::End<K>
-}
-
-
-
-
-
-
-
-
-## Function `iter_get_key`
-
-Returns the key of the given iterator.
-
-
-public fun iter_get_key<K>(self: &big_ordered_map::Iterator<K>): &K
-
-
-
-
-
-Implementation
-
-
-public fun iter_get_key<K>(self: &Iterator<K>): &K {
- assert!(!(self is Iterator::End<K>), E_INVALID_PARAMETER);
- &self.key
-}
-
-
-
-
@@ -744,10 +667,10 @@ key, or an end iterator if such element doesn't exist.
};
let node = self.nodes.borrow(leaf);
- assert!(node.is_leaf, E_INTERNAL);
+ assert!(node.is_leaf, error::invalid_state(EINTERNAL_INVARIANT_BROKEN));
let child_lower_bound = node.children.lower_bound(key);
- if (child_lower_bound.iter_is_end()) {
+ if (child_lower_bound.iter_is_end(&node.children)) {
self.new_end_iter()
} else {
let iter_key = *child_lower_bound.iter_borrow_key(&node.children);
@@ -779,7 +702,7 @@ iterator if the key is not found.
public fun find<K: drop + copy + store, V: store>(self: &BigOrderedMap<K, V>, key: &K): Iterator<K> {
let lower_bound = self.lower_bound(key);
- if (is_end_iter(self, &lower_bound)) {
+ if (lower_bound.iter_is_end(self)) {
lower_bound
} else if (&lower_bound.key == key) {
lower_bound
@@ -797,7 +720,7 @@ iterator if the key is not found.
## Function `contains`
-Returns true iff the key exists in the tree.
+Returns true iff the key exists in the map.
public fun contains<K: copy, drop, store, V: store>(self: &big_ordered_map::BigOrderedMap<K, V>, key: &K): bool
@@ -811,7 +734,7 @@ Returns true iff the key exists in the tree.
public fun contains<K: drop + copy + store, V: store>(self: &BigOrderedMap<K, V>, key: &K): bool {
let lower_bound = self.lower_bound(key);
- if (is_end_iter(self, &lower_bound)) {
+ if (lower_bound.iter_is_end(self)) {
false
} else if (&lower_bound.key == key) {
true
@@ -844,7 +767,7 @@ Returns a reference to the element with its key, aborts if the key is not found.
public fun borrow<K: drop + copy + store, V: store>(self: &BigOrderedMap<K, V>, key: K): &V {
let iter = self.find(&key);
- assert!(is_end_iter(self, &iter), E_INVALID_PARAMETER);
+ assert!(iter.iter_is_end(self), error::invalid_argument(EKEY_NOT_FOUND));
let children = &self.nodes.borrow(iter.node_index).children;
&iter.child_iter.iter_borrow(children).value
}
@@ -854,14 +777,14 @@ Returns a reference to the element with its key, aborts if the key is not found.
-
+
-## Function `borrow_mut`
+## Function `new_begin_iter`
-Returns a mutable reference to the element with its key at the given index, aborts if the key is not found.
+Return the begin iterator.
-public fun borrow_mut<K: copy, drop, store, V: store>(self: &mut big_ordered_map::BigOrderedMap<K, V>, key: K): &mut V
+public fun new_begin_iter<K: copy, store, V: store>(self: &big_ordered_map::BigOrderedMap<K, V>): big_ordered_map::Iterator<K>
@@ -870,12 +793,16 @@ Returns a mutable reference to the element with its key at the given index, abor
Implementation
-public fun borrow_mut<K: drop + copy + store, V: store>(self: &mut BigOrderedMap<K, V>, key: K): &mut V {
- let iter = self.find(&key);
+public fun new_begin_iter<K: copy + store, V: store>(self: &BigOrderedMap<K, V>): Iterator<K> {
+ if (self.is_empty()) {
+ return Iterator::End;
+ };
- assert!(is_end_iter(self, &iter), E_INVALID_PARAMETER);
- let children = &mut self.nodes.borrow_mut(iter.node_index).children;
- &mut iter.child_iter.iter_borrow_mut(children).value
+ let node = self.nodes.borrow(self.min_leaf_index);
+ assert!(!node.children.is_empty(), error::invalid_state(EINTERNAL_INVARIANT_BROKEN));
+ let begin_child_iter = node.children.new_begin_iter();
+ let begin_child_key = *begin_child_iter.iter_borrow_key(&node.children);
+ new_iter(self.min_leaf_index, begin_child_iter, begin_child_key)
}
@@ -883,14 +810,14 @@ Returns a mutable reference to the element with its key at the given index, abor
-
+
-## Function `new_begin_iter`
+## Function `new_end_iter`
-Return the begin iterator.
+Return the end iterator.
-public fun new_begin_iter<K: copy, store, V: store>(self: &big_ordered_map::BigOrderedMap<K, V>): big_ordered_map::Iterator<K>
+public fun new_end_iter<K: copy, store, V: store>(self: &big_ordered_map::BigOrderedMap<K, V>): big_ordered_map::Iterator<K>
@@ -899,16 +826,36 @@ Return the begin iterator.
Implementation
-public fun new_begin_iter<K: copy + store, V: store>(self: &BigOrderedMap<K, V>): Iterator<K> {
- if (self.is_empty()) {
- return Iterator::End;
- };
+public fun new_end_iter<K: copy + store, V: store>(self: &BigOrderedMap<K, V>): Iterator<K> {
+ Iterator::End
+}
+
- let node = self.nodes.borrow(self.min_leaf_index);
- assert!(!node.children.is_empty(), E_INTERNAL);
- let begin_child_iter = node.children.new_begin_iter();
- let begin_child_key = *begin_child_iter.iter_borrow_key(&node.children);
- new_iter(self.min_leaf_index, begin_child_iter, begin_child_key)
+
+
+
+
+
+
+## Function `iter_is_begin`
+
+
+
+public fun iter_is_begin<K: store, V: store>(self: &big_ordered_map::Iterator<K>, map: &big_ordered_map::BigOrderedMap<K, V>): bool
+
+
+
+
+
+Implementation
+
+
+public fun iter_is_begin<K: store, V: store>(self: &Iterator<K>, map: &BigOrderedMap<K, V>): bool {
+ if (self is Iterator::End<K>) {
+ map.is_empty()
+ } else {
+ (self.node_index == map.min_leaf_index && self.child_iter.iter_is_begin_from_non_empty())
+ }
}
@@ -916,14 +863,13 @@ Return the begin iterator.
-
+
-## Function `new_end_iter`
+## Function `iter_is_end`
-Return the end iterator.
-public fun new_end_iter<K: copy, store, V: store>(self: &big_ordered_map::BigOrderedMap<K, V>): big_ordered_map::Iterator<K>
+public fun iter_is_end<K: store, V: store>(self: &big_ordered_map::Iterator<K>, _map: &big_ordered_map::BigOrderedMap<K, V>): bool
@@ -932,8 +878,34 @@ Return the end iterator.
Implementation
-public fun new_end_iter<K: copy + store, V: store>(self: &BigOrderedMap<K, V>): Iterator<K> {
- Iterator::End
+public fun iter_is_end<K: store, V: store>(self: &Iterator<K>, _map: &BigOrderedMap<K, V>): bool {
+ self is Iterator::End<K>
+}
+
+
+
+
+
+
+
+
+## Function `iter_get_key`
+
+Returns the key of the given iterator.
+
+
+public fun iter_get_key<K>(self: &big_ordered_map::Iterator<K>): &K
+
+
+
+
+
+Implementation
+
+
+public fun iter_get_key<K>(self: &Iterator<K>): &K {
+ assert!(!(self is Iterator::End<K>), error::invalid_argument(EITER_OUT_OF_BOUNDS));
+ &self.key
}
@@ -941,15 +913,15 @@ Return the end iterator.
-
+
-## Function `next_iter`
+## Function `iter_next`
Returns the next iterator, or none if already at the end iterator.
-Requires the tree is not changed after the input iterator is generated.
+Requires the map is not changed after the input iterator is generated.
-public fun next_iter<K: copy, drop, store, V: store>(tree: &big_ordered_map::BigOrderedMap<K, V>, iter: big_ordered_map::Iterator<K>): big_ordered_map::Iterator<K>
+public fun iter_next<K: copy, drop, store, V: store>(self: big_ordered_map::Iterator<K>, map: &big_ordered_map::BigOrderedMap<K, V>): big_ordered_map::Iterator<K>
@@ -958,29 +930,29 @@ Requires the tree is not changed after the input iterator is generated.
Implementation
-public fun next_iter<K: drop + copy + store, V: store>(tree: &BigOrderedMap<K, V>, iter: Iterator<K>): Iterator<K> {
- assert!(!(iter is Iterator::End<K>), E_INVALID_PARAMETER);
+public fun iter_next<K: drop + copy + store, V: store>(self: Iterator<K>, map: &BigOrderedMap<K, V>): Iterator<K> {
+ assert!(!(self is Iterator::End<K>), error::invalid_argument(EITER_OUT_OF_BOUNDS));
- let node_index = iter.node_index;
- let node = tree.nodes.borrow(node_index);
+ let node_index = self.node_index;
+ let node = map.nodes.borrow(node_index);
- let child_iter = iter.child_iter.iter_next(&node.children);
- if (!child_iter.iter_is_end()) {
+ let child_iter = self.child_iter.iter_next(&node.children);
+ if (!child_iter.iter_is_end(&node.children)) {
let iter_key = *child_iter.iter_borrow_key(&node.children);
return new_iter(node_index, child_iter, iter_key);
};
let next_index = node.next;
if (next_index != NULL_INDEX) {
- let next_node = tree.nodes.borrow(next_index);
+ let next_node = map.nodes.borrow(next_index);
let child_iter = next_node.children.new_begin_iter();
- assert!(!iter.child_iter.iter_is_end(), E_INTERNAL);
+ assert!(!child_iter.iter_is_end(&next_node.children), error::invalid_state(EINTERNAL_INVARIANT_BROKEN));
let iter_key = *child_iter.iter_borrow_key(&next_node.children);
return new_iter(next_index, child_iter, iter_key);
};
- new_end_iter(tree)
+ new_end_iter(map)
}
@@ -988,15 +960,15 @@ Requires the tree is not changed after the input iterator is generated.
-
+
-## Function `prev_iter`
+## Function `iter_prev`
Returns the previous iterator, or none if already at the begin iterator.
-Requires the tree is not changed after the input iterator is generated.
+Requires the map is not changed after the input iterator is generated.
-public fun prev_iter<K: copy, drop, store, V: store>(tree: &big_ordered_map::BigOrderedMap<K, V>, iter: big_ordered_map::Iterator<K>): big_ordered_map::Iterator<K>
+public fun iter_prev<K: copy, drop, store, V: store>(self: big_ordered_map::Iterator<K>, map: &big_ordered_map::BigOrderedMap<K, V>): big_ordered_map::Iterator<K>
@@ -1005,27 +977,27 @@ Requires the tree is not changed after the input iterator is generated.
Implementation
-public fun prev_iter<K: drop + copy + store, V: store>(tree: &BigOrderedMap<K, V>, iter: Iterator<K>): Iterator<K> {
- let prev_index = if (iter is Iterator::End<K>) {
- tree.max_leaf_index
+public fun iter_prev<K: drop + copy + store, V: store>(self: Iterator<K>, map: &BigOrderedMap<K, V>): Iterator<K> {
+ let prev_index = if (self is Iterator::End<K>) {
+ map.max_leaf_index
} else {
- let node_index = iter.node_index;
- let node = tree.nodes.borrow(node_index);
+ let node_index = self.node_index;
+ let node = map.nodes.borrow(node_index);
- if (!iter.child_iter.iter_is_begin(&node.children)) {
- let child_iter = iter.child_iter.iter_prev(&node.children);
+ if (!self.child_iter.iter_is_begin(&node.children)) {
+ let child_iter = self.child_iter.iter_prev(&node.children);
let key = *child_iter.iter_borrow_key(&node.children);
return new_iter(node_index, child_iter, key);
};
node.prev
};
- assert!(prev_index != NULL_INDEX, E_INTERNAL);
+ assert!(prev_index != NULL_INDEX, error::invalid_argument(EITER_OUT_OF_BOUNDS));
- let prev_node = tree.nodes.borrow(prev_index);
+ let prev_node = map.nodes.borrow(prev_index);
let prev_children = &prev_node.children;
- let child_iter = prev_children.new_end_iter().iter_prev(prev_children);
+ let child_iter = prev_children.new_end_iter().iter_prev(prev_children);
let iter_key = *child_iter.iter_borrow_key(prev_children);
new_iter(prev_index, child_iter, iter_key)
}
@@ -1035,13 +1007,13 @@ Requires the tree is not changed after the input iterator is generated.
-
+
-## Function `init_max_degrees`
+## Function `add_or_upsert_impl`
-fun init_max_degrees<K: store, V: store>(self: &mut big_ordered_map::BigOrderedMap<K, V>, key: &K, value: &V)
+fun add_or_upsert_impl<K: copy, drop, store, V: store>(self: &mut big_ordered_map::BigOrderedMap<K, V>, key: K, value: V, allow_overwrite: bool): option::Option<big_ordered_map::Child<V>>
@@ -1050,19 +1022,65 @@ Requires the tree is not changed after the input iterator is generated.
Implementation
-fun init_max_degrees<K: store, V: store>(self: &mut BigOrderedMap<K, V>, key: &K, value: &V) {
- if (self.inner_max_degree == 0 || self.leaf_max_degree == 0) {
- let key_size = bcs::serialized_size(key);
+fun add_or_upsert_impl<K: drop + copy + store, V: store>(self: &mut BigOrderedMap<K, V>, key: K, value: V, allow_overwrite: bool): Option<Child<V>> {
+ // TODO cache the check if K and V have constant size
+ // if (self.inner_max_degree == 0 || self.leaf_max_degree == 0) {
+ self.validate_size_and_init_max_degrees(&key, &value);
+ // };
- if (self.inner_max_degree == 0) {
- self.inner_max_degree = max(min(MAX_DEGREE, DEFAULT_TARGET_NODE_SIZE / key_size), DEFAULT_INNER_MIN_DEGREE as u64) as u16;
- };
+ let leaf = self.find_leaf(&key);
- if (self.leaf_max_degree == 0) {
- let value_size = bcs::serialized_size(value);
- self.leaf_max_degree = max(min(MAX_DEGREE, DEFAULT_TARGET_NODE_SIZE / (key_size + value_size)), DEFAULT_LEAF_MIN_DEGREE as u64) as u16;
- };
+ if (leaf == NULL_INDEX) {
+ // In this case, the key is greater than all keys in the map.
+ leaf = self.max_leaf_index;
+ let current = self.nodes.borrow(leaf).parent;
+ while (current != NULL_INDEX) {
+ let current_node = self.nodes.borrow_mut(current);
+
+ let last_value = current_node.children.new_end_iter().iter_prev(¤t_node.children).iter_remove(&mut current_node.children);
+ current_node.children.add(key, last_value);
+ current = current_node.parent;
+ }
+ };
+
+ self.add_at(leaf, key, new_leaf_child(value), allow_overwrite)
+}
+
+
+
+
+
+
+
+
+## Function `validate_size_and_init_max_degrees`
+
+
+
+fun validate_size_and_init_max_degrees<K: store, V: store>(self: &mut big_ordered_map::BigOrderedMap<K, V>, key: &K, value: &V)
+
+
+
+
+
+Implementation
+
+
+fun validate_size_and_init_max_degrees<K: store, V: store>(self: &mut BigOrderedMap<K, V>, key: &K, value: &V) {
+ let key_size = bcs::serialized_size(key);
+ let value_size = bcs::serialized_size(value);
+ let entry_size = key_size + value_size;
+
+ if (self.inner_max_degree == 0) {
+ self.inner_max_degree = max(min(MAX_DEGREE, DEFAULT_TARGET_NODE_SIZE / key_size), DEFAULT_INNER_MIN_DEGREE as u64) as u16;
+ };
+
+ if (self.leaf_max_degree == 0) {
+ self.leaf_max_degree = max(min(MAX_DEGREE, DEFAULT_TARGET_NODE_SIZE / (key_size + value_size)), DEFAULT_LEAF_MIN_DEGREE as u64) as u16;
};
+
+ assert!(key_size * (self.inner_max_degree as u64) <= MAX_NODE_BYTES, error::invalid_argument(EARGUMENT_BYTES_TOO_LARGE));
+ assert!(entry_size * (self.leaf_max_degree as u64) <= MAX_NODE_BYTES, error::invalid_argument(EARGUMENT_BYTES_TOO_LARGE));
}
@@ -1113,7 +1131,7 @@ Requires the tree is not changed after the input iterator is generated.
fun destroy_empty_node<K: store, V: store>(self: Node<K, V>) {
let Node { children, is_leaf: _, parent: _, prev: _, next: _ } = self;
- assert!(children.is_empty(), E_TREE_NOT_EMPTY);
+ assert!(children.is_empty(), error::invalid_argument(EMAP_NOT_EMPTY));
children.destroy_empty();
}
@@ -1286,7 +1304,7 @@ Requires the tree is not changed after the input iterator is generated.
};
let children = &node.children;
let child_iter = children.lower_bound(key);
- if (child_iter.iter_is_end()) {
+ if (child_iter.iter_is_end(children)) {
return NULL_INDEX;
} else {
current = child_iter.iter_borrow(children).node_index;
@@ -1346,7 +1364,7 @@ Requires the tree is not changed after the input iterator is generated.
fun add_at<K: drop + copy + store, V: store>(self: &mut BigOrderedMap<K, V>, node_index: u64, key: K, child: Child<V>, allow_overwrite: bool): Option<Child<V>> {
{
- let node = self.nodes.borrow_mut(node_index);
+ let node = self.nodes.borrow_mut(node_index);
let children = &mut node.children;
let current_size = children.length();
@@ -1360,17 +1378,17 @@ Requires the tree is not changed after the input iterator is generated.
let result = children.upsert(key, child);
if (node.is_leaf) {
- assert!(allow_overwrite || result.is_none(), EKEY_ALREADY_EXISTS);
+ assert!(allow_overwrite || result.is_none(), error::invalid_argument(EKEY_ALREADY_EXISTS));
return result;
} else {
- assert!(!allow_overwrite && result.is_none(), E_INTERNAL);
+ assert!(!allow_overwrite && result.is_none(), error::invalid_state(EINTERNAL_INVARIANT_BROKEN));
return result;
};
};
if (allow_overwrite) {
let iter = children.find(&key);
- if (!iter.iter_is_end()) {
+ if (!iter.iter_is_end(children)) {
return option::some(iter.iter_replace(children, child));
}
}
@@ -1387,7 +1405,7 @@ Requires the tree is not changed after the input iterator is generated.
if (parent_index == NULL_INDEX) {
// Splitting root now, need to create a new root.
let parent_node = new_node(/*is_leaf=*/false, /*parent=*/NULL_INDEX);
- let max_element = *children.new_end_iter().iter_prev(children).iter_borrow_key(children);
+ let max_element = *children.new_end_iter().iter_prev(children).iter_borrow_key(children);
if (cmp::compare(&max_element, &key).is_less_than()) {
max_element = key;
};
@@ -1407,10 +1425,10 @@ Requires the tree is not changed after the input iterator is generated.
let target_size = (max_degree + 1) / 2;
children.add(key, child);
- let new_node_children = children.split_off(target_size);
+ let new_node_children = children.trim(target_size);
- assert!(children.length() <= max_degree, E_INTERNAL);
- assert!(new_node_children.length() <= max_degree, E_INTERNAL);
+ assert!(children.length() <= max_degree, error::invalid_state(EINTERNAL_INVARIANT_BROKEN));
+ assert!(new_node_children.length() <= max_degree, error::invalid_state(EINTERNAL_INVARIANT_BROKEN));
let right_node = new_node_with_children(*is_leaf, parent_index, new_node_children);
@@ -1420,16 +1438,16 @@ Requires the tree is not changed after the input iterator is generated.
*next = node_index;
right_node.prev = left_node_index;
if (*prev != NULL_INDEX) {
- self.nodes.borrow_mut(*prev).next = left_node_index;
+ self.nodes.borrow_mut(*prev).next = left_node_index;
};
if (!*is_leaf) {
children.for_each_ref(|_key, child| {
- self.nodes.borrow_mut(child.node_index).parent = left_node_index;
+ self.nodes.borrow_mut(child.node_index).parent = left_node_index;
});
};
- let split_key = *children.new_end_iter().iter_prev(children).iter_borrow_key(children);
+ let split_key = *children.new_end_iter().iter_prev(children).iter_borrow_key(children);
self.nodes.fill_reserved_slot(left_node_slot, node);
self.nodes.fill_reserved_slot(right_node_slot, right_node);
@@ -1466,11 +1484,11 @@ Requires the tree is not changed after the input iterator is generated.
return
};
- let node = self.nodes.borrow_mut(node_index);
+ let node = self.nodes.borrow_mut(node_index);
let children = &mut node.children;
children.replace_key_inplace(old_key, new_key);
- if (children.new_end_iter().iter_prev(children).iter_borrow_key(children) == &new_key) {
+ if (children.new_end_iter().iter_prev(children).iter_borrow_key(children) == &new_key) {
self.update_key(node.parent, old_key, new_key);
};
}
@@ -1496,14 +1514,8 @@ Requires the tree is not changed after the input iterator is generated.
fun remove_at<K: drop + copy + store, V: store>(self: &mut BigOrderedMap<K, V>, node_index: u64, key: &K): Child<V> {
- // aptos_std::debug::print(&std::string::utf8(b"remove_at"));
- // aptos_std::debug::print(&node_index);
- // aptos_std::debug::print(key);
-
let old_child = {
- let node = self.nodes.borrow_mut(node_index);
- // aptos_std::debug::print(&std::string::utf8(b"node, borrowed"));
- // aptos_std::debug::print(node);
+ let node = self.nodes.borrow_mut(node_index);
let children = &mut node.children;
let current_size = children.length();
@@ -1518,7 +1530,7 @@ Requires the tree is not changed after the input iterator is generated.
let old_child = children.remove(key);
current_size = current_size - 1;
- let new_max_key = *children.new_end_iter().iter_prev(children).iter_borrow_key(children);
+ let new_max_key = *children.new_end_iter().iter_prev(children).iter_borrow_key(children);
let max_key_updated = cmp::compare(&new_max_key, key).is_less_than();
let max_degree = if (node.is_leaf) {
@@ -1537,9 +1549,9 @@ Requires the tree is not changed after the input iterator is generated.
if (current_size == 1 && !is_leaf) {
let Child::Inner {
node_index: inner_child_index,
- } = children.new_end_iter().iter_prev(children).iter_remove(children);
+ } = children.new_end_iter().iter_prev(children).iter_remove(children);
self.root_index = inner_child_index;
- self.nodes.borrow_mut(self.root_index).parent = NULL_INDEX;
+ self.nodes.borrow_mut(self.root_index).parent = NULL_INDEX;
destroy_empty_node(self.nodes.remove(node_index));
} else {
// nothing to change
@@ -1548,7 +1560,7 @@ Requires the tree is not changed after the input iterator is generated.
};
if (max_key_updated) {
- assert!(current_size >= 1, E_INTERNAL);
+ assert!(current_size >= 1, error::invalid_state(EINTERNAL_INVARIANT_BROKEN));
let parent = node.parent;
@@ -1562,11 +1574,9 @@ Requires the tree is not changed after the input iterator is generated.
old_child
};
- // We need to update tree beyond the current node
+ // We need to update map beyond the current node
let (node_slot, node) = self.nodes.remove_and_reserve(node_index);
- // aptos_std::debug::print(&std::string::utf8(b"node, removed and reserved"));
- // aptos_std::debug::print(&node);
let prev = node.prev;
let next = node.next;
@@ -1583,45 +1593,33 @@ Requires the tree is not changed after the input iterator is generated.
brother_index = prev;
};
let (brother_slot, brother_node) = self.nodes.remove_and_reserve(brother_index);
- // aptos_std::debug::print(&std::string::utf8(b"brother, removed and reserved"));
- // aptos_std::debug::print(&brother_node);
let brother_children = &mut brother_node.children;
if ((brother_children.length() - 1) * 2 >= max_degree) {
- // aptos_std::debug::print(&std::string::utf8(b"The brother node has enough elements, borrow an element from the brother node."));
// The brother node has enough elements, borrow an element from the brother node.
if (brother_index == next) {
- // aptos_std::debug::print(&std::string::utf8(b"brother_index == next. Moving from brother."));
-
- let old_max_key = *children.new_end_iter().iter_prev(children).iter_borrow_key(children);
+ let old_max_key = *children.new_end_iter().iter_prev(children).iter_borrow_key(children);
let brother_begin_iter = brother_children.new_begin_iter();
let borrowed_max_key = *brother_begin_iter.iter_borrow_key(brother_children);
let borrowed_element = brother_begin_iter.iter_remove(brother_children);
if (borrowed_element is Child::Inner<V>) {
- self.nodes.borrow_mut(borrowed_element.node_index).parent = node_index;
+ self.nodes.borrow_mut(borrowed_element.node_index).parent = node_index;
};
- // aptos_std::debug::print(&borrowed_max_key);
- // aptos_std::debug::print(&old_max_key);
-
children.add(borrowed_max_key, borrowed_element);
self.update_key(parent, &old_max_key, borrowed_max_key);
} else {
- // aptos_std::debug::print(&std::string::utf8(b"brother_index != next. Moving from brother"));
-
- let brother_end_iter = brother_children.new_end_iter().iter_prev(brother_children);
+ let brother_end_iter = brother_children.new_end_iter().iter_prev(brother_children);
let borrowed_max_key = *brother_end_iter.iter_borrow_key(brother_children);
let borrowed_element = brother_end_iter.iter_remove(brother_children);
if (borrowed_element is Child::Inner<V>) {
- self.nodes.borrow_mut(borrowed_element.node_index).parent = node_index;
+ self.nodes.borrow_mut(borrowed_element.node_index).parent = node_index;
};
- // aptos_std::debug::print(&borrowed_max_key);
-
children.add(borrowed_max_key, borrowed_element);
- self.update_key(parent, &borrowed_max_key, *brother_children.new_end_iter().iter_prev(brother_children).iter_borrow_key(brother_children));
+ self.update_key(parent, &borrowed_max_key, *brother_children.new_end_iter().iter_prev(brother_children).iter_borrow_key(brother_children));
};
self.nodes.fill_reserved_slot(node_slot, node);
@@ -1629,37 +1627,27 @@ Requires the tree is not changed after the input iterator is generated.
return old_child;
};
- // aptos_std::debug::print(&std::string::utf8(b"The brother node doesn't have enough elements to borrow, merge with the brother node."));
-
// The brother node doesn't have enough elements to borrow, merge with the brother node.
if (brother_index == next) {
- // aptos_std::debug::print(&std::string::utf8(b"brother_index == next"));
-
if (!is_leaf) {
children.for_each_ref(|_key, child| {
- self.nodes.borrow_mut(child.node_index).parent = brother_index;
+ self.nodes.borrow_mut(child.node_index).parent = brother_index;
});
};
let Node { children: brother_children, is_leaf: _, parent: _, prev: _, next: brother_next } = brother_node;
- let key_to_remove = *children.new_end_iter().iter_prev(children).iter_borrow_key(children);
+ let key_to_remove = *children.new_end_iter().iter_prev(children).iter_borrow_key(children);
children.append(brother_children);
node.next = brother_next;
move children;
if (node.next != NULL_INDEX) {
- self.nodes.borrow_mut(node.next).prev = brother_index;
+ self.nodes.borrow_mut(node.next).prev = brother_index;
};
if (node.prev != NULL_INDEX) {
- self.nodes.borrow_mut(node.prev).next = brother_index;
+ self.nodes.borrow_mut(node.prev).next = brother_index;
};
- // aptos_std::debug::print(&std::string::utf8(b"keeping node"));
- // aptos_std::debug::print(&brother_slot);
- // aptos_std::debug::print(&node);
- // aptos_std::debug::print(&std::string::utf8(b"freeing node"));
- // aptos_std::debug::print(&node_slot);
-
self.nodes.fill_reserved_slot(brother_slot, node);
self.nodes.free_reserved_slot(node_slot);
if (self.min_leaf_index == node_index) {
@@ -1670,34 +1658,26 @@ Requires the tree is not changed after the input iterator is generated.
destroy_inner_child(self.remove_at(parent, &key_to_remove));
};
} else {
- // aptos_std::debug::print(&std::string::utf8(b"brother_index != next"));
-
if (!is_leaf) {
brother_children.for_each_ref(|_key, child| {
- self.nodes.borrow_mut(child.node_index).parent = node_index;
+ self.nodes.borrow_mut(child.node_index).parent = node_index;
});
};
let Node { children: node_children, is_leaf: _, parent: _, prev: _, next: node_next } = node;
- let key_to_remove = *brother_children.new_end_iter().iter_prev(brother_children).iter_borrow_key(brother_children);
+ let key_to_remove = *brother_children.new_end_iter().iter_prev(brother_children).iter_borrow_key(brother_children);
brother_children.append(node_children);
brother_node.next = node_next;
move brother_children;
if (brother_node.next != NULL_INDEX) {
- self.nodes.borrow_mut(brother_node.next).prev = node_index;
+ self.nodes.borrow_mut(brother_node.next).prev = node_index;
};
if (brother_node.prev != NULL_INDEX) {
- self.nodes.borrow_mut(brother_node.prev).next = node_index;
+ self.nodes.borrow_mut(brother_node.prev).next = node_index;
};
- // aptos_std::debug::print(&std::string::utf8(b"keeping node"));
- // aptos_std::debug::print(&node_slot);
- // aptos_std::debug::print(&brother_node);
- // aptos_std::debug::print(&std::string::utf8(b"freeing node"));
- // aptos_std::debug::print(&brother_slot);
-
self.nodes.fill_reserved_slot(node_slot, brother_node);
self.nodes.free_reserved_slot(brother_slot);
if (self.min_leaf_index == brother_index) {
diff --git a/aptos-move/framework/aptos-stdlib/doc/fixed_point64.md b/aptos-move/framework/aptos-stdlib/doc/fixed_point64.md
index d13fba14f020fb..c9d35f6de494ec 100644
--- a/aptos-move/framework/aptos-stdlib/doc/fixed_point64.md
+++ b/aptos-move/framework/aptos-stdlib/doc/fixed_point64.md
@@ -144,22 +144,22 @@ The multiplied value would be too large to be held in a u128
-
+
-Abort code on calculation result is negative.
+The computed ratio when converting to a FixedPoint64
would be unrepresentable
-const ENEGATIVE_RESULT: u64 = 65542;
+const ERATIO_OUT_OF_RANGE: u64 = 131077;
-
+
-The computed ratio when converting to a FixedPoint64
would be unrepresentable
+Abort code on calculation result is negative.
-const ERATIO_OUT_OF_RANGE: u64 = 131077;
+const ENEGATIVE_RESULT: u64 = 65542;
diff --git a/aptos-move/framework/aptos-stdlib/doc/overview.md b/aptos-move/framework/aptos-stdlib/doc/overview.md
index 50dc6a5d6a0f49..fa811e7bef51de 100644
--- a/aptos-move/framework/aptos-stdlib/doc/overview.md
+++ b/aptos-move/framework/aptos-stdlib/doc/overview.md
@@ -14,6 +14,7 @@ This is the reference documentation of the Aptos standard library.
- [`0x1::any`](any.md#0x1_any)
- [`0x1::aptos_hash`](hash.md#0x1_aptos_hash)
+- [`0x1::big_ordered_map`](big_ordered_map.md#0x1_big_ordered_map)
- [`0x1::big_vector`](big_vector.md#0x1_big_vector)
- [`0x1::bls12381`](bls12381.md#0x1_bls12381)
- [`0x1::bls12381_algebra`](bls12381_algebra.md#0x1_bls12381_algebra)
@@ -42,6 +43,7 @@ This is the reference documentation of the Aptos standard library.
- [`0x1::simple_map`](simple_map.md#0x1_simple_map)
- [`0x1::smart_table`](smart_table.md#0x1_smart_table)
- [`0x1::smart_vector`](smart_vector.md#0x1_smart_vector)
+- [`0x1::storage_slots_allocator`](storage_slots_allocator.md#0x1_storage_slots_allocator)
- [`0x1::string_utils`](string_utils.md#0x1_string_utils)
- [`0x1::table`](table.md#0x1_table)
- [`0x1::table_with_length`](table_with_length.md#0x1_table_with_length)
diff --git a/aptos-move/framework/aptos-stdlib/doc/storage_slots_allocator.md b/aptos-move/framework/aptos-stdlib/doc/storage_slots_allocator.md
index 718f347b51fd30..1c75deb7663ae7 100644
--- a/aptos-move/framework/aptos-stdlib/doc/storage_slots_allocator.md
+++ b/aptos-move/framework/aptos-stdlib/doc/storage_slots_allocator.md
@@ -38,7 +38,7 @@ for example:
- [Function `fill_reserved_slot`](#0x1_storage_slots_allocator_fill_reserved_slot)
- [Function `remove_and_reserve`](#0x1_storage_slots_allocator_remove_and_reserve)
- [Function `free_reserved_slot`](#0x1_storage_slots_allocator_free_reserved_slot)
-- [Function `push_to_reuse_queue`](#0x1_storage_slots_allocator_push_to_reuse_queue)
+- [Function `push_to_reuse_queue_if_enabled`](#0x1_storage_slots_allocator_push_to_reuse_queue_if_enabled)
- [Function `next_slot_index`](#0x1_storage_slots_allocator_next_slot_index)
@@ -288,7 +288,7 @@ for example:
for (i in 0..num_to_preallocate) {
let slot_index = self.next_slot_index();
- self.push_to_reuse_queue(slot_index);
+ self.push_to_reuse_queue_if_enabled(slot_index);
};
self
@@ -353,7 +353,7 @@ for example:
public fun remove<T: store>(self: &mut StorageSlotsAllocator<T>, slot_index: u64): T {
let Link::Occupied { value } = self.slots.remove(slot_index);
- self.push_to_reuse_queue(slot_index);
+ self.push_to_reuse_queue_if_enabled(slot_index);
value
}
@@ -581,7 +581,7 @@ Remove storage slot, but reserve it for later.
public fun free_reserved_slot<T: store>(self: &mut StorageSlotsAllocator<T>, slot: ReservedSlot) {
let ReservedSlot { slot_index } = slot;
- self.push_to_reuse_queue(slot_index);
+ self.push_to_reuse_queue_if_enabled(slot_index);
}
@@ -589,13 +589,13 @@ Remove storage slot, but reserve it for later.
-
+
-## Function `push_to_reuse_queue`
+## Function `push_to_reuse_queue_if_enabled`
-fun push_to_reuse_queue<T: store>(self: &mut storage_slots_allocator::StorageSlotsAllocator<T>, slot_index: u64)
+fun push_to_reuse_queue_if_enabled<T: store>(self: &mut storage_slots_allocator::StorageSlotsAllocator<T>, slot_index: u64)
@@ -604,7 +604,7 @@ Remove storage slot, but reserve it for later.
Implementation
-fun push_to_reuse_queue<T: store>(self: &mut StorageSlotsAllocator<T>, slot_index: u64) {
+fun push_to_reuse_queue_if_enabled<T: store>(self: &mut StorageSlotsAllocator<T>, slot_index: u64) {
if (self is StorageSlotsAllocator::Reuse<T>) {
self.slots.add(slot_index, Link::Vacant { next: self.reuse_head_index });
self.reuse_head_index = slot_index;
diff --git a/aptos-move/framework/aptos-stdlib/sources/data_structures/big_ordered_map.move b/aptos-move/framework/aptos-stdlib/sources/data_structures/big_ordered_map.move
index 9d876d27bb8fd4..09631e22107e63 100644
--- a/aptos-move/framework/aptos-stdlib/sources/data_structures/big_ordered_map.move
+++ b/aptos-move/framework/aptos-stdlib/sources/data_structures/big_ordered_map.move
@@ -1,10 +1,21 @@
-/// Type of large-scale search trees.
+/// This module provides an implementation for an big ordered map.
+/// Big means that it is stored across multiple resources, and doesn't have an
+/// upper limit on number of elements it can contain.
///
-/// It internally uses BTree to organize the search tree data structure for keys. Comparing with
-/// other common search trees like AVL or Red-black tree, a BTree node has more children, and packs
-/// more metadata into one node, which is more disk friendly (and gas friendly).
-
+/// Keys point to values, and each key in the map must be unique.
+///
+/// Currently, one implementation is provided - BPlusTreeMap, backed by a B+Tree,
+/// with each node being a separate resource, internally containing OrderedMap.
+///
+/// BPlusTreeMap is chosen since the biggest (performance and gast)
+/// costs are reading resources, and it:
+/// * reduces number of resource accesses
+/// * reduces number of rebalancing operations, and makes each rebalancing
+/// operation touch only few resources
+/// * it allows for parallelism for keys that are not close to each other,
+/// once it contains enough keys
module aptos_std::big_ordered_map {
+ use std::error;
use std::option::{Self, Option};
use std::bcs;
use aptos_std::ordered_map::{Self, OrderedMap};
@@ -12,16 +23,23 @@ module aptos_std::big_ordered_map {
use aptos_std::storage_slots_allocator::{Self, StorageSlotsAllocator};
use aptos_std::math64::{max, min};
+ /// Map key already exists
+ const EKEY_ALREADY_EXISTS: u64 = 1;
+ /// Map key is not found
+ const EKEY_NOT_FOUND: u64 = 2;
+ // Trying to do an operation on an Iterator that would go out of bounds
+ const EITER_OUT_OF_BOUNDS: u64 = 3;
+ // The provided configuration parameter is invalid.
+ const EINVALID_CONFIG_PARAMETER: u64 = 4;
+ // Map isn't empty
+ const EMAP_NOT_EMPTY: u64 = 5;
+ // Trying to insert too large of an object into the mp.
+ const EARGUMENT_BYTES_TOO_LARGE: u64 = 6;
+
// Internal errors.
- const E_INTERNAL: u64 = 0;
- // The tree is not empty, and cannot be destroyed.
- const E_TREE_NOT_EMPTY: u64 = 1;
- // The tree is too big for insertion.
- const E_TREE_TOO_BIG: u64 = 2;
- // The provided parameter is invalid.
- const E_INVALID_PARAMETER: u64 = 3;
+ const EINTERNAL_INVARIANT_BROKEN: u64 = 7;
- const EKEY_ALREADY_EXISTS: u64 = 4;
+ // Internal constants.
const NULL_INDEX: u64 = 0;
const DEFAULT_TARGET_NODE_SIZE: u64 = 2048;
@@ -31,15 +49,22 @@ module aptos_std::big_ordered_map {
const DEFAULT_LEAF_MIN_DEGREE: u16 = 3;
const MAX_DEGREE: u64 = 4096;
+ const MAX_NODE_BYTES: u64 = 204800; // 200 KB, well bellow the max resource limit.
+
/// A node of the BigOrderedMap.
+ ///
+ /// Inner node will have all children be Child::Inner, pointing to the child nodes.
+ /// Leaf node will have all children be Child::Leaf.
+ /// Basically - Leaf node is a single-resource OrderedMap, containing as much keys as can fit.
+ /// So Leaf node contains multiple values, not just one.
struct Node has store {
// Whether this node is a leaf node.
is_leaf: bool,
// The node index of its parent node, or NULL_INDEX if it doesn't have parent.
parent: u64,
// The children of the nodes.
- // When node is inner node, K represents max_key of the recursive children.
- // When the node is leaf node, K represents key of the leaf.
+ // When node is inner node, K represents max_key within the child subtree, and values are Child::Inner.
+ // When the node is leaf node, K represents key of the leaf, and values are Child::Leaf.
children: OrderedMap>,
// The node index of its previous node at the same level, or NULL_INDEX if it doesn't have a previous node.
prev: u64,
@@ -50,10 +75,11 @@ module aptos_std::big_ordered_map {
/// The metadata of a child of a node.
enum Child has store {
Inner {
- // The node index of it's child, or NULL_INDEX if the current node is a leaf node.
+ // The node index of it's child
node_index: u64,
},
Leaf {
+ // Value associated with the leaf node.
value: V,
}
}
@@ -93,9 +119,7 @@ module aptos_std::big_ordered_map {
}
}
- /////////////////////////////////
- // Constructors && Destructors //
- /////////////////////////////////
+ // ======================= Constructors && Destructors ====================
/// Returns a new BigOrderedMap with the default configuration.
public fun new(): BigOrderedMap {
@@ -105,12 +129,12 @@ module aptos_std::big_ordered_map {
/// Returns a new BigOrderedMap with the provided max degree consts (the maximum # of children a node can have).
/// If 0 is passed, then it is dynamically computed based on size of first key and value.
public fun new_with_config(inner_max_degree: u16, leaf_max_degree: u16, reuse_slots: bool, num_to_preallocate: u64): BigOrderedMap {
- assert!(inner_max_degree == 0 || inner_max_degree >= DEFAULT_INNER_MIN_DEGREE, E_INVALID_PARAMETER);
- assert!(leaf_max_degree == 0 || leaf_max_degree >= DEFAULT_LEAF_MIN_DEGREE, E_INVALID_PARAMETER);
+ assert!(inner_max_degree == 0 || inner_max_degree >= DEFAULT_INNER_MIN_DEGREE, error::invalid_argument(EINVALID_CONFIG_PARAMETER));
+ assert!(leaf_max_degree == 0 || leaf_max_degree >= DEFAULT_LEAF_MIN_DEGREE, error::invalid_argument(EINVALID_CONFIG_PARAMETER));
let nodes = if (reuse_slots) {
storage_slots_allocator::new_reuse_storage_slots(num_to_preallocate)
} else {
- assert!(num_to_preallocate == 0, E_INVALID_PARAMETER);
+ assert!(num_to_preallocate == 0, error::invalid_argument(EINVALID_CONFIG_PARAMETER));
storage_slots_allocator::new_storage_slots()
};
let root_index = nodes.add(new_node(/*is_leaf=*/true, /*parent=*/NULL_INDEX));
@@ -124,25 +148,22 @@ module aptos_std::big_ordered_map {
}
}
- /// Destroys the tree if it's empty, otherwise aborts.
+ /// Destroys the map if it's empty, otherwise aborts.
public fun destroy_empty(self: BigOrderedMap) {
let BigOrderedMap::BPlusTreeMap { nodes, root_index, min_leaf_index: _, max_leaf_index: _, inner_max_degree: _, leaf_max_degree: _ } = self;
- // aptos_std::debug::print(&nodes);
nodes.remove(root_index).destroy_empty_node();
nodes.destroy();
}
- ///////////////
- // Modifiers //
- ///////////////
+ // ======================= Section with Modifiers =========================
/// Inserts the key/value into the BigOrderedMap.
- /// Aborts if the key is already in the tree.
+ /// Aborts if the key is already in the map.
public fun add(self: &mut BigOrderedMap, key: K, value: V) {
self.add_or_upsert_impl(key, value, false).destroy_none()
}
- /// If the key doesn't exist in the tree, inserts the key/value, and returns none.
+ /// If the key doesn't exist in the map, inserts the key/value, and returns none.
/// Otherwise updates the value under the given key, and returns the old value.
public fun upsert(self: &mut BigOrderedMap, key: K, value: V): Option {
let result = self.add_or_upsert_impl(key, value, true);
@@ -157,34 +178,11 @@ module aptos_std::big_ordered_map {
}
}
- fun add_or_upsert_impl(self: &mut BigOrderedMap, key: K, value: V, allow_overwrite: bool): Option> {
- if (self.inner_max_degree == 0 || self.leaf_max_degree == 0) {
- self.init_max_degrees(&key, &value);
- };
-
- let leaf = self.find_leaf(&key);
-
- if (leaf == NULL_INDEX) {
- // In this case, the key is greater than all keys in the tree.
- leaf = self.max_leaf_index;
- let current = self.nodes.borrow(leaf).parent;
- while (current != NULL_INDEX) {
- let current_node = self.nodes.borrow_mut(current);
-
- let last_value = current_node.children.new_end_iter().iter_prev(¤t_node.children).iter_remove(&mut current_node.children);
- current_node.children.add(key, last_value);
- current = current_node.parent;
- }
- };
-
- self.add_at(leaf, key, new_leaf_child(value), allow_overwrite)
- }
-
/// Removes the entry from BigOrderedMap and returns the value which `key` maps to.
/// Aborts if there is no entry for `key`.
public fun remove(self: &mut BigOrderedMap, key: &K): V {
let iter = self.find(key);
- assert!(!is_end_iter(self, &iter), E_INTERNAL);
+ assert!(!iter.iter_is_end(self), error::invalid_argument(EKEY_NOT_FOUND));
let Child::Leaf {
value,
@@ -193,29 +191,7 @@ module aptos_std::big_ordered_map {
value
}
- ///////////////
- // Accessors //
- ///////////////
-
- // Returns true iff the iterator is a begin iterator.
- public fun is_begin_iter(tree: &BigOrderedMap, iter: &Iterator): bool {
- if (iter is Iterator::End) {
- tree.is_empty()
- } else {
- (iter.node_index == tree.min_leaf_index && iter.child_iter.iter_is_begin_from_non_empty())
- }
- }
-
- // Returns true iff the iterator is an end iterator.
- public fun is_end_iter(_tree: &BigOrderedMap, iter: &Iterator): bool {
- iter is Iterator::End
- }
-
- /// Returns the key of the given iterator.
- public fun iter_get_key(self: &Iterator): &K {
- assert!(!(self is Iterator::End), E_INVALID_PARAMETER);
- &self.key
- }
+ // ============================= Accessors ================================
/// Returns an iterator pointing to the first element that is greater or equal to the provided
/// key, or an end iterator if such element doesn't exist.
@@ -226,10 +202,10 @@ module aptos_std::big_ordered_map {
};
let node = self.nodes.borrow(leaf);
- assert!(node.is_leaf, E_INTERNAL);
+ assert!(node.is_leaf, error::invalid_state(EINTERNAL_INVARIANT_BROKEN));
let child_lower_bound = node.children.lower_bound(key);
- if (child_lower_bound.iter_is_end()) {
+ if (child_lower_bound.iter_is_end(&node.children)) {
self.new_end_iter()
} else {
let iter_key = *child_lower_bound.iter_borrow_key(&node.children);
@@ -241,7 +217,7 @@ module aptos_std::big_ordered_map {
/// iterator if the key is not found.
public fun find(self: &BigOrderedMap, key: &K): Iterator {
let lower_bound = self.lower_bound(key);
- if (is_end_iter(self, &lower_bound)) {
+ if (lower_bound.iter_is_end(self)) {
lower_bound
} else if (&lower_bound.key == key) {
lower_bound
@@ -250,10 +226,10 @@ module aptos_std::big_ordered_map {
}
}
- /// Returns true iff the key exists in the tree.
+ /// Returns true iff the key exists in the map.
public fun contains(self: &BigOrderedMap, key: &K): bool {
let lower_bound = self.lower_bound(key);
- if (is_end_iter(self, &lower_bound)) {
+ if (lower_bound.iter_is_end(self)) {
false
} else if (&lower_bound.key == key) {
true
@@ -266,19 +242,22 @@ module aptos_std::big_ordered_map {
public fun borrow(self: &BigOrderedMap, key: K): &V {
let iter = self.find(&key);
- assert!(is_end_iter(self, &iter), E_INVALID_PARAMETER);
+ assert!(iter.iter_is_end(self), error::invalid_argument(EKEY_NOT_FOUND));
let children = &self.nodes.borrow(iter.node_index).children;
&iter.child_iter.iter_borrow(children).value
}
- /// Returns a mutable reference to the element with its key at the given index, aborts if the key is not found.
- public fun borrow_mut(self: &mut BigOrderedMap, key: K): &mut V {
- let iter = self.find(&key);
+ // TODO: Add back for fixed sized structs only.
+ // /// Returns a mutable reference to the element with its key at the given index, aborts if the key is not found.
+ // public fun borrow_mut(self: &mut BigOrderedMap, key: K): &mut V {
+ // let iter = self.find(&key);
+ //
+ // assert!(iter.iter_is_end(self), error::invalid_argument(EKEY_NOT_FOUND));
+ // let children = &mut self.nodes.borrow_mut(iter.node_index).children;
+ // &mut iter.child_iter.iter_borrow_mut(children).value
+ // }
- assert!(is_end_iter(self, &iter), E_INVALID_PARAMETER);
- let children = &mut self.nodes.borrow_mut(iter.node_index).children;
- &mut iter.child_iter.iter_borrow_mut(children).value
- }
+ // ========================= Iterator functions ===========================
/// Return the begin iterator.
public fun new_begin_iter(self: &BigOrderedMap): Iterator {
@@ -287,7 +266,7 @@ module aptos_std::big_ordered_map {
};
let node = self.nodes.borrow(self.min_leaf_index);
- assert!(!node.children.is_empty(), E_INTERNAL);
+ assert!(!node.children.is_empty(), error::invalid_state(EINTERNAL_INVARIANT_BROKEN));
let begin_child_iter = node.children.new_begin_iter();
let begin_child_key = *begin_child_iter.iter_borrow_key(&node.children);
new_iter(self.min_leaf_index, begin_child_iter, begin_child_key)
@@ -298,53 +277,73 @@ module aptos_std::big_ordered_map {
Iterator::End
}
+ // Returns true iff the iterator is a begin iterator.
+ public fun iter_is_begin(self: &Iterator, map: &BigOrderedMap): bool {
+ if (self is Iterator::End) {
+ map.is_empty()
+ } else {
+ (self.node_index == map.min_leaf_index && self.child_iter.iter_is_begin_from_non_empty())
+ }
+ }
+
+ // Returns true iff the iterator is an end iterator.
+ public fun iter_is_end(self: &Iterator, _map: &BigOrderedMap): bool {
+ self is Iterator::End
+ }
+
+ /// Returns the key of the given iterator.
+ public fun iter_get_key(self: &Iterator): &K {
+ assert!(!(self is Iterator::End), error::invalid_argument(EITER_OUT_OF_BOUNDS));
+ &self.key
+ }
+
/// Returns the next iterator, or none if already at the end iterator.
- /// Requires the tree is not changed after the input iterator is generated.
- public fun next_iter(tree: &BigOrderedMap, iter: Iterator): Iterator {
- assert!(!(iter is Iterator::End), E_INVALID_PARAMETER);
+ /// Requires the map is not changed after the input iterator is generated.
+ public fun iter_next(self: Iterator, map: &BigOrderedMap): Iterator {
+ assert!(!(self is Iterator::End), error::invalid_argument(EITER_OUT_OF_BOUNDS));
- let node_index = iter.node_index;
- let node = tree.nodes.borrow(node_index);
+ let node_index = self.node_index;
+ let node = map.nodes.borrow(node_index);
- let child_iter = iter.child_iter.iter_next(&node.children);
- if (!child_iter.iter_is_end()) {
+ let child_iter = self.child_iter.iter_next(&node.children);
+ if (!child_iter.iter_is_end(&node.children)) {
let iter_key = *child_iter.iter_borrow_key(&node.children);
return new_iter(node_index, child_iter, iter_key);
};
let next_index = node.next;
if (next_index != NULL_INDEX) {
- let next_node = tree.nodes.borrow(next_index);
+ let next_node = map.nodes.borrow(next_index);
let child_iter = next_node.children.new_begin_iter();
- assert!(!iter.child_iter.iter_is_end(), E_INTERNAL);
+ assert!(!child_iter.iter_is_end(&next_node.children), error::invalid_state(EINTERNAL_INVARIANT_BROKEN));
let iter_key = *child_iter.iter_borrow_key(&next_node.children);
return new_iter(next_index, child_iter, iter_key);
};
- new_end_iter(tree)
+ new_end_iter(map)
}
/// Returns the previous iterator, or none if already at the begin iterator.
- /// Requires the tree is not changed after the input iterator is generated.
- public fun prev_iter(tree: &BigOrderedMap, iter: Iterator): Iterator {
- let prev_index = if (iter is Iterator::End) {
- tree.max_leaf_index
+ /// Requires the map is not changed after the input iterator is generated.
+ public fun iter_prev(self: Iterator, map: &BigOrderedMap): Iterator {
+ let prev_index = if (self is Iterator::End) {
+ map.max_leaf_index
} else {
- let node_index = iter.node_index;
- let node = tree.nodes.borrow(node_index);
+ let node_index = self.node_index;
+ let node = map.nodes.borrow(node_index);
- if (!iter.child_iter.iter_is_begin(&node.children)) {
- let child_iter = iter.child_iter.iter_prev(&node.children);
+ if (!self.child_iter.iter_is_begin(&node.children)) {
+ let child_iter = self.child_iter.iter_prev(&node.children);
let key = *child_iter.iter_borrow_key(&node.children);
return new_iter(node_index, child_iter, key);
};
node.prev
};
- assert!(prev_index != NULL_INDEX, E_INTERNAL);
+ assert!(prev_index != NULL_INDEX, error::invalid_argument(EITER_OUT_OF_BOUNDS));
- let prev_node = tree.nodes.borrow(prev_index);
+ let prev_node = map.nodes.borrow(prev_index);
let prev_children = &prev_node.children;
let child_iter = prev_children.new_end_iter().iter_prev(prev_children);
@@ -352,23 +351,47 @@ module aptos_std::big_ordered_map {
new_iter(prev_index, child_iter, iter_key)
}
- //////////////////////////////
- // Internal Implementations //
- //////////////////////////////
+ // ====================== Internal Implementations ========================
- fun init_max_degrees(self: &mut BigOrderedMap, key: &K, value: &V) {
- if (self.inner_max_degree == 0 || self.leaf_max_degree == 0) {
- let key_size = bcs::serialized_size(key);
+ fun add_or_upsert_impl(self: &mut BigOrderedMap, key: K, value: V, allow_overwrite: bool): Option> {
+ // TODO cache the check if K and V have constant size
+ // if (self.inner_max_degree == 0 || self.leaf_max_degree == 0) {
+ self.validate_size_and_init_max_degrees(&key, &value);
+ // };
- if (self.inner_max_degree == 0) {
- self.inner_max_degree = max(min(MAX_DEGREE, DEFAULT_TARGET_NODE_SIZE / key_size), DEFAULT_INNER_MIN_DEGREE as u64) as u16;
- };
+ let leaf = self.find_leaf(&key);
- if (self.leaf_max_degree == 0) {
- let value_size = bcs::serialized_size(value);
- self.leaf_max_degree = max(min(MAX_DEGREE, DEFAULT_TARGET_NODE_SIZE / (key_size + value_size)), DEFAULT_LEAF_MIN_DEGREE as u64) as u16;
- };
+ if (leaf == NULL_INDEX) {
+ // In this case, the key is greater than all keys in the map.
+ leaf = self.max_leaf_index;
+ let current = self.nodes.borrow(leaf).parent;
+ while (current != NULL_INDEX) {
+ let current_node = self.nodes.borrow_mut(current);
+
+ let last_value = current_node.children.new_end_iter().iter_prev(¤t_node.children).iter_remove(&mut current_node.children);
+ current_node.children.add(key, last_value);
+ current = current_node.parent;
+ }
};
+
+ self.add_at(leaf, key, new_leaf_child(value), allow_overwrite)
+ }
+
+ fun validate_size_and_init_max_degrees(self: &mut BigOrderedMap, key: &K, value: &V) {
+ let key_size = bcs::serialized_size(key);
+ let value_size = bcs::serialized_size(value);
+ let entry_size = key_size + value_size;
+
+ if (self.inner_max_degree == 0) {
+ self.inner_max_degree = max(min(MAX_DEGREE, DEFAULT_TARGET_NODE_SIZE / key_size), DEFAULT_INNER_MIN_DEGREE as u64) as u16;
+ };
+
+ if (self.leaf_max_degree == 0) {
+ self.leaf_max_degree = max(min(MAX_DEGREE, DEFAULT_TARGET_NODE_SIZE / (key_size + value_size)), DEFAULT_LEAF_MIN_DEGREE as u64) as u16;
+ };
+
+ assert!(key_size * (self.inner_max_degree as u64) <= MAX_NODE_BYTES, error::invalid_argument(EARGUMENT_BYTES_TOO_LARGE));
+ assert!(entry_size * (self.leaf_max_degree as u64) <= MAX_NODE_BYTES, error::invalid_argument(EARGUMENT_BYTES_TOO_LARGE));
}
fun destroy_inner_child(self: Child) {
@@ -379,7 +402,7 @@ module aptos_std::big_ordered_map {
fun destroy_empty_node(self: Node) {
let Node { children, is_leaf: _, parent: _, prev: _, next: _ } = self;
- assert!(children.is_empty(), E_TREE_NOT_EMPTY);
+ assert!(children.is_empty(), error::invalid_argument(EMAP_NOT_EMPTY));
children.destroy_empty();
}
@@ -432,7 +455,7 @@ module aptos_std::big_ordered_map {
};
let children = &node.children;
let child_iter = children.lower_bound(key);
- if (child_iter.iter_is_end()) {
+ if (child_iter.iter_is_end(children)) {
return NULL_INDEX;
} else {
current = child_iter.iter_borrow(children).node_index;
@@ -466,17 +489,17 @@ module aptos_std::big_ordered_map {
let result = children.upsert(key, child);
if (node.is_leaf) {
- assert!(allow_overwrite || result.is_none(), EKEY_ALREADY_EXISTS);
+ assert!(allow_overwrite || result.is_none(), error::invalid_argument(EKEY_ALREADY_EXISTS));
return result;
} else {
- assert!(!allow_overwrite && result.is_none(), E_INTERNAL);
+ assert!(!allow_overwrite && result.is_none(), error::invalid_state(EINTERNAL_INVARIANT_BROKEN));
return result;
};
};
if (allow_overwrite) {
let iter = children.find(&key);
- if (!iter.iter_is_end()) {
+ if (!iter.iter_is_end(children)) {
return option::some(iter.iter_replace(children, child));
}
}
@@ -513,10 +536,10 @@ module aptos_std::big_ordered_map {
let target_size = (max_degree + 1) / 2;
children.add(key, child);
- let new_node_children = children.split_off(target_size);
+ let new_node_children = children.trim(target_size);
- assert!(children.length() <= max_degree, E_INTERNAL);
- assert!(new_node_children.length() <= max_degree, E_INTERNAL);
+ assert!(children.length() <= max_degree, error::invalid_state(EINTERNAL_INVARIANT_BROKEN));
+ assert!(new_node_children.length() <= max_degree, error::invalid_state(EINTERNAL_INVARIANT_BROKEN));
let right_node = new_node_with_children(*is_leaf, parent_index, new_node_children);
@@ -562,14 +585,8 @@ module aptos_std::big_ordered_map {
}
fun remove_at(self: &mut BigOrderedMap, node_index: u64, key: &K): Child {
- // aptos_std::debug::print(&std::string::utf8(b"remove_at"));
- // aptos_std::debug::print(&node_index);
- // aptos_std::debug::print(key);
-
let old_child = {
let node = self.nodes.borrow_mut(node_index);
- // aptos_std::debug::print(&std::string::utf8(b"node, borrowed"));
- // aptos_std::debug::print(node);
let children = &mut node.children;
let current_size = children.length();
@@ -614,7 +631,7 @@ module aptos_std::big_ordered_map {
};
if (max_key_updated) {
- assert!(current_size >= 1, E_INTERNAL);
+ assert!(current_size >= 1, error::invalid_state(EINTERNAL_INVARIANT_BROKEN));
let parent = node.parent;
@@ -628,11 +645,9 @@ module aptos_std::big_ordered_map {
old_child
};
- // We need to update tree beyond the current node
+ // We need to update map beyond the current node
let (node_slot, node) = self.nodes.remove_and_reserve(node_index);
- // aptos_std::debug::print(&std::string::utf8(b"node, removed and reserved"));
- // aptos_std::debug::print(&node);
let prev = node.prev;
let next = node.next;
@@ -649,17 +664,12 @@ module aptos_std::big_ordered_map {
brother_index = prev;
};
let (brother_slot, brother_node) = self.nodes.remove_and_reserve(brother_index);
- // aptos_std::debug::print(&std::string::utf8(b"brother, removed and reserved"));
- // aptos_std::debug::print(&brother_node);
let brother_children = &mut brother_node.children;
if ((brother_children.length() - 1) * 2 >= max_degree) {
- // aptos_std::debug::print(&std::string::utf8(b"The brother node has enough elements, borrow an element from the brother node."));
// The brother node has enough elements, borrow an element from the brother node.
if (brother_index == next) {
- // aptos_std::debug::print(&std::string::utf8(b"brother_index == next. Moving from brother."));
-
let old_max_key = *children.new_end_iter().iter_prev(children).iter_borrow_key(children);
let brother_begin_iter = brother_children.new_begin_iter();
let borrowed_max_key = *brother_begin_iter.iter_borrow_key(brother_children);
@@ -668,14 +678,9 @@ module aptos_std::big_ordered_map {
self.nodes.borrow_mut(borrowed_element.node_index).parent = node_index;
};
- // aptos_std::debug::print(&borrowed_max_key);
- // aptos_std::debug::print(&old_max_key);
-
children.add(borrowed_max_key, borrowed_element);
self.update_key(parent, &old_max_key, borrowed_max_key);
} else {
- // aptos_std::debug::print(&std::string::utf8(b"brother_index != next. Moving from brother"));
-
let brother_end_iter = brother_children.new_end_iter().iter_prev(brother_children);
let borrowed_max_key = *brother_end_iter.iter_borrow_key(brother_children);
let borrowed_element = brother_end_iter.iter_remove(brother_children);
@@ -684,8 +689,6 @@ module aptos_std::big_ordered_map {
self.nodes.borrow_mut(borrowed_element.node_index).parent = node_index;
};
- // aptos_std::debug::print(&borrowed_max_key);
-
children.add(borrowed_max_key, borrowed_element);
self.update_key(parent, &borrowed_max_key, *brother_children.new_end_iter().iter_prev(brother_children).iter_borrow_key(brother_children));
};
@@ -695,12 +698,8 @@ module aptos_std::big_ordered_map {
return old_child;
};
- // aptos_std::debug::print(&std::string::utf8(b"The brother node doesn't have enough elements to borrow, merge with the brother node."));
-
// The brother node doesn't have enough elements to borrow, merge with the brother node.
if (brother_index == next) {
- // aptos_std::debug::print(&std::string::utf8(b"brother_index == next"));
-
if (!is_leaf) {
children.for_each_ref(|_key, child| {
self.nodes.borrow_mut(child.node_index).parent = brother_index;
@@ -720,12 +719,6 @@ module aptos_std::big_ordered_map {
self.nodes.borrow_mut(node.prev).next = brother_index;
};
- // aptos_std::debug::print(&std::string::utf8(b"keeping node"));
- // aptos_std::debug::print(&brother_slot);
- // aptos_std::debug::print(&node);
- // aptos_std::debug::print(&std::string::utf8(b"freeing node"));
- // aptos_std::debug::print(&node_slot);
-
self.nodes.fill_reserved_slot(brother_slot, node);
self.nodes.free_reserved_slot(node_slot);
if (self.min_leaf_index == node_index) {
@@ -736,8 +729,6 @@ module aptos_std::big_ordered_map {
destroy_inner_child(self.remove_at(parent, &key_to_remove));
};
} else {
- // aptos_std::debug::print(&std::string::utf8(b"brother_index != next"));
-
if (!is_leaf) {
brother_children.for_each_ref(|_key, child| {
self.nodes.borrow_mut(child.node_index).parent = node_index;
@@ -758,12 +749,6 @@ module aptos_std::big_ordered_map {
self.nodes.borrow_mut(brother_node.prev).next = node_index;
};
- // aptos_std::debug::print(&std::string::utf8(b"keeping node"));
- // aptos_std::debug::print(&node_slot);
- // aptos_std::debug::print(&brother_node);
- // aptos_std::debug::print(&std::string::utf8(b"freeing node"));
- // aptos_std::debug::print(&brother_slot);
-
self.nodes.fill_reserved_slot(node_slot, brother_node);
self.nodes.free_reserved_slot(brother_slot);
if (self.min_leaf_index == brother_index) {
@@ -796,14 +781,23 @@ module aptos_std::big_ordered_map {
}
}
+ /// Returns true iff the BigOrderedMap is empty.
+ fun is_empty(self: &BigOrderedMap): bool {
+ let node = self.nodes.borrow(self.min_leaf_index);
+
+ node.children.is_empty()
+ }
+
+ // ============================= Tests ====================================
+
#[test_only]
- fun print_tree(self: &BigOrderedMap) {
+ fun print_map(self: &BigOrderedMap) {
aptos_std::debug::print(self);
- self.print_tree_for_node(self.root_index, 0);
+ self.print_map_for_node(self.root_index, 0);
}
#[test_only]
- fun print_tree_for_node(self: &BigOrderedMap, node_index: u64, level: u64) {
+ fun print_map_for_node(self: &BigOrderedMap, node_index: u64, level: u64) {
let node = self.nodes.borrow(node_index);
aptos_std::debug::print(&level);
@@ -812,30 +806,19 @@ module aptos_std::big_ordered_map {
if (!node.is_leaf) {
node.children.for_each_ref(|_key, node| {
- self.print_tree_for_node(node.node_index, level + 1);
+ self.print_map_for_node(node.node_index, level + 1);
});
};
}
- /// Returns true iff the BigOrderedMap is empty.
- fun is_empty(self: &BigOrderedMap): bool {
- let node = self.nodes.borrow(self.min_leaf_index);
-
- node.children.is_empty()
- }
-
- ///////////
- // Tests //
- ///////////
-
#[test_only]
fun destroy(self: BigOrderedMap) {
let it = new_begin_iter(&self);
- while (!is_end_iter(&self, &it)) {
+ while (!it.iter_is_end(&self)) {
remove(&mut self, it.iter_get_key());
- assert!(is_end_iter(&self, &find(&self, it.iter_get_key())), E_INTERNAL);
+ assert!(find(&self, it.iter_get_key()).iter_is_end(&self), error::invalid_state(EINTERNAL_INVARIANT_BROKEN));
it = new_begin_iter(&self);
- self.validate_tree();
+ self.validate_map();
};
self.destroy_empty();
@@ -846,26 +829,26 @@ module aptos_std::big_ordered_map {
let expected_num_elements = self.length();
let num_elements = 0;
let it = new_begin_iter(self);
- while (!is_end_iter(self, &it)) {
+ while (!it.iter_is_end(self)) {
num_elements = num_elements + 1;
- it = next_iter(self, it);
+ it = it.iter_next(self);
};
- assert!(num_elements == expected_num_elements, E_INTERNAL);
+ assert!(num_elements == expected_num_elements, error::invalid_state(EINTERNAL_INVARIANT_BROKEN));
let num_elements = 0;
let it = new_end_iter(self);
- while (!is_begin_iter(self, &it)) {
- it = prev_iter(self, it);
+ while (!it.iter_is_begin(self)) {
+ it = it.iter_prev(self);
num_elements = num_elements + 1;
};
- assert!(num_elements == expected_num_elements, E_INTERNAL);
+ assert!(num_elements == expected_num_elements, error::invalid_state(EINTERNAL_INVARIANT_BROKEN));
let it = new_end_iter(self);
- if (!is_begin_iter(self, &it)) {
- it = prev_iter(self, it);
- assert!(it.node_index == self.max_leaf_index, E_INTERNAL);
+ if (!it.iter_is_begin(self)) {
+ it = it.iter_prev(self);
+ assert!(it.node_index == self.max_leaf_index, error::invalid_state(EINTERNAL_INVARIANT_BROKEN));
} else {
- assert!(expected_num_elements == 0, E_INTERNAL);
+ assert!(expected_num_elements == 0, error::invalid_state(EINTERNAL_INVARIANT_BROKEN));
};
}
@@ -873,14 +856,14 @@ module aptos_std::big_ordered_map {
fun validate_subtree(self: &BigOrderedMap, node_index: u64, expected_lower_bound_key: Option, expected_max_key: Option, expected_parent: u64) {
let node = self.nodes.borrow(node_index);
let len = node.children.length();
- assert!(len <= self.get_max_degree(node.is_leaf), E_INTERNAL);
+ assert!(len <= self.get_max_degree(node.is_leaf), error::invalid_state(EINTERNAL_INVARIANT_BROKEN));
if (node_index != self.root_index) {
- assert!(len >= 1, E_INTERNAL);
- assert!(len * 2 >= self.get_max_degree(node.is_leaf) || node_index == self.root_index, E_INTERNAL);
+ assert!(len >= 1, error::invalid_state(EINTERNAL_INVARIANT_BROKEN));
+ assert!(len * 2 >= self.get_max_degree(node.is_leaf) || node_index == self.root_index, error::invalid_state(EINTERNAL_INVARIANT_BROKEN));
};
- assert!(node.parent == expected_parent, E_INTERNAL);
+ assert!(node.parent == expected_parent, error::invalid_state(EINTERNAL_INVARIANT_BROKEN));
node.children.validate_ordered();
@@ -889,48 +872,48 @@ module aptos_std::big_ordered_map {
if (!node.is_leaf) {
self.validate_subtree(child.node_index, previous_max_key, option::some(*key), node_index);
} else {
- assert!((child is Child::Leaf), E_INTERNAL);
+ assert!((child is Child::Leaf), error::invalid_state(EINTERNAL_INVARIANT_BROKEN));
};
previous_max_key = option::some(*key);
});
if (option::is_some(&expected_max_key)) {
let expected_max_key = option::extract(&mut expected_max_key);
- assert!(&expected_max_key == node.children.new_end_iter().iter_prev(&node.children).iter_borrow_key(&node.children), E_INTERNAL);
+ assert!(&expected_max_key == node.children.new_end_iter().iter_prev(&node.children).iter_borrow_key(&node.children), error::invalid_state(EINTERNAL_INVARIANT_BROKEN));
};
if (option::is_some(&expected_lower_bound_key)) {
let expected_lower_bound_key = option::extract(&mut expected_lower_bound_key);
- assert!(cmp::compare(&expected_lower_bound_key, node.children.new_begin_iter().iter_borrow_key(&node.children)).is_less_than(), E_INTERNAL);
+ assert!(cmp::compare(&expected_lower_bound_key, node.children.new_begin_iter().iter_borrow_key(&node.children)).is_less_than(), error::invalid_state(EINTERNAL_INVARIANT_BROKEN));
};
}
#[test_only]
- fun validate_tree(self: &BigOrderedMap) {
+ fun validate_map(self: &BigOrderedMap) {
self.validate_subtree(self.root_index, option::none(), option::none(), NULL_INDEX);
self.validate_iteration();
}
#[test]
- fun test_smart_tree() {
+ fun test_small_example() {
let map = new_with_config(5, 3, true, 2);
- map.print_tree(); map.validate_tree();
- add(&mut map, 1, 1); map.print_tree(); map.validate_tree();
- add(&mut map, 2, 2); map.print_tree(); map.validate_tree();
- let r1 = upsert(&mut map, 3, 3); map.print_tree(); map.validate_tree();
- assert!(r1 == option::none(), E_INTERNAL);
- add(&mut map, 4, 4); map.print_tree(); map.validate_tree();
- let r2 = upsert(&mut map, 4, 8); map.print_tree(); map.validate_tree();
- assert!(r2 == option::some(4), E_INTERNAL);
- add(&mut map, 5, 5); map.print_tree(); map.validate_tree();
- add(&mut map, 6, 6); map.print_tree(); map.validate_tree();
-
- remove(&mut map, &5); map.print_tree(); map.validate_tree();
- remove(&mut map, &4); map.print_tree(); map.validate_tree();
- remove(&mut map, &1); map.print_tree(); map.validate_tree();
- remove(&mut map, &3); map.print_tree(); map.validate_tree();
- remove(&mut map, &2); map.print_tree(); map.validate_tree();
- remove(&mut map, &6); map.print_tree(); map.validate_tree();
+ map.print_map(); map.validate_map();
+ add(&mut map, 1, 1); map.print_map(); map.validate_map();
+ add(&mut map, 2, 2); map.print_map(); map.validate_map();
+ let r1 = upsert(&mut map, 3, 3); map.print_map(); map.validate_map();
+ assert!(r1 == option::none(), 1);
+ add(&mut map, 4, 4); map.print_map(); map.validate_map();
+ let r2 = upsert(&mut map, 4, 8); map.print_map(); map.validate_map();
+ assert!(r2 == option::some(4), 2);
+ add(&mut map, 5, 5); map.print_map(); map.validate_map();
+ add(&mut map, 6, 6); map.print_map(); map.validate_map();
+
+ remove(&mut map, &5); map.print_map(); map.validate_map();
+ remove(&mut map, &4); map.print_map(); map.validate_map();
+ remove(&mut map, &1); map.print_map(); map.validate_map();
+ remove(&mut map, &3); map.print_map(); map.validate_map();
+ remove(&mut map, &2); map.print_map(); map.validate_map();
+ remove(&mut map, &6); map.print_map(); map.validate_map();
destroy_empty(map);
}
@@ -941,42 +924,42 @@ module aptos_std::big_ordered_map {
for (i in 0..50) {
map.upsert(i, i);
- map.validate_tree();
+ map.validate_map();
};
for (i in 0..40) {
map.remove(&i);
- map.validate_tree();
+ map.validate_map();
};
for (i in 50..100) {
map.upsert(i, i);
- map.validate_tree();
+ map.validate_map();
};
for (i in 50..90) {
map.remove(&i);
- map.validate_tree();
+ map.validate_map();
};
for (i in 100..150) {
map.upsert(i, i);
- map.validate_tree();
+ map.validate_map();
};
for (i in 100..150) {
map.remove(&i);
- map.validate_tree();
+ map.validate_map();
};
for (i in 40..50) {
map.remove(&i);
- map.validate_tree();
+ map.validate_map();
};
for (i in 90..100) {
map.remove(&i);
- map.validate_tree();
+ map.validate_map();
};
destroy_empty(map);
@@ -995,10 +978,10 @@ module aptos_std::big_ordered_map {
let it = new_begin_iter(&map);
let i = 0;
- while (!is_end_iter(&map, &it)) {
- assert!(i == it.key, E_INTERNAL);
+ while (!it.iter_is_end(&map)) {
+ assert!(i == it.key, i);
i = i + 1;
- it = next_iter(&map, it);
+ it = it.iter_next(&map);
};
destroy(map);
@@ -1022,13 +1005,13 @@ module aptos_std::big_ordered_map {
while (i < len) {
let element = data.borrow(i);
let it = find(&map, element);
- assert!(!is_end_iter(&map, &it), E_INTERNAL);
- assert!(it.iter_get_key() == element, E_INTERNAL);
+ assert!(!it.iter_is_end(&map), i);
+ assert!(it.iter_get_key() == element, i);
i = i + 1;
};
- assert!(is_end_iter(&map, &find(&map, &4)), E_INTERNAL);
- assert!(is_end_iter(&map, &find(&map, &9)), E_INTERNAL);
+ assert!(find(&map, &4).iter_is_end(&map), 0);
+ assert!(find(&map, &9).iter_is_end(&map), 1);
destroy(map);
}
@@ -1051,76 +1034,80 @@ module aptos_std::big_ordered_map {
while (i < len) {
let element = *data.borrow(i);
let it = lower_bound(&map, &element);
- assert!(!is_end_iter(&map, &it), E_INTERNAL);
- assert!(it.key == element, E_INTERNAL);
+ assert!(!it.iter_is_end(&map), i);
+ assert!(it.key == element, i);
i = i + 1;
};
- assert!(lower_bound(&map, &0).key == 1, E_INTERNAL);
- assert!(lower_bound(&map, &4).key == 5, E_INTERNAL);
- assert!(lower_bound(&map, &9).key == 10, E_INTERNAL);
- assert!(is_end_iter(&map, &lower_bound(&map, &13)), E_INTERNAL);
+ assert!(lower_bound(&map, &0).key == 1, 0);
+ assert!(lower_bound(&map, &4).key == 5, 1);
+ assert!(lower_bound(&map, &9).key == 10, 2);
+ assert!(lower_bound(&map, &13).iter_is_end(&map), 3);
remove(&mut map, &3);
- assert!(lower_bound(&map, &3).key == 5, E_INTERNAL);
+ assert!(lower_bound(&map, &3).key == 5, 4);
remove(&mut map, &5);
- assert!(lower_bound(&map, &3).key == 6, E_INTERNAL);
- assert!(lower_bound(&map, &4).key == 6, E_INTERNAL);
+ assert!(lower_bound(&map, &3).key == 6, 5);
+ assert!(lower_bound(&map, &4).key == 6, 6);
destroy(map);
}
#[test_only]
fun test_large_data_set_helper(inner_max_degree: u16, leaf_max_degree: u16, reuse_slots: bool) {
+ use std::vector;
+
let map = new_with_config(inner_max_degree, leaf_max_degree, reuse_slots, if (reuse_slots) {4} else {0});
let data = ordered_map::large_dataset();
let shuffled_data = ordered_map::large_dataset_shuffled();
- let i = 0;
let len = data.length();
- while (i < len) {
+ for (i in 0..len) {
let element = *data.borrow(i);
map.upsert(element, element);
- map.validate_tree();
- i = i + 1;
+ map.validate_map();
};
- let i = 0;
- while (i < len) {
+ for (i in 0..len) {
let element = shuffled_data.borrow(i);
let it = map.find(element);
- assert!(!is_end_iter(&map, &it), E_INTERNAL);
- assert!(it.iter_get_key() == element, E_INTERNAL);
+ assert!(!it.iter_is_end(&map), i);
+ assert!(it.iter_get_key() == element, i);
// aptos_std::debug::print(&it);
- let it_next = next_iter(&map, it);
+ let it_next = it.iter_next(&map);
let it_after = map.lower_bound(&(*element + 1));
// aptos_std::debug::print(&it_next);
// aptos_std::debug::print(&it_after);
// aptos_std::debug::print(&std::string::utf8(b"bla"));
- assert!(it_next == it_after, E_INTERNAL);
-
- i = i + 1;
+ assert!(it_next == it_after, i);
};
-
- let i = 0;
- while (i < len) {
+ let removed = vector::empty();
+ for (i in 0..len) {
let element = shuffled_data.borrow(i);
- map.remove(element);
- map.validate_map();
- i = i + 1;
+ if (!removed.contains(element)) {
+ removed.push_back(*element);
+ map.remove(element);
+ map.validate_map();
+ } else {
+ assert!(!map.contains(element));
+ };
};
map.destroy_empty();
}
#[test]
- fun test_large_data_set_order_5() {
+ fun test_large_data_set_order_5_false() {
test_large_data_set_helper(5, 5, false);
+ }
+
+ #[test]
+ fun test_large_data_set_order_5_true() {
test_large_data_set_helper(5, 5, true);
}
@@ -1135,56 +1122,92 @@ module aptos_std::big_ordered_map {
}
#[test]
- fun test_large_data_set_order_4_4() {
+ fun test_large_data_set_order_4_4_false() {
test_large_data_set_helper(4, 4, false);
+ }
+
+ #[test]
+ fun test_large_data_set_order_4_4_true() {
test_large_data_set_helper(4, 4, true);
}
#[test]
- fun test_large_data_set_order_6() {
+ fun test_large_data_set_order_6_false() {
test_large_data_set_helper(6, 6, false);
+ }
+
+ #[test]
+ fun test_large_data_set_order_6_true() {
test_large_data_set_helper(6, 6, true);
}
#[test]
- fun test_large_data_set_order_6_3() {
+ fun test_large_data_set_order_6_3_false() {
test_large_data_set_helper(6, 3, false);
+ }
+
+ #[test]
+ fun test_large_data_set_order_6_3_true() {
test_large_data_set_helper(6, 3, true);
}
#[test]
- fun test_large_data_set_order_4_6() {
+ fun test_large_data_set_order_4_6_false() {
test_large_data_set_helper(4, 6, false);
+ }
+
+ #[test]
+ fun test_large_data_set_order_4_6_true() {
test_large_data_set_helper(4, 6, true);
}
#[test]
- fun test_large_data_set_order_16() {
+ fun test_large_data_set_order_16_false() {
test_large_data_set_helper(16, 16, false);
+ }
+
+ #[test]
+ fun test_large_data_set_order_16_true() {
test_large_data_set_helper(16, 16, true);
}
#[test]
- fun test_large_data_set_order_31() {
+ fun test_large_data_set_order_31_false() {
test_large_data_set_helper(31, 31, false);
+ }
+
+ #[test]
+ fun test_large_data_set_order_31_true() {
test_large_data_set_helper(31, 31, true);
}
#[test]
- fun test_large_data_set_order_31_3() {
+ fun test_large_data_set_order_31_3_false() {
test_large_data_set_helper(31, 3, false);
+ }
+
+ #[test]
+ fun test_large_data_set_order_31_3_true() {
test_large_data_set_helper(31, 3, true);
}
#[test]
- fun test_large_data_set_order_31_5() {
+ fun test_large_data_set_order_31_5_false() {
test_large_data_set_helper(31, 5, false);
+ }
+
+ #[test]
+ fun test_large_data_set_order_31_5_true() {
test_large_data_set_helper(31, 5, true);
}
#[test]
- fun test_large_data_set_order_32() {
+ fun test_large_data_set_order_32_false() {
test_large_data_set_helper(32, 32, false);
+ }
+
+ #[test]
+ fun test_large_data_set_order_32_true() {
test_large_data_set_helper(32, 32, true);
}
}
diff --git a/aptos-move/framework/aptos-stdlib/sources/data_structures/storage_slots_allocator.move b/aptos-move/framework/aptos-stdlib/sources/data_structures/storage_slots_allocator.move
index bdce7fb38235a9..2c08f05d670fe1 100644
--- a/aptos-move/framework/aptos-stdlib/sources/data_structures/storage_slots_allocator.move
+++ b/aptos-move/framework/aptos-stdlib/sources/data_structures/storage_slots_allocator.move
@@ -42,6 +42,7 @@ module aptos_std::storage_slots_allocator {
new_slot_index: u64,
reuse_head_index: u64,
},
+ // TODO implement variant that inlines first node?
}
struct ReservedSlot {
@@ -64,7 +65,7 @@ module aptos_std::storage_slots_allocator {
for (i in 0..num_to_preallocate) {
let slot_index = self.next_slot_index();
- self.push_to_reuse_queue(slot_index);
+ self.push_to_reuse_queue_if_enabled(slot_index);
};
self
@@ -89,7 +90,7 @@ module aptos_std::storage_slots_allocator {
public fun remove(self: &mut StorageSlotsAllocator, slot_index: u64): T {
let Link::Occupied { value } = self.slots.remove(slot_index);
- self.push_to_reuse_queue(slot_index);
+ self.push_to_reuse_queue_if_enabled(slot_index);
value
}
@@ -126,7 +127,11 @@ module aptos_std::storage_slots_allocator {
self.slot_index
}
- // splitting add into getting ReservedSlot, and then inserting it later
+ // We also provide here operations where `add()` is split into `reserve_slot`,
+ // and then doing fill_reserved_slot later.
+
+ // Similarly we have `remove_and_reserve`, and then `fill_reserved_slot` later.
+
public fun reserve_slot(self: &mut StorageSlotsAllocator): ReservedSlot {
if (self is StorageSlotsAllocator::Reuse) {
let slot_index = self.reuse_head_index;
@@ -158,10 +163,10 @@ module aptos_std::storage_slots_allocator {
public fun free_reserved_slot(self: &mut StorageSlotsAllocator, slot: ReservedSlot) {
let ReservedSlot { slot_index } = slot;
- self.push_to_reuse_queue(slot_index);
+ self.push_to_reuse_queue_if_enabled(slot_index);
}
- fun push_to_reuse_queue(self: &mut StorageSlotsAllocator, slot_index: u64) {
+ fun push_to_reuse_queue_if_enabled(self: &mut StorageSlotsAllocator, slot_index: u64) {
if (self is StorageSlotsAllocator::Reuse) {
self.slots.add(slot_index, Link::Vacant { next: self.reuse_head_index });
self.reuse_head_index = slot_index;