Skip to content
Closed
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
1 change: 1 addition & 0 deletions Cargo.lock

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

1 change: 1 addition & 0 deletions src/dfx_info/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ pub trait Serializer: Sized {
fn serialize_struct(self) -> Result<Self::Compound, Self::Error>;
fn serialize_vec(self, len: usize) -> Result<Self::Compound, Self::Error>;
fn serialize_variant(self, index: u64) -> Result<Self::Compound, Self::Error>;
fn serialize_blob(self, v: &[u8]) -> Result<(), Self::Error>;
}

pub trait Compound {
Expand Down
3 changes: 3 additions & 0 deletions src/dfx_info/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ pub enum Type {
Vec(Box<Type>),
Record(Vec<Field>),
Variant(Vec<Field>),
Service,
}

#[derive(Debug, PartialEq, Hash, Eq, Clone)]
Expand All @@ -38,10 +39,12 @@ pub struct Field {
pub ty: Type,
}

// This function decides only non-primitive types go into the type table
pub fn is_primitive(t: &Type) -> bool {
use Type::*;
match t {
Null | Bool | Nat | Int | Text => true,
Service => false,
Unknown => panic!("Unknown type"),
Knot(_) => true,
Opt(_) | Vec(_) | Record(_) | Variant(_) => false,
Expand Down
1 change: 1 addition & 0 deletions src/serde_idl/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ serde_bytes = "0.11.2"
paste = "0.1"
num_enum = "0.4.1"
dfx_info = { path = "../dfx_info" }
ic-http-agent = { path = "../ic_http_agent" }
lalrpop-util = "0.17.2"
pretty = "0.5"

Expand Down
48 changes: 42 additions & 6 deletions src/serde_idl/src/de.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ enum Opcode {
Vec = -19,
Record = -20,
Variant = -21,
Service = -23,
}

/// Use this struct to deserialize a sequence of Rust values (heterogeneous) from IDL binary message.
Expand Down Expand Up @@ -152,17 +153,21 @@ impl<'de> Deserializer<'de> {
fn sleb128_read(&mut self) -> Result<i64> {
sleb128_decode(&mut self.input).map_err(Error::msg)
}
fn parse_string(&mut self, len: usize) -> Result<String> {
let mut buf = Vec::new();
buf.resize(len, 0);
self.input.read_exact(&mut buf)?;
String::from_utf8(buf).map_err(Error::msg)
}
fn parse_byte(&mut self) -> Result<u8> {
let mut buf = [0u8; 1];
self.input.read_exact(&mut buf)?;
Ok(buf[0])
}
fn parse_bytes(&mut self, len: usize) -> Result<Vec<u8>> {
let mut buf = Vec::new();
buf.resize(len, 0);
self.input.read_exact(&mut buf)?;
Ok(buf)
}
fn parse_string(&mut self, len: usize) -> Result<String> {
let buf = self.parse_bytes(len)?;
String::from_utf8(buf).map_err(Error::msg)
}
fn parse_magic(&mut self) -> Result<()> {
let mut buf = [0u8; 4];
match self.input.read(&mut buf) {
Expand Down Expand Up @@ -190,6 +195,18 @@ impl<'de> Deserializer<'de> {
buf.push(RawValue::I(self.sleb128_read()?));
}
}
Ok(Opcode::Service) => {
let meth_len = self.leb128_read()?;
buf.push(RawValue::U(meth_len));
for _ in 0..meth_len {
let name_len = self.leb128_read()?;
buf.push(RawValue::U(name_len));
for _ in 0..name_len {
buf.push(RawValue::U(self.parse_byte()? as u64));
}
buf.push(RawValue::I(self.sleb128_read()?));
}
}
_ => {
return Err(Error::msg(format!(
"Unsupported op_code {} in type table",
Expand Down Expand Up @@ -298,6 +315,7 @@ impl<'de, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de> {
Opcode::Opt => self.deserialize_option(visitor),
Opcode::Record => self.deserialize_struct("_", &[], visitor),
Opcode::Variant => self.deserialize_enum("_", &[], visitor),
Opcode::Service => self.deserialize_any(visitor),
}
}

Expand Down Expand Up @@ -342,6 +360,24 @@ impl<'de, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de> {
}
visitor.visit_enum(Compound::new(&mut self, Style::Enum { len, fs }))
}
Opcode::Service => {
self.check_type(Opcode::Service)?;
let len = self.pop_current_type()?.get_u64()? as u32;
for _ in 0..len {
let name_len = self.pop_current_type()?.get_u64()? as u32;
for _ in 0..name_len {
self.pop_current_type()?.get_u64()?;
}
self.pop_current_type()?.get_i64()?;
}
let bit = self.parse_byte()?;
if bit != 1u8 {
return Err(Error::msg("Opaque reference not supported"));
}
let len = self.leb128_read()? as usize;
let vec = self.parse_bytes(len)?;
visitor.visit_byte_buf(vec)
}
}
}

Expand Down
1 change: 1 addition & 0 deletions src/serde_idl/src/grammar.lalrpop
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ Arg: IDLValue = {
IDLValue::Record(fs)
},
"variant" "{" <VariantField> "}" => IDLValue::Variant(Box::new(<>)),
"service" <"text"> => IDLValue::Service(<>),
}

Field: IDLField = {
Expand Down
1 change: 1 addition & 0 deletions src/serde_idl/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
//! ```

extern crate dfx_info;
extern crate ic_http_agent;
extern crate leb128;
extern crate num_enum;
extern crate serde;
Expand Down
17 changes: 16 additions & 1 deletion src/serde_idl/src/ser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use super::value::IDLValue;
use dfx_info::types::{Field, Type};
use std::collections::HashMap;
use std::io;
use std::io::Write;
use std::vec::Vec;

use leb128::write::{signed as sleb128_encode, unsigned as leb128_encode};
Expand Down Expand Up @@ -67,14 +68,17 @@ impl ValueSerializer {
fn write_leb128(&mut self, value: u64) {
leb128_encode(&mut self.value, value).unwrap();
}
fn write(&mut self, bytes: &[u8]) {
self.value.write_all(bytes).unwrap();
}
}

impl<'a> dfx_info::Serializer for &'a mut ValueSerializer {
type Error = Error;
type Compound = Compound<'a>;
fn serialize_bool(self, v: bool) -> Result<()> {
let v = if v { 1 } else { 0 };
self.write_leb128(v);
self.write(&[v]);
Ok(())
}
fn serialize_int(self, v: i64) -> Result<()> {
Expand All @@ -91,6 +95,12 @@ impl<'a> dfx_info::Serializer for &'a mut ValueSerializer {
self.value.append(&mut buf);
Ok(())
}
fn serialize_blob(self, blob: &[u8]) -> Result<()> {
self.write(&[1]);
self.write_leb128(blob.len() as u64);
self.write(blob);
Ok(())
}
fn serialize_null(self, _v: ()) -> Result<()> {
Ok(())
}
Expand Down Expand Up @@ -209,6 +219,11 @@ impl TypeSerialize {
self.encode(&mut buf, ty)?;
}
}
Type::Service => {
sleb128_encode(&mut buf, -23)?;
// TODO add method types
leb128_encode(&mut buf, 0)?;
}
_ => panic!("unreachable"),
};
self.type_table[idx] = buf;
Expand Down
12 changes: 12 additions & 0 deletions src/serde_idl/src/value.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use dfx_info::types::{Field, Type};
use ic_http_agent::CanisterId;
use serde::de;
use serde::de::{Deserialize, Visitor};
use std::fmt;
Expand All @@ -16,6 +17,7 @@ pub enum IDLValue {
Vec(Vec<IDLValue>),
Record(Vec<IDLField>),
Variant(Box<IDLField>),
Service(String),
}

#[derive(Debug, PartialEq, Clone)]
Expand Down Expand Up @@ -110,6 +112,7 @@ impl fmt::Display for IDLValue {
write!(f, "}}")
}
IDLValue::Variant(ref v) => write!(f, "variant {{ {} }}", v),
IDLValue::Service(ref s) => write!(f, "service \"{}\"", s),
}
}
}
Expand Down Expand Up @@ -169,6 +172,7 @@ impl dfx_info::IDLType for IDLValue {
};
Type::Variant(vec![f])
}
IDLValue::Service(_) => Type::Service,
}
}
fn idl_serialize<S>(&self, serializer: S) -> Result<(), S::Error>
Expand Down Expand Up @@ -203,6 +207,10 @@ impl dfx_info::IDLType for IDLValue {
ser.serialize_element(&v.val)?;
Ok(())
}
IDLValue::Service(ref s) => {
let blob = CanisterId::from_text(s).unwrap().into_blob();
serializer.serialize_blob(blob.as_slice())
}
}
}
}
Expand Down Expand Up @@ -237,6 +245,10 @@ impl<'de> Deserialize<'de> for IDLValue {
{
self.visit_string(String::from(value))
}
fn visit_byte_buf<E>(self, blob: Vec<u8>) -> Result<IDLValue, E> {
let canister_id = CanisterId::from_bytes(blob);
Ok(IDLValue::Service(canister_id.to_text()))
}
fn visit_none<E>(self) -> Result<IDLValue, E> {
Ok(IDLValue::None)
}
Expand Down
11 changes: 11 additions & 0 deletions src/serde_idl/tests/value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ fn test_parser() {
parse_check(
"(variant { cons=record{ 42; variant { cons=record{43; variant { nil=record{} }} } } })",
);
parse_check("(service \"ic:00\")");
}

#[test]
Expand Down Expand Up @@ -67,6 +68,16 @@ fn test_variant() {
test_decode(&encoded, &value);
}

#[test]
fn test_reference() {
use IDLValue::*;
check(Service("ic:00".to_string()), "4449444c01690001000100");
check(
Service("ic:ABCD01A7".to_string()),
"4449444c01690001000103abcd01",
);
}

fn parse_check(str: &str) {
let args = str.parse::<IDLArgs>().unwrap();
let encoded = args.to_bytes().unwrap();
Expand Down