Skip to content

Commit

Permalink
Merge 4e9c4a1 into 3033772
Browse files Browse the repository at this point in the history
  • Loading branch information
chenyan-dfinity authored Oct 4, 2023
2 parents 3033772 + 4e9c4a1 commit d608a6b
Show file tree
Hide file tree
Showing 6 changed files with 129 additions and 43 deletions.
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.

0 comments on commit d608a6b

Please sign in to comment.