1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
// This file is part of Substrate.

// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0

// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// 	http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use super::*;
use wasmtime::{AsContext, AsContextMut};
use sp_std::ops::Range;

/// Construct a range from an offset to a data length after the offset.
/// Returns None if the end of the range would exceed some maximum offset.
pub fn checked_range(offset: usize, len: usize, max: usize) -> Option<Range<usize>> {
	let end = offset.checked_add(len)?;
	(end <= max).then(|| offset..end)
}

pub fn write_memory_from(
    mut ctx: impl AsContextMut<Data = StoreData>,
    address: Pointer<u8>,
    data: &[u8],
) -> Result<()> {
    let memory = ctx.as_context().data().memory();
    let memory = memory.data_mut(&mut ctx);

    let range = checked_range(address.into(), data.len(), memory.len())
        .ok_or_else(|| String::from("memory write is out of bounds"))?;
    memory[range].copy_from_slice(data);
    Ok(())
}

pub fn read_memory_into(
    ctx: impl AsContext<Data = StoreData>,
    address: Pointer<u8>,
    dest: &mut [u8],
) -> Result<()> {
    let memory = ctx.as_context().data().memory().data(&ctx);

    let range = checked_range(address.into(), dest.len(), memory.len())
        .ok_or_else(|| String::from("memory read is out of bounds"))?;
    dest.copy_from_slice(&memory[range]);
    Ok(())
}

pub fn read_memory(
    ctx: impl AsContext<Data = StoreData>,
    address: Pointer<u8>,
    size: WordSize,
) -> Result<Vec<u8>> {
    let mut vec = vec![0; size as usize];
    read_memory_into(ctx, address, &mut vec)?;
    Ok(vec)
}

#[track_caller]
fn host_state_mut<'a>(caller: &'a mut Caller<'_, StoreData>) -> &'a mut HostState {
    caller
        .data_mut()
        .host_state_mut()
        .expect("host state is not empty when calling a function in wasm; qed")
}

pub fn allocate_memory(caller: &mut Caller<'_, StoreData>, size: WordSize) -> Result<Pointer<u8>> {
    let mut allocator = host_state_mut(caller)
        .allocator
        .take()
        .expect("allocator is not empty when calling a function in wasm; qed");

    let memory = caller.data().memory();
    // We can not return on error early, as we need to store back allocator.
    let res = allocator
        .allocate(&mut MemoryWrapper::from((&memory, &mut caller.as_context_mut())), size)
        .map_err(|e| e.to_string());

    host_state_mut(caller).allocator = Some(allocator);

    res
}

pub fn deallocate_memory(caller: &mut Caller<'_, StoreData>, ptr: Pointer<u8>) -> Result<()> {
    let mut allocator = host_state_mut(caller)
        .allocator
        .take()
        .expect("allocator is not empty when calling a function in wasm; qed");

    let memory = caller.data().memory();

    // We can not return on error early, as we need to store back allocator.
    let res = allocator
        .deallocate(&mut MemoryWrapper::from((&memory, &mut caller.as_context_mut())), ptr)
        .map_err(|e| e.to_string());

    host_state_mut(caller).allocator = Some(allocator);

    res
}