Skip to content

Commit

Permalink
[move][stdlib] Implement mem::swap native move call
Browse files Browse the repository at this point in the history
  • Loading branch information
igor-aptos committed Oct 4, 2024
1 parent 514653c commit 6db4bf8
Show file tree
Hide file tree
Showing 12 changed files with 372 additions and 5 deletions.
4 changes: 2 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 8 additions & 2 deletions aptos-move/aptos-gas-schedule/src/gas_schedule/move_stdlib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,11 @@

//! This module defines the gas parameters for Move Stdlib.

use crate::{gas_feature_versions::RELEASE_V1_18, gas_schedule::NativeGasParameters};
use aptos_gas_algebra::{InternalGas, InternalGasPerByte};
use crate::{
gas_feature_versions::{RELEASE_V1_18, RELEASE_V1_22},
gas_schedule::NativeGasParameters,
};
use aptos_gas_algebra::{InternalGas, InternalGasPerAbstractValueUnit, InternalGasPerByte};

crate::gas_schedule::macros::define_gas_parameters!(
MoveStdlibGasParameters,
Expand Down Expand Up @@ -36,5 +39,8 @@ crate::gas_schedule::macros::define_gas_parameters!(
[bcs_serialized_size_base: InternalGas, { RELEASE_V1_18.. => "bcs.serialized_size.base" }, 735],
[bcs_serialized_size_per_byte_serialized: InternalGasPerByte, { RELEASE_V1_18.. => "bcs.serialized_size.per_byte_serialized" }, 36],
[bcs_serialized_size_failure: InternalGas, { RELEASE_V1_18.. => "bcs.serialized_size.failure" }, 3676],

[mem_swap_base: InternalGas, { RELEASE_V1_22.. => "mem.swap.base" }, 367],
[mem_swap_per_abs_val_unit: InternalGasPerAbstractValueUnit, { RELEASE_V1_22.. => "mem.swap.per_abs_val_unit"}, 14],
]
);
3 changes: 2 additions & 1 deletion aptos-move/aptos-gas-schedule/src/ver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@
/// global operations.
/// - V1
/// - TBA
pub const LATEST_GAS_FEATURE_VERSION: u64 = gas_feature_versions::RELEASE_V1_21;
pub const LATEST_GAS_FEATURE_VERSION: u64 = gas_feature_versions::RELEASE_V1_22;

