#[cfg(target_arch = "wasm32")]
#[expect(
unsafe_code,
reason = "test-only raw IC system calls and Memory::read_unsafe"
)]
mod canister {
use ic_memory::{
GenericRangePolicy, MemoryManagerConfig, bootstrap_default_memory_manager_with_config,
committed_allocations, default_memory_manager_memory_allocation_summary,
ic_stable_structures::Memory, open_default_memory_manager_memory,
open_default_memory_manager_memory_by_key,
};
const PAGE: u64 = 65_536;
const ROWS: &str = "io_qualification.rows.v1";
const NEIGHBOR: &str = "io_qualification.neighbor.v1";
ic_memory::ic_memory_range!(authority = "io_qualification", start = 10, end = 11);
ic_memory::ic_memory_declaration!(
authority = "io_qualification",
key = "io_qualification.neighbor.v1",
label = "neighbor",
id = 10
);
ic_memory::ic_memory_declaration!(
authority = "io_qualification",
key = "io_qualification.rows.v1",
label = "rows",
id = 11
);
#[link(wasm_import_module = "ic0")]
unsafe extern "C" {
fn msg_arg_data_size() -> usize;
fn msg_arg_data_copy(dst: usize, offset: usize, size: usize);
fn msg_reply_data_append(src: usize, size: usize);
fn msg_reply();
fn performance_counter(counter_type: u32) -> u64;
}
fn arguments<const N: usize>() -> [u8; N] {
let mut bytes = [0; N];
unsafe {
assert_eq!(msg_arg_data_size(), N);
msg_arg_data_copy(bytes.as_mut_ptr() as usize, 0, N);
}
bytes
}
fn reply(bytes: &[u8]) {
unsafe {
msg_reply_data_append(bytes.as_ptr() as usize, bytes.len());
msg_reply();
}
}
fn bootstrap(mode: u8) {
let pages = if mode == 2 { 16 } else { 8 };
bootstrap_default_memory_manager_with_config(
MemoryManagerConfig::new(pages).unwrap(),
&GenericRangePolicy,
)
.unwrap();
assert_ne!(mode, 1, "injected trap after durable bootstrap write");
}
#[unsafe(export_name = "canister_init")]
extern "C" fn init() {
bootstrap(0);
let neighbor = open_default_memory_manager_memory_by_key(NEIGHBOR).unwrap();
neighbor.grow(8).unwrap();
neighbor.write(8 * PAGE - 1, &[0x6D]);
let rows = open_default_memory_manager_memory_by_key(ROWS).unwrap();
rows.grow(1).unwrap();
rows.write(0, &[0x42]);
}
#[unsafe(export_name = "canister_post_upgrade")]
extern "C" fn post_upgrade() {
bootstrap(arguments::<1>()[0]);
}
#[unsafe(export_name = "canister_query snapshot")]
extern "C" fn snapshot() {
let mut bytes = committed_allocations()
.unwrap()
.generation()
.to_le_bytes()
.to_vec();
let rows = open_default_memory_manager_memory(ROWS, 11).unwrap();
let neighbor = open_default_memory_manager_memory(NEIGHBOR, 10).unwrap();
let mut markers = [0; 2];
rows.read(0, &mut markers[..1]);
neighbor.read(8 * PAGE - 1, &mut markers[1..]);
bytes.extend_from_slice(&markers);
let summary = default_memory_manager_memory_allocation_summary().unwrap();
bytes.extend_from_slice(&summary.physical_extent.wasm_pages.to_le_bytes());
bytes.extend_from_slice(&rows.size().to_le_bytes());
reply(&bytes);
}
#[unsafe(export_name = "canister_update io")]
extern "C" fn io() {
let args = arguments::<10>();
let operation = args[0];
let count = usize::from(args[1]);
assert!(count <= 2);
let offset = u64::from_le_bytes(args[2..].try_into().unwrap());
let rows = open_default_memory_manager_memory_by_key(ROWS).unwrap();
rows.write(0, &[0x99]);
let mut dst = [0; 2];
let start = unsafe { performance_counter(0) };
for _ in 0..1000 {
match operation {
0 => rows.read(offset, &mut dst[..count]),
1 => unsafe { rows.read_unsafe(offset, dst.as_mut_ptr(), count) },
2 => rows.write(offset, &[0xA5; 2][..count]),
_ => panic!("unknown IO operation"),
}
}
let instructions = unsafe { performance_counter(0) } - start;
let mut bytes = dst.to_vec();
bytes.extend_from_slice(&instructions.to_le_bytes());
reply(&bytes);
}
}