ic-platform 0.31.0

Controlled Candid and IC runtime dependency boundary
Documentation
use super::*;
use crate::candid_codec::{
    decoder::{Limits, decode_one},
    encode_one,
};
use candid as reference;
fn limits() -> Limits {
    Limits {
        bytes: 1_000_000,
        header: 100_000,
        types: 1024,
        work: 10_000_000,
        allocation: 4_000_000,
    }
}
fn natural(value: BigUint) {
    let wire = reference::encode_one(value.to_string().parse::<reference::Nat>().unwrap()).unwrap();
    assert_eq!(encode_one(&value, wire.len()).unwrap(), wire);
    assert_eq!(decode_one::<BigUint>(&wire, limits()).unwrap(), value);
    assert_eq!(
        decode_one::<BigInt>(&wire, limits()).unwrap(),
        BigInt::from(value)
    );
}
fn integer(value: BigInt) {
    let wire = reference::encode_one(value.to_string().parse::<reference::Int>().unwrap()).unwrap();
    assert_eq!(encode_one(&value, wire.len()).unwrap(), wire);
    assert_eq!(decode_one::<BigInt>(&wire, limits()).unwrap(), value);
    assert!(decode_one::<BigUint>(&wire, limits()).is_err());
}
#[test]
fn arbitrary_precision_boundaries_match_upstream() {
    natural(BigUint::from(0u8));
    integer(BigInt::from(0));
    for bit in [
        0usize, 6, 7, 8, 13, 14, 31, 32, 63, 64, 127, 128, 255, 256, 1024, 8192,
    ] {
        let power = BigUint::from(1u8) << bit;
        for value in [&power - 1u8, power.clone(), &power + 1u8] {
            natural(value.clone());
            integer(BigInt::from(value.clone()));
            integer(-BigInt::from(value));
        }
    }
}
#[test]
fn extended_integer_encodings_and_truncation() {
    for (opcode, payload) in [
        (0x7du8, vec![0x80, 0x80, 0]),
        (0x7c, vec![0xff, 0xff, 0x7f]),
        (0x7c, vec![0xc0, 0x80, 0]),
    ] {
        let mut bytes = vec![b'D', b'I', b'D', b'L', 0, 1, opcode];
        bytes.extend(payload);
        let expected = reference::decode_args_with_config::<(reference::Int,)>(
            &bytes,
            &reference::DecoderConfig::new(),
        )
        .unwrap()
        .0
        .0;
        assert_eq!(
            decode_one::<BigInt>(&bytes, limits()).unwrap().to_string(),
            expected.to_string()
        );
        for n in 0..bytes.len() {
            assert!(decode_one::<BigInt>(&bytes[..n], limits()).is_err());
        }
    }
}
#[test]
fn output_and_integer_allocation_budgets() {
    let value = BigUint::from(1u8) << 8192usize;
    assert_eq!(encode_one(&value, 10), Err(Error::Limit));
    assert_eq!(
        encode_one(&(-BigInt::from(value.clone())), 10),
        Err(Error::Limit)
    );
    let wire = encode_one(&value, 100_000).unwrap();
    let mut budget = limits();
    budget.allocation = 8;
    assert_eq!(
        decode_one::<BigUint>(&wire, budget),
        Err(crate::candid_codec::decoder::Error::Limit)
    );
}

#[test]
fn composite_size_workload_cross_decodes() {
    type Owned = (
        Option<std::result::Result<Vec<u64>, String>>,
        (BigUint, BigInt),
    );
    type Reference = (
        Option<std::result::Result<Vec<u64>, String>>,
        (reference::Nat, reference::Int),
    );
    let natural = BigUint::from(1u8) << 1024usize;
    let integer = -(BigInt::from(1) << 2048usize);
    for outcome in [
        None,
        Some(Ok(vec![])),
        Some(Ok(vec![0, 127, 128, u64::MAX])),
        Some(Err("denied 🌍".to_owned())),
    ] {
        let owned: Owned = (outcome.clone(), (natural.clone(), integer.clone()));
        let expected: Reference = (
            outcome,
            (
                natural.to_string().parse::<reference::Nat>().unwrap(),
                integer.to_string().parse::<reference::Int>().unwrap(),
            ),
        );
        let upstream_bytes = reference::encode_one(&expected).unwrap();
        assert_eq!(
            decode_one::<Owned>(&upstream_bytes, limits()).unwrap(),
            owned
        );
        let own_bytes = encode_one(&owned, 1_000_000).unwrap();
        let decoded = reference::decode_args_with_config::<(Reference,)>(
            &own_bytes,
            &reference::DecoderConfig::new(),
        )
        .unwrap()
        .0;
        assert_eq!(decoded, expected);
        for n in 0..upstream_bytes.len() {
            assert!(decode_one::<Owned>(&upstream_bytes[..n], limits()).is_err());
        }
    }
}

#[test]
#[ignore = "Explicit mixed-workload native throughput experiment."]
fn native_composite_throughput() {
    use std::{hint::black_box, time::Instant};
    type Owned = (
        Option<std::result::Result<Vec<u64>, String>>,
        (BigUint, BigInt),
    );
    let reference_limits = crate::tests::reference::DecodeLimits {
        reply_bytes: 1_000_000,
        header_bytes: 100_000,
        type_entries: 1024,
        decoding_quota: 10_000_000,
        skipping_quota: 10_000_000,
    };
    type Reference = (
        Option<std::result::Result<Vec<u64>, String>>,
        (reference::Nat, reference::Int),
    );
    println!("rows,integer_bits,iterations,sample,implementation,operation,total_ns");
    for (rows, bits) in [(0usize, 256usize), (16, 1024), (1024, 8192)] {
        let natural = BigUint::from(1u8) << bits;
        let integer = -BigInt::from(natural.clone()) - 1u8;
        let owned: Owned = (
            Some(Ok((0..rows as u64).collect())),
            (natural.clone(), integer.clone()),
        );
        let reference: Reference = (
            owned.0.clone(),
            (
                natural.to_string().parse::<reference::Nat>().unwrap(),
                integer.to_string().parse::<reference::Int>().unwrap(),
            ),
        );
        let bytes = reference::encode_one(&reference).unwrap();
        assert_eq!(decode_one::<Owned>(&bytes, limits()).unwrap(), owned);
        let own_bytes = encode_one(&owned, 1_000_000).unwrap();
        let (decoded,): (Reference,) =
            reference::decode_args_with_config(&own_bytes, &reference::DecoderConfig::new())
                .unwrap();
        assert_eq!(decoded, reference);
        for sample in 0..5 {
            for implementation in if sample % 2 == 0 { [0, 1] } else { [1, 0] } {
                let start = Instant::now();
                for _ in 0..1000 {
                    if implementation == 0 {
                        black_box(encode_one(black_box(&owned), 1_000_000).unwrap());
                    } else {
                        black_box(reference::encode_one(black_box(&reference)).unwrap());
                    }
                }
                println!(
                    "{rows},{bits},1000,{sample},{implementation},encode,{}",
                    start.elapsed().as_nanos()
                );
                let start = Instant::now();
                for _ in 0..1000 {
                    if implementation == 0 {
                        black_box(decode_one::<Owned>(black_box(&bytes), limits()).unwrap());
                    } else {
                        black_box(
                            crate::tests::reference::decode_one::<Reference>(
                                black_box(&bytes),
                                reference_limits,
                            )
                            .unwrap(),
                        );
                    }
                }
                println!(
                    "{rows},{bits},1000,{sample},{implementation},decode,{}",
                    start.elapsed().as_nanos()
                );
            }
        }
    }
}