pub mod gas_feature_versions {
pub const RELEASE_V1_8: u64 = 11;
Expand All @@ -86,4 +86,5 @@ pub mod gas_feature_versions {
pub const RELEASE_V1_19: u64 = 23;
pub const RELEASE_V1_20: u64 = 24;
pub const RELEASE_V1_21: u64 = 25;
pub const RELEASE_V1_22: u64 = 26;
}
113 changes: 113 additions & 0 deletions aptos-move/framework/move-stdlib/doc/mem.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@

<a id="0x1_mem"></a>

# Module `0x1::mem`

Module with methods for safe memory manipulation.
I.e. swapping/replacing non-copyable/non-droppable types.


- [Function `swap`](#0x1_mem_swap)
- [Function `replace`](#0x1_mem_replace)
- [Specification](#@Specification_0)
- [Function `swap`](#@Specification_0_swap)
- [Function `replace`](#@Specification_0_replace)


<pre><code></code></pre>



<a id="0x1_mem_swap"></a>

## Function `swap`

Swap contents of two passed mutable references.


<pre><code><b>public</b> <b>fun</b> <a href="mem.md#0x1_mem_swap">swap</a>&lt;T&gt;(left: &<b>mut</b> T, right: &<b>mut</b> T)
</code></pre>



<details>
<summary>Implementation</summary>


<pre><code><b>public</b> <b>native</b> <b>fun</b> <a href="mem.md#0x1_mem_swap">swap</a>&lt;T&gt;(left: &<b>mut</b> T, right: &<b>mut</b> T);
</code></pre>



</details>

<a id="0x1_mem_replace"></a>

## Function `replace`

Replace value reference points to with the given new value,
and return value it had before.


<pre><code><b>public</b> <b>fun</b> <a href="mem.md#0x1_mem_replace">replace</a>&lt;T&gt;(ref: &<b>mut</b> T, new: T): T
</code></pre>



<details>
<summary>Implementation</summary>


<pre><code><b>public</b> <b>fun</b> <a href="mem.md#0x1_mem_replace">replace</a>&lt;T&gt;(ref: &<b>mut</b> T, new: T): T {
<a href="mem.md#0x1_mem_swap">swap</a>(ref, &<b>mut</b> new);
new
}
</code></pre>



</details>

<a id="@Specification_0"></a>

## Specification


<a id="@Specification_0_swap"></a>

### Function `swap`


<pre><code><b>public</b> <b>fun</b> <a href="mem.md#0x1_mem_swap">swap</a>&lt;T&gt;(left: &<b>mut</b> T, right: &<b>mut</b> T)
</code></pre>




<pre><code><b>pragma</b> opaque;
<b>aborts_if</b> <b>false</b>;
<b>ensures</b> right == <b>old</b>(left);
<b>ensures</b> left == <b>old</b>(right);
</code></pre>



<a id="@Specification_0_replace"></a>

### Function `replace`


<pre><code><b>public</b> <b>fun</b> <a href="mem.md#0x1_mem_replace">replace</a>&lt;T&gt;(ref: &<b>mut</b> T, new: T): T
</code></pre>




<pre><code><b>pragma</b> opaque;
<b>aborts_if</b> <b>false</b>;
<b>ensures</b> result == <b>old</b>(ref);
<b>ensures</b> ref == new;
</code></pre>


[move-book]: https://aptos.dev/move/book/SUMMARY
1 change: 1 addition & 0 deletions aptos-move/framework/move-stdlib/doc/overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ For on overview of the Move language, see the [Move Book][move-book].
- [`0x1::features`](features.md#0x1_features)
- [`0x1::fixed_point32`](fixed_point32.md#0x1_fixed_point32)
- [`0x1::hash`](hash.md#0x1_hash)
- [`0x1::mem`](mem.md#0x1_mem)
- [`0x1::option`](option.md#0x1_option)
- [`0x1::signer`](signer.md#0x1_signer)
- [`0x1::string`](string.md#0x1_string)
Expand Down
27 changes: 27 additions & 0 deletions aptos-move/framework/move-stdlib/sources/mem.move
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/// Module with methods for safe memory manipulation.
/// I.e. swapping/replacing non-copyable/non-droppable types.
module std::mem {
/// Swap contents of two passed mutable references.
public native fun swap<T>(left: &mut T, right: &mut T);

/// Replace value reference points to with the given new value,
/// and return value it had before.
public fun replace<T>(ref: &mut T, new: T): T {
swap(ref, &mut new);
new
}

spec swap<T>(left: &mut T, right: &mut T) {
pragma opaque;
aborts_if false;
ensures right == old(left);
ensures left == old(right);
}

spec replace<T>(ref: &mut T, new: T): T {
pragma opaque;
aborts_if false;
ensures result == old(ref);
ensures ref == new;
}
}
75 changes: 75 additions & 0 deletions aptos-move/framework/move-stdlib/src/natives/mem.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
// Copyright © Aptos Foundation
// SPDX-License-Identifier: Apache-2.0

// Copyright (c) The Diem Core Contributors
// Copyright (c) The Move Contributors
// SPDX-License-Identifier: Apache-2.0

//! Implementation of native functions for utf8 strings.

use aptos_gas_schedule::gas_params::natives::move_stdlib::{
MEM_SWAP_BASE, MEM_SWAP_PER_ABS_VAL_UNIT,
};
use aptos_native_interface::{
safely_pop_arg, RawSafeNative, SafeNativeBuilder, SafeNativeContext, SafeNativeError,
SafeNativeResult,
};
use move_core_types::vm_status::StatusCode;
use move_vm_runtime::native_functions::NativeFunction;
use move_vm_types::{
loaded_data::runtime_types::Type,
natives::function::PartialVMError,
values::{Reference, Value},
};
use smallvec::{smallvec, SmallVec};
use std::collections::VecDeque;

/***************************************************************************************************
* native fun native_swap
*
* gas cost: MEM_SWAP_BASE + MEM_SWAP_PER_ABS_VAL_UNIT * abstract_size_of_arguments
*
**************************************************************************************************/
fn native_swap(
context: &mut SafeNativeContext,
_ty_args: Vec<Type>,
mut args: VecDeque<Value>,
) -> SafeNativeResult<SmallVec<[Value; 1]>> {
debug_assert!(args.len() == 2);

if args.len() != 2 {
return Err(SafeNativeError::InvariantViolation(PartialVMError::new(
StatusCode::UNKNOWN_INVARIANT_VIOLATION_ERROR,
)));

Check warning on line 43 in aptos-move/framework/move-stdlib/src/natives/mem.rs

View check run for this annotation

Codecov / codecov/patch

aptos-move/framework/move-stdlib/src/natives/mem.rs#L41-L43

Added lines #L41 - L43 were not covered by tests
}

let cost = MEM_SWAP_BASE
+ MEM_SWAP_PER_ABS_VAL_UNIT
* (context.abs_val_size(&args[0]) + context.abs_val_size(&args[1]));
context.charge(cost)?;

let ref1 = safely_pop_arg!(args, Reference);
let ref0 = safely_pop_arg!(args, Reference);

ref0.swap_ref(|value0| {
let mut value1_opt = Option::None;
ref1.swap_ref(|value1| {
value1_opt = Option::Some(value1);
Ok(value0)
})?;
Ok(value1_opt.unwrap())
})?;

Ok(smallvec![])
}

/***************************************************************************************************
* module
**************************************************************************************************/
pub fn make_all(
builder: &SafeNativeBuilder,
) -> impl Iterator<Item = (String, NativeFunction)> + '_ {
let natives = [("swap", native_swap as RawSafeNative)];

builder.make_named_natives(natives)
}
2 changes: 2 additions & 0 deletions aptos-move/framework/move-stdlib/src/natives/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

pub mod bcs;
pub mod hash;
pub mod mem;
pub mod signer;
pub mod string;
#[cfg(feature = "testing")]
Expand All @@ -33,6 +34,7 @@ pub fn all_natives(
builder.with_incremental_gas_charging(false, |builder| {
add_natives!("bcs", bcs::make_all(builder));
add_natives!("hash", hash::make_all(builder));
add_natives!("mem", mem::make_all(builder));
add_natives!("signer", signer::make_all(builder));
add_natives!("string", string::make_all(builder));
#[cfg(feature = "testing")]
Expand Down
65 changes: 65 additions & 0 deletions aptos-move/framework/move-stdlib/tests/mem_tests.move
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
#[test_only]
module std::mem_tests {
use std::vector;
use std::mem::{swap, replace};

#[test]
fun test_swap_ints() {
let a = 1;
let b = 2;
let v = vector[3, 4, 5, 6];

swap(&mut a, &mut b);
assert!(a == 2, 0);
assert!(b == 1, 1);

swap(&mut a, vector::borrow_mut(&mut v, 0));
assert!(a == 3, 0);
assert!(vector::borrow(&v, 0) == &2, 1);

swap(vector::borrow_mut(&mut v, 2), &mut a);
assert!(a == 5, 0);
assert!(vector::borrow(&v, 2) == &3, 1);
}


#[test]
fun test_replace_ints() {
let a = 1;
let b = 2;

assert!(replace(&mut a, b) == 1, 0);
assert!(a == 2, 1);
}

#[test_only]
struct SomeStruct has drop {
f: u64,
v: vector<u64>,
}

#[test]
fun test_swap_struct() {
let a = 1;
let s1 = SomeStruct { f: 2, v: vector[3, 4]};
let s2 = SomeStruct { f: 5, v: vector[6, 7]};
let vs = vector[SomeStruct { f: 8, v: vector[9, 10]}, SomeStruct { f: 11, v: vector[12, 13]}];


swap(&mut s1, &mut s2);
assert!(&s1 == &SomeStruct { f: 5, v: vector[6, 7]}, 0);
assert!(&s2 == &SomeStruct { f: 2, v: vector[3, 4]}, 1);

swap(&mut s1.f, &mut a);
assert!(s1.f == 1, 2);
assert!(a == 5, 3);

swap(&mut s1.f, vector::borrow_mut(&mut s1.v, 0));
assert!(s1.f == 6, 4);
assert!(vector::borrow(&s1.v, 0) == &1, 5);

swap(&mut s2, vector::borrow_mut(&mut vs, 0));
assert!(&s2 == &SomeStruct { f: 8, v: vector[9, 10]}, 6);
assert!(vector::borrow(&vs, 0) == &SomeStruct { f: 2, v: vector[3, 4]}, 7);
}
}
Loading

0 comments on commit 6db4bf8

Please sign in to comment.