use super::{IcSnapshotInfo, IcSnapshotReplyError, MAX_IC_SNAPSHOT_REPLY_ENTRIES};
use crate::model::{
ic_candid::BoundedVec,
ic_request::{IcManagementMethodRecord, MAX_IC_SNAPSHOT_ID_BYTES},
};
use ic_platform::candid_codec::{
Encode, Error, Type, Writer,
decoder::{self, Decode, Limits, Reader, decode_one_with_skip_limit},
};
pub(crate) struct SnapshotId(pub(crate) Vec<u8>);
impl Encode for SnapshotId {
const TYPE: &'static Type = <Vec<u8> as Encode>::TYPE;
fn write(&self, out: &mut Writer) -> Result<(), Error> {
self.0.write(out)
}
}
impl<'a> Decode<'a> for SnapshotId {
fn read(reader: &mut Reader<'a>, wire: i64) -> Result<Self, decoder::Error> {
let bytes = reader.bounded_vec(wire, MAX_IC_SNAPSHOT_ID_BYTES)?;
if bytes.is_empty() {
return Err(decoder::Error::Type);
}
Ok(Self(bytes))
}
}
struct WireSnapshot {
id: SnapshotId,
taken_at_timestamp: u64,
total_size: u64,
}
ic_platform::candid_record!(WireSnapshot {
23_515 => id: SnapshotId,
1_926_775_388 => total_size: u64,
3_569_147_778 => taken_at_timestamp: u64,
});
impl From<WireSnapshot> for IcSnapshotInfo {
fn from(value: WireSnapshot) -> Self {
Self {
id: value.id.0,
taken_at_timestamp: value.taken_at_timestamp,
total_size: value.total_size,
}
}
}
pub(super) fn decode(
method: IcManagementMethodRecord,
bytes: &[u8],
) -> Result<Vec<IcSnapshotInfo>, IcSnapshotReplyError> {
let limits = Limits {
bytes: super::MAX_IC_SNAPSHOT_REPLY_BYTES,
header: 64 * 1024,
types: 16,
work: 2 * 1024 * 1024,
allocation: 2 * 1024 * 1024,
};
let snapshots =
match method {
IcManagementMethodRecord::TakeCanisterSnapshot => vec![
decode_one_with_skip_limit::<WireSnapshot>(bytes, limits, 0)
.map_err(|_| IcSnapshotReplyError::InvalidReply)?,
],
IcManagementMethodRecord::ListCanisterSnapshots => decode_one_with_skip_limit::<
BoundedVec<WireSnapshot, MAX_IC_SNAPSHOT_REPLY_ENTRIES>,
>(bytes, limits, 0)
.map_err(|_| IcSnapshotReplyError::InvalidReply)?
.0,
_ => return Err(IcSnapshotReplyError::UnsupportedMethod { method }),
};
Ok(snapshots.into_iter().map(IcSnapshotInfo::from).collect())
}