From 0d2ea8f1371a2f184aea7f0d1fcb6c2adf44f758 Mon Sep 17 00:00:00 2001 From: Igor Date: Wed, 25 Sep 2024 08:57:32 -0700 Subject: [PATCH] [move][stdlib] Add vector::split_off and vector::remove library methods --- .../framework/move-stdlib/doc/vector.md | 69 +++++++++++++++++++ .../framework/move-stdlib/sources/vector.move | 27 ++++++++ .../move-stdlib/tests/vector_tests.move | 35 ++++++++++ 3 files changed, 131 insertions(+) diff --git a/aptos-move/framework/move-stdlib/doc/vector.md b/aptos-move/framework/move-stdlib/doc/vector.md index d5e6a7bfa2ecda..42039158c4158b 100644 --- a/aptos-move/framework/move-stdlib/doc/vector.md +++ b/aptos-move/framework/move-stdlib/doc/vector.md @@ -29,6 +29,7 @@ the return on investment didn't seem worth it for these simple functions. - [Function `reverse_slice`](#0x1_vector_reverse_slice) - [Function `append`](#0x1_vector_append) - [Function `reverse_append`](#0x1_vector_reverse_append) +- [Function `split_off`](#0x1_vector_split_off) - [Function `trim`](#0x1_vector_trim) - [Function `trim_reverse`](#0x1_vector_trim_reverse) - [Function `is_empty`](#0x1_vector_is_empty) @@ -39,6 +40,7 @@ the return on investment didn't seem worth it for these simple functions. - [Function `remove`](#0x1_vector_remove) - [Function `remove_value`](#0x1_vector_remove_value) - [Function `swap_remove`](#0x1_vector_swap_remove) +- [Function `replace`](#0x1_vector_replace) - [Function `for_each`](#0x1_vector_for_each) - [Function `for_each_reverse`](#0x1_vector_for_each_reverse) - [Function `for_each_ref`](#0x1_vector_for_each_ref) @@ -481,6 +483,42 @@ Pushes all of the elements of the other vector into the self< + + + + +## Function `split_off` + +Splits the collection into two at the given index. +Returns a newly allocated vector containing the elements in the range [at, len). +After the call, the original vector will be left containing the elements [0, at) +with its previous capacity unchanged. + + +
public fun split_off<Element>(self: &mut vector<Element>, at: u64): vector<Element>
+
+ + + +
+Implementation + + +
public fun split_off<Element>(self: &mut vector<Element>, at: u64): vector<Element> {
+    let len = length(self);
+    assert!(at <= len, EINDEX_OUT_OF_BOUNDS);
+    let other = empty();
+    while (len > at) {
+        push_back(&mut other, pop_back(self));
+        len = len - 1;
+    };
+    reverse(&mut other);
+    other
+}
+
+ + +
@@ -800,6 +838,37 @@ Aborts if i is out of bounds. + + + + +## Function `replace` + +Replace the ith element of the vector self with the given value, and return +to the caller the value that was there before. +Aborts if i is out of bounds. + + +
public fun replace<Element>(self: &mut vector<Element>, i: u64, val: Element): Element
+
+ + + +
+Implementation + + +
public fun replace<Element>(self: &mut vector<Element>, i: u64, val: Element): Element {
+    let last_idx = length(self);
+    assert!(i < last_idx, EINDEX_OUT_OF_BOUNDS);
+    push_back(self, val);
+    swap(self, i, last_idx);
+    pop_back(self)
+}
+
+ + +
diff --git a/aptos-move/framework/move-stdlib/sources/vector.move b/aptos-move/framework/move-stdlib/sources/vector.move index 1f7754a2a4cb81..ba58f20ab10ae0 100644 --- a/aptos-move/framework/move-stdlib/sources/vector.move +++ b/aptos-move/framework/move-stdlib/sources/vector.move @@ -122,6 +122,22 @@ module std::vector { pragma intrinsic = true; } + /// Splits the collection into two at the given index. + /// Returns a newly allocated vector containing the elements in the range [at, len). + /// After the call, the original vector will be left containing the elements [0, at) + /// with its previous capacity unchanged. + public fun split_off(self: &mut vector, at: u64): vector { + let len = length(self); + assert!(at <= len, EINDEX_OUT_OF_BOUNDS); + let other = empty(); + while (len > at) { + push_back(&mut other, pop_back(self)); + len = len - 1; + }; + reverse(&mut other); + other + } + /// Trim a vector to a smaller size, returning the evicted elements in order public fun trim(self: &mut vector, new_len: u64): vector { let res = trim_reverse(self, new_len); @@ -266,6 +282,17 @@ module std::vector { pragma intrinsic = true; } + /// Replace the `i`th element of the vector `self` with the given value, and return + /// to the caller the value that was there before. + /// Aborts if `i` is out of bounds. + public fun replace(self: &mut vector, i: u64, val: Element): Element { + let last_idx = length(self); + assert!(i < last_idx, EINDEX_OUT_OF_BOUNDS); + push_back(self, val); + swap(self, i, last_idx); + pop_back(self) + } + /// Apply the function to each element in the vector, consuming it. public inline fun for_each(self: vector, f: |Element|) { reverse(&mut self); // We need to reverse the vector to consume it efficiently diff --git a/aptos-move/framework/move-stdlib/tests/vector_tests.move b/aptos-move/framework/move-stdlib/tests/vector_tests.move index b8c9e19a4dd84e..4a95517f7960eb 100644 --- a/aptos-move/framework/move-stdlib/tests/vector_tests.move +++ b/aptos-move/framework/move-stdlib/tests/vector_tests.move @@ -954,4 +954,39 @@ module std::vector_tests { let v = vector[MoveOnly {}]; vector::destroy(v, |m| { let MoveOnly {} = m; }) } + + #[test] + #[expected_failure(abort_code = V::EINDEX_OUT_OF_BOUNDS)] + fun test_replace_empty_abort() { + let v = vector[]; + let MoveOnly {} = vector::replace(&mut v, 0, MoveOnly {}); + vector::destroy_empty(v); + } + + #[test] + fun test_replace() { + let v = vector[1, 2, 3, 4]; + vector::replace(&mut v, 1, 17); + assert!(v == vector[1, 17, 3, 4], 0); + } + + #[test] + fun test_split_off() { + let v = vector[1, 2, 3, 4, 5, 6]; + let other = vector::split_off(&mut v, 4); + assert!(v == vector[1, 2, 3, 4], 0); + assert!(other == vector[5, 6], 1); + + let other_empty = vector::split_off(&mut v, 4); + assert!(v == vector[1, 2, 3, 4], 2); + assert!(other_empty == vector[], 3); + + } + + #[test] + #[expected_failure(abort_code = V::EINDEX_OUT_OF_BOUNDS)] + fun test_split_off_abort() { + let v = vector[1, 2, 3]; + vector::split_off(&mut v, 4); + } }