blob: cc57185b28a9fc3e1841cb3f188d54f37e6b7d6b [file] [log] [blame]
// Copyright 2019 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use fidl::encoding::{
AtRestFlags, DynamicFlags, ALLOC_PRESENT_U32, ALLOC_PRESENT_U64, MAGIC_NUMBER_INITIAL,
};
use fidl::AsHandleRef as _;
use std::collections::HashMap;
use crate::error::{Error, Result};
use crate::library;
use crate::util::*;
use crate::value::Value;
/// Callback used to encode out-of-line data after in-line data has been returned.
type DeferCallback<'n, 't> = dyn FnOnce(&mut EncodeBuffer<'n>, RecursionCounter) -> Result<()> + 't;
/// Turn a list of callbacks into one callback that calls each of the list in sequence.
fn combine_calls<'n: 't, 't>(calls: Vec<Box<DeferCallback<'n, 't>>>) -> Box<DeferCallback<'n, 't>> {
Box::new(move |this, counter| {
for call in calls {
call(this, counter)?;
}
Ok(())
})
}
enum HandleType<'s> {
ClientEnd(&'s str),
ServerEnd(&'s str),
Bare,
}
/// Buffer containing a partially or fully encoded value.
struct EncodeBuffer<'n> {
ns: &'n library::Namespace,
bytes: Vec<u8>,
handles: Vec<fidl::HandleDisposition<'static>>,
}
impl<'n> EncodeBuffer<'n> {
/// Pad the output buffer so the next thing we add will be aligned to 8 bytes.
fn align_8(&mut self) {
self.bytes
.extend(std::iter::repeat(0u8).take(alignment_padding_for_size(self.bytes.len())));
}
fn encode_transaction<'n_i: 't, 't>(
ns: &'n_i library::Namespace,
txid: u32,
protocol_name: &str,
direction: Direction,
method_name: &str,
value: Value,
) -> Result<(Vec<u8>, Vec<fidl::HandleDisposition<'static>>)> {
let mut buf = EncodeBuffer { ns, bytes: Vec::new(), handles: Vec::new() };
let protocol = match ns.lookup(protocol_name)? {
library::LookupResult::Protocol(i) => Ok(i),
_ => Err(Error::LibraryError(format!("Could not find protocol '{}'.", protocol_name))),
}?;
let method = protocol.methods.get(method_name).ok_or(Error::LibraryError(format!(
"Could not find method '{}' on protocol '{}'",
method_name, protocol_name
)))?;
let (ty, has) = match direction {
Direction::Request => {
if !method.has_response && txid != 0 {
return Err(Error::EncodeError(
"Non-zero transaction ID for one-way method.".to_owned(),
));
}
(method.request.as_ref(), method.has_request)
}
Direction::Response => (method.response.as_ref(), method.has_response),
};
let dynamic_flags =
if method.strict { DynamicFlags::empty() } else { DynamicFlags::FLEXIBLE };
if !has {
return Err(Error::LibraryError(format!(
"Method '{}' on protocol '{}' has no {}",
method_name,
protocol_name,
direction.to_string()
)));
}
buf.bytes.extend(&txid.to_le_bytes());
buf.bytes.extend(&AtRestFlags::USE_V2_WIRE_FORMAT.bits().to_le_bytes());
buf.bytes.push(dynamic_flags.bits());
buf.bytes.push(MAGIC_NUMBER_INITIAL);
buf.bytes.extend(&method.ordinal.to_le_bytes());
if let Some(ty) = ty {
buf.encode_type(ty, value)?(&mut buf, RecursionCounter::new())?
} else if !matches!(value, Value::Null) {
return Err(Error::EncodeError("Value must be null.".to_owned()));
} else {
};
buf.align_8();
Ok((buf.bytes, buf.handles))
}
fn encode_struct_nonnull<'t>(
&mut self,
st: &'n library::Struct,
value: Value,
start_offset: usize,
) -> Result<Box<DeferCallback<'n, 't>>>
where
'n: 't,
{
let start_offset = self.bytes.len() - start_offset;
let values = match value {
Value::Object(s) => Ok(s),
_ => Err(Error::EncodeError("Value is not a struct.".to_owned())),
}?;
let mut values = {
let mut map = HashMap::with_capacity(values.len());
for (k, v) in values {
map.insert(k, v);
}
map
};
let mut calls = Vec::new();
for member in &st.members {
let value = values.remove(&member.name).unwrap_or(Value::Null);
self.bytes.extend(
std::iter::repeat(0u8).take(member.offset - (self.bytes.len() - start_offset)),
);
calls.push(self.encode_type(&member.ty, value)?);
}
if let Some((name, _)) = values.into_iter().next() {
Err(Error::EncodeError(format!("Unknown struct member: {}", name)))
} else {
self.bytes
.extend(std::iter::repeat(0u8).take(st.size - (self.bytes.len() - start_offset)));
Ok(combine_calls(calls))
}
}
fn encode_type<'t>(
&mut self,
ty: &'n library::Type,
value: Value,
) -> Result<Box<DeferCallback<'n, 't>>>
where
'n: 't,
{
use library::Type::*;
match ty {
Unknown(_) | UnknownString(_) => {
return Err(Error::LibraryError("Unknown type".to_owned()))
}
Bool => self.encode_raw(if bool::try_from(value)? { &[1u8] } else { &[0u8] }),
U8 => self.encode_raw(&u8::try_from(value)?.to_le_bytes()),
U16 => self.encode_raw(&u16::try_from(value)?.to_le_bytes()),
U32 => self.encode_raw(&u32::try_from(value)?.to_le_bytes()),
U64 => self.encode_raw(&u64::try_from(value)?.to_le_bytes()),
I8 => self.encode_raw(&i8::try_from(value)?.to_le_bytes()),
I16 => self.encode_raw(&i16::try_from(value)?.to_le_bytes()),
I32 => self.encode_raw(&i32::try_from(value)?.to_le_bytes()),
I64 => self.encode_raw(&i64::try_from(value)?.to_le_bytes()),
F32 => self.encode_raw(&f32::try_from(value)?.to_le_bytes()),
F64 => self.encode_raw(&f64::try_from(value)?.to_le_bytes()),
Array(ty, size) => self.encode_array(ty, *size, value),
Vector { ty, nullable, element_count } => {
self.encode_vector(ty, *nullable, value, *element_count)
}
String { nullable, byte_count } => self.encode_string(*nullable, value, *byte_count),
Handle { object_type, rights, nullable } => {
self.encode_handle(*object_type, *rights, HandleType::Bare, *nullable, value)
}
FrameworkError => self.encode_raw(&[0, 0, 0, 0]),
Request { identifier, rights, nullable } => self.encode_handle(
fidl::ObjectType::CHANNEL,
*rights,
HandleType::ServerEnd(identifier),
*nullable,
value,
),
Identifier { name, nullable } => self.encode_identifier(name.clone(), *nullable, value),
}
}
fn encode_raw<'t>(&mut self, data: &[u8]) -> Result<Box<DeferCallback<'n, 't>>>
where
'n: 't,
{
self.bytes.extend(data);
Ok(Box::new(|_, _| Ok(())))
}
fn encode_array<'t>(
&mut self,
ty: &'n library::Type,
size: usize,
value: Value,
) -> Result<Box<DeferCallback<'n, 't>>>
where
'n: 't,
{
let values = if let Value::List(v) = value {
Ok(v)
} else {
Err(Error::EncodeError("Expected a list".to_owned()))
}?;
if values.len() != size {
return Err(Error::EncodeError(format!("Expected list of length {}", size)));
}
let mut calls = Vec::with_capacity(size);
for value in values {
calls.push(self.encode_type(ty, value)?);
}
Ok(combine_calls(calls))
}
fn encode_vector<'t>(
&mut self,
ty: &'n library::Type,
nullable: bool,
value: Value,
element_count: Option<usize>,
) -> Result<Box<DeferCallback<'n, 't>>>
where
'n: 't,
{
let values = match (value, nullable) {
(Value::Null, true) => Ok(None),
(Value::Null, false) => {
Err(Error::EncodeError("Got null for non-nullable list".to_owned()))
}
(Value::List(v), _) => Ok(Some(v)),
_ => Err(Error::EncodeError("Expected a list".to_owned())),
}?;
if let Some(values) = values {
if element_count.map(|x| x < values.len()).unwrap_or(false) {
return Err(Error::EncodeError("Vector exceeded max size".to_owned()));
}
self.bytes.extend(&(values.len() as u64).to_le_bytes());
self.bytes.extend(&ALLOC_PRESENT_U64.to_le_bytes());
Ok(Box::new(move |this, counter| {
let counter = counter.next()?;
let mut calls = Vec::with_capacity(values.len());
for value in values {
calls.push(this.encode_type(ty, value)?);
}
this.align_8();
for call in calls {
call(this, counter)?;
}
Ok(())
}))
} else {
self.bytes.extend(std::iter::repeat(0u8).take(16));
Ok(Box::new(|_, _| Ok(())))
}
}
fn encode_string<'t>(
&mut self,
nullable: bool,
value: Value,
byte_count: Option<usize>,
) -> Result<Box<DeferCallback<'n, 't>>>
where
'n: 't,
{
let string = match (value, nullable) {
(Value::Null, true) => Ok(None),
(Value::Null, false) => {
Err(Error::EncodeError("Got null for non-nullable string".to_owned()))
}
(Value::String(s), _) => Ok(Some(s)),
_ => Err(Error::EncodeError("Expected a string".to_owned())),
}?;
if let Some(string) = string {
if byte_count.map(|x| x < string.len()).unwrap_or(false) {
return Err(Error::EncodeError("String exceeded max size".to_owned()));
}
self.bytes.extend(&(string.len() as u64).to_le_bytes());
self.bytes.extend(&ALLOC_PRESENT_U64.to_le_bytes());
Ok(Box::new(move |this, counter| {
let _counter = counter.next()?;
this.bytes.extend(string.as_bytes());
this.align_8();
Ok(())
}))
} else {
self.bytes.extend(std::iter::repeat(0u8).take(16));
Ok(Box::new(|_, _| Ok(())))
}
}
fn encode_handle<'t>(
&mut self,
object_type: fidl::ObjectType,
rights: fidl::Rights,
expect: HandleType<'_>,
nullable: bool,
value: Value,
) -> Result<Box<DeferCallback<'n, 't>>>
where
'n: 't,
{
let handle_op = match (value, nullable, expect) {
(Value::Null, true, _) => Ok(None),
(Value::Handle(h, _), true, _) if h.is_invalid() => Ok(None),
(Value::ServerEnd(h, _), true, _) if h.as_handle_ref().is_invalid() => Ok(None),
(Value::ClientEnd(h, _), true, _) if h.as_handle_ref().is_invalid() => Ok(None),
(Value::Handle(h, _), false, _) if h.is_invalid() => {
Err(Error::EncodeError("Got invalid handle for non-nullable handle".to_owned()))
}
(Value::ServerEnd(h, _), false, _) if h.as_handle_ref().is_invalid() => {
Err(Error::EncodeError("Got invalid handle for non-nullable handle".to_owned()))
}
(Value::ClientEnd(h, _), false, _) if h.as_handle_ref().is_invalid() => {
Err(Error::EncodeError("Got invalid handle for non-nullable handle".to_owned()))
}
(Value::Null, false, _) => {
Err(Error::EncodeError("Got null for non-nullable handle".to_owned()))
}
(Value::Handle(h, s), _, _) => {
if s != object_type && s != fidl::ObjectType::NONE {
Err(Error::EncodeError(format!(
"Expected object type {object_type:?} got {s:?}"
)))
} else {
Ok(Some(fidl::HandleOp::Move(h)))
}
}
(Value::ServerEnd(h, s), _, HandleType::ServerEnd(expect))
| (Value::ClientEnd(h, s), _, HandleType::ClientEnd(expect)) => {
if expect != s {
Err(Error::EncodeError(format!(
"Expected endpoint for protocol {expect}, got one for {s}"
)))
} else if object_type != fidl::ObjectType::CHANNEL {
Err(Error::EncodeError(format!(
"Expected object type {object_type:?} got channel for protocol {s}"
)))
} else {
Ok(Some(fidl::HandleOp::Move(h.into())))
}
}
(Value::ServerEnd(_, s), _, HandleType::ClientEnd(expect))
| (Value::ClientEnd(_, s), _, HandleType::ServerEnd(expect)) => {
if expect != s {
Err(Error::EncodeError(format!(
"Expected endpoint for protocol {expect}, got one for {s}"
)))
} else if object_type != fidl::ObjectType::CHANNEL {
Err(Error::EncodeError(format!(
"Expected object type {object_type:?} got channel for protocol {s}"
)))
} else {
Err(Error::EncodeError(format!("Got wrong end of channel for {expect}")))
}
}
(Value::ServerEnd(h, s), _, HandleType::Bare)
| (Value::ClientEnd(h, s), _, HandleType::Bare) => {
if object_type != fidl::ObjectType::CHANNEL {
Err(Error::EncodeError(format!(
"Expected object type {object_type:?} got channel for protocol {s}"
)))
} else {
Ok(Some(fidl::HandleOp::Move(h.into())))
}
}
_ => Err(Error::EncodeError("Expected a handle".to_owned())),
}?;
if let Some(handle_op) = handle_op {
self.bytes.extend(&ALLOC_PRESENT_U32.to_le_bytes());
Ok(Box::new(move |this, _| {
this.handles.push(fidl::HandleDisposition {
handle_op,
object_type,
rights,
result: fidl::Status::OK,
});
Ok(())
}))
} else {
self.bytes.extend(&0u32.to_le_bytes());
Ok(Box::new(|_, _| Ok(())))
}
}
fn encode_identifier<'t>(
&mut self,
name: String,
nullable: bool,
value: Value,
) -> Result<Box<DeferCallback<'n, 't>>>
where
'n: 't,
{
use library::LookupResult::*;
match (self.ns.lookup(&name)?, nullable) {
(Bits(b), false) => self.encode_bits(b, value),
(Enum(e), false) => self.encode_enum(e, value),
(Table(t), false) => self.encode_table(t, value),
(Struct(s), nullable) => self.encode_struct(s, nullable, value),
(Union(u), nullable) => self.encode_union(u, nullable, value),
(Protocol(_), nullable) => self.encode_handle(
fidl::ObjectType::CHANNEL,
fidl::Rights::CHANNEL_DEFAULT,
HandleType::ClientEnd(&name),
nullable,
value,
),
_ => Err(Error::LibraryError(format!("Type {} shouldn't be nullable", name))),
}
}
fn encode_bits<'t>(
&mut self,
bits: &'n library::Bits,
value: Value,
) -> Result<Box<DeferCallback<'n, 't>>>
where
'n: 't,
{
let value = match value {
Value::Bits(name, inner) => {
if name == bits.name {
*inner
} else {
return Err(Error::EncodeError(format!(
"Expected {}, got {}",
bits.name, name
)));
}
}
_ => value,
};
// If we can't get the bits, this is probably the wrong type of value. Use 0 so we ignore
// the mask check and let encode_type return the error message.
let data = u64::try_from(&value).unwrap_or(0);
if bits.strict && data & !bits.mask != 0 {
Err(Error::EncodeError(format!("Invalid bits set on {}", bits.name)))
} else {
self.encode_type(&bits.ty, value)
}
}
fn encode_enum<'t>(
&mut self,
en: &'n library::Enum,
value: Value,
) -> Result<Box<DeferCallback<'n, 't>>>
where
'n: 't,
{
let value = match value {
Value::Enum(name, inner) => {
if name == en.name {
*inner
} else {
return Err(Error::EncodeError(format!("Expected {}, got {}", en.name, name)));
}
}
_ => value,
};
for item in &en.members {
if !en.strict || item.value.cast_equals(&value) {
return self.encode_type(&en.ty, value);
}
}
Err(Error::EncodeError("Invalid enum variant".to_owned()))
}
fn encode_struct<'t>(
&mut self,
st: &'n library::Struct,
nullable: bool,
value: Value,
) -> Result<Box<DeferCallback<'n, 't>>>
where
'n: 't,
{
let value = match (value, nullable) {
(Value::Null, true) => Ok(None),
(Value::Null, false) => Err(Error::EncodeError("Struct can't be null".to_owned())),
(value, _) => Ok(Some(value)),
}?;
if let Some(value) = value {
if nullable {
self.bytes.extend(&ALLOC_PRESENT_U64.to_le_bytes());
Ok(Box::new(move |this, counter| {
let counter = counter.next()?;
let call = this.encode_struct_nonnull(st, value, 0)?;
this.align_8();
call(this, counter)
}))
} else {
self.encode_struct_nonnull(st, value, 0)
}
} else {
self.bytes.extend(&0u64.to_le_bytes());
Ok(Box::new(|_, _| Ok(())))
}
}
fn encode_envelope<'t>(
&mut self,
ty: &'n library::Type,
value: Value,
) -> Result<Box<DeferCallback<'n, 't>>>
where
'n: 't,
{
let header_pos = self.bytes.len();
self.bytes.extend(&[0u8; 8]);
if let Value::Null = value {
Ok(Box::new(|_, _| Ok(())))
} else {
Ok(Box::new(move |this, counter| {
let counter = counter.next()?;
let start = this.bytes.len();
let handle_start = this.handles.len();
let header = if ty.inline_size(this.ns)? > 4 {
let call = this.encode_type(ty, value)?;
this.align_8();
call(this, counter)?;
let size = (this.bytes.len() - start) as u32;
let handle_count = (this.handles.len() - handle_start) as u16;
debug_assert!(size > 0 || handle_count > 0);
let mut header = Vec::new();
header.extend(&size.to_le_bytes());
header.extend(&handle_count.to_le_bytes());
header.extend(&0u16.to_le_bytes());
header
} else {
let mut header_buf =
EncodeBuffer { ns: this.ns, bytes: Vec::new(), handles: Vec::new() };
header_buf.encode_type(ty, value)?(&mut header_buf, counter)?;
let EncodeBuffer { bytes: mut header, handles, .. } = header_buf;
header.resize(4, 0);
header.extend(&(handles.len() as u16).to_le_bytes());
header.extend(&1u16.to_le_bytes());
this.handles.extend(handles);
header
};
this.bytes.splice(header_pos..(header_pos + header.len()), header.into_iter());
Ok(())
}))
}
}
fn encode_union<'t>(
&mut self,
union: &'n library::TableOrUnion,
nullable: bool,
value: Value,
) -> Result<Box<DeferCallback<'n, 't>>>
where
'n: 't,
{
let entry = match value {
Value::Null => Ok(None),
Value::Union(u, n, b) if *u == union.name => Ok(Some((n, *b))),
_ => Err(Error::EncodeError(format!("Expected {}", union.name))),
}?;
if let Some((variant, value)) = entry {
for member in union.members.values() {
if *member.name == *variant {
self.bytes.extend(&member.ordinal.to_le_bytes());
return self.encode_envelope(&member.ty, value);
}
}
Err(Error::EncodeError(format!("Unrecognized union variant: '{}'", variant)))
} else if nullable {
self.bytes.extend(std::iter::repeat(0u8).take(16));
Ok(Box::new(|_, _| Ok(())))
} else {
Err(Error::EncodeError("Got null for non-nullable Union".to_owned()))
}
}
fn encode_table<'t>(
&mut self,
table: &'n library::TableOrUnion,
value: Value,
) -> Result<Box<DeferCallback<'n, 't>>>
where
'n: 't,
{
let values = match value {
Value::Object(values) => Ok(values),
_ => Err(Error::EncodeError(format!("Could not convert to {}", table.name))),
}?;
let mut values_array = Vec::new();
for (value_name, value) in values {
for (&ord, member) in &table.members {
let array_idx = usize::try_from(ord - 1).unwrap();
if values_array.len() <= array_idx {
values_array.resize_with(array_idx + 1, || None);
}
if *member.name == value_name {
values_array[array_idx] = Some((&member.ty, value));
break;
}
}
}
while values_array.last().map(|x| x.is_none()).unwrap_or(false) {
values_array.pop();
}
self.bytes.extend(&(values_array.len() as u64).to_le_bytes());
self.bytes.extend(&ALLOC_PRESENT_U64.to_le_bytes());
Ok(Box::new(move |this, counter| {
let counter = counter.next()?;
let mut calls = Vec::with_capacity(values_array.len());
for slot in values_array.into_iter() {
if let Some((ty, item)) = slot {
calls.push(this.encode_envelope(ty, item)?);
} else {
this.bytes.extend(&0u64.to_le_bytes());
}
}
for call in calls {
call(this, counter)?;
}
Ok(())
}))
}
}
/// Serialize a value into a FIDL request.
pub fn encode_request(
ns: &library::Namespace,
txid: u32,
protocol_name: &str,
method_name: &str,
value: Value,
) -> Result<(Vec<u8>, Vec<fidl::HandleDisposition<'static>>)> {
EncodeBuffer::encode_transaction(
ns,
txid,
protocol_name,
Direction::Request,
method_name,
value,
)
}
/// Serialize a value into a FIDL response.
pub fn encode_response(
ns: &library::Namespace,
txid: u32,
protocol_name: &str,
method_name: &str,
value: Value,
) -> Result<(Vec<u8>, Vec<fidl::HandleDisposition<'static>>)> {
EncodeBuffer::encode_transaction(
ns,
txid,
protocol_name,
Direction::Response,
method_name,
value,
)
}
/// Serialize a value into a FIDL serialized value.
pub fn encode(
ns: &library::Namespace,
type_name: &str,
nullable: bool,
value: Value,
) -> Result<(Vec<u8>, Vec<fidl::HandleDisposition<'static>>)> {
let mut buf = EncodeBuffer { ns, bytes: Vec::new(), handles: Vec::new() };
let cb = buf.encode_identifier(type_name.to_owned(), nullable, value)?;
buf.align_8();
cb(&mut buf, RecursionCounter::new()).map(|_| (buf.bytes, buf.handles))
}