Files
tarpc/src/protocol/reader.rs
Tim be5f55c5f6 Extend snake_to_camel plugin to replace {} in the doc string with the original snake-cased ident. (#50)
* Extend snake_to_camel plugin to replace {} in the doc string with the origin snake-cased ident.

Also, track tokio-rs master.

This is really ad-hoc, undiscoverable, and unintuitive, but there's no way to programmatically create doc strings
in regular code, and I want to produce better doc strings for the associated types.

Given `fn foo_bar`:

Before: `/// The type of future returned by the function of the same name.`
After: ``/// The type of future returned by `{}`.``
    => `/// The type of future returned by foo_bar.`

* Fix some docs

* Use a helper fn on pipeline::Frame instead of handrolled match.

* Don't hide docs for ClientFuture.

It's exposed in the Connect impl of FutureService -- the tradeoff for not generating *another* item -- and hiding it breaks doc links.

* Formatting

* Rename snake_to_camel plugin => tarpc-plugins

* Update README

* Mangle a lot of names in macro expansion.

To lower the chance of any issues, prefix idents in service expansion with __tarpc_service.
In future_enum, prefix with __future_enum. The pattern is basically __macro_name_ident.

Any imported enum variant will conflict with a let binding or a function arg, so we basically
can't use any generic idents at all. Example:

    enum Req { request(..) }
    use self::Req::request;

    fn make_request(request: Request) { ... }

                    ^^^^^^^ conflict here

Additionally, suffix generated associated types with Fut to avoid conflicts with camelcased rpcs.
Why someone would do that, I don't know, but we shouldn't allow that wart.
2016-09-14 01:19:24 -07:00

155 lines
4.4 KiB
Rust

// Copyright 2016 Google Inc. All Rights Reserved.
//
// Licensed under the MIT License, <LICENSE or http://opensource.org/licenses/MIT>.
// This file may not be copied, modified, or distributed except according to those terms.
use byteorder::{BigEndian, ReadBytesExt};
use bytes::{MutBuf, Take};
use futures::Async;
use std::io;
use std::mem;
use tokio_proto::TryRead;
use tokio_proto::pipeline::Frame;
use util::Never;
#[derive(Debug)]
pub struct U64Reader {
read: usize,
data: [u8; 8],
}
impl U64Reader {
fn new() -> Self {
U64Reader {
read: 0,
data: [0; 8],
}
}
}
impl MutBuf for U64Reader {
fn remaining(&self) -> usize {
8 - self.read
}
unsafe fn advance(&mut self, count: usize) {
self.read += count;
}
unsafe fn mut_bytes(&mut self) -> &mut [u8] {
&mut self.data[self.read..]
}
}
#[derive(Debug)]
enum NextReadAction<R> {
Continue,
Stop(Result<R, io::Error>),
}
trait MutBufExt: MutBuf + Sized {
type Inner;
fn take(&mut self) -> Self::Inner;
fn try_read<R: TryRead>(&mut self, stream: &mut R) -> io::Result<NextReadAction<Self::Inner>> {
while let Some(bytes_read) = stream.try_read_buf(self)? {
debug!("Reader: read {} bytes, {} remaining.",
bytes_read,
self.remaining());
if bytes_read == 0 {
debug!("Reader: connection broken.");
let err = io::Error::new(io::ErrorKind::BrokenPipe, "The connection was closed.");
return Ok(NextReadAction::Stop(Err(err)));
}
if !self.has_remaining() {
trace!("Reader: finished.");
return Ok(NextReadAction::Stop(Ok(self.take())));
}
}
Ok(NextReadAction::Continue)
}
}
impl MutBufExt for U64Reader {
type Inner = u64;
fn take(&mut self) -> u64 {
(&self.data as &[u8]).read_u64::<BigEndian>().unwrap()
}
}
impl MutBufExt for Take<Vec<u8>> {
type Inner = Vec<u8>;
fn take(&mut self) -> Vec<u8> {
mem::replace(self.get_mut(), vec![])
}
}
/// A state machine that reads packets in non-blocking fashion.
#[derive(Debug)]
pub enum ReadState {
/// Tracks how many bytes of the message size have been read.
Len(U64Reader),
/// Tracks read progress.
Data(Take<Vec<u8>>),
}
#[derive(Debug)]
enum NextReadState {
Same,
Next(ReadState),
Reset(Vec<u8>),
}
impl ReadState {
pub fn init() -> ReadState {
ReadState::Len(U64Reader::new())
}
pub fn next<R: TryRead>(&mut self,
socket: &mut R)
-> io::Result<Async<Frame<Vec<u8>, Never, io::Error>>> {
loop {
let next = match *self {
ReadState::Len(ref mut len) => {
match len.try_read(socket)? {
NextReadAction::Continue => NextReadState::Same,
NextReadAction::Stop(result) => {
match result {
Ok(len) => {
let buf = Vec::with_capacity(len as usize);
NextReadState::Next(ReadState::Data(Take::new(buf,
len as usize)))
}
Err(e) => return Ok(Async::Ready(Frame::Error(e))),
}
}
}
}
ReadState::Data(ref mut buf) => {
match buf.try_read(socket)? {
NextReadAction::Continue => NextReadState::Same,
NextReadAction::Stop(result) => {
match result {
Ok(buf) => NextReadState::Reset(buf),
Err(e) => return Ok(Async::Ready(Frame::Error(e))),
}
}
}
}
};
match next {
NextReadState::Same => return Ok(Async::NotReady),
NextReadState::Next(next) => *self = next,
NextReadState::Reset(packet) => {
*self = ReadState::init();
return Ok(Async::Ready(Frame::Message(packet)));
}
}
}
}
}