Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

set default config for different targets #470

Merged
merged 4 commits into from
Oct 4, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions Changelog.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@

# Changelog

## Rust 0.9.9

* Set different config values for `full_error_message` and `zero_sized_values` for Wasm and non-Wasm target.

## 2023-09-27

### Rust 0.9.8
Expand Down
2 changes: 1 addition & 1 deletion rust/candid/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "candid"
version = "0.9.8"
version = "0.9.9"
edition = "2021"
authors = ["DFINITY Team"]
description = "Candid is an interface description language (IDL) for interacting with canisters running on the Internet Computer."
Expand Down
100 changes: 60 additions & 40 deletions rust/candid/src/de.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

use super::{
error::{Error, Result},
types::internal::{type_of, TypeId},
types::internal::{text_size, type_of, TypeId},
types::{Field, Label, SharedLabel, Type, TypeEnv, TypeInner},
CandidType, Int, Nat,
};
Expand All @@ -17,27 +17,35 @@ use serde::de::{self, Visitor};
use std::fmt::Write;
use std::{collections::VecDeque, io::Cursor, mem::replace};

const MAX_TYPE_LEN: i32 = 500;

/// Use this struct to deserialize a sequence of Rust values (heterogeneous) from IDL binary message.
pub struct IDLDeserialize<'de> {
de: Deserializer<'de>,
}
impl<'de> IDLDeserialize<'de> {
/// Create a new deserializer with IDL binary message.
pub fn new(bytes: &'de [u8]) -> Result<Self> {
let de = Deserializer::from_bytes(bytes)
.with_context(|| format!("Cannot parse header {}", &hex::encode(bytes)))?;
let de = Deserializer::from_bytes(bytes).with_context(|| {
if bytes.len() <= 500 {
format!("Cannot parse header {}", &hex::encode(bytes))
} else {
"Cannot parse header".to_string()
}
})?;
Ok(IDLDeserialize { de })
}
/// Create a new deserializer with IDL binary message. The config is used to adjust some parameters in the deserializer.
pub fn new_with_config(bytes: &'de [u8], config: Config) -> Result<Self> {
let mut de = if config.minize_error_message {
Deserializer::from_bytes(bytes)?
} else {
Deserializer::from_bytes(bytes)
.with_context(|| format!("Cannot parse header {}", &hex::encode(bytes)))?
};
let mut de = Deserializer::from_bytes(bytes).with_context(|| {
if config.full_error_message || bytes.len() <= 500 {
format!("Cannot parse header {}", &hex::encode(bytes))
} else {
"Cannot parse header".to_string()
}
})?;
de.zero_sized_values = config.zero_sized_values;
de.minize_error_message = config.minize_error_message;
de.full_error_message = config.full_error_message;
Ok(IDLDeserialize { de })
}
/// Deserialize one value from deserializer.
Expand Down Expand Up @@ -70,12 +78,13 @@ impl<'de> IDLDeserialize<'de> {
self.de.expect_type = expected_type;
self.de.wire_type = TypeInner::Reserved.into();
return T::deserialize(&mut self.de);
} else if self.de.minize_error_message {
return Err(Error::msg("No more values on the wire"));
} else {
} else if self.de.full_error_message || text_size(&expected_type, MAX_TYPE_LEN).is_ok()
{
return Err(Error::msg(format!(
"No more values on the wire, the expected type {expected_type} is not opt, null, or reserved"
)));
} else {
return Err(Error::msg("No more values on the wire"));
}
}

Expand All @@ -88,16 +97,20 @@ impl<'de> IDLDeserialize<'de> {
};
self.de.wire_type = ty.clone();

