pub mod arguments;
mod containers;
pub mod decoder;
#[cfg(feature = "candid-codec-endpoint")]
pub mod endpoint;
mod int;
mod integers;
mod mappings;
mod nat;
mod reserved;
pub use reserved::Reserved;
pub mod references;
mod schema;
#[cfg(not(target_family = "wasm"))]
pub mod service;
use std::ptr;
pub use int::Int;
pub use nat::Nat;
#[derive(Debug)]
pub enum Type {
Deferred(fn() -> &'static Type),
Primitive(i8),
Opt(&'static Type),
Vec(&'static Type),
Record(&'static [(u32, &'static Type)]),
Variant(&'static [(u32, &'static Type)]),
Function {
args: &'static [&'static Type],
results: &'static [&'static Type],
annotations: &'static [u8],
},
Service(&'static [(&'static str, &'static Type)]),
}
#[derive(Debug, PartialEq, Eq)]
pub enum Error {
Schema,
Limit,
}
pub struct Writer {
bytes: Vec<u8>,
limit: usize,
}
impl Writer {
pub fn bytes(&mut self, bytes: &[u8]) -> Result<(), Error> {
if bytes.len() > self.limit.saturating_sub(self.bytes.len()) {
return Err(Error::Limit);
}
self.bytes.extend_from_slice(bytes);
Ok(())
}
pub fn unsigned(&mut self, mut n: u128) -> Result<(), Error> {
loop {
let byte = (n & 127) as u8;
n >>= 7;
self.bytes(&[byte | if n == 0 { 0 } else { 128 }])?;
if n == 0 {
return Ok(());
}
}
}
pub fn signed(&mut self, mut n: i128) -> Result<(), Error> {
loop {
let byte = (n & 127) as u8;
n >>= 7;
let done = (n == 0 && byte & 64 == 0) || (n == -1 && byte & 64 != 0);
self.bytes(&[byte | if done { 0 } else { 128 }])?;
if done {
return Ok(());
}
}
}
}
pub trait Encode {
const TYPE: &'static Type;
fn write(&self, output: &mut Writer) -> Result<(), Error>;
}
fn register(
root: &'static Type,
table: &mut Vec<&'static Type>,
limit: usize,
) -> Result<(), Error> {
let mut pending = vec![root];
while let Some(t) = pending.pop() {
let t = t.resolved()?;
if let Type::Primitive(code) = t {
if !matches!(*code, -17..=-1 | -24) {
return Err(Error::Schema);
}
continue;
}
if table.iter().any(|entry| ptr::eq(*entry, t)) {
continue;
}
if table.len() >= limit {
return Err(Error::Limit);
}
table.push(t);
match t {
Type::Opt(inner) | Type::Vec(inner) => pending.push(inner),
Type::Record(fields) | Type::Variant(fields) => {
if fields.windows(2).any(|pair| pair[0].0 >= pair[1].0) {
return Err(Error::Schema);
}
pending.extend(fields.iter().rev().map(|(_, field)| *field));
}
Type::Function {
args,
results,
annotations,
} => {
if !matches!(*annotations, [] | [1] | [2] | [3])
|| (*annotations == [2] && !results.is_empty())
{
return Err(Error::Schema);
}
pending.extend(args.iter().chain(results.iter()).rev().copied());
}
Type::Service(methods) => {
if methods.windows(2).any(|pair| pair[0].0 >= pair[1].0)
|| methods
.iter()
.any(|(_, method)| !matches!(method.resolved(), Ok(Type::Function { .. })))
{
return Err(Error::Schema);
}
pending.extend(methods.iter().rev().map(|(_, method)| *method));
}
Type::Primitive(_) | Type::Deferred(_) => unreachable!(),
}
}
Ok(())
}
fn reference(
t: &'static Type,
table: &[&'static Type],
aliases: &schema::Aliases,
output: &mut Writer,
) -> Result<(), Error> {
let t = t.resolved()?;
let index = match t {
Type::Primitive(code) => i128::from(*code),
_ => aliases
.iter()
.find(|(source, _)| ptr::eq(*source, t))
.and_then(|(_, target)| table.iter().position(|entry| ptr::eq(*entry, *target)))
.ok_or(Error::Schema)? as i128,
};
output.signed(index)
}
fn refs(
types: &[&'static Type],
table: &[&'static Type],
aliases: &schema::Aliases,
output: &mut Writer,
) -> Result<(), Error> {
output.unsigned(types.len() as u128)?;
for t in types {
reference(t, table, aliases, output)?;
}
Ok(())
}
fn header(types: &[&'static Type], output: &mut Writer) -> Result<(), Error> {
let mut table = Vec::new();
for t in types {
register(t, &mut table, output.limit)?;
}
let aliases = schema::compact(&mut table)?;
output.bytes(b"DIDL")?;
output.unsigned(table.len() as u128)?;
for t in &table {
match t {
Type::Opt(inner) | Type::Vec(inner) => {
output.signed(if matches!(t, Type::Opt(_)) { -18 } else { -19 })?;
reference(inner, &table, &aliases, output)?;
}
Type::Record(fields) | Type::Variant(fields) => {
output.signed(if matches!(t, Type::Record(_)) {
-20
} else {
-21
})?;
output.unsigned(fields.len() as u128)?;
for (id, field) in *fields {
output.unsigned(u128::from(*id))?;
reference(field, &table, &aliases, output)?;
}
}
Type::Function {
args,
results,
annotations,
} => {
output.signed(-22)?;
refs(args, &table, &aliases, output)?;
refs(results, &table, &aliases, output)?;
output.unsigned(annotations.len() as u128)?;
output.bytes(annotations)?;
}
Type::Service(methods) => {
output.signed(-23)?;
output.unsigned(methods.len() as u128)?;
for (name, method) in *methods {
name.write(output)?;
reference(method, &table, &aliases, output)?;
}
}
Type::Primitive(_) | Type::Deferred(_) => unreachable!(),
}
}
refs(types, &table, &aliases, output)
}
pub fn encode_one<T: Encode + ?Sized>(value: &T, limit: usize) -> Result<Vec<u8>, Error> {
arguments::encode_args(&(value,), limit)
}
impl<T: Encode + ?Sized> Encode for &T {
const TYPE: &'static Type = T::TYPE;
fn write(&self, output: &mut Writer) -> Result<(), Error> {
T::write(self, output)
}
}
macro_rules! fixed {
($($t:ty => $code:expr),* $(,)?) => {$ (
impl Encode for $t {
const TYPE: &'static Type = &Type::Primitive($code);
fn write(&self, output: &mut Writer) -> Result<(), Error> { output.bytes(&self.to_le_bytes()) }
}
)*};
}
fixed!(u8 => -5, u16 => -6, u32 => -7, u64 => -8, i8 => -9, i16 => -10, i32 => -11, i64 => -12, f32 => -13, f64 => -14);
impl Encode for bool {
const TYPE: &'static Type = &Type::Primitive(-2);
fn write(&self, output: &mut Writer) -> Result<(), Error> {
output.bytes(&[u8::from(*self)])
}
}
impl Encode for () {
const TYPE: &'static Type = &Type::Primitive(-1);
fn write(&self, _: &mut Writer) -> Result<(), Error> {
Ok(())
}
}
impl Encode for str {
const TYPE: &'static Type = &Type::Primitive(-15);
fn write(&self, output: &mut Writer) -> Result<(), Error> {
output.unsigned(self.len() as u128)?;
output.bytes(self.as_bytes())
}
}
impl Encode for String {
const TYPE: &'static Type = str::TYPE;
fn write(&self, output: &mut Writer) -> Result<(), Error> {
self.as_str().write(output)
}
}
impl<T: Encode> Encode for Option<T> {
const TYPE: &'static Type = &Type::Opt(T::TYPE);
fn write(&self, output: &mut Writer) -> Result<(), Error> {
output.bytes(&[u8::from(self.is_some())])?;
if let Some(value) = self {
value.write(output)?;
}
Ok(())
}
}
impl<T: Encode> Encode for Vec<T> {
const TYPE: &'static Type = &Type::Vec(T::TYPE);
fn write(&self, output: &mut Writer) -> Result<(), Error> {
output.unsigned(self.len() as u128)?;
for value in self {
value.write(output)?;
}
Ok(())
}
}
impl<A: Encode, B: Encode> Encode for (A, B) {
const TYPE: &'static Type = &Type::Record(&[(0, A::TYPE), (1, B::TYPE)]);
fn write(&self, output: &mut Writer) -> Result<(), Error> {
self.0.write(output)?;
self.1.write(output)
}
}
#[cfg(test)]
mod tests;
impl<T: Encode, E: Encode> Encode for Result<T, E> {
const TYPE: &'static Type = &Type::Variant(&[(17724, T::TYPE), (3456837, E::TYPE)]);
fn write(&self, output: &mut Writer) -> Result<(), Error> {
match self {
Ok(value) => {
output.unsigned(0)?;
value.write(output)
}
Err(value) => {
output.unsigned(1)?;
value.write(output)
}
}
}
}
#[cfg(feature = "principal")]
impl Encode for crate::Principal {
const TYPE: &'static Type = &Type::Primitive(-24);
fn write(&self, output: &mut Writer) -> Result<(), Error> {
output.bytes(&[1])?;
output.unsigned(self.as_slice().len() as u128)?;
output.bytes(self.as_slice())
}
}
impl Type {
pub fn resolved(&'static self) -> Result<&'static Self, Error> {
let schema = match self {
Self::Deferred(resolve) => resolve(),
other => other,
};
if matches!(schema, Self::Deferred(_)) {
Err(Error::Schema)
} else {
Ok(schema)
}
}
}