ic-backup 0.64.1

Host-side snapshot backup and same-release recovery for Internet Computer canisters
Documentation
//! Exact capture/inventory mappings with field limits checked before allocation.
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())
}