let v = if self.de.minize_error_message {
T::deserialize(&mut self.de)?
} else {
T::deserialize(&mut self.de)
.with_context(|| self.de.dump_state())
.with_context(|| {
format!("Fail to decode argument {ind} from {ty} to {expected_type}")
})?
};
Ok(v)
let mut v = T::deserialize(&mut self.de).with_context(|| {
if self.de.full_error_message
|| (text_size(&ty, MAX_TYPE_LEN).is_ok()
&& text_size(&expected_type, MAX_TYPE_LEN).is_ok())
{
format!("Fail to decode argument {ind} from {ty} to {expected_type}")
} else {
format!("Fail to decode argument {ind}")
}
});
if self.de.full_error_message {
v = v.with_context(|| self.de.dump_state());
}
Ok(v?)
}
/// Check if we finish deserializing all values.
pub fn is_done(&self) -> bool {
Expand All @@ -111,7 +124,7 @@ impl<'de> IDLDeserialize<'de> {
let ind = self.de.input.position() as usize;
let rest = &self.de.input.get_ref()[ind..];
if !rest.is_empty() {
if self.de.minize_error_message {
if !self.de.full_error_message {
return Err(Error::msg("Trailing value after finishing deserialization"));
} else {
return Err(anyhow!(self.de.dump_state()))
Expand All @@ -124,7 +137,7 @@ impl<'de> IDLDeserialize<'de> {

pub struct Config {
pub zero_sized_values: usize,
pub minize_error_message: bool,
pub full_error_message: bool,
}

macro_rules! assert {
Expand Down Expand Up @@ -198,7 +211,7 @@ struct Deserializer<'de> {
// It only affects the field id generation in enum type.
is_untyped: bool,
zero_sized_values: usize,
minize_error_message: bool,
full_error_message: bool,
#[cfg(not(target_arch = "wasm32"))]
recursion_depth: u16,
}
Expand All @@ -217,8 +230,14 @@ impl<'de> Deserializer<'de> {
gamma: Gamma::default(),
field_name: None,
is_untyped: false,
#[cfg(not(target_arch = "wasm32"))]
zero_sized_values: 2_000_000,
minize_error_message: false,
#[cfg(target_arch = "wasm32")]
zero_sized_values: 0,
#[cfg(not(target_arch = "wasm32"))]
full_error_message: true,
#[cfg(target_arch = "wasm32")]
full_error_message: false,
#[cfg(not(target_arch = "wasm32"))]
recursion_depth: 0,
})
Expand Down Expand Up @@ -254,25 +273,26 @@ impl<'de> Deserializer<'de> {
Ok(res)
}
fn check_subtype(&mut self) -> Result<()> {
let res = subtype(
subtype(
&mut self.gamma,
&self.table,
&self.wire_type,
&self.expect_type,
);
if res.is_err() {
if self.minize_error_message {
return Err(Error::subtype(format!("{}", self.wire_type)));
)
.with_context(|| {
if self.full_error_message
|| (text_size(&self.wire_type, MAX_TYPE_LEN).is_ok()
&& text_size(&self.expect_type, MAX_TYPE_LEN).is_ok())
{
format!(
"{} is not a subtype of {}",
self.wire_type, self.expect_type,
)
} else {
res.with_context(|| {
format!(
"{} is not a subtype of {}",
self.wire_type, self.expect_type,
)
})
.map_err(Error::subtype)?;
"subtype mismatch".to_string()
}
}
})
.map_err(Error::subtype)?;
Ok(())
}
fn unroll_type(&mut self) -> Result<()> {
Expand Down
62 changes: 62 additions & 0 deletions rust/candid/src/types/internal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,68 @@ impl fmt::Display for TypeInner {
)
}
}
pub(crate) fn text_size(t: &Type, limit: i32) -> Result<i32, ()> {
use TypeInner::*;
if limit <= 1 {
return Err(());
}
let cost = match t.as_ref() {
Null | Bool | Text | Nat8 | Int8 => 4,
Nat | Int => 3,
Nat16 | Nat32 | Nat64 | Int16 | Int32 | Int64 | Empty => 5,
Float32 | Float64 => 7,
Reserved => 8,
Principal => 9,
Knot(_) => 5,
Var(id) => id.len() as i32,
Opt(t) => 4 + text_size(t, limit - 4)?,
Vec(t) => 4 + text_size(t, limit - 4)?,
Record(fs) | Variant(fs) => {
let mut cnt = 0;
let mut limit = limit;
for f in fs.iter() {
let id_size = match f.id.as_ref() {
Label::Named(n) => n.len() as i32,
Label::Id(_) => 4,
_ => 0,
};
cnt += id_size + text_size(&f.ty, limit - id_size - 3)? + 3;
limit -= cnt;
}
9 + cnt
}
Func(func) => {
let mode = if func.modes.is_empty() { 0 } else { 6 };
let mut cnt = mode + 6;
let mut limit = limit - cnt;
for t in func.args.iter() {
cnt += text_size(t, limit)?;
limit -= cnt;
}
for t in func.rets.iter() {
cnt += text_size(t, limit)?;
limit -= cnt;
}
cnt
}
Service(ms) => {
let mut cnt = 0;
let mut limit = limit;
for (name, f) in ms.iter() {
let len = name.len() as i32;
cnt += len + text_size(f, limit - len - 3)? + 3;
limit -= cnt;
}
10 + cnt
}
Class(_, _) | Future | Unknown => unimplemented!(),
};
if cost > limit {
Err(())
} else {
Ok(cost)
}
}

#[derive(Debug, Eq, Clone)]
pub enum Label {
Expand Down
2 changes: 1 addition & 1 deletion tools/ui/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.