diff --git a/aptos-move/framework/aptos-framework/doc/big_ordered_map.md b/aptos-move/framework/aptos-framework/doc/big_ordered_map.md index 83415aaeb25cd3..935161481639a1 100644 --- a/aptos-move/framework/aptos-framework/doc/big_ordered_map.md +++ b/aptos-move/framework/aptos-framework/doc/big_ordered_map.md @@ -32,19 +32,34 @@ allowing cleaner iterator APIs. - [Enum `BigOrderedMap`](#0x1_big_ordered_map_BigOrderedMap) - [Constants](#@Constants_0) - [Function `new`](#0x1_big_ordered_map_new) +- [Function `new_with_type_size_hints`](#0x1_big_ordered_map_new_with_type_size_hints) - [Function `new_with_config`](#0x1_big_ordered_map_new_with_config) - [Function `new_from`](#0x1_big_ordered_map_new_from) - [Function `destroy_empty`](#0x1_big_ordered_map_destroy_empty) - [Function `allocate_spare_slots`](#0x1_big_ordered_map_allocate_spare_slots) +- [Function `is_empty`](#0x1_big_ordered_map_is_empty) +- [Function `compute_length`](#0x1_big_ordered_map_compute_length) - [Function `add`](#0x1_big_ordered_map_add) - [Function `upsert`](#0x1_big_ordered_map_upsert) - [Function `remove`](#0x1_big_ordered_map_remove) - [Function `add_all`](#0x1_big_ordered_map_add_all) +- [Function `pop_front`](#0x1_big_ordered_map_pop_front) +- [Function `pop_back`](#0x1_big_ordered_map_pop_back) - [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 `borrow_front`](#0x1_big_ordered_map_borrow_front) +- [Function `borrow_back`](#0x1_big_ordered_map_borrow_back) +- [Function `prev_key`](#0x1_big_ordered_map_prev_key) +- [Function `next_key`](#0x1_big_ordered_map_next_key) +- [Function `to_ordered_map`](#0x1_big_ordered_map_to_ordered_map) +- [Function `keys`](#0x1_big_ordered_map_keys) +- [Function `for_each_and_clear`](#0x1_big_ordered_map_for_each_and_clear) +- [Function `for_each`](#0x1_big_ordered_map_for_each) +- [Function `for_each_ref`](#0x1_big_ordered_map_for_each_ref) +- [Function `destroy`](#0x1_big_ordered_map_destroy) - [Function `new_begin_iter`](#0x1_big_ordered_map_new_begin_iter) - [Function `new_end_iter`](#0x1_big_ordered_map_new_end_iter) - [Function `iter_is_begin`](#0x1_big_ordered_map_iter_is_begin) @@ -54,9 +69,7 @@ allowing cleaner iterator APIs. - [Function `iter_borrow_mut`](#0x1_big_ordered_map_iter_borrow_mut) - [Function `iter_next`](#0x1_big_ordered_map_iter_next) - [Function `iter_prev`](#0x1_big_ordered_map_iter_prev) -- [Function `for_each`](#0x1_big_ordered_map_for_each) -- [Function `for_each_ref`](#0x1_big_ordered_map_for_each_ref) -- [Function `destroy`](#0x1_big_ordered_map_destroy) +- [Function `for_each_leaf_node_ref`](#0x1_big_ordered_map_for_each_leaf_node_ref) - [Function `borrow_node`](#0x1_big_ordered_map_borrow_node) - [Function `borrow_node_mut`](#0x1_big_ordered_map_borrow_node_mut) - [Function `add_or_upsert_impl`](#0x1_big_ordered_map_add_or_upsert_impl) @@ -77,13 +90,9 @@ allowing cleaner iterator APIs. - [Function `add_at`](#0x1_big_ordered_map_add_at) - [Function `update_key`](#0x1_big_ordered_map_update_key) - [Function `remove_at`](#0x1_big_ordered_map_remove_at) -- [Function `length`](#0x1_big_ordered_map_length) -- [Function `length_for_node`](#0x1_big_ordered_map_length_for_node) -- [Function `is_empty`](#0x1_big_ordered_map_is_empty) - [Specification](#@Specification_1) - [Function `add_at`](#@Specification_1_add_at) - [Function `remove_at`](#@Specification_1_remove_at) - - [Function `length_for_node`](#@Specification_1_length_for_node)
use 0x1::bcs;
@@ -425,24 +434,6 @@ Map key is not found
 
 
 
-
-
-
-
-
const DEFAULT_INNER_MIN_DEGREE: u16 = 4;
-
- - - - - - - -
const DEFAULT_LEAF_MIN_DEGREE: u16 = 3;
-
- - - @@ -493,6 +484,24 @@ Map isn't empty + + + + +
const INNER_MIN_DEGREE: u16 = 4;
+
+ + + + + + + +
const LEAF_MIN_DEGREE: u16 = 3;
+
+ + + @@ -506,7 +515,7 @@ Map isn't empty -
const MAX_NODE_BYTES: u64 = 204800;
+
const MAX_NODE_BYTES: u64 = 409600;
 
@@ -539,6 +548,7 @@ it is required to use new_with_config, to explicitly select automatic or specifi
public fun new<K: store, V: store>(): BigOrderedMap<K, V> {
+    // Use new_with_type_size_hints or new_with_config if your types have variable sizes.
     assert!(
         bcs::constant_serialized_size<K>().is_some() && bcs::constant_serialized_size<V>().is_some(),
         error::invalid_argument(EINVALID_CONFIG_PARAMETER)
@@ -550,6 +560,49 @@ it is required to use new_with_config, to explicitly select automatic or specifi
 
 
 
+
+
+
+
+## Function `new_with_type_size_hints`
+
+Returns a new BigOrderedMap, configured based on passed key and value serialized size hints.
+
+
+
public fun new_with_type_size_hints<K: store, V: store>(avg_key_bytes: u64, max_key_bytes: u64, avg_value_bytes: u64, max_value_bytes: u64): big_ordered_map::BigOrderedMap<K, V>
+
+ + + +
+Implementation + + +
public fun new_with_type_size_hints<K: store, V: store>(avg_key_bytes: u64, max_key_bytes: u64, avg_value_bytes: u64, max_value_bytes: u64): BigOrderedMap<K, V> {
+    assert!(avg_key_bytes <= max_key_bytes, error::invalid_argument(EINVALID_CONFIG_PARAMETER));
+    assert!(avg_value_bytes <= max_value_bytes, error::invalid_argument(EINVALID_CONFIG_PARAMETER));
+
+    let inner_max_degree_from_avg = max(min(MAX_DEGREE, DEFAULT_TARGET_NODE_SIZE / avg_key_bytes), INNER_MIN_DEGREE as u64);
+    let inner_max_degree_from_max = MAX_NODE_BYTES / max_key_bytes;
+    assert!(inner_max_degree_from_max >= (INNER_MIN_DEGREE as u64), error::invalid_argument(EINVALID_CONFIG_PARAMETER));
+
+    let avg_entry_size = avg_key_bytes + avg_value_bytes;
+    let max_entry_size = max_key_bytes + max_value_bytes;
+
+    let leaf_max_degree_from_avg = max(min(MAX_DEGREE, DEFAULT_TARGET_NODE_SIZE / avg_entry_size), LEAF_MIN_DEGREE as u64);
+    let leaf_max_degree_from_max = MAX_NODE_BYTES / max_entry_size;
+    assert!(leaf_max_degree_from_max >= (INNER_MIN_DEGREE as u64), error::invalid_argument(EINVALID_CONFIG_PARAMETER));
+
+    new_with_config(
+        min(inner_max_degree_from_avg, inner_max_degree_from_max) as u16,
+        min(leaf_max_degree_from_avg, leaf_max_degree_from_max) as u16,
+        false,
+    )
+}
+
+ + +
@@ -565,6 +618,10 @@ Sizes of all elements must respect (or their additions will be rejected): If keys or values have variable size, and first element could be non-representative in size (i.e. smaller than future ones), it is important to compute and pass inner_max_degree and leaf_max_degree based on the largest element you want to be able to insert. +reuse_slots means that removing elements from the map doesn't free the storage slots and returns the refund. +Together with allocate_spare_slots, it allows to preallocate slots and have inserts have predictable gas costs. +(otherwise, inserts that require map to add new nodes, cost significantly more, compared to the rest) +
public fun new_with_config<K: store, V: store>(inner_max_degree: u16, leaf_max_degree: u16, reuse_slots: bool): big_ordered_map::BigOrderedMap<K, V>
 
@@ -576,8 +633,8 @@ it is important to compute and pass inner_max_degree and leaf_max_degree based o
public fun new_with_config<K: store, V: store>(inner_max_degree: u16, leaf_max_degree: u16, reuse_slots: bool): BigOrderedMap<K, V> {
-    assert!(inner_max_degree == 0 || (inner_max_degree >= DEFAULT_INNER_MIN_DEGREE && (inner_max_degree as u64) <= MAX_DEGREE), error::invalid_argument(EINVALID_CONFIG_PARAMETER));
-    assert!(leaf_max_degree == 0 || (leaf_max_degree >= DEFAULT_LEAF_MIN_DEGREE && (leaf_max_degree as u64) <= MAX_DEGREE), error::invalid_argument(EINVALID_CONFIG_PARAMETER));
+    assert!(inner_max_degree == 0 || (inner_max_degree >= INNER_MIN_DEGREE && (inner_max_degree as u64) <= MAX_DEGREE), error::invalid_argument(EINVALID_CONFIG_PARAMETER));
+    assert!(leaf_max_degree == 0 || (leaf_max_degree >= LEAF_MIN_DEGREE && (leaf_max_degree as u64) <= MAX_DEGREE), error::invalid_argument(EINVALID_CONFIG_PARAMETER));
 
     // Assert that storage_slots_allocator special indices are aligned:
     assert!(storage_slots_allocator::is_null_index(NULL_INDEX), error::invalid_state(EINTERNAL_INVARIANT_BROKEN));
@@ -686,6 +743,62 @@ and better bounded/fair.
 
 
 
+
+
+
+
+## Function `is_empty`
+
+Returns true iff the BigOrderedMap is empty.
+
+
+
public fun is_empty<K: store, V: store>(self: &big_ordered_map::BigOrderedMap<K, V>): bool
+
+ + + +
+Implementation + + +
public fun is_empty<K: store, V: store>(self: &BigOrderedMap<K, V>): bool {
+    let node = self.borrow_node(self.min_leaf_index);
+    node.children.is_empty()
+}
+
+ + + +
+ + + +## Function `compute_length` + +Returns the number of elements in the BigOrderedMap. +This is an expensive function, as it goes through all the leaves to compute it. + + +
public fun compute_length<K: store, V: store>(self: &big_ordered_map::BigOrderedMap<K, V>): u64
+
+ + + +
+Implementation + + +
public fun compute_length<K: store, V: store>(self: &BigOrderedMap<K, V>): u64 {
+    let size = 0;
+    self.for_each_leaf_node_ref(|node| {
+        size += node.children.length();
+    });
+    size
+}
+
+ + +
@@ -819,6 +932,60 @@ Aborts with EKEY_ALREADY_EXISTS if key already exist, or duplicate keys are pass + + + + +## Function `pop_front` + + + +
public fun pop_front<K: copy, drop, store, V: store>(self: &mut big_ordered_map::BigOrderedMap<K, V>): (K, V)
+
+ + + +
+Implementation + + +
public fun pop_front<K: drop + copy + store, V: store>(self: &mut BigOrderedMap<K, V>): (K, V) {
+    let it = self.new_begin_iter();
+    let k = *it.iter_borrow_key();
+    let v = self.remove(&k);
+    (k, v)
+}
+
+ + + +
+ + + +## Function `pop_back` + + + +
public fun pop_back<K: copy, drop, store, V: store>(self: &mut big_ordered_map::BigOrderedMap<K, V>): (K, V)
+
+ + + +
+Implementation + + +
public fun pop_back<K: drop + copy + store, V: store>(self: &mut BigOrderedMap<K, V>): (K, V) {
+    let it = self.new_end_iter().iter_prev(self);
+    let k = *it.iter_borrow_key();
+    let v = self.remove(&k);
+    (k, v)
+}
+
+ + +
@@ -946,12 +1113,7 @@ Returns a reference to the element with its key, aborts if the key is not found. let iter = self.find(key); assert!(!iter.iter_is_end(self), error::invalid_argument(EKEY_NOT_FOUND)); - // TODO cannot call iter_borrow, because reference checks assume return has reference to iter that is being destroyed - // iter.iter_borrow(self) - - assert!(!iter.iter_is_end(self), error::invalid_argument(EITER_OUT_OF_BOUNDS)); - let children = &self.borrow_node(iter.node_index).children; - &iter.child_iter.iter_borrow(children).value + iter.iter_borrow(self) }
@@ -964,6 +1126,9 @@ Returns a reference to the element with its key, aborts if the key is not found. ## Function `borrow_mut` Returns a mutable reference to the element with its key at the given index, aborts if the key is not found. +Aborts with EBORROW_MUT_REQUIRES_CONSTANT_KV_SIZE if KV size doesn't have constant size, +because if it doesn't we cannot assert invariants on the size. +In case of variable size, use either borrow, copy then upsert, or remove and add instead of mutable borrow.
public fun borrow_mut<K: copy, drop, store, V: store>(self: &mut big_ordered_map::BigOrderedMap<K, V>, key: &K): &mut V
@@ -984,6 +1149,303 @@ Returns a mutable reference to the element with its key at the given index, abor
 
 
 
+
+
+
+
+## Function `borrow_front`
+
+
+
+
public fun borrow_front<K: copy, drop, store, V: store>(self: &big_ordered_map::BigOrderedMap<K, V>): (K, &V)
+
+ + + +
+Implementation + + +
public fun borrow_front<K: drop + copy + store, V: store>(self: &BigOrderedMap<K, V>): (K, &V) {
+    let it = self.new_begin_iter();
+    let key = *it.iter_borrow_key();
+    (key, it.iter_borrow(self))
+}
+
+ + + +
+ + + +## Function `borrow_back` + + + +
public fun borrow_back<K: copy, drop, store, V: store>(self: &big_ordered_map::BigOrderedMap<K, V>): (K, &V)
+
+ + + +
+Implementation + + +
public fun borrow_back<K: drop + copy + store, V: store>(self: &BigOrderedMap<K, V>): (K, &V) {
+    let it = self.new_end_iter().iter_prev(self);
+    let key = *it.iter_borrow_key();
+    (key, it.iter_borrow(self))
+}
+
+ + + +
+ + + +## Function `prev_key` + + + +
public fun prev_key<K: copy, drop, store, V: store>(self: &big_ordered_map::BigOrderedMap<K, V>, key: &K): option::Option<K>
+
+ + + +
+Implementation + + +
public fun prev_key<K: drop + copy + store, V: store>(self: &BigOrderedMap<K, V>, key: &K): Option<K> {
+    let it = self.lower_bound(key);
+    if (it.iter_is_begin(self)) {
+        option::none()
+    } else {
+        option::some(*it.iter_prev(self).iter_borrow_key())
+    }
+}
+
+ + + +
+ + + +## Function `next_key` + + + +
public fun next_key<K: copy, drop, store, V: store>(self: &big_ordered_map::BigOrderedMap<K, V>, key: &K): option::Option<K>
+
+ + + +
+Implementation + + +
public fun next_key<K: drop + copy + store, V: store>(self: &BigOrderedMap<K, V>, key: &K): Option<K> {
+    let it = self.lower_bound(key);
+    if (it.iter_is_end(self)) {
+        option::none()
+    } else {
+        let cur_key = it.iter_borrow_key();
+        if (key == cur_key) {
+            let it = it.iter_next(self);
+            if (it.iter_is_end(self)) {
+                option::none()
+            } else {
+                option::some(*it.iter_borrow_key())
+            }
+        } else {
+            option::some(*cur_key)
+        }
+    }
+}
+
+ + + +
+ + + +## Function `to_ordered_map` + +Convert a BigOrderedMap to an OrderedMap, which is supposed to be called mostly by view functions to get an atomic +view of the whole map. +Disclaimer: This function may be costly as the BigOrderedMap may be huge in size. Use it at your own discretion. + + +
public fun to_ordered_map<K: copy, drop, store, V: copy, store>(self: &big_ordered_map::BigOrderedMap<K, V>): ordered_map::OrderedMap<K, V>
+
+ + + +
+Implementation + + +
public fun to_ordered_map<K: drop + copy + store, V: copy + store>(self: &BigOrderedMap<K, V>): OrderedMap<K, V> {
+    let result = ordered_map::new();
+    self.for_each_ref(|k, v| {
+        result.new_end_iter().iter_add(&mut result, *k, *v);
+    });
+    result
+}
+
+ + + +
+ + + +## Function `keys` + +Get all keys. + +For a large enough BigOrderedMap this function will fail due to execution gas limits, +use iterartor or next_key/prev_key to iterate over across portion of the map. + + +
public fun keys<K: copy, drop, store, V: copy, store>(self: &big_ordered_map::BigOrderedMap<K, V>): vector<K>
+
+ + + +
+Implementation + + +
public fun keys<K: store + copy + drop, V: store + copy>(self: &BigOrderedMap<K, V>): vector<K> {
+    let result = vector[];
+    self.for_each_ref(|k, _v| {
+        result.push_back(*k);
+    });
+    result
+}
+
+ + + +
+ + + +## Function `for_each_and_clear` + +Apply the function to each element in the vector, consuming it, leaving the map empty. + + +
public fun for_each_and_clear<K: copy, drop, store, V: store>(self: &mut big_ordered_map::BigOrderedMap<K, V>, f: |(K, V)|)
+
+ + + +
+Implementation + + +
public inline fun for_each_and_clear<K: drop + copy + store, V: store>(self: &mut BigOrderedMap<K, V>, f: |K, V|) {
+    // TODO - this can be done more efficiently, by destroying the leaves directly
+    // but that requires more complicated code and testing.
+    while (!self.is_empty()) {
+        let (k, v) = self.pop_front();
+        f(k, v);
+    };
+}
+
+ + + +
+ + + +## Function `for_each` + +Apply the function to each element in the vector, consuming it, and consuming the map + + +
public fun for_each<K: copy, drop, store, V: store>(self: big_ordered_map::BigOrderedMap<K, V>, f: |(K, V)|)
+
+ + + +
+Implementation + + +
public inline fun for_each<K: drop + copy + store, V: store>(self: BigOrderedMap<K, V>, f: |K, V|) {
+    // TODO - this can be done more efficiently, by destroying the leaves directly
+    // but that requires more complicated code and testing.
+    self.for_each_and_clear(|k, v| f(k, v));
+    destroy_empty(self)
+}
+
+ + + +
+ + + +## Function `for_each_ref` + +Apply the function to a reference of each element in the vector. + + +
public fun for_each_ref<K: copy, drop, store, V: store>(self: &big_ordered_map::BigOrderedMap<K, V>, f: |(&K, &V)|)
+
+ + + +
+Implementation + + +
public inline fun for_each_ref<K: drop + copy + store, V: store>(self: &BigOrderedMap<K, V>, f: |&K, &V|) {
+    self.for_each_leaf_node_ref(|node| {
+        node.children.for_each_ref(|k: &K, v: &Child<V>| {
+            f(k, &v.value);
+        });
+    })
+}
+
+ + + +
+ + + +## Function `destroy` + +Destroy a map, by destroying elements individually. + + +
public fun destroy<K: copy, drop, store, V: store>(self: big_ordered_map::BigOrderedMap<K, V>, dv: |V|)
+
+ + + +
+Implementation + + +
public inline fun destroy<K: drop + copy + store, V: store>(self: BigOrderedMap<K, V>, dv: |V|) {
+    for_each(self, |_k, v| {
+        dv(v);
+    });
+}
+
+ + +
@@ -1133,7 +1595,7 @@ Aborts with EITER_OUT_OF_BOUNDS if iterator is pointing to the end. Note: Requires that the map is not changed after the input iterator is generated. -
public(friend) fun iter_borrow<K: store, V: store>(self: &big_ordered_map::IteratorPtr<K>, map: &big_ordered_map::BigOrderedMap<K, V>): &V
+
public(friend) fun iter_borrow<K: drop, store, V: store>(self: big_ordered_map::IteratorPtr<K>, map: &big_ordered_map::BigOrderedMap<K, V>): &V
 
@@ -1142,10 +1604,11 @@ Note: Requires that the map is not changed after the input iterator is generated Implementation -
public(friend) fun iter_borrow<K: store, V: store>(self: &IteratorPtr<K>, map: &BigOrderedMap<K, V>): &V {
+
public(friend) fun iter_borrow<K: drop + store, V: store>(self: IteratorPtr<K>, map: &BigOrderedMap<K, V>): &V {
     assert!(!self.iter_is_end(map), error::invalid_argument(EITER_OUT_OF_BOUNDS));
-    let children = &map.borrow_node(self.node_index).children;
-    &self.child_iter.iter_borrow(children).value
+    let IteratorPtr::Some { node_index, child_iter, key: _ } = self;
+    let children = &map.borrow_node(node_index).children;
+    &child_iter.iter_borrow(children).value
 }
 
@@ -1160,11 +1623,13 @@ Note: Requires that the map is not changed after the input iterator is generated Mutably borrows the value iterator points to. Aborts with EITER_OUT_OF_BOUNDS if iterator is pointing to the end. Aborts with EBORROW_MUT_REQUIRES_CONSTANT_KV_SIZE if KV size doesn't have constant size, -because if it doesn't - we need to call upsert to be able to check size invariants after modification. +because if it doesn't we cannot assert invariants on the size. +In case of variable size, use either borrow, copy then upsert, or remove and add instead of mutable borrow. + Note: Requires that the map is not changed after the input iterator is generated. -
public(friend) fun iter_borrow_mut<K: store, V: store>(self: &big_ordered_map::IteratorPtr<K>, map: &mut big_ordered_map::BigOrderedMap<K, V>): &mut V
+
public(friend) fun iter_borrow_mut<K: drop, store, V: store>(self: big_ordered_map::IteratorPtr<K>, map: &mut big_ordered_map::BigOrderedMap<K, V>): &mut V
 
@@ -1173,11 +1638,12 @@ Note: Requires that the map is not changed after the input iterator is generated Implementation -
public(friend) fun iter_borrow_mut<K: store, V: store>(self: &IteratorPtr<K>, map: &mut BigOrderedMap<K, V>): &mut V {
+
public(friend) fun iter_borrow_mut<K: drop + store, V: store>(self: IteratorPtr<K>, map: &mut BigOrderedMap<K, V>): &mut V {
     assert!(map.constant_kv_size, error::invalid_argument(EBORROW_MUT_REQUIRES_CONSTANT_KV_SIZE));
     assert!(!self.iter_is_end(map), error::invalid_argument(EITER_OUT_OF_BOUNDS));
-    let children = &mut map.borrow_node_mut(self.node_index).children;
-    &mut self.child_iter.iter_borrow_mut(children).value
+    let IteratorPtr::Some { node_index, child_iter, key: _ } = self;
+    let children = &mut map.borrow_node_mut(node_index).children;
+    &mut child_iter.iter_borrow_mut(children).value
 }
 
@@ -1285,14 +1751,13 @@ Requires the map is not changed after the input iterator is generated. - + -## Function `for_each` +## Function `for_each_leaf_node_ref` -Apply the function to each element in the vector, consuming it. -
public(friend) fun for_each<K: copy, drop, store, V: store>(self: big_ordered_map::BigOrderedMap<K, V>, f: |(K, V)|)
+
fun for_each_leaf_node_ref<K: store, V: store>(self: &big_ordered_map::BigOrderedMap<K, V>, f: |&big_ordered_map::Node<K, V>|)
 
@@ -1301,73 +1766,14 @@ Apply the function to each element in the vector, consuming it. Implementation -
public(friend) inline fun for_each<K: drop + copy + store, V: store>(self: BigOrderedMap<K, V>, f: |K, V|) {
-    // TODO - this can be done more efficiently, by destroying the leaves directly
-    // but that requires more complicated code and testing.
-    let it = self.new_begin_iter();
-    while (!it.iter_is_end(&self)) {
-        let k = *it.iter_borrow_key();
-        let v = self.remove(&k);
-        f(k, v);
-        it = self.new_begin_iter();
-    };
-    destroy_empty(self)
-}
-
- - +
inline fun for_each_leaf_node_ref<K: store, V: store>(self: &BigOrderedMap<K, V>, f: |&Node<K, V>|) {
+    let cur_node_index = self.min_leaf_index;
 
-
-
-
-
-## Function `for_each_ref`
-
-Apply the function to a reference of each element in the vector.
-
-
-
public(friend) fun for_each_ref<K: copy, drop, store, V: store>(self: &big_ordered_map::BigOrderedMap<K, V>, f: |(&K, &V)|)
-
- - - -
-Implementation - - -
public(friend) inline fun for_each_ref<K: drop + copy + store, V: store>(self: &BigOrderedMap<K, V>, f: |&K, &V|) {
-    let it = self.new_begin_iter();
-    while (!it.iter_is_end(self)) {
-        f(it.iter_borrow_key(), it.iter_borrow(self));
-        it = it.iter_next(self);
-    };
-}
-
- - - -
- - - -## Function `destroy` - -Destroy a map, by destroying elements individually. - - -
public(friend) fun destroy<K: copy, drop, store, V: store>(self: big_ordered_map::BigOrderedMap<K, V>, dv: |V|)
-
- - - -
-Implementation - - -
public(friend) inline fun destroy<K: drop + copy + store, V: store>(self: BigOrderedMap<K, V>, dv: |V|) {
-    for_each(self, |_k, v| {
-        dv(v);
-    });
+    while (cur_node_index != NULL_INDEX) {
+        let node = self.borrow_node(cur_node_index);
+        f(node);
+        cur_node_index = node.next;
+    }
 }
 
@@ -1457,7 +1863,7 @@ Borrow a node mutably, given an index. Works for both root (i.e. inline) node an // (optimizes out borrowing and path creation in `find_leaf_path`) if (self.root.is_leaf) { let children = &mut self.root.children; - let degree = children.length(); + let degree = children.length(); if (degree < (self.leaf_max_degree as u64)) { let result = children.upsert(key, new_leaf_child(value)); @@ -1571,11 +1977,11 @@ Borrow a node mutably, given an index. Works for both root (i.e. inline) node an 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; + self.inner_max_degree = max(min(MAX_DEGREE, DEFAULT_TARGET_NODE_SIZE / key_size), 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 / entry_size), DEFAULT_LEAF_MIN_DEGREE as u64) as u16; + self.leaf_max_degree = max(min(MAX_DEGREE, DEFAULT_TARGET_NODE_SIZE / entry_size), LEAF_MIN_DEGREE as u64) as u16; }; // Make sure that no nodes can exceed the upper size limit. @@ -1966,14 +2372,14 @@ If allow_overwrite is not set, function will abort if keyfun add_at<K: drop + copy + store, V: store>(self: &mut BigOrderedMap<K, V>, path_to_node: vector<u64>, key: K, child: Child<V>, allow_overwrite: bool): Option<Child<V>> { // Last node in the path is one where we need to add the child to. - let node_index = path_to_node.pop_back(); + let node_index = path_to_node.pop_back(); { // First check if we can perform this operation, without changing structure of the tree (i.e. without adding any nodes). // For that we can just borrow the single node let node = self.borrow_node_mut(node_index); let children = &mut node.children; - let degree = children.length(); + let degree = children.length(); // Compute directly, as we cannot use get_max_degree(), as self is already mutably borrowed. let max_degree = if (node.is_leaf) { @@ -2090,8 +2496,8 @@ If allow_overwrite is not set, function will abort if keyadd(key, child); let right_node_children = left_children.trim(target_size); - assert!(left_children.length() <= max_degree, error::invalid_state(EINTERNAL_INVARIANT_BROKEN)); - assert!(right_node_children.length() <= max_degree, error::invalid_state(EINTERNAL_INVARIANT_BROKEN)); + assert!(left_children.length() <= max_degree, error::invalid_state(EINTERNAL_INVARIANT_BROKEN)); + assert!(right_node_children.length() <= max_degree, error::invalid_state(EINTERNAL_INVARIANT_BROKEN)); let right_node = new_node_with_children(is_leaf, right_node_children); @@ -2149,7 +2555,7 @@ Given a path to node (excluding the node itself), which is currently stored unde
fun update_key<K: drop + copy + store, V: store>(self: &mut BigOrderedMap<K, V>, path_to_node: vector<u64>, old_key: &K, new_key: K) {
     while (!path_to_node.is_empty()) {
-        let node_index = path_to_node.pop_back();
+        let node_index = path_to_node.pop_back();
         let node = self.borrow_node_mut(node_index);
         let children = &mut node.children;
         children.replace_key_inplace(old_key, new_key);
@@ -2183,7 +2589,7 @@ Given a path to node (excluding the node itself), which is currently stored unde
 
 
fun remove_at<K: drop + copy + store, V: store>(self: &mut BigOrderedMap<K, V>, path_to_node: vector<u64>, key: &K): Child<V> {
     // Last node in the path is one where we need to remove the child from.
-    let node_index = path_to_node.pop_back();
+    let node_index = path_to_node.pop_back();
     let old_child = {
         // First check if we can perform this operation, without changing structure of the tree (i.e. without rebalancing any nodes).
 
@@ -2200,7 +2606,7 @@ Given a path to node (excluding the node itself), which is currently stored unde
 
             assert!(path_to_node.is_empty(), error::invalid_state(EINTERNAL_INVARIANT_BROKEN));
 
-            if (!is_leaf && children.length() == 1) {
+            if (!is_leaf && children.length() == 1) {
                 // If root is not leaf, but has a single child, promote only child to root,
                 // and drop current root. Since root is stored directly in the resource, we
                 // "move" the child into the root.
@@ -2226,7 +2632,7 @@ Given a path to node (excluding the node itself), which is currently stored unde
         } else {
             self.inner_max_degree as u64
         };
-        let degree = children.length();
+        let degree = children.length();
 
         // See if the node is big enough, or we need to merge it with another node on this level.
         let big_enough = degree * 2 >= max_degree;
@@ -2262,8 +2668,8 @@ Given a path to node (excluding the node itself), which is currently stored unde
 
     // index of the node we will rebalance with.
     let sibling_index = {
-        let parent_children = &self.borrow_node(*path_to_node.borrow(path_to_node.length() - 1)).children;
-        assert!(parent_children.length() >= 2, error::invalid_state(EINTERNAL_INVARIANT_BROKEN));
+        let parent_children = &self.borrow_node(*path_to_node.borrow(path_to_node.length() - 1)).children;
+        assert!(parent_children.length() >= 2, error::invalid_state(EINTERNAL_INVARIANT_BROKEN));
         // If we are the largest node from the parent, we merge with the `prev`
         // (which is then guaranteed to have the same parent, as any node has >1 children),
         // otherwise we merge with `next`.
@@ -2280,7 +2686,7 @@ Given a path to node (excluding the node itself), which is currently stored unde
     assert!(is_leaf == sibling_node.is_leaf, error::invalid_state(EINTERNAL_INVARIANT_BROKEN));
     let sibling_children = &mut sibling_node.children;
 
-    if ((sibling_children.length() - 1) * 2 >= max_degree) {
+    if ((sibling_children.length() - 1) * 2 >= max_degree) {
         // The sibling node has enough elements, we can just borrow an element from the sibling node.
         if (sibling_index == next) {
             // if sibling is the node with larger keys, we remove a child from the start
@@ -2374,92 +2780,6 @@ Given a path to node (excluding the node itself), which is currently stored unde
 
 
 
-
- - - -## Function `length` - -Returns the number of elements in the BigOrderedMap. - - -
fun length<K: store, V: store>(self: &big_ordered_map::BigOrderedMap<K, V>): u64
-
- - - -
-Implementation - - -
fun length<K: store, V: store>(self: &BigOrderedMap<K, V>): u64 {
-    self.length_for_node(ROOT_INDEX)
-}
-
- - - -
- - - -## Function `length_for_node` - - - -
fun length_for_node<K: store, V: store>(self: &big_ordered_map::BigOrderedMap<K, V>, node_index: u64): u64
-
- - - -
-Implementation - - -
fun length_for_node<K: store, V: store>(self: &BigOrderedMap<K, V>, node_index: u64): u64 {
-    let node = self.borrow_node(node_index);
-    if (node.is_leaf) {
-        node.children.length()
-    } else {
-        let size = 0;
-
-        node.children.for_each_ref(|_key, child| {
-            size = size + self.length_for_node(child.node_index.stored_to_index());
-        });
-        size
-    }
-}
-
- - - -
- - - -## Function `is_empty` - -Returns true iff the BigOrderedMap is empty. - - -
fun is_empty<K: store, V: store>(self: &big_ordered_map::BigOrderedMap<K, V>): bool
-
- - - -
-Implementation - - -
fun is_empty<K: store, V: store>(self: &BigOrderedMap<K, V>): bool {
-    let node = self.borrow_node(self.min_leaf_index);
-
-    node.children.is_empty()
-}
-
- - -
@@ -2500,22 +2820,6 @@ Returns true iff the BigOrderedMap is empty. -
pragma opaque;
-
- - - - - -### Function `length_for_node` - - -
fun length_for_node<K: store, V: store>(self: &big_ordered_map::BigOrderedMap<K, V>, node_index: u64): u64
-
- - - -
pragma opaque;
 
diff --git a/aptos-move/framework/aptos-framework/doc/ordered_map.md b/aptos-move/framework/aptos-framework/doc/ordered_map.md index cd90763cdbfe86..81c6c5b1bdf8f5 100644 --- a/aptos-move/framework/aptos-framework/doc/ordered_map.md +++ b/aptos-move/framework/aptos-framework/doc/ordered_map.md @@ -49,6 +49,12 @@ allowing cleaner iterator APIs. - [Function `append_disjoint`](#0x1_ordered_map_append_disjoint) - [Function `append_impl`](#0x1_ordered_map_append_impl) - [Function `trim`](#0x1_ordered_map_trim) +- [Function `borrow_front`](#0x1_ordered_map_borrow_front) +- [Function `borrow_back`](#0x1_ordered_map_borrow_back) +- [Function `pop_front`](#0x1_ordered_map_pop_front) +- [Function `pop_back`](#0x1_ordered_map_pop_back) +- [Function `prev_key`](#0x1_ordered_map_prev_key) +- [Function `next_key`](#0x1_ordered_map_next_key) - [Function `lower_bound`](#0x1_ordered_map_lower_bound) - [Function `find`](#0x1_ordered_map_find) - [Function `new_begin_iter`](#0x1_ordered_map_new_begin_iter) @@ -739,7 +745,7 @@ Takes all elements from other and adds them to self, r loop { let ord = cmp::compare(&self.entries[cur_i].key, &other_entries[other_i].key); if (ord.is_gt()) { - reverse_result.push_back(self.entries.pop_back()); + reverse_result.push_back(self.entries.pop_back()); if (cur_i == 0) { // make other_entries empty, and rest in entries. // TODO cannot use mem::swap until it is public/released @@ -753,10 +759,10 @@ Takes all elements from other and adds them to self, r // is_lt or is_eq if (ord.is_eq()) { // we skip the entries one, and below put in the result one from other. - overwritten.push_back(self.entries.pop_back()); + overwritten.push_back(self.entries.pop_back()); }; - reverse_result.push_back(other_entries.pop_back()); + reverse_result.push_back(other_entries.pop_back()); if (other_i == 0) { other_entries.destroy_empty(); break; @@ -805,6 +811,174 @@ After the call, the original map will be left containing the elements [0, at). + + + + +## Function `borrow_front` + + + +
public fun borrow_front<K, V>(self: &ordered_map::OrderedMap<K, V>): (&K, &V)
+
+ + + +
+Implementation + + +
public fun borrow_front<K, V>(self: &OrderedMap<K, V>): (&K, &V) {
+    let entry = self.entries.borrow(0);
+    (&entry.key, &entry.value)
+}
+
+ + + +
+ + + +## Function `borrow_back` + + + +
public fun borrow_back<K, V>(self: &ordered_map::OrderedMap<K, V>): (&K, &V)
+
+ + + +
+Implementation + + +
public fun borrow_back<K, V>(self: &OrderedMap<K, V>): (&K, &V) {
+    let entry = self.entries.borrow(self.entries.length() - 1);
+    (&entry.key, &entry.value)
+}
+
+ + + +
+ + + +## Function `pop_front` + + + +
public fun pop_front<K, V>(self: &mut ordered_map::OrderedMap<K, V>): (K, V)
+
+ + + +
+Implementation + + +
public fun pop_front<K, V>(self: &mut OrderedMap<K, V>): (K, V) {
+    let Entry { key, value } = self.entries.remove(0);
+    (key, value)
+}
+
+ + + +
+ + + +## Function `pop_back` + + + +
public fun pop_back<K, V>(self: &mut ordered_map::OrderedMap<K, V>): (K, V)
+
+ + + +
+Implementation + + +
public fun pop_back<K, V>(self: &mut OrderedMap<K, V>): (K, V) {
+    let Entry { key, value } = self.entries.pop_back();
+    (key, value)
+}
+
+ + + +
+ + + +## Function `prev_key` + + + +
public fun prev_key<K: copy, V>(self: &ordered_map::OrderedMap<K, V>, key: &K): option::Option<K>
+
+ + + +
+Implementation + + +
public fun prev_key<K: copy, V>(self: &OrderedMap<K, V>, key: &K): Option<K> {
+    let it = self.lower_bound(key);
+    if (it.iter_is_begin(self)) {
+        option::none()
+    } else {
+        option::some(*it.iter_prev(self).iter_borrow_key(self))
+    }
+}
+
+ + + +
+ + + +## Function `next_key` + + + +
public fun next_key<K: copy, V>(self: &ordered_map::OrderedMap<K, V>, key: &K): option::Option<K>
+
+ + + +
+Implementation + + +
public fun next_key<K: copy, V>(self: &OrderedMap<K, V>, key: &K): Option<K> {
+    let it = self.lower_bound(key);
+    if (it.iter_is_end(self)) {
+        option::none()
+    } else {
+        let cur_key = it.iter_borrow_key(self);
+        if (key == cur_key) {
+            let it = it.iter_next(self);
+            if (it.iter_is_end(self)) {
+                option::none()
+            } else {
+                option::some(*it.iter_borrow_key(self))
+            }
+        } else {
+            option::some(*cur_key)
+        }
+    }
+}
+
+ + +
@@ -1002,7 +1176,7 @@ Note: Requires that the map is not changed after the input iterator is generated Returns whether the iterator is a begin iterator. -
public fun iter_is_begin<K, V>(self: &ordered_map::IteratorPtr, map: &ordered_map::OrderedMap<K, V>): bool
+
public(friend) fun iter_is_begin<K, V>(self: &ordered_map::IteratorPtr, map: &ordered_map::OrderedMap<K, V>): bool
 
@@ -1011,7 +1185,7 @@ Returns whether the iterator is a begin iterator. Implementation -
public fun iter_is_begin<K, V>(self: &IteratorPtr, map: &OrderedMap<K, V>): bool {
+
public(friend) fun iter_is_begin<K, V>(self: &IteratorPtr, map: &OrderedMap<K, V>): bool {
     if (self is IteratorPtr::End) {
         map.is_empty()
     } else {
diff --git a/aptos-move/framework/aptos-framework/sources/datastructures/big_ordered_map.move b/aptos-move/framework/aptos-framework/sources/datastructures/big_ordered_map.move
index 6d1fd915b17278..d57f5aaba4d9a5 100644
--- a/aptos-move/framework/aptos-framework/sources/datastructures/big_ordered_map.move
+++ b/aptos-move/framework/aptos-framework/sources/datastructures/big_ordered_map.move
@@ -29,8 +29,6 @@ module aptos_std::big_ordered_map {
     use aptos_std::storage_slots_allocator::{Self, StorageSlotsAllocator, StoredSlot};
     use aptos_std::math64::{max, min};
 
-    friend aptos_framework::permissioned_signer;
-
     // Error constants shared with ordered_map (so try using same values)
 
     /// Map key already exists
@@ -60,13 +58,13 @@ module aptos_std::big_ordered_map {
     // Internal constants.
 
     const DEFAULT_TARGET_NODE_SIZE: u64 = 4096;
-    const DEFAULT_INNER_MIN_DEGREE: u16 = 4;
+    const INNER_MIN_DEGREE: u16 = 4;
     // We rely on 1 being valid size only for root node,
     // so this cannot be below 3 (unless that is changed)
-    const DEFAULT_LEAF_MIN_DEGREE: u16 = 3;
+    const LEAF_MIN_DEGREE: u16 = 3;
     const MAX_DEGREE: u64 = 4096;
 
-    const MAX_NODE_BYTES: u64 = 204800; // 200 KB, well bellow the max resource limit.
+    const MAX_NODE_BYTES: u64 = 409600; // 400 KB, bellow the max resource limit.
 
     // Constants aligned with storage_slots_allocator
     const NULL_INDEX: u64 = 0;
@@ -151,6 +149,7 @@ module aptos_std::big_ordered_map {
     /// Only allowed to be called with constant size types. For variable sized types,
     /// it is required to use new_with_config, to explicitly select automatic or specific degree selection.
     public fun new(): BigOrderedMap {
+        // Use new_with_type_size_hints or new_with_config if your types have variable sizes.
         assert!(
             bcs::constant_serialized_size().is_some() && bcs::constant_serialized_size().is_some(),
             error::invalid_argument(EINVALID_CONFIG_PARAMETER)
@@ -159,6 +158,29 @@ module aptos_std::big_ordered_map {
         new_with_config(0, 0, false)
     }
 
+    /// Returns a new BigOrderedMap, configured based on passed key and value serialized size hints.
+    public fun new_with_type_size_hints(avg_key_bytes: u64, max_key_bytes: u64, avg_value_bytes: u64, max_value_bytes: u64): BigOrderedMap {
+        assert!(avg_key_bytes <= max_key_bytes, error::invalid_argument(EINVALID_CONFIG_PARAMETER));
+        assert!(avg_value_bytes <= max_value_bytes, error::invalid_argument(EINVALID_CONFIG_PARAMETER));
+
+        let inner_max_degree_from_avg = max(min(MAX_DEGREE, DEFAULT_TARGET_NODE_SIZE / avg_key_bytes), INNER_MIN_DEGREE as u64);
+        let inner_max_degree_from_max = MAX_NODE_BYTES / max_key_bytes;
+        assert!(inner_max_degree_from_max >= (INNER_MIN_DEGREE as u64), error::invalid_argument(EINVALID_CONFIG_PARAMETER));
+
+        let avg_entry_size = avg_key_bytes + avg_value_bytes;
+        let max_entry_size = max_key_bytes + max_value_bytes;
+
+        let leaf_max_degree_from_avg = max(min(MAX_DEGREE, DEFAULT_TARGET_NODE_SIZE / avg_entry_size), LEAF_MIN_DEGREE as u64);
+        let leaf_max_degree_from_max = MAX_NODE_BYTES / max_entry_size;
+        assert!(leaf_max_degree_from_max >= (INNER_MIN_DEGREE as u64), error::invalid_argument(EINVALID_CONFIG_PARAMETER));
+
+        new_with_config(
+            min(inner_max_degree_from_avg, inner_max_degree_from_max) as u16,
+            min(leaf_max_degree_from_avg, leaf_max_degree_from_max) as u16,
+            false,
+        )
+    }
+
     /// Returns a new BigOrderedMap with the provided max degree consts (the maximum # of children a node can have, both inner and leaf).
     /// If 0 is passed, then it is dynamically computed based on size of first key and value.
     ///
@@ -167,9 +189,13 @@ module aptos_std::big_ordered_map {
     ///   `entry_size * leaf_max_degree <= MAX_NODE_BYTES`
     /// If keys or values have variable size, and first element could be non-representative in size (i.e. smaller than future ones),
     /// it is important to compute and pass inner_max_degree and leaf_max_degree based on the largest element you want to be able to insert.
+    ///
+    /// `reuse_slots` means that removing elements from the map doesn't free the storage slots and returns the refund.
+    /// Together with `allocate_spare_slots`, it allows to preallocate slots and have inserts have predictable gas costs.
+    /// (otherwise, inserts that require map to add new nodes, cost significantly more, compared to the rest)
     public fun new_with_config(inner_max_degree: u16, leaf_max_degree: u16, reuse_slots: bool): BigOrderedMap {
-        assert!(inner_max_degree == 0 || (inner_max_degree >= DEFAULT_INNER_MIN_DEGREE && (inner_max_degree as u64) <= MAX_DEGREE), error::invalid_argument(EINVALID_CONFIG_PARAMETER));
-        assert!(leaf_max_degree == 0 || (leaf_max_degree >= DEFAULT_LEAF_MIN_DEGREE && (leaf_max_degree as u64) <= MAX_DEGREE), error::invalid_argument(EINVALID_CONFIG_PARAMETER));
+        assert!(inner_max_degree == 0 || (inner_max_degree >= INNER_MIN_DEGREE && (inner_max_degree as u64) <= MAX_DEGREE), error::invalid_argument(EINVALID_CONFIG_PARAMETER));
+        assert!(leaf_max_degree == 0 || (leaf_max_degree >= LEAF_MIN_DEGREE && (leaf_max_degree as u64) <= MAX_DEGREE), error::invalid_argument(EINVALID_CONFIG_PARAMETER));
 
         // Assert that storage_slots_allocator special indices are aligned:
         assert!(storage_slots_allocator::is_null_index(NULL_INDEX), error::invalid_state(EINTERNAL_INVARIANT_BROKEN));
@@ -215,6 +241,22 @@ module aptos_std::big_ordered_map {
         self.nodes.allocate_spare_slots(num_to_allocate)
     }
 
+    /// Returns true iff the BigOrderedMap is empty.
+    public fun is_empty(self: &BigOrderedMap): bool {
+        let node = self.borrow_node(self.min_leaf_index);
+        node.children.is_empty()
+    }
+
+    /// Returns the number of elements in the BigOrderedMap.
+    /// This is an expensive function, as it goes through all the leaves to compute it.
+    public fun compute_length(self: &BigOrderedMap): u64 {
+        let size = 0;
+        self.for_each_leaf_node_ref(|node| {
+            size += node.children.length();
+        });
+        size
+    }
+
     // ======================= Section with Modifiers =========================
 
     /// Inserts the key/value into the BigOrderedMap.
@@ -270,6 +312,20 @@ module aptos_std::big_ordered_map {
         });
     }
 
+    public fun pop_front(self: &mut BigOrderedMap): (K, V) {
+        let it = self.new_begin_iter();
+        let k = *it.iter_borrow_key();
+        let v = self.remove(&k);
+        (k, v)
+    }
+
+    public fun pop_back(self: &mut BigOrderedMap): (K, V) {
+        let it = self.new_end_iter().iter_prev(self);
+        let k = *it.iter_borrow_key();
+        let v = self.remove(&k);
+        (k, v)
+    }
+
     // ============================= Accessors ================================
 
     /// Returns an iterator pointing to the first element that is greater or equal to the provided
@@ -322,20 +378,116 @@ module aptos_std::big_ordered_map {
         let iter = self.find(key);
         assert!(!iter.iter_is_end(self), error::invalid_argument(EKEY_NOT_FOUND));
 
-        // TODO cannot call iter_borrow, because reference checks assume return has reference to iter that is being destroyed
-        // iter.iter_borrow(self)
-
-        assert!(!iter.iter_is_end(self), error::invalid_argument(EITER_OUT_OF_BOUNDS));
-        let children = &self.borrow_node(iter.node_index).children;
-        &iter.child_iter.iter_borrow(children).value
+        iter.iter_borrow(self)
     }
 
     /// Returns a mutable reference to the element with its key at the given index, aborts if the key is not found.
+    /// Aborts with EBORROW_MUT_REQUIRES_CONSTANT_KV_SIZE if KV size doesn't have constant size,
+    /// because if it doesn't we cannot assert invariants on the size.
+    /// In case of variable size, use either `borrow`, `copy` then `upsert`, or `remove` and `add` instead of mutable borrow.
     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));
         iter.iter_borrow_mut(self)
     }
+    public fun borrow_front(self: &BigOrderedMap): (K, &V) {
+        let it = self.new_begin_iter();
+        let key = *it.iter_borrow_key();
+        (key, it.iter_borrow(self))
+    }
+
+    public fun borrow_back(self: &BigOrderedMap): (K, &V) {
+        let it = self.new_end_iter().iter_prev(self);
+        let key = *it.iter_borrow_key();
+        (key, it.iter_borrow(self))
+    }
+
+    public fun prev_key(self: &BigOrderedMap, key: &K): Option {
+        let it = self.lower_bound(key);
+        if (it.iter_is_begin(self)) {
+            option::none()
+        } else {
+            option::some(*it.iter_prev(self).iter_borrow_key())
+        }
+    }
+
+    public fun next_key(self: &BigOrderedMap, key: &K): Option {
+        let it = self.lower_bound(key);
+        if (it.iter_is_end(self)) {
+            option::none()
+        } else {
+            let cur_key = it.iter_borrow_key();
+            if (key == cur_key) {
+                let it = it.iter_next(self);
+                if (it.iter_is_end(self)) {
+                    option::none()
+                } else {
+                    option::some(*it.iter_borrow_key())
+                }
+            } else {
+                option::some(*cur_key)
+            }
+        }
+    }
+
+    // =========================== Views and Traversals ==============================
+
+    /// Convert a BigOrderedMap to an OrderedMap, which is supposed to be called mostly by view functions to get an atomic
+    /// view of the whole map.
+    /// Disclaimer: This function may be costly as the BigOrderedMap may be huge in size. Use it at your own discretion.
+    public fun to_ordered_map(self: &BigOrderedMap): OrderedMap {
+        let result = ordered_map::new();
+        self.for_each_ref(|k, v| {
+            result.new_end_iter().iter_add(&mut result, *k, *v);
+        });
+        result
+    }
+
+    /// Get all keys.
+    ///
+    /// For a large enough BigOrderedMap this function will fail due to execution gas limits,
+    /// use iterartor or next_key/prev_key to iterate over across portion of the map.
+    public fun keys(self: &BigOrderedMap): vector {
+        let result = vector[];
+        self.for_each_ref(|k, _v| {
+            result.push_back(*k);
+        });
+        result
+    }
+
+    /// Apply the function to each element in the vector, consuming it, leaving the map empty.
+    public inline fun for_each_and_clear(self: &mut BigOrderedMap, f: |K, V|) {
+        // TODO - this can be done more efficiently, by destroying the leaves directly
+        // but that requires more complicated code and testing.
+        while (!self.is_empty()) {
+            let (k, v) = self.pop_front();
+            f(k, v);
+        };
+    }
+
+    /// Apply the function to each element in the vector, consuming it, and consuming the map
+    public inline fun for_each(self: BigOrderedMap, f: |K, V|) {
+        // TODO - this can be done more efficiently, by destroying the leaves directly
+        // but that requires more complicated code and testing.
+        self.for_each_and_clear(|k, v| f(k, v));
+        destroy_empty(self)
+    }
+
+    /// Apply the function to a reference of each element in the vector.
+    public inline fun for_each_ref(self: &BigOrderedMap, f: |&K, &V|) {
+        self.for_each_leaf_node_ref(|node| {
+            node.children.for_each_ref(|k: &K, v: &Child| {
+                f(k, &v.value);
+            });
+        })
+    }
+
+    /// Destroy a map, by destroying elements individually.
+    public inline fun destroy(self: BigOrderedMap, dv: |V|) {
+        for_each(self, |_k, v| {
+            dv(v);
+        });
+    }
 
     // ========================= IteratorPtr functions ===========================
 
@@ -382,22 +534,26 @@ module aptos_std::big_ordered_map {
     /// Borrows the value given iterator points to.
     /// Aborts with EITER_OUT_OF_BOUNDS if iterator is pointing to the end.
     /// Note: Requires that the map is not changed after the input iterator is generated.
-    public(friend) fun iter_borrow(self: &IteratorPtr, map: &BigOrderedMap): &V {
+    public(friend) fun iter_borrow(self: IteratorPtr, map: &BigOrderedMap): &V {
         assert!(!self.iter_is_end(map), error::invalid_argument(EITER_OUT_OF_BOUNDS));
-        let children = &map.borrow_node(self.node_index).children;
-        &self.child_iter.iter_borrow(children).value
+        let IteratorPtr::Some { node_index, child_iter, key: _ } = self;
+        let children = &map.borrow_node(node_index).children;
+        &child_iter.iter_borrow(children).value
     }
 
     /// Mutably borrows the value iterator points to.
     /// Aborts with EITER_OUT_OF_BOUNDS if iterator is pointing to the end.
     /// Aborts with EBORROW_MUT_REQUIRES_CONSTANT_KV_SIZE if KV size doesn't have constant size,
-    /// because if it doesn't - we need to call `upsert` to be able to check size invariants after modification.
+    /// because if it doesn't we cannot assert invariants on the size.
+    /// In case of variable size, use either `borrow`, `copy` then `upsert`, or `remove` and `add` instead of mutable borrow.
+    ///
     /// Note: Requires that the map is not changed after the input iterator is generated.
-    public(friend) fun iter_borrow_mut(self: &IteratorPtr, map: &mut BigOrderedMap): &mut V {
+    public(friend) fun iter_borrow_mut(self: IteratorPtr, map: &mut BigOrderedMap): &mut V {
         assert!(map.constant_kv_size, error::invalid_argument(EBORROW_MUT_REQUIRES_CONSTANT_KV_SIZE));
         assert!(!self.iter_is_end(map), error::invalid_argument(EITER_OUT_OF_BOUNDS));
-        let children = &mut map.borrow_node_mut(self.node_index).children;
-        &mut self.child_iter.iter_borrow_mut(children).value
+        let IteratorPtr::Some { node_index, child_iter, key: _ } = self;
+        let children = &mut map.borrow_node_mut(node_index).children;
+        &mut child_iter.iter_borrow_mut(children).value
     }
 
     /// Returns the next iterator.
@@ -460,38 +616,18 @@ module aptos_std::big_ordered_map {
         new_iter(prev_index, child_iter, iter_key)
     }
 
-    /// Apply the function to each element in the vector, consuming it.
-    public(friend) inline fun for_each(self: BigOrderedMap, f: |K, V|) {
-        // TODO - this can be done more efficiently, by destroying the leaves directly
-        // but that requires more complicated code and testing.
-        let it = self.new_begin_iter();
-        while (!it.iter_is_end(&self)) {
-            let k = *it.iter_borrow_key();
-            let v = self.remove(&k);
-            f(k, v);
-            it = self.new_begin_iter();
-        };
-        destroy_empty(self)
-    }
+    // ====================== Internal Implementations ========================
 
-    /// Apply the function to a reference of each element in the vector.
-    public(friend) inline fun for_each_ref(self: &BigOrderedMap, f: |&K, &V|) {
-        let it = self.new_begin_iter();
-        while (!it.iter_is_end(self)) {
-            f(it.iter_borrow_key(), it.iter_borrow(self));
-            it = it.iter_next(self);
-        };
-    }
+    inline fun for_each_leaf_node_ref(self: &BigOrderedMap, f: |&Node|) {
+        let cur_node_index = self.min_leaf_index;
 
-    /// Destroy a map, by destroying elements individually.
-    public(friend) inline fun destroy(self: BigOrderedMap, dv: |V|) {
-        for_each(self, |_k, v| {
-            dv(v);
-        });
+        while (cur_node_index != NULL_INDEX) {
+            let node = self.borrow_node(cur_node_index);
+            f(node);
+            cur_node_index = node.next;
+        }
     }
 
-    // ====================== Internal Implementations ========================
-
     /// Borrow a node, given an index. Works for both root (i.e. inline) node and separately stored nodes
     inline fun borrow_node(self: &BigOrderedMap, node_index: u64): &Node {
         if (node_index == ROOT_INDEX) {
@@ -573,11 +709,11 @@ module aptos_std::big_ordered_map {
         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;
+            self.inner_max_degree = max(min(MAX_DEGREE, DEFAULT_TARGET_NODE_SIZE / key_size), 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 / entry_size), DEFAULT_LEAF_MIN_DEGREE as u64) as u16;
+            self.leaf_max_degree = max(min(MAX_DEGREE, DEFAULT_TARGET_NODE_SIZE / entry_size), LEAF_MIN_DEGREE as u64) as u16;
         };
 
         // Make sure that no nodes can exceed the upper size limit.
@@ -1093,32 +1229,6 @@ module aptos_std::big_ordered_map {
         old_child
     }
 
-    /// Returns the number of elements in the BigOrderedMap.
-    fun length(self: &BigOrderedMap): u64 {
-        self.length_for_node(ROOT_INDEX)
-    }
-
-    fun length_for_node(self: &BigOrderedMap, node_index: u64): u64 {
-        let node = self.borrow_node(node_index);
-        if (node.is_leaf) {
-            node.children.length()
-        } else {
-            let size = 0;
-
-            node.children.for_each_ref(|_key, child| {
-                size = size + self.length_for_node(child.node_index.stored_to_index());
-            });
-            size
-        }
-    }
-
-    /// Returns true iff the BigOrderedMap is empty.
-    fun is_empty(self: &BigOrderedMap): bool {
-        let node = self.borrow_node(self.min_leaf_index);
-
-        node.children.is_empty()
-    }
-
     // ===== spec ===========
 
     spec module {
@@ -1135,10 +1245,6 @@ module aptos_std::big_ordered_map {
         pragma opaque;
     }
 
-    spec length_for_node {
-        pragma opaque;
-    }
-
     // ============================= Tests ====================================
 
     #[test_only]
@@ -1179,7 +1285,7 @@ module aptos_std::big_ordered_map {
 
     #[test_only]
     fun validate_iteration(self: &BigOrderedMap) {
-        let expected_num_elements = self.length();
+        let expected_num_elements = self.compute_length();
         let num_elements = 0;
         let it = new_begin_iter(self);
         while (!it.iter_is_end(self)) {
@@ -1300,6 +1406,22 @@ module aptos_std::big_ordered_map {
         });
     }
 
+    #[test]
+    fun test_for_each_ref() {
+        let map = new_with_config(4, 3, false);
+        map.add_all(vector[1, 3, 6, 2, 9, 5, 7, 4, 8], vector[1, 3, 6, 2, 9, 5, 7, 4, 8]);
+
+        let expected = vector[1, 2, 3, 4, 5, 6, 7, 8, 9];
+        let index = 0;
+        map.for_each_ref(|k, v| {
+            assert!(*k == expected[index], *k + 100);
+            assert!(*v == expected[index], *k + 200);
+            index += 1;
+        });
+
+        map.destroy(|_v| {});
+    }
+
     #[test]
     fun test_variable_size() {
         let map = new_with_config, vector>(0, 0, false);
@@ -1468,6 +1590,37 @@ module aptos_std::big_ordered_map {
         map.destroy(|_v| {});
     }
 
+    #[test]
+    fun test_non_iterator_ordering() {
+        let map = new_from(vector[1, 2, 3], vector[10, 20, 30]);
+        assert!(map.prev_key(&1).is_none(), 1);
+        assert!(map.next_key(&1) == option::some(2), 1);
+
+        assert!(map.prev_key(&2) == option::some(1), 2);
+        assert!(map.next_key(&2) == option::some(3), 3);
+
+        assert!(map.prev_key(&3) == option::some(2), 4);
+        assert!(map.next_key(&3).is_none(), 5);
+
+        let (front_k, front_v) = map.borrow_front();
+        assert!(front_k == 1, 6);
+        assert!(front_v == &10, 7);
+
+        let (back_k, back_v) = map.borrow_back();
+        assert!(back_k == 3, 8);
+        assert!(back_v == &30, 9);
+
+        let (front_k, front_v) = map.pop_front();
+        assert!(front_k == 1, 10);
+        assert!(front_v == 10, 11);
+
+        let (back_k, back_v) = map.pop_back();
+        assert!(back_k == 3, 12);
+        assert!(back_v == 30, 13);
+
+        map.destroy(|_v| {});
+    }
+
     #[test]
     #[expected_failure(abort_code = 0x1000B, location = Self)] /// EINVALID_CONFIG_PARAMETER
     fun test_inner_max_degree_too_large() {
@@ -1628,7 +1781,7 @@ module aptos_std::big_ordered_map {
     fun test_adding_key_too_large() {
         let map = new_with_config(0, 0, false);
         map.add(vector[1], 1);
-        map.add(vector_range(0, 57), 1);
+        map.add(vector_range(0, 143), 1);
         map.destroy_and_validate();
     }
 
@@ -1637,7 +1790,7 @@ module aptos_std::big_ordered_map {
     fun test_adding_value_too_large() {
         let map = new_with_config(0, 0, false);
         map.add(1, vector[1]);
-        map.add(2, vector_range(0, 107));
+        map.add(2, vector_range(0, 268));
         map.destroy_and_validate();
     }
 
diff --git a/aptos-move/framework/aptos-framework/sources/datastructures/ordered_map.move b/aptos-move/framework/aptos-framework/sources/datastructures/ordered_map.move
index 435be1ef324466..79a5f9654d3595 100644
--- a/aptos-move/framework/aptos-framework/sources/datastructures/ordered_map.move
+++ b/aptos-move/framework/aptos-framework/sources/datastructures/ordered_map.move
@@ -273,6 +273,54 @@ module aptos_std::ordered_map {
         }
     }
 
+    public fun borrow_front(self: &OrderedMap): (&K, &V) {
+        let entry = self.entries.borrow(0);
+        (&entry.key, &entry.value)
+    }
+
+    public fun borrow_back(self: &OrderedMap): (&K, &V) {
+        let entry = self.entries.borrow(self.entries.length() - 1);
+        (&entry.key, &entry.value)
+    }
+
+    public fun pop_front(self: &mut OrderedMap): (K, V) {
+        let Entry { key, value } = self.entries.remove(0);
+        (key, value)
+    }
+
+    public fun pop_back(self: &mut OrderedMap): (K, V) {
+        let Entry { key, value } = self.entries.pop_back();
+        (key, value)
+    }
+
+    public fun prev_key(self: &OrderedMap, key: &K): Option {
+        let it = self.lower_bound(key);
+        if (it.iter_is_begin(self)) {
+            option::none()
+        } else {
+            option::some(*it.iter_prev(self).iter_borrow_key(self))
+        }
+    }
+
+    public fun next_key(self: &OrderedMap, key: &K): Option {
+        let it = self.lower_bound(key);
+        if (it.iter_is_end(self)) {
+            option::none()
+        } else {
+            let cur_key = it.iter_borrow_key(self);
+            if (key == cur_key) {
+                let it = it.iter_next(self);
+                if (it.iter_is_end(self)) {
+                    option::none()
+                } else {
+                    option::some(*it.iter_borrow_key(self))
+                }
+            } else {
+                option::some(*cur_key)
+            }
+        }
+    }
+
     // TODO: see if it is more understandable if iterator points between elements,
     // and there is iter_borrow_next and iter_borrow_prev, and provide iter_insert.
 
@@ -349,7 +397,7 @@ module aptos_std::ordered_map {
     }
 
     /// Returns whether the iterator is a begin iterator.
-    public fun iter_is_begin(self: &IteratorPtr, map: &OrderedMap): bool {
+    public(friend) fun iter_is_begin(self: &IteratorPtr, map: &OrderedMap): bool {
         if (self is IteratorPtr::End) {
             map.is_empty()
         } else {
@@ -867,6 +915,35 @@ module aptos_std::ordered_map {
         assert!(rest == new_from(vector[3], vector[30]), 2);
     }
 
+    #[test]
+    fun test_non_iterator_ordering() {
+        let map = new_from(vector[1, 2, 3], vector[10, 20, 30]);
+        assert!(map.prev_key(&1).is_none(), 1);
+        assert!(map.next_key(&1) == option::some(2), 1);
+
+        assert!(map.prev_key(&2) == option::some(1), 2);
+        assert!(map.next_key(&2) == option::some(3), 3);
+
+        assert!(map.prev_key(&3) == option::some(2), 4);
+        assert!(map.next_key(&3).is_none(), 5);
+
+        let (front_k, front_v) = map.borrow_front();
+        assert!(front_k == &1, 6);
+        assert!(front_v == &10, 7);
+
+        let (back_k, back_v) = map.borrow_back();
+        assert!(back_k == &3, 8);
+        assert!(back_v == &30, 9);
+
+        let (front_k, front_v) = map.pop_front();
+        assert!(front_k == 1, 10);
+        assert!(front_v == 10, 11);
+
+        let (back_k, back_v) = map.pop_back();
+        assert!(back_k == 3, 12);
+        assert!(back_v == 30, 13);
+    }
+
     #[test]
     fun test_replace_key_inplace() {
         let map = new_from(vector[1, 3, 5], vector[10, 30, 50]);
diff --git a/aptos-move/framework/aptos-stdlib/doc/smart_table.md b/aptos-move/framework/aptos-stdlib/doc/smart_table.md
index 83eb27ba47fff6..7598a018429423 100644
--- a/aptos-move/framework/aptos-stdlib/doc/smart_table.md
+++ b/aptos-move/framework/aptos-stdlib/doc/smart_table.md
@@ -10,6 +10,9 @@ when expanding to avoid unexpected gas cost.
 SmartTable uses faster hash function SipHash instead of cryptographically secure hash functions like sha3-256 since
 it tolerates collisions.
 
+DEPRECATED: since it's implementation is inneficient, it
+has been deprecated in favor of big_ordered_map.move.
+
 
 -  [Struct `Entry`](#0x1_smart_table_Entry)
 -  [Struct `SmartTable`](#0x1_smart_table_SmartTable)
diff --git a/aptos-move/framework/aptos-stdlib/sources/data_structures/smart_table.move b/aptos-move/framework/aptos-stdlib/sources/data_structures/smart_table.move
index c9c36712738da9..edc0f201396de7 100644
--- a/aptos-move/framework/aptos-stdlib/sources/data_structures/smart_table.move
+++ b/aptos-move/framework/aptos-stdlib/sources/data_structures/smart_table.move
@@ -4,6 +4,9 @@
 /// when expanding to avoid unexpected gas cost.
 /// SmartTable uses faster hash function SipHash instead of cryptographically secure hash functions like sha3-256 since
 /// it tolerates collisions.
+///
+/// DEPRECATED: since it's implementation is inneficient, it
+/// has been deprecated in favor of `big_ordered_map.move`.
 module aptos_std::smart_table {
     use std::error;
     use std::vector;