From 5a630067cbae096e9dd65e7dc6d6f671bbb5b4c7 Mon Sep 17 00:00:00 2001 From: whitequark Date: Thu, 6 Oct 2016 06:31:27 +0000 Subject: [PATCH] Rust: implement sending for all RPC types. --- artiq/runtime.rs/src/proto.rs | 14 +++ artiq/runtime.rs/src/rpc.rs | 130 ++++++++++++++++++++++---- artiq/runtime.rs/src/session_proto.rs | 11 --- 3 files changed, 126 insertions(+), 29 deletions(-) diff --git a/artiq/runtime.rs/src/proto.rs b/artiq/runtime.rs/src/proto.rs index d7df40b8c..87746e494 100644 --- a/artiq/runtime.rs/src/proto.rs +++ b/artiq/runtime.rs/src/proto.rs @@ -2,6 +2,7 @@ use std::io::{self, Read, Write}; use std::vec::Vec; +use std::string::String; use byteorder::{ByteOrder, NetworkEndian}; // FIXME: replace these with byteorder core io traits once those are in @@ -63,3 +64,16 @@ pub fn write_bytes(writer: &mut Write, value: &[u8]) -> io::Result<()> { try!(write_u32(writer, value.len() as u32)); writer.write_all(value) } + +pub fn read_string(reader: &mut Read) -> io::Result { + let mut bytes = try!(read_bytes(reader)); + let len = bytes.len() - 1; // length without trailing \0 + bytes.resize(len, 0); // FIXME: don't send \0 in the first place + String::from_utf8(bytes).map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e)) +} + +pub fn write_string(writer: &mut Write, value: &str) -> io::Result<()> { + try!(write_u32(writer, (value.len() + 1) as u32)); + try!(writer.write_all(value.as_bytes())); + write_u8(writer, 0) +} diff --git a/artiq/runtime.rs/src/rpc.rs b/artiq/runtime.rs/src/rpc.rs index 5507a98e0..cc01c8d10 100644 --- a/artiq/runtime.rs/src/rpc.rs +++ b/artiq/runtime.rs/src/rpc.rs @@ -1,5 +1,5 @@ use std::io::{self, Read, Write}; -use proto::{write_u8, write_bytes}; +use proto::*; use self::tag::{Tag, TagIterator, split_tag}; fn recv_value(reader: &mut Read, tag: Tag, data: &mut *const ()) -> io::Result<()> { @@ -19,10 +19,88 @@ pub fn recv_return(reader: &mut Read, tag_bytes: &[u8], data: *const ()) -> io:: Ok(()) } -fn send_value(writer: &mut Write, tag: Tag, data: &mut *const ()) -> io::Result<()> { +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 => write_u8(writer, b'n'), - _ => unreachable!() + 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 => { + consume_value!(u64, |ptr| + write_u64(writer, *ptr)) + } + 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)) + } } } @@ -37,7 +115,7 @@ pub fn send_args(writer: &mut Write, tag_bytes: &[u8], for index in 0.. { if let Some(arg_tag) = args_it.next() { let mut data = unsafe { *data.offset(index) }; - try!(send_value(writer, arg_tag, &mut data)); + try!(unsafe { send_value(writer, arg_tag, &mut data) }); } else { break } @@ -61,7 +139,7 @@ mod tag { (arg_tags_bytes, return_tag_bytes) } - #[derive(Debug)] + #[derive(Debug, Clone, Copy)] pub enum Tag<'a> { None, Bool, @@ -77,6 +155,25 @@ mod tag { 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', + } + } + } + #[derive(Debug, Clone, Copy)] pub struct TagIterator<'a> { data: &'a [u8] @@ -104,23 +201,23 @@ mod tag { b't' => { let count = self.data[0]; self.data = &self.data[1..]; - Tag::Tuple(self.skip(count), count) + Tag::Tuple(self.sub(count), count) } - b'l' => Tag::List(self.skip(1)), - b'a' => Tag::Array(self.skip(1)), - b'r' => Tag::Range(self.skip(1)), - b'k' => Tag::Keyword(self.skip(1)), + 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 skip(&mut self, count: u8) -> TagIterator<'a> { - let origin = self.clone(); + fn sub(&mut self, count: u8) -> TagIterator<'a> { + let data = self.data; for _ in 0..count { self.next().expect("truncated tag"); } - origin + TagIterator { data: &data[..(data.len() - self.data.len())] } } } @@ -150,10 +247,7 @@ mod tag { try!(write!(f, "String")), Tag::Tuple(it, cnt) => { try!(write!(f, "Tuple(")); - for i in 0..cnt { - try!(it.fmt(f)); - if i != cnt - 1 { try!(write!(f, ", ")) } - } + try!(it.fmt(f)); try!(write!(f, ")")) } Tag::List(it) => { diff --git a/artiq/runtime.rs/src/session_proto.rs b/artiq/runtime.rs/src/session_proto.rs index ba32fbc5a..fcac0b516 100644 --- a/artiq/runtime.rs/src/session_proto.rs +++ b/artiq/runtime.rs/src/session_proto.rs @@ -2,17 +2,6 @@ use std::prelude::v1::*; use std::io::{self, Read, Write}; use proto::*; -fn read_string(reader: &mut Read) -> io::Result { - let mut bytes = try!(read_bytes(reader)); - let len = bytes.len() - 1; // length without trailing \0 - bytes.resize(len, 0); // FIXME: don't send \0 in the first place - String::from_utf8(bytes).map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e)) -} - -fn write_string(writer: &mut Write, value: &str) -> io::Result<()> { - write_bytes(writer, value.as_bytes()) -} - fn read_sync(reader: &mut Read) -> io::Result<()> { let mut sync = [0; 4]; for i in 0.. {