forked from M-Labs/artiq
370 lines
12 KiB
Rust
370 lines
12 KiB
Rust
#![allow(dead_code)]
|
|
|
|
use core::slice;
|
|
use std::io::{self, Read, Write};
|
|
use proto::*;
|
|
use self::tag::{Tag, TagIterator, split_tag};
|
|
|
|
unsafe fn recv_value(reader: &mut Read, tag: Tag, data: &mut *mut (),
|
|
alloc: &Fn(usize) -> io::Result<*mut ()>) -> io::Result<()> {
|
|
macro_rules! consume_value {
|
|
($ty:ty, |$ptr:ident| $map:expr) => ({
|
|
let ptr = (*data) as *mut $ty;
|
|
*data = ptr.offset(1) as *mut ();
|
|
(|$ptr: *mut $ty| $map)(ptr)
|
|
})
|
|
}
|
|
|
|
match tag {
|
|
Tag::None => Ok(()),
|
|
Tag::Bool =>
|
|
consume_value!(u8, |ptr| {
|
|
*ptr = try!(read_u8(reader)); Ok(())
|
|
}),
|
|
Tag::Int32 =>
|
|
consume_value!(u32, |ptr| {
|
|
*ptr = try!(read_u32(reader)); Ok(())
|
|
}),
|
|
Tag::Int64 | Tag::Float64 =>
|
|
consume_value!(u64, |ptr| {
|
|
*ptr = try!(read_u64(reader)); Ok(())
|
|
}),
|
|
Tag::String => {
|
|
consume_value!(*mut u8, |ptr| {
|
|
let length = try!(read_u32(reader));
|
|
// NB: the received string includes a trailing \0
|
|
*ptr = try!(alloc(length as usize)) as *mut u8;
|
|
try!(reader.read_exact(slice::from_raw_parts_mut(*ptr, length as usize)));
|
|
Ok(())
|
|
})
|
|
}
|
|
Tag::Tuple(it, arity) => {
|
|
let mut it = it.clone();
|
|
for _ in 0..arity {
|
|
let tag = it.next().expect("truncated tag");
|
|
try!(recv_value(reader, tag, data, alloc))
|
|
}
|
|
Ok(())
|
|
}
|
|
Tag::List(it) | Tag::Array(it) => {
|
|
struct List { length: u32, elements: *mut () };
|
|
consume_value!(List, |ptr| {
|
|
(*ptr).length = try!(read_u32(reader));
|
|
|
|
let tag = it.clone().next().expect("truncated tag");
|
|
(*ptr).elements = try!(alloc(tag.size() * (*ptr).length as usize));
|
|
|
|
let mut data = (*ptr).elements;
|
|
for _ in 0..(*ptr).length as usize {
|
|
try!(recv_value(reader, tag, &mut data, alloc));
|
|
}
|
|
Ok(())
|
|
})
|
|
}
|
|
Tag::Range(it) => {
|
|
let tag = it.clone().next().expect("truncated tag");
|
|
try!(recv_value(reader, tag, data, alloc));
|
|
try!(recv_value(reader, tag, data, alloc));
|
|
try!(recv_value(reader, tag, data, alloc));
|
|
Ok(())
|
|
}
|
|
Tag::Keyword(_) => unreachable!(),
|
|
Tag::Object => unreachable!()
|
|
}
|
|
}
|
|
|
|
pub fn recv_return(reader: &mut Read, tag_bytes: &[u8], data: *mut (),
|
|
alloc: &Fn(usize) -> io::Result<*mut ()>) -> io::Result<()> {
|
|
let mut it = TagIterator::new(tag_bytes);
|
|
#[cfg(not(ksupport))]
|
|
trace!("recv ...->{}", it);
|
|
|
|
let tag = it.next().expect("truncated tag");
|
|
let mut data = data;
|
|
try!(unsafe { recv_value(reader, tag, &mut data, alloc) });
|
|
|
|
Ok(())
|
|
}
|
|
|
|
pub unsafe fn from_c_str<'a>(ptr: *const u8) -> &'a str {
|
|
use core::{str, slice};
|
|
extern { fn strlen(ptr: *const u8) -> usize; }
|
|
str::from_utf8_unchecked(slice::from_raw_parts(ptr as *const u8, strlen(ptr)))
|
|
}
|
|
|
|
unsafe fn send_value(writer: &mut Write, tag: Tag, data: &mut *const ()) -> io::Result<()> {
|
|
macro_rules! consume_value {
|
|
($ty:ty, |$ptr:ident| $map:expr) => ({
|
|
let ptr = (*data) as *const $ty;
|
|
*data = ptr.offset(1) as *const ();
|
|
(|$ptr: *const $ty| $map)(ptr)
|
|
})
|
|
}
|
|
|
|
try!(write_u8(writer, tag.as_u8()));
|
|
match tag {
|
|
Tag::None => Ok(()),
|
|
Tag::Bool =>
|
|
consume_value!(u8, |ptr|
|
|
write_u8(writer, *ptr)),
|
|
Tag::Int32 =>
|
|
consume_value!(u32, |ptr|
|
|
write_u32(writer, *ptr)),
|
|
Tag::Int64 | Tag::Float64 =>
|
|
consume_value!(u64, |ptr|
|
|
write_u64(writer, *ptr)),
|
|
Tag::String =>
|
|
consume_value!(*const u8, |ptr|
|
|
write_string(writer, from_c_str(*ptr))),
|
|
Tag::Tuple(it, arity) => {
|
|
let mut it = it.clone();
|
|
try!(write_u8(writer, arity));
|
|
for _ in 0..arity {
|
|
let tag = it.next().expect("truncated tag");
|
|
try!(send_value(writer, tag, data))
|
|
}
|
|
Ok(())
|
|
}
|
|
Tag::List(it) | Tag::Array(it) => {
|
|
struct List { length: u32, elements: *const () };
|
|
consume_value!(List, |ptr| {
|
|
try!(write_u32(writer, (*ptr).length));
|
|
let tag = it.clone().next().expect("truncated tag");
|
|
let mut data = (*ptr).elements;
|
|
for _ in 0..(*ptr).length as usize {
|
|
try!(send_value(writer, tag, &mut data));
|
|
}
|
|
Ok(())
|
|
})
|
|
}
|
|
Tag::Range(it) => {
|
|
let tag = it.clone().next().expect("truncated tag");
|
|
try!(send_value(writer, tag, data));
|
|
try!(send_value(writer, tag, data));
|
|
try!(send_value(writer, tag, data));
|
|
Ok(())
|
|
}
|
|
Tag::Keyword(it) => {
|
|
struct Keyword { name: *const u8, contents: () };
|
|
consume_value!(Keyword, |ptr| {
|
|
try!(write_string(writer, from_c_str((*ptr).name)));
|
|
let tag = it.clone().next().expect("truncated tag");
|
|
let mut data = &(*ptr).contents as *const ();
|
|
send_value(writer, tag, &mut data)
|
|
})
|
|
// Tag::Keyword never appears in composite types, so we don't have
|
|
// to accurately advance data.
|
|
}
|
|
Tag::Object => {
|
|
struct Object { id: u32 };
|
|
consume_value!(*const Object, |ptr|
|
|
write_u32(writer, (**ptr).id))
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn send_args(writer: &mut Write, service: u32, tag_bytes: &[u8],
|
|
data: *const *const ()) -> io::Result<()> {
|
|
let (arg_tags_bytes, return_tag_bytes) = split_tag(tag_bytes);
|
|
|
|
let mut args_it = TagIterator::new(arg_tags_bytes);
|
|
let return_it = TagIterator::new(return_tag_bytes);
|
|
#[cfg(not(ksupport))]
|
|
trace!("send<{}>({})->{}", service, args_it, return_it);
|
|
|
|
try!(write_u32(writer, service));
|
|
for index in 0.. {
|
|
if let Some(arg_tag) = args_it.next() {
|
|
let mut data = unsafe { *data.offset(index) };
|
|
try!(unsafe { send_value(writer, arg_tag, &mut data) });
|
|
} else {
|
|
break
|
|
}
|
|
}
|
|
try!(write_u8(writer, 0));
|
|
try!(write_bytes(writer, return_tag_bytes));
|
|
|
|
Ok(())
|
|
}
|
|
|
|
mod tag {
|
|
use core::fmt;
|
|
|
|
pub fn split_tag(tag_bytes: &[u8]) -> (&[u8], &[u8]) {
|
|
let tag_separator =
|
|
tag_bytes.iter()
|
|
.position(|&b| b == b':')
|
|
.expect("tag without a return separator");
|
|
let (arg_tags_bytes, rest) = tag_bytes.split_at(tag_separator);
|
|
let return_tag_bytes = &rest[1..];
|
|
(arg_tags_bytes, return_tag_bytes)
|
|
}
|
|
|
|
#[derive(Debug, Clone, Copy)]
|
|
pub enum Tag<'a> {
|
|
None,
|
|
Bool,
|
|
Int32,
|
|
Int64,
|
|
Float64,
|
|
String,
|
|
Tuple(TagIterator<'a>, u8),
|
|
List(TagIterator<'a>),
|
|
Array(TagIterator<'a>),
|
|
Range(TagIterator<'a>),
|
|
Keyword(TagIterator<'a>),
|
|
Object
|
|
}
|
|
|
|
impl<'a> Tag<'a> {
|
|
pub fn as_u8(self) -> u8 {
|
|
match self {
|
|
Tag::None => b'n',
|
|
Tag::Bool => b'b',
|
|
Tag::Int32 => b'i',
|
|
Tag::Int64 => b'I',
|
|
Tag::Float64 => b'f',
|
|
Tag::String => b's',
|
|
Tag::Tuple(_, _) => b't',
|
|
Tag::List(_) => b'l',
|
|
Tag::Array(_) => b'a',
|
|
Tag::Range(_) => b'r',
|
|
Tag::Keyword(_) => b'k',
|
|
Tag::Object => b'O',
|
|
}
|
|
}
|
|
|
|
pub fn size(self) -> usize {
|
|
match self {
|
|
Tag::None => 0,
|
|
Tag::Bool => 1,
|
|
Tag::Int32 => 4,
|
|
Tag::Int64 => 8,
|
|
Tag::Float64 => 8,
|
|
Tag::String => 4,
|
|
Tag::Tuple(it, arity) => {
|
|
let mut size = 0;
|
|
for _ in 0..arity {
|
|
let tag = it.clone().next().expect("truncated tag");
|
|
size += tag.size();
|
|
}
|
|
size
|
|
}
|
|
Tag::List(_) => 8,
|
|
Tag::Array(_) => 8,
|
|
Tag::Range(it) => {
|
|
let tag = it.clone().next().expect("truncated tag");
|
|
tag.size() * 3
|
|
}
|
|
Tag::Keyword(_) => unreachable!(),
|
|
Tag::Object => unreachable!(),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone, Copy)]
|
|
pub struct TagIterator<'a> {
|
|
data: &'a [u8]
|
|
}
|
|
|
|
impl<'a> TagIterator<'a> {
|
|
pub fn new(data: &'a [u8]) -> TagIterator<'a> {
|
|
TagIterator { data: data }
|
|
}
|
|
|
|
pub fn next(&mut self) -> Option<Tag<'a>> {
|
|
if self.data.len() == 0 {
|
|
return None
|
|
}
|
|
|
|
let tag_byte = self.data[0];
|
|
self.data = &self.data[1..];
|
|
Some(match tag_byte {
|
|
b'n' => Tag::None,
|
|
b'b' => Tag::Bool,
|
|
b'i' => Tag::Int32,
|
|
b'I' => Tag::Int64,
|
|
b'f' => Tag::Float64,
|
|
b's' => Tag::String,
|
|
b't' => {
|
|
let count = self.data[0];
|
|
self.data = &self.data[1..];
|
|
Tag::Tuple(self.sub(count), count)
|
|
}
|
|
b'l' => Tag::List(self.sub(1)),
|
|
b'a' => Tag::Array(self.sub(1)),
|
|
b'r' => Tag::Range(self.sub(1)),
|
|
b'k' => Tag::Keyword(self.sub(1)),
|
|
b'O' => Tag::Object,
|
|
_ => unreachable!()
|
|
})
|
|
}
|
|
|
|
fn sub(&mut self, count: u8) -> TagIterator<'a> {
|
|
let data = self.data;
|
|
for _ in 0..count {
|
|
self.next().expect("truncated tag");
|
|
}
|
|
TagIterator { data: &data[..(data.len() - self.data.len())] }
|
|
}
|
|
}
|
|
|
|
impl<'a> fmt::Display for TagIterator<'a> {
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
let mut it = self.clone();
|
|
let mut first = true;
|
|
while let Some(tag) = it.next() {
|
|
if first {
|
|
first = false
|
|
} else {
|
|
try!(write!(f, ", "))
|
|
}
|
|
|
|
match tag {
|
|
Tag::None =>
|
|
try!(write!(f, "None")),
|
|
Tag::Bool =>
|
|
try!(write!(f, "Bool")),
|
|
Tag::Int32 =>
|
|
try!(write!(f, "Int32")),
|
|
Tag::Int64 =>
|
|
try!(write!(f, "Int64")),
|
|
Tag::Float64 =>
|
|
try!(write!(f, "Float64")),
|
|
Tag::String =>
|
|
try!(write!(f, "String")),
|
|
Tag::Tuple(it, _) => {
|
|
try!(write!(f, "Tuple("));
|
|
try!(it.fmt(f));
|
|
try!(write!(f, ")"))
|
|
}
|
|
Tag::List(it) => {
|
|
try!(write!(f, "List("));
|
|
try!(it.fmt(f));
|
|
try!(write!(f, ")"))
|
|
}
|
|
Tag::Array(it) => {
|
|
try!(write!(f, "Array("));
|
|
try!(it.fmt(f));
|
|
try!(write!(f, ")"))
|
|
}
|
|
Tag::Range(it) => {
|
|
try!(write!(f, "Range("));
|
|
try!(it.fmt(f));
|
|
try!(write!(f, ")"))
|
|
}
|
|
Tag::Keyword(it) => {
|
|
try!(write!(f, "Keyword("));
|
|
try!(it.fmt(f));
|
|
try!(write!(f, ")"))
|
|
}
|
|
Tag::Object =>
|
|
try!(write!(f, "Object"))
|
|
}
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
}
|
|
}
|