THRIFT-2945 Add Rust support
Client: Rust
Patch: Allen George <allen.george@gmail.com>

This closes #1147
diff --git a/lib/rs/src/protocol/binary.rs b/lib/rs/src/protocol/binary.rs
new file mode 100644
index 0000000..f3c9ea2
--- /dev/null
+++ b/lib/rs/src/protocol/binary.rs
@@ -0,0 +1,817 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+use byteorder::{BigEndian, ByteOrder, ReadBytesExt, WriteBytesExt};
+use std::cell::RefCell;
+use std::convert::From;
+use std::io::{Read, Write};
+use std::rc::Rc;
+use try_from::TryFrom;
+
+use ::{ProtocolError, ProtocolErrorKind};
+use ::transport::TTransport;
+use super::{TFieldIdentifier, TInputProtocol, TInputProtocolFactory, TListIdentifier,
+            TMapIdentifier, TMessageIdentifier, TMessageType};
+use super::{TOutputProtocol, TOutputProtocolFactory, TSetIdentifier, TStructIdentifier, TType};
+
+const BINARY_PROTOCOL_VERSION_1: u32 = 0x80010000;
+
+/// Read messages encoded in the Thrift simple binary encoding.
+///
+/// There are two available modes: `strict` and `non-strict`, where the
+/// `non-strict` version does not check for the protocol version in the
+/// received message header.
+///
+/// # Examples
+///
+/// Create and use a `TBinaryInputProtocol`.
+///
+/// ```no_run
+/// use std::cell::RefCell;
+/// use std::rc::Rc;
+/// use thrift::protocol::{TBinaryInputProtocol, TInputProtocol};
+/// use thrift::transport::{TTcpTransport, TTransport};
+///
+/// let mut transport = TTcpTransport::new();
+/// transport.open("localhost:9090").unwrap();
+/// let transport = Rc::new(RefCell::new(Box::new(transport) as Box<TTransport>));
+///
+/// let mut i_prot = TBinaryInputProtocol::new(transport, true);
+///
+/// let recvd_bool = i_prot.read_bool().unwrap();
+/// let recvd_string = i_prot.read_string().unwrap();
+/// ```
+pub struct TBinaryInputProtocol {
+    strict: bool,
+    transport: Rc<RefCell<Box<TTransport>>>,
+}
+
+impl TBinaryInputProtocol {
+    /// Create a `TBinaryInputProtocol` that reads bytes from `transport`.
+    ///
+    /// Set `strict` to `true` if all incoming messages contain the protocol
+    /// version number in the protocol header.
+    pub fn new(transport: Rc<RefCell<Box<TTransport>>>, strict: bool) -> TBinaryInputProtocol {
+        TBinaryInputProtocol {
+            strict: strict,
+            transport: transport,
+        }
+    }
+}
+
+impl TInputProtocol for TBinaryInputProtocol {
+    #[cfg_attr(feature = "cargo-clippy", allow(collapsible_if))]
+    fn read_message_begin(&mut self) -> ::Result<TMessageIdentifier> {
+        let mut first_bytes = vec![0; 4];
+        self.transport.borrow_mut().read_exact(&mut first_bytes[..])?;
+
+        // the thrift version header is intentionally negative
+        // so the first check we'll do is see if the sign bit is set
+        // and if so - assume it's the protocol-version header
+        if first_bytes[0] >= 8 {
+            // apparently we got a protocol-version header - check
+            // it, and if it matches, read the rest of the fields
+            if first_bytes[0..2] != [0x80, 0x01] {
+                Err(::Error::Protocol(ProtocolError {
+                    kind: ProtocolErrorKind::BadVersion,
+                    message: format!("received bad version: {:?}", &first_bytes[0..2]),
+                }))
+            } else {
+                let message_type: TMessageType = TryFrom::try_from(first_bytes[3])?;
+                let name = self.read_string()?;
+                let sequence_number = self.read_i32()?;
+                Ok(TMessageIdentifier::new(name, message_type, sequence_number))
+            }
+        } else {
+            // apparently we didn't get a protocol-version header,
+            // which happens if the sender is not using the strict protocol
+            if self.strict {
+                // we're in strict mode however, and that always
+                // requires the protocol-version header to be written first
+                Err(::Error::Protocol(ProtocolError {
+                    kind: ProtocolErrorKind::BadVersion,
+                    message: format!("received bad version: {:?}", &first_bytes[0..2]),
+                }))
+            } else {
+                // in the non-strict version the first message field
+                // is the message name. strings (byte arrays) are length-prefixed,
+                // so we've just read the length in the first 4 bytes
+                let name_size = BigEndian::read_i32(&first_bytes) as usize;
+                let mut name_buf: Vec<u8> = Vec::with_capacity(name_size);
+                self.transport.borrow_mut().read_exact(&mut name_buf)?;
+                let name = String::from_utf8(name_buf)?;
+
+                // read the rest of the fields
+                let message_type: TMessageType = self.read_byte().and_then(TryFrom::try_from)?;
+                let sequence_number = self.read_i32()?;
+                Ok(TMessageIdentifier::new(name, message_type, sequence_number))
+            }
+        }
+    }
+
+    fn read_message_end(&mut self) -> ::Result<()> {
+        Ok(())
+    }
+
+    fn read_struct_begin(&mut self) -> ::Result<Option<TStructIdentifier>> {
+        Ok(None)
+    }
+
+    fn read_struct_end(&mut self) -> ::Result<()> {
+        Ok(())
+    }
+
+    fn read_field_begin(&mut self) -> ::Result<TFieldIdentifier> {
+        let field_type_byte = self.read_byte()?;
+        let field_type = field_type_from_u8(field_type_byte)?;
+        let id = match field_type {
+            TType::Stop => Ok(0),
+            _ => self.read_i16(),
+        }?;
+        Ok(TFieldIdentifier::new::<Option<String>, String, i16>(None, field_type, id))
+    }
+
+    fn read_field_end(&mut self) -> ::Result<()> {
+        Ok(())
+    }
+
+    fn read_bytes(&mut self) -> ::Result<Vec<u8>> {
+        let num_bytes = self.transport.borrow_mut().read_i32::<BigEndian>()? as usize;
+        let mut buf = vec![0u8; num_bytes];
+        self.transport.borrow_mut().read_exact(&mut buf).map(|_| buf).map_err(From::from)
+    }
+
+    fn read_bool(&mut self) -> ::Result<bool> {
+        let b = self.read_i8()?;
+        match b {
+            0 => Ok(false),
+            _ => Ok(true),
+        }
+    }
+
+    fn read_i8(&mut self) -> ::Result<i8> {
+        self.transport.borrow_mut().read_i8().map_err(From::from)
+    }
+
+    fn read_i16(&mut self) -> ::Result<i16> {
+        self.transport.borrow_mut().read_i16::<BigEndian>().map_err(From::from)
+    }
+
+    fn read_i32(&mut self) -> ::Result<i32> {
+        self.transport.borrow_mut().read_i32::<BigEndian>().map_err(From::from)
+    }
+
+    fn read_i64(&mut self) -> ::Result<i64> {
+        self.transport.borrow_mut().read_i64::<BigEndian>().map_err(From::from)
+    }
+
+    fn read_double(&mut self) -> ::Result<f64> {
+        self.transport.borrow_mut().read_f64::<BigEndian>().map_err(From::from)
+    }
+
+    fn read_string(&mut self) -> ::Result<String> {
+        let bytes = self.read_bytes()?;
+        String::from_utf8(bytes).map_err(From::from)
+    }
+
+    fn read_list_begin(&mut self) -> ::Result<TListIdentifier> {
+        let element_type: TType = self.read_byte().and_then(field_type_from_u8)?;
+        let size = self.read_i32()?;
+        Ok(TListIdentifier::new(element_type, size))
+    }
+
+    fn read_list_end(&mut self) -> ::Result<()> {
+        Ok(())
+    }
+
+    fn read_set_begin(&mut self) -> ::Result<TSetIdentifier> {
+        let element_type: TType = self.read_byte().and_then(field_type_from_u8)?;
+        let size = self.read_i32()?;
+        Ok(TSetIdentifier::new(element_type, size))
+    }
+
+    fn read_set_end(&mut self) -> ::Result<()> {
+        Ok(())
+    }
+
+    fn read_map_begin(&mut self) -> ::Result<TMapIdentifier> {
+        let key_type: TType = self.read_byte().and_then(field_type_from_u8)?;
+        let value_type: TType = self.read_byte().and_then(field_type_from_u8)?;
+        let size = self.read_i32()?;
+        Ok(TMapIdentifier::new(key_type, value_type, size))
+    }
+
+    fn read_map_end(&mut self) -> ::Result<()> {
+        Ok(())
+    }
+
+    // utility
+    //
+
+    fn read_byte(&mut self) -> ::Result<u8> {
+        self.transport.borrow_mut().read_u8().map_err(From::from)
+    }
+}
+
+/// Factory for creating instances of `TBinaryInputProtocol`.
+#[derive(Default)]
+pub struct TBinaryInputProtocolFactory;
+
+impl TBinaryInputProtocolFactory {
+    /// Create a `TBinaryInputProtocolFactory`.
+    pub fn new() -> TBinaryInputProtocolFactory {
+        TBinaryInputProtocolFactory {}
+    }
+}
+
+impl TInputProtocolFactory for TBinaryInputProtocolFactory {
+    fn create(&mut self, transport: Rc<RefCell<Box<TTransport>>>) -> Box<TInputProtocol> {
+        Box::new(TBinaryInputProtocol::new(transport, true)) as Box<TInputProtocol>
+    }
+}
+
+/// Write messages using the Thrift simple binary encoding.
+///
+/// There are two available modes: `strict` and `non-strict`, where the
+/// `strict` version writes the protocol version number in the outgoing message
+/// header and the `non-strict` version does not.
+///
+/// # Examples
+///
+/// Create and use a `TBinaryOutputProtocol`.
+///
+/// ```no_run
+/// use std::cell::RefCell;
+/// use std::rc::Rc;
+/// use thrift::protocol::{TBinaryOutputProtocol, TOutputProtocol};
+/// use thrift::transport::{TTcpTransport, TTransport};
+///
+/// let mut transport = TTcpTransport::new();
+/// transport.open("localhost:9090").unwrap();
+/// let transport = Rc::new(RefCell::new(Box::new(transport) as Box<TTransport>));
+///
+/// let mut o_prot = TBinaryOutputProtocol::new(transport, true);
+///
+/// o_prot.write_bool(true).unwrap();
+/// o_prot.write_string("test_string").unwrap();
+/// ```
+pub struct TBinaryOutputProtocol {
+    strict: bool,
+    transport: Rc<RefCell<Box<TTransport>>>,
+}
+
+impl TBinaryOutputProtocol {
+    /// Create a `TBinaryOutputProtocol` that writes bytes to `transport`.
+    ///
+    /// Set `strict` to `true` if all outgoing messages should contain the
+    /// protocol version number in the protocol header.
+    pub fn new(transport: Rc<RefCell<Box<TTransport>>>, strict: bool) -> TBinaryOutputProtocol {
+        TBinaryOutputProtocol {
+            strict: strict,
+            transport: transport,
+        }
+    }
+
+    fn write_transport(&mut self, buf: &[u8]) -> ::Result<()> {
+        self.transport.borrow_mut().write(buf).map(|_| ()).map_err(From::from)
+    }
+}
+
+impl TOutputProtocol for TBinaryOutputProtocol {
+    fn write_message_begin(&mut self, identifier: &TMessageIdentifier) -> ::Result<()> {
+        if self.strict {
+            let message_type: u8 = identifier.message_type.into();
+            let header = BINARY_PROTOCOL_VERSION_1 | (message_type as u32);
+            self.transport.borrow_mut().write_u32::<BigEndian>(header)?;
+            self.write_string(&identifier.name)?;
+            self.write_i32(identifier.sequence_number)
+        } else {
+            self.write_string(&identifier.name)?;
+            self.write_byte(identifier.message_type.into())?;
+            self.write_i32(identifier.sequence_number)
+        }
+    }
+
+    fn write_message_end(&mut self) -> ::Result<()> {
+        Ok(())
+    }
+
+    fn write_struct_begin(&mut self, _: &TStructIdentifier) -> ::Result<()> {
+        Ok(())
+    }
+
+    fn write_struct_end(&mut self) -> ::Result<()> {
+        Ok(())
+    }
+
+    fn write_field_begin(&mut self, identifier: &TFieldIdentifier) -> ::Result<()> {
+        if identifier.id.is_none() && identifier.field_type != TType::Stop {
+            return Err(::Error::Protocol(ProtocolError {
+                kind: ProtocolErrorKind::Unknown,
+                message: format!("cannot write identifier {:?} without sequence number",
+                                 &identifier),
+            }));
+        }
+
+        self.write_byte(field_type_to_u8(identifier.field_type))?;
+        if let Some(id) = identifier.id {
+            self.write_i16(id)
+        } else {
+            Ok(())
+        }
+    }
+
+    fn write_field_end(&mut self) -> ::Result<()> {
+        Ok(())
+    }
+
+    fn write_field_stop(&mut self) -> ::Result<()> {
+        self.write_byte(field_type_to_u8(TType::Stop))
+    }
+
+    fn write_bytes(&mut self, b: &[u8]) -> ::Result<()> {
+        self.write_i32(b.len() as i32)?;
+        self.write_transport(b)
+    }
+
+    fn write_bool(&mut self, b: bool) -> ::Result<()> {
+        if b {
+            self.write_i8(1)
+        } else {
+            self.write_i8(0)
+        }
+    }
+
+    fn write_i8(&mut self, i: i8) -> ::Result<()> {
+        self.transport.borrow_mut().write_i8(i).map_err(From::from)
+    }
+
+    fn write_i16(&mut self, i: i16) -> ::Result<()> {
+        self.transport.borrow_mut().write_i16::<BigEndian>(i).map_err(From::from)
+    }
+
+    fn write_i32(&mut self, i: i32) -> ::Result<()> {
+        self.transport.borrow_mut().write_i32::<BigEndian>(i).map_err(From::from)
+    }
+
+    fn write_i64(&mut self, i: i64) -> ::Result<()> {
+        self.transport.borrow_mut().write_i64::<BigEndian>(i).map_err(From::from)
+    }
+
+    fn write_double(&mut self, d: f64) -> ::Result<()> {
+        self.transport.borrow_mut().write_f64::<BigEndian>(d).map_err(From::from)
+    }
+
+    fn write_string(&mut self, s: &str) -> ::Result<()> {
+        self.write_bytes(s.as_bytes())
+    }
+
+    fn write_list_begin(&mut self, identifier: &TListIdentifier) -> ::Result<()> {
+        self.write_byte(field_type_to_u8(identifier.element_type))?;
+        self.write_i32(identifier.size)
+    }
+
+    fn write_list_end(&mut self) -> ::Result<()> {
+        Ok(())
+    }
+
+    fn write_set_begin(&mut self, identifier: &TSetIdentifier) -> ::Result<()> {
+        self.write_byte(field_type_to_u8(identifier.element_type))?;
+        self.write_i32(identifier.size)
+    }
+
+    fn write_set_end(&mut self) -> ::Result<()> {
+        Ok(())
+    }
+
+    fn write_map_begin(&mut self, identifier: &TMapIdentifier) -> ::Result<()> {
+        let key_type = identifier.key_type
+            .expect("map identifier to write should contain key type");
+        self.write_byte(field_type_to_u8(key_type))?;
+        let val_type = identifier.value_type
+            .expect("map identifier to write should contain value type");
+        self.write_byte(field_type_to_u8(val_type))?;
+        self.write_i32(identifier.size)
+    }
+
+    fn write_map_end(&mut self) -> ::Result<()> {
+        Ok(())
+    }
+
+    fn flush(&mut self) -> ::Result<()> {
+        self.transport.borrow_mut().flush().map_err(From::from)
+    }
+
+    // utility
+    //
+
+    fn write_byte(&mut self, b: u8) -> ::Result<()> {
+        self.transport.borrow_mut().write_u8(b).map_err(From::from)
+    }
+}
+
+/// Factory for creating instances of `TBinaryOutputProtocol`.
+#[derive(Default)]
+pub struct TBinaryOutputProtocolFactory;
+
+impl TBinaryOutputProtocolFactory {
+    /// Create a `TBinaryOutputProtocolFactory`.
+    pub fn new() -> TBinaryOutputProtocolFactory {
+        TBinaryOutputProtocolFactory {}
+    }
+}
+
+impl TOutputProtocolFactory for TBinaryOutputProtocolFactory {
+    fn create(&mut self, transport: Rc<RefCell<Box<TTransport>>>) -> Box<TOutputProtocol> {
+        Box::new(TBinaryOutputProtocol::new(transport, true)) as Box<TOutputProtocol>
+    }
+}
+
+fn field_type_to_u8(field_type: TType) -> u8 {
+    match field_type {
+        TType::Stop => 0x00,
+        TType::Void => 0x01,
+        TType::Bool => 0x02,
+        TType::I08 => 0x03, // equivalent to TType::Byte
+        TType::Double => 0x04,
+        TType::I16 => 0x06,
+        TType::I32 => 0x08,
+        TType::I64 => 0x0A,
+        TType::String | TType::Utf7 => 0x0B,
+        TType::Struct => 0x0C,
+        TType::Map => 0x0D,
+        TType::Set => 0x0E,
+        TType::List => 0x0F,
+        TType::Utf8 => 0x10,
+        TType::Utf16 => 0x11,
+    }
+}
+
+fn field_type_from_u8(b: u8) -> ::Result<TType> {
+    match b {
+        0x00 => Ok(TType::Stop),
+        0x01 => Ok(TType::Void),
+        0x02 => Ok(TType::Bool),
+        0x03 => Ok(TType::I08), // Equivalent to TType::Byte
+        0x04 => Ok(TType::Double),
+        0x06 => Ok(TType::I16),
+        0x08 => Ok(TType::I32),
+        0x0A => Ok(TType::I64),
+        0x0B => Ok(TType::String), // technically, also a UTF7, but we'll treat it as string
+        0x0C => Ok(TType::Struct),
+        0x0D => Ok(TType::Map),
+        0x0E => Ok(TType::Set),
+        0x0F => Ok(TType::List),
+        0x10 => Ok(TType::Utf8),
+        0x11 => Ok(TType::Utf16),
+        unkn => {
+            Err(::Error::Protocol(ProtocolError {
+                kind: ProtocolErrorKind::InvalidData,
+                message: format!("cannot convert {} to TType", unkn),
+            }))
+        }
+    }
+}
+
+#[cfg(test)]
+mod tests {
+
+    use std::rc::Rc;
+    use std::cell::RefCell;
+
+    use ::protocol::{TFieldIdentifier, TMessageIdentifier, TMessageType, TInputProtocol,
+                     TListIdentifier, TMapIdentifier, TOutputProtocol, TSetIdentifier,
+                     TStructIdentifier, TType};
+    use ::transport::{TPassThruTransport, TTransport};
+    use ::transport::mem::TBufferTransport;
+
+    use super::*;
+
+    #[test]
+    fn must_write_message_call_begin() {
+        let (trans, _, mut o_prot) = test_objects();
+
+        let ident = TMessageIdentifier::new("test", TMessageType::Call, 1);
+        assert!(o_prot.write_message_begin(&ident).is_ok());
+
+        let buf = trans.borrow().write_buffer_to_vec();
+
+        let expected: [u8; 16] = [0x80, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x74, 0x65,
+                                  0x73, 0x74, 0x00, 0x00, 0x00, 0x01];
+
+        assert_eq!(&expected, buf.as_slice());
+    }
+
+
+    #[test]
+    fn must_write_message_reply_begin() {
+        let (trans, _, mut o_prot) = test_objects();
+
+        let ident = TMessageIdentifier::new("test", TMessageType::Reply, 10);
+        assert!(o_prot.write_message_begin(&ident).is_ok());
+
+        let buf = trans.borrow().write_buffer_to_vec();
+
+        let expected: [u8; 16] = [0x80, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0x04, 0x74, 0x65,
+                                  0x73, 0x74, 0x00, 0x00, 0x00, 0x0A];
+
+        assert_eq!(&expected, buf.as_slice());
+    }
+
+    #[test]
+    fn must_round_trip_strict_message_begin() {
+        let (trans, mut i_prot, mut o_prot) = test_objects();
+
+        let sent_ident = TMessageIdentifier::new("test", TMessageType::Call, 1);
+        assert!(o_prot.write_message_begin(&sent_ident).is_ok());
+
+        trans.borrow_mut().copy_write_buffer_to_read_buffer();
+
+        let received_ident = assert_success!(i_prot.read_message_begin());
+        assert_eq!(&received_ident, &sent_ident);
+    }
+
+    #[test]
+    fn must_write_message_end() {
+        assert_no_write(|o| o.write_message_end());
+    }
+
+    #[test]
+    fn must_write_struct_begin() {
+        assert_no_write(|o| o.write_struct_begin(&TStructIdentifier::new("foo")));
+    }
+
+    #[test]
+    fn must_write_struct_end() {
+        assert_no_write(|o| o.write_struct_end());
+    }
+
+    #[test]
+    fn must_write_field_begin() {
+        let (trans, _, mut o_prot) = test_objects();
+
+        assert!(o_prot.write_field_begin(&TFieldIdentifier::new("some_field", TType::String, 22))
+            .is_ok());
+
+        let expected: [u8; 3] = [0x0B, 0x00, 0x16];
+        let buf = trans.borrow().write_buffer_to_vec();
+        assert_eq!(&expected, buf.as_slice());
+    }
+
+    #[test]
+    fn must_round_trip_field_begin() {
+        let (trans, mut i_prot, mut o_prot) = test_objects();
+
+        let sent_field_ident = TFieldIdentifier::new("foo", TType::I64, 20);
+        assert!(o_prot.write_field_begin(&sent_field_ident).is_ok());
+
+        trans.borrow_mut().copy_write_buffer_to_read_buffer();
+
+        let expected_ident = TFieldIdentifier {
+            name: None,
+            field_type: TType::I64,
+            id: Some(20),
+        }; // no name
+        let received_ident = assert_success!(i_prot.read_field_begin());
+        assert_eq!(&received_ident, &expected_ident);
+    }
+
+    #[test]
+    fn must_write_stop_field() {
+        let (trans, _, mut o_prot) = test_objects();
+
+        assert!(o_prot.write_field_stop().is_ok());
+
+        let expected: [u8; 1] = [0x00];
+        let buf = trans.borrow().write_buffer_to_vec();
+        assert_eq!(&expected, buf.as_slice());
+    }
+
+    #[test]
+    fn must_round_trip_field_stop() {
+        let (trans, mut i_prot, mut o_prot) = test_objects();
+
+        assert!(o_prot.write_field_stop().is_ok());
+
+        trans.borrow_mut().copy_write_buffer_to_read_buffer();
+
+        let expected_ident = TFieldIdentifier {
+            name: None,
+            field_type: TType::Stop,
+            id: Some(0),
+        }; // we get id 0
+
+        let received_ident = assert_success!(i_prot.read_field_begin());
+        assert_eq!(&received_ident, &expected_ident);
+    }
+
+    #[test]
+    fn must_write_field_end() {
+        assert_no_write(|o| o.write_field_end());
+    }
+
+    #[test]
+    fn must_write_list_begin() {
+        let (trans, _, mut o_prot) = test_objects();
+
+        assert!(o_prot.write_list_begin(&TListIdentifier::new(TType::Bool, 5)).is_ok());
+
+        let expected: [u8; 5] = [0x02, 0x00, 0x00, 0x00, 0x05];
+        let buf = trans.borrow().write_buffer_to_vec();
+        assert_eq!(&expected, buf.as_slice());
+    }
+
+    #[test]
+    fn must_round_trip_list_begin() {
+        let (trans, mut i_prot, mut o_prot) = test_objects();
+
+        let ident = TListIdentifier::new(TType::List, 900);
+        assert!(o_prot.write_list_begin(&ident).is_ok());
+
+        trans.borrow_mut().copy_write_buffer_to_read_buffer();
+
+        let received_ident = assert_success!(i_prot.read_list_begin());
+        assert_eq!(&received_ident, &ident);
+    }
+
+    #[test]
+    fn must_write_list_end() {
+        assert_no_write(|o| o.write_list_end());
+    }
+
+    #[test]
+    fn must_write_set_begin() {
+        let (trans, _, mut o_prot) = test_objects();
+
+        assert!(o_prot.write_set_begin(&TSetIdentifier::new(TType::I16, 7)).is_ok());
+
+        let expected: [u8; 5] = [0x06, 0x00, 0x00, 0x00, 0x07];
+        let buf = trans.borrow().write_buffer_to_vec();
+        assert_eq!(&expected, buf.as_slice());
+    }
+
+    #[test]
+    fn must_round_trip_set_begin() {
+        let (trans, mut i_prot, mut o_prot) = test_objects();
+
+        let ident = TSetIdentifier::new(TType::I64, 2000);
+        assert!(o_prot.write_set_begin(&ident).is_ok());
+
+        trans.borrow_mut().copy_write_buffer_to_read_buffer();
+
+        let received_ident_result = i_prot.read_set_begin();
+        assert!(received_ident_result.is_ok());
+        assert_eq!(&received_ident_result.unwrap(), &ident);
+    }
+
+    #[test]
+    fn must_write_set_end() {
+        assert_no_write(|o| o.write_set_end());
+    }
+
+    #[test]
+    fn must_write_map_begin() {
+        let (trans, _, mut o_prot) = test_objects();
+
+        assert!(o_prot.write_map_begin(&TMapIdentifier::new(TType::I64, TType::Struct, 32))
+            .is_ok());
+
+        let expected: [u8; 6] = [0x0A, 0x0C, 0x00, 0x00, 0x00, 0x20];
+        let buf = trans.borrow().write_buffer_to_vec();
+        assert_eq!(&expected, buf.as_slice());
+    }
+
+    #[test]
+    fn must_round_trip_map_begin() {
+        let (trans, mut i_prot, mut o_prot) = test_objects();
+
+        let ident = TMapIdentifier::new(TType::Map, TType::Set, 100);
+        assert!(o_prot.write_map_begin(&ident).is_ok());
+
+        trans.borrow_mut().copy_write_buffer_to_read_buffer();
+
+        let received_ident = assert_success!(i_prot.read_map_begin());
+        assert_eq!(&received_ident, &ident);
+    }
+
+    #[test]
+    fn must_write_map_end() {
+        assert_no_write(|o| o.write_map_end());
+    }
+
+    #[test]
+    fn must_write_bool_true() {
+        let (trans, _, mut o_prot) = test_objects();
+
+        assert!(o_prot.write_bool(true).is_ok());
+
+        let expected: [u8; 1] = [0x01];
+        let buf = trans.borrow().write_buffer_to_vec();
+        assert_eq!(&expected, buf.as_slice());
+    }
+
+    #[test]
+    fn must_write_bool_false() {
+        let (trans, _, mut o_prot) = test_objects();
+
+        assert!(o_prot.write_bool(false).is_ok());
+
+        let expected: [u8; 1] = [0x00];
+        let buf = trans.borrow().write_buffer_to_vec();
+        assert_eq!(&expected, buf.as_slice());
+    }
+
+    #[test]
+    fn must_read_bool_true() {
+        let (trans, mut i_prot, _) = test_objects();
+
+        trans.borrow_mut().set_readable_bytes(&[0x01]);
+
+        let read_bool = assert_success!(i_prot.read_bool());
+        assert_eq!(read_bool, true);
+    }
+
+    #[test]
+    fn must_read_bool_false() {
+        let (trans, mut i_prot, _) = test_objects();
+
+        trans.borrow_mut().set_readable_bytes(&[0x00]);
+
+        let read_bool = assert_success!(i_prot.read_bool());
+        assert_eq!(read_bool, false);
+    }
+
+    #[test]
+    fn must_allow_any_non_zero_value_to_be_interpreted_as_bool_true() {
+        let (trans, mut i_prot, _) = test_objects();
+
+        trans.borrow_mut().set_readable_bytes(&[0xAC]);
+
+        let read_bool = assert_success!(i_prot.read_bool());
+        assert_eq!(read_bool, true);
+    }
+
+    #[test]
+    fn must_write_bytes() {
+        let (trans, _, mut o_prot) = test_objects();
+
+        let bytes: [u8; 10] = [0x0A, 0xCC, 0xD1, 0x84, 0x99, 0x12, 0xAB, 0xBB, 0x45, 0xDF];
+
+        assert!(o_prot.write_bytes(&bytes).is_ok());
+
+        let buf = trans.borrow().write_buffer_to_vec();
+        assert_eq!(&buf[0..4], [0x00, 0x00, 0x00, 0x0A]); // length
+        assert_eq!(&buf[4..], bytes); // actual bytes
+    }
+
+    #[test]
+    fn must_round_trip_bytes() {
+        let (trans, mut i_prot, mut o_prot) = test_objects();
+
+        let bytes: [u8; 25] = [0x20, 0xFD, 0x18, 0x84, 0x99, 0x12, 0xAB, 0xBB, 0x45, 0xDF, 0x34,
+                               0xDC, 0x98, 0xA4, 0x6D, 0xF3, 0x99, 0xB4, 0xB7, 0xD4, 0x9C, 0xA5,
+                               0xB3, 0xC9, 0x88];
+
+        assert!(o_prot.write_bytes(&bytes).is_ok());
+
+        trans.borrow_mut().copy_write_buffer_to_read_buffer();
+
+        let received_bytes = assert_success!(i_prot.read_bytes());
+        assert_eq!(&received_bytes, &bytes);
+    }
+
+    fn test_objects
+        ()
+        -> (Rc<RefCell<Box<TBufferTransport>>>, TBinaryInputProtocol, TBinaryOutputProtocol)
+    {
+        let mem = Rc::new(RefCell::new(Box::new(TBufferTransport::with_capacity(40, 40))));
+
+        let inner: Box<TTransport> = Box::new(TPassThruTransport { inner: mem.clone() });
+        let inner = Rc::new(RefCell::new(inner));
+
+        let i_prot = TBinaryInputProtocol::new(inner.clone(), true);
+        let o_prot = TBinaryOutputProtocol::new(inner.clone(), true);
+
+        (mem, i_prot, o_prot)
+    }
+
+    fn assert_no_write<F: FnMut(&mut TBinaryOutputProtocol) -> ::Result<()>>(mut write_fn: F) {
+        let (trans, _, mut o_prot) = test_objects();
+        assert!(write_fn(&mut o_prot).is_ok());
+        assert_eq!(trans.borrow().write_buffer_as_ref().len(), 0);
+    }
+}
diff --git a/lib/rs/src/protocol/compact.rs b/lib/rs/src/protocol/compact.rs
new file mode 100644
index 0000000..96fa8ef
--- /dev/null
+++ b/lib/rs/src/protocol/compact.rs
@@ -0,0 +1,2085 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
+use integer_encoding::{VarIntReader, VarIntWriter};
+use std::cell::RefCell;
+use std::convert::From;
+use std::rc::Rc;
+use std::io::{Read, Write};
+use try_from::TryFrom;
+
+use ::transport::TTransport;
+use super::{TFieldIdentifier, TListIdentifier, TMapIdentifier, TMessageIdentifier, TMessageType,
+            TInputProtocol, TInputProtocolFactory};
+use super::{TOutputProtocol, TOutputProtocolFactory, TSetIdentifier, TStructIdentifier, TType};
+
+const COMPACT_PROTOCOL_ID: u8 = 0x82;
+const COMPACT_VERSION: u8 = 0x01;
+const COMPACT_VERSION_MASK: u8 = 0x1F;
+
+/// Read messages encoded in the Thrift compact protocol.
+///
+/// # Examples
+///
+/// Create and use a `TCompactInputProtocol`.
+///
+/// ```no_run
+/// use std::cell::RefCell;
+/// use std::rc::Rc;
+/// use thrift::protocol::{TCompactInputProtocol, TInputProtocol};
+/// use thrift::transport::{TTcpTransport, TTransport};
+///
+/// let mut transport = TTcpTransport::new();
+/// transport.open("localhost:9090").unwrap();
+/// let transport = Rc::new(RefCell::new(Box::new(transport) as Box<TTransport>));
+///
+/// let mut i_prot = TCompactInputProtocol::new(transport);
+///
+/// let recvd_bool = i_prot.read_bool().unwrap();
+/// let recvd_string = i_prot.read_string().unwrap();
+/// ```
+pub struct TCompactInputProtocol {
+    // Identifier of the last field deserialized for a struct.
+    last_read_field_id: i16,
+    // Stack of the last read field ids (a new entry is added each time a nested struct is read).
+    read_field_id_stack: Vec<i16>,
+    // Boolean value for a field.
+    // Saved because boolean fields and their value are encoded in a single byte,
+    // and reading the field only occurs after the field id is read.
+    pending_read_bool_value: Option<bool>,
+    // Underlying transport used for byte-level operations.
+    transport: Rc<RefCell<Box<TTransport>>>,
+}
+
+impl TCompactInputProtocol {
+    /// Create a `TCompactInputProtocol` that reads bytes from `transport`.
+    pub fn new(transport: Rc<RefCell<Box<TTransport>>>) -> TCompactInputProtocol {
+        TCompactInputProtocol {
+            last_read_field_id: 0,
+            read_field_id_stack: Vec::new(),
+            pending_read_bool_value: None,
+            transport: transport,
+        }
+    }
+
+    fn read_list_set_begin(&mut self) -> ::Result<(TType, i32)> {
+        let header = self.read_byte()?;
+        let element_type = collection_u8_to_type(header & 0x0F)?;
+
+        let element_count;
+        let possible_element_count = (header & 0xF0) >> 4;
+        if possible_element_count != 15 {
+            // high bits set high if count and type encoded separately
+            element_count = possible_element_count as i32;
+        } else {
+            element_count = self.transport.borrow_mut().read_varint::<u32>()? as i32;
+        }
+
+        Ok((element_type, element_count))
+    }
+}
+
+impl TInputProtocol for TCompactInputProtocol {
+    fn read_message_begin(&mut self) -> ::Result<TMessageIdentifier> {
+        let compact_id = self.read_byte()?;
+        if compact_id != COMPACT_PROTOCOL_ID {
+            Err(::Error::Protocol(::ProtocolError {
+                kind: ::ProtocolErrorKind::BadVersion,
+                message: format!("invalid compact protocol header {:?}", compact_id),
+            }))
+        } else {
+            Ok(())
+        }?;
+
+        let type_and_byte = self.read_byte()?;
+        let received_version = type_and_byte & COMPACT_VERSION_MASK;
+        if received_version != COMPACT_VERSION {
+            Err(::Error::Protocol(::ProtocolError {
+                kind: ::ProtocolErrorKind::BadVersion,
+                message: format!("cannot process compact protocol version {:?}",
+                                 received_version),
+            }))
+        } else {
+            Ok(())
+        }?;
+
+        // NOTE: unsigned right shift will pad with 0s
+        let message_type: TMessageType = TMessageType::try_from(type_and_byte >> 5)?;
+        let sequence_number = self.read_i32()?;
+        let service_call_name = self.read_string()?;
+
+        self.last_read_field_id = 0;
+
+        Ok(TMessageIdentifier::new(service_call_name, message_type, sequence_number))
+    }
+
+    fn read_message_end(&mut self) -> ::Result<()> {
+        Ok(())
+    }
+
+    fn read_struct_begin(&mut self) -> ::Result<Option<TStructIdentifier>> {
+        self.read_field_id_stack.push(self.last_read_field_id);
+        self.last_read_field_id = 0;
+        Ok(None)
+    }
+
+    fn read_struct_end(&mut self) -> ::Result<()> {
+        self.last_read_field_id = self.read_field_id_stack
+            .pop()
+            .expect("should have previous field ids");
+        Ok(())
+    }
+
+    fn read_field_begin(&mut self) -> ::Result<TFieldIdentifier> {
+        // we can read at least one byte, which is:
+        // - the type
+        // - the field delta and the type
+        let field_type = self.read_byte()?;
+        let field_delta = (field_type & 0xF0) >> 4;
+        let field_type = match field_type & 0x0F {
+            0x01 => {
+                self.pending_read_bool_value = Some(true);
+                Ok(TType::Bool)
+            }
+            0x02 => {
+                self.pending_read_bool_value = Some(false);
+                Ok(TType::Bool)
+            }
+            ttu8 => u8_to_type(ttu8),
+        }?;
+
+        match field_type {
+            TType::Stop => {
+                Ok(TFieldIdentifier::new::<Option<String>, String, Option<i16>>(None,
+                                                                                TType::Stop,
+                                                                                None))
+            }
+            _ => {
+                if field_delta != 0 {
+                    self.last_read_field_id += field_delta as i16;
+                } else {
+                    self.last_read_field_id = self.read_i16()?;
+                };
+
+                Ok(TFieldIdentifier {
+                    name: None,
+                    field_type: field_type,
+                    id: Some(self.last_read_field_id),
+                })
+            }
+        }
+    }
+
+    fn read_field_end(&mut self) -> ::Result<()> {
+        Ok(())
+    }
+
+    fn read_bool(&mut self) -> ::Result<bool> {
+        match self.pending_read_bool_value.take() {
+            Some(b) => Ok(b),
+            None => {
+                let b = self.read_byte()?;
+                match b {
+                    0x01 => Ok(true),
+                    0x02 => Ok(false),
+                    unkn => {
+                        Err(::Error::Protocol(::ProtocolError {
+                            kind: ::ProtocolErrorKind::InvalidData,
+                            message: format!("cannot convert {} into bool", unkn),
+                        }))
+                    }
+                }
+            }
+        }
+    }
+
+    fn read_bytes(&mut self) -> ::Result<Vec<u8>> {
+        let len = self.transport.borrow_mut().read_varint::<u32>()?;
+        let mut buf = vec![0u8; len as usize];
+        self.transport.borrow_mut().read_exact(&mut buf).map_err(From::from).map(|_| buf)
+    }
+
+    fn read_i8(&mut self) -> ::Result<i8> {
+        self.read_byte().map(|i| i as i8)
+    }
+
+    fn read_i16(&mut self) -> ::Result<i16> {
+        self.transport.borrow_mut().read_varint::<i16>().map_err(From::from)
+    }
+
+    fn read_i32(&mut self) -> ::Result<i32> {
+        self.transport.borrow_mut().read_varint::<i32>().map_err(From::from)
+    }
+
+    fn read_i64(&mut self) -> ::Result<i64> {
+        self.transport.borrow_mut().read_varint::<i64>().map_err(From::from)
+    }
+
+    fn read_double(&mut self) -> ::Result<f64> {
+        self.transport.borrow_mut().read_f64::<BigEndian>().map_err(From::from)
+    }
+
+    fn read_string(&mut self) -> ::Result<String> {
+        let bytes = self.read_bytes()?;
+        String::from_utf8(bytes).map_err(From::from)
+    }
+
+    fn read_list_begin(&mut self) -> ::Result<TListIdentifier> {
+        let (element_type, element_count) = self.read_list_set_begin()?;
+        Ok(TListIdentifier::new(element_type, element_count))
+    }
+
+    fn read_list_end(&mut self) -> ::Result<()> {
+        Ok(())
+    }
+
+    fn read_set_begin(&mut self) -> ::Result<TSetIdentifier> {
+        let (element_type, element_count) = self.read_list_set_begin()?;
+        Ok(TSetIdentifier::new(element_type, element_count))
+    }
+
+    fn read_set_end(&mut self) -> ::Result<()> {
+        Ok(())
+    }
+
+    fn read_map_begin(&mut self) -> ::Result<TMapIdentifier> {
+        let element_count = self.transport.borrow_mut().read_varint::<u32>()? as i32;
+        if element_count == 0 {
+            Ok(TMapIdentifier::new(None, None, 0))
+        } else {
+            let type_header = self.read_byte()?;
+            let key_type = collection_u8_to_type((type_header & 0xF0) >> 4)?;
+            let val_type = collection_u8_to_type(type_header & 0x0F)?;
+            Ok(TMapIdentifier::new(key_type, val_type, element_count))
+        }
+    }
+
+    fn read_map_end(&mut self) -> ::Result<()> {
+        Ok(())
+    }
+
+    // utility
+    //
+
+    fn read_byte(&mut self) -> ::Result<u8> {
+        let mut buf = [0u8; 1];
+        self.transport.borrow_mut().read_exact(&mut buf).map_err(From::from).map(|_| buf[0])
+    }
+}
+
+/// Factory for creating instances of `TCompactInputProtocol`.
+#[derive(Default)]
+pub struct TCompactInputProtocolFactory;
+
+impl TCompactInputProtocolFactory {
+    /// Create a `TCompactInputProtocolFactory`.
+    pub fn new() -> TCompactInputProtocolFactory {
+        TCompactInputProtocolFactory {}
+    }
+}
+
+impl TInputProtocolFactory for TCompactInputProtocolFactory {
+    fn create(&mut self, transport: Rc<RefCell<Box<TTransport>>>) -> Box<TInputProtocol> {
+        Box::new(TCompactInputProtocol::new(transport)) as Box<TInputProtocol>
+    }
+}
+
+/// Write messages using the Thrift compact protocol.
+///
+/// # Examples
+///
+/// Create and use a `TCompactOutputProtocol`.
+///
+/// ```no_run
+/// use std::cell::RefCell;
+/// use std::rc::Rc;
+/// use thrift::protocol::{TCompactOutputProtocol, TOutputProtocol};
+/// use thrift::transport::{TTcpTransport, TTransport};
+///
+/// let mut transport = TTcpTransport::new();
+/// transport.open("localhost:9090").unwrap();
+/// let transport = Rc::new(RefCell::new(Box::new(transport) as Box<TTransport>));
+///
+/// let mut o_prot = TCompactOutputProtocol::new(transport);
+///
+/// o_prot.write_bool(true).unwrap();
+/// o_prot.write_string("test_string").unwrap();
+/// ```
+pub struct TCompactOutputProtocol {
+    // Identifier of the last field serialized for a struct.
+    last_write_field_id: i16,
+    // Stack of the last written field ids (a new entry is added each time a nested struct is written).
+    write_field_id_stack: Vec<i16>,
+    // Field identifier of the boolean field to be written.
+    // Saved because boolean fields and their value are encoded in a single byte
+    pending_write_bool_field_identifier: Option<TFieldIdentifier>,
+    // Underlying transport used for byte-level operations.
+    transport: Rc<RefCell<Box<TTransport>>>,
+}
+
+impl TCompactOutputProtocol {
+    /// Create a `TCompactOutputProtocol` that writes bytes to `transport`.
+    pub fn new(transport: Rc<RefCell<Box<TTransport>>>) -> TCompactOutputProtocol {
+        TCompactOutputProtocol {
+            last_write_field_id: 0,
+            write_field_id_stack: Vec::new(),
+            pending_write_bool_field_identifier: None,
+            transport: transport,
+        }
+    }
+
+    // FIXME: field_type as unconstrained u8 is bad
+    fn write_field_header(&mut self, field_type: u8, field_id: i16) -> ::Result<()> {
+        let field_delta = field_id - self.last_write_field_id;
+        if field_delta > 0 && field_delta < 15 {
+            self.write_byte(((field_delta as u8) << 4) | field_type)?;
+        } else {
+            self.write_byte(field_type)?;
+            self.write_i16(field_id)?;
+        }
+        self.last_write_field_id = field_id;
+        Ok(())
+    }
+
+    fn write_list_set_begin(&mut self, element_type: TType, element_count: i32) -> ::Result<()> {
+        let elem_identifier = collection_type_to_u8(element_type);
+        if element_count <= 14 {
+            let header = (element_count as u8) << 4 | elem_identifier;
+            self.write_byte(header)
+        } else {
+            let header = 0xF0 | elem_identifier;
+            self.write_byte(header)?;
+            self.transport
+                .borrow_mut()
+                .write_varint(element_count as u32)
+                .map_err(From::from)
+                .map(|_| ())
+        }
+    }
+
+    fn assert_no_pending_bool_write(&self) {
+        if let Some(ref f) = self.pending_write_bool_field_identifier {
+            panic!("pending bool field {:?} not written", f)
+        }
+    }
+}
+
+impl TOutputProtocol for TCompactOutputProtocol {
+    fn write_message_begin(&mut self, identifier: &TMessageIdentifier) -> ::Result<()> {
+        self.write_byte(COMPACT_PROTOCOL_ID)?;
+        self.write_byte((u8::from(identifier.message_type) << 5) | COMPACT_VERSION)?;
+        self.write_i32(identifier.sequence_number)?;
+        self.write_string(&identifier.name)?;
+        Ok(())
+    }
+
+    fn write_message_end(&mut self) -> ::Result<()> {
+        self.assert_no_pending_bool_write();
+        Ok(())
+    }
+
+    fn write_struct_begin(&mut self, _: &TStructIdentifier) -> ::Result<()> {
+        self.write_field_id_stack.push(self.last_write_field_id);
+        self.last_write_field_id = 0;
+        Ok(())
+    }
+
+    fn write_struct_end(&mut self) -> ::Result<()> {
+        self.assert_no_pending_bool_write();
+        self.last_write_field_id =
+            self.write_field_id_stack.pop().expect("should have previous field ids");
+        Ok(())
+    }
+
+    fn write_field_begin(&mut self, identifier: &TFieldIdentifier) -> ::Result<()> {
+        match identifier.field_type {
+            TType::Bool => {
+                if self.pending_write_bool_field_identifier.is_some() {
+                    panic!("should not have a pending bool while writing another bool with id: \
+                            {:?}",
+                           identifier)
+                }
+                self.pending_write_bool_field_identifier = Some(identifier.clone());
+                Ok(())
+            }
+            _ => {
+                let field_type = type_to_u8(identifier.field_type);
+                let field_id = identifier.id.expect("non-stop field should have field id");
+                self.write_field_header(field_type, field_id)
+            }
+        }
+    }
+
+    fn write_field_end(&mut self) -> ::Result<()> {
+        self.assert_no_pending_bool_write();
+        Ok(())
+    }
+
+    fn write_field_stop(&mut self) -> ::Result<()> {
+        self.assert_no_pending_bool_write();
+        self.write_byte(type_to_u8(TType::Stop))
+    }
+
+    fn write_bool(&mut self, b: bool) -> ::Result<()> {
+        match self.pending_write_bool_field_identifier.take() {
+            Some(pending) => {
+                let field_id = pending.id.expect("bool field should have a field id");
+                let field_type_as_u8 = if b { 0x01 } else { 0x02 };
+                self.write_field_header(field_type_as_u8, field_id)
+            }
+            None => {
+                if b {
+                    self.write_byte(0x01)
+                } else {
+                    self.write_byte(0x02)
+                }
+            }
+        }
+    }
+
+    fn write_bytes(&mut self, b: &[u8]) -> ::Result<()> {
+        self.transport.borrow_mut().write_varint(b.len() as u32)?;
+        self.transport.borrow_mut().write_all(b).map_err(From::from)
+    }
+
+    fn write_i8(&mut self, i: i8) -> ::Result<()> {
+        self.write_byte(i as u8)
+    }
+
+    fn write_i16(&mut self, i: i16) -> ::Result<()> {
+        self.transport.borrow_mut().write_varint(i).map_err(From::from).map(|_| ())
+    }
+
+    fn write_i32(&mut self, i: i32) -> ::Result<()> {
+        self.transport.borrow_mut().write_varint(i).map_err(From::from).map(|_| ())
+    }
+
+    fn write_i64(&mut self, i: i64) -> ::Result<()> {
+        self.transport.borrow_mut().write_varint(i).map_err(From::from).map(|_| ())
+    }
+
+    fn write_double(&mut self, d: f64) -> ::Result<()> {
+        self.transport.borrow_mut().write_f64::<BigEndian>(d).map_err(From::from)
+    }
+
+    fn write_string(&mut self, s: &str) -> ::Result<()> {
+        self.write_bytes(s.as_bytes())
+    }
+
+    fn write_list_begin(&mut self, identifier: &TListIdentifier) -> ::Result<()> {
+        self.write_list_set_begin(identifier.element_type, identifier.size)
+    }
+
+    fn write_list_end(&mut self) -> ::Result<()> {
+        Ok(())
+    }
+
+    fn write_set_begin(&mut self, identifier: &TSetIdentifier) -> ::Result<()> {
+        self.write_list_set_begin(identifier.element_type, identifier.size)
+    }
+
+    fn write_set_end(&mut self) -> ::Result<()> {
+        Ok(())
+    }
+
+    fn write_map_begin(&mut self, identifier: &TMapIdentifier) -> ::Result<()> {
+        if identifier.size == 0 {
+            self.write_byte(0)
+        } else {
+            self.transport.borrow_mut().write_varint(identifier.size as u32)?;
+
+            let key_type = identifier.key_type
+                .expect("map identifier to write should contain key type");
+            let key_type_byte = collection_type_to_u8(key_type) << 4;
+
+            let val_type = identifier.value_type
+                .expect("map identifier to write should contain value type");
+            let val_type_byte = collection_type_to_u8(val_type);
+
+            let map_type_header = key_type_byte | val_type_byte;
+            self.write_byte(map_type_header)
+        }
+    }
+
+    fn write_map_end(&mut self) -> ::Result<()> {
+        Ok(())
+    }
+
+    fn flush(&mut self) -> ::Result<()> {
+        self.transport.borrow_mut().flush().map_err(From::from)
+    }
+
+    // utility
+    //
+
+    fn write_byte(&mut self, b: u8) -> ::Result<()> {
+        self.transport.borrow_mut().write(&[b]).map_err(From::from).map(|_| ())
+    }
+}
+
+/// Factory for creating instances of `TCompactOutputProtocol`.
+#[derive(Default)]
+pub struct TCompactOutputProtocolFactory;
+
+impl TCompactOutputProtocolFactory {
+    /// Create a `TCompactOutputProtocolFactory`.
+    pub fn new() -> TCompactOutputProtocolFactory {
+        TCompactOutputProtocolFactory {}
+    }
+}
+
+impl TOutputProtocolFactory for TCompactOutputProtocolFactory {
+    fn create(&mut self, transport: Rc<RefCell<Box<TTransport>>>) -> Box<TOutputProtocol> {
+        Box::new(TCompactOutputProtocol::new(transport)) as Box<TOutputProtocol>
+    }
+}
+
+fn collection_type_to_u8(field_type: TType) -> u8 {
+    match field_type {
+        TType::Bool => 0x01,
+        f => type_to_u8(f),
+    }
+}
+
+fn type_to_u8(field_type: TType) -> u8 {
+    match field_type {
+        TType::Stop => 0x00,
+        TType::I08 => 0x03, // equivalent to TType::Byte
+        TType::I16 => 0x04,
+        TType::I32 => 0x05,
+        TType::I64 => 0x06,
+        TType::Double => 0x07,
+        TType::String => 0x08,
+        TType::List => 0x09,
+        TType::Set => 0x0A,
+        TType::Map => 0x0B,
+        TType::Struct => 0x0C,
+        _ => panic!(format!("should not have attempted to convert {} to u8", field_type)),
+    }
+}
+
+fn collection_u8_to_type(b: u8) -> ::Result<TType> {
+    match b {
+        0x01 => Ok(TType::Bool),
+        o => u8_to_type(o),
+    }
+}
+
+fn u8_to_type(b: u8) -> ::Result<TType> {
+    match b {
+        0x00 => Ok(TType::Stop),
+        0x03 => Ok(TType::I08), // equivalent to TType::Byte
+        0x04 => Ok(TType::I16),
+        0x05 => Ok(TType::I32),
+        0x06 => Ok(TType::I64),
+        0x07 => Ok(TType::Double),
+        0x08 => Ok(TType::String),
+        0x09 => Ok(TType::List),
+        0x0A => Ok(TType::Set),
+        0x0B => Ok(TType::Map),
+        0x0C => Ok(TType::Struct),
+        unkn => {
+            Err(::Error::Protocol(::ProtocolError {
+                kind: ::ProtocolErrorKind::InvalidData,
+                message: format!("cannot convert {} into TType", unkn),
+            }))
+        }
+    }
+}
+
+#[cfg(test)]
+mod tests {
+
+    use std::rc::Rc;
+    use std::cell::RefCell;
+
+    use ::protocol::{TFieldIdentifier, TMessageIdentifier, TMessageType, TInputProtocol,
+                     TListIdentifier, TMapIdentifier, TOutputProtocol, TSetIdentifier,
+                     TStructIdentifier, TType};
+    use ::transport::{TPassThruTransport, TTransport};
+    use ::transport::mem::TBufferTransport;
+
+    use super::*;
+
+    #[test]
+    fn must_write_message_begin_0() {
+        let (trans, _, mut o_prot) = test_objects();
+
+        assert_success!(o_prot.write_message_begin(&TMessageIdentifier::new("foo", TMessageType::Call, 431)));
+
+        let expected: [u8; 8] =
+            [0x82 /* protocol ID */, 0x21 /* message type | protocol version */, 0xDE,
+             0x06 /* zig-zag varint sequence number */, 0x03 /* message-name length */,
+             0x66, 0x6F, 0x6F /* "foo" */];
+
+        assert_eq!(trans.borrow().write_buffer_as_ref(), &expected);
+    }
+
+    #[test]
+    fn must_write_message_begin_1() {
+        let (trans, _, mut o_prot) = test_objects();
+
+        assert_success!(o_prot.write_message_begin(&TMessageIdentifier::new("bar", TMessageType::Reply, 991828)));
+
+        let expected: [u8; 9] =
+            [0x82 /* protocol ID */, 0x41 /* message type | protocol version */, 0xA8,
+             0x89, 0x79 /* zig-zag varint sequence number */,
+             0x03 /* message-name length */, 0x62, 0x61, 0x72 /* "bar" */];
+
+        assert_eq!(trans.borrow().write_buffer_as_ref(), &expected);
+    }
+
+    #[test]
+    fn must_round_trip_message_begin() {
+        let (trans, mut i_prot, mut o_prot) = test_objects();
+
+        let ident = TMessageIdentifier::new("service_call", TMessageType::Call, 1283948);
+
+        assert_success!(o_prot.write_message_begin(&ident));
+
+        trans.borrow_mut().copy_write_buffer_to_read_buffer();
+
+        let res = assert_success!(i_prot.read_message_begin());
+        assert_eq!(&res, &ident);
+    }
+
+    #[test]
+    fn must_write_message_end() {
+        assert_no_write(|o| o.write_message_end());
+    }
+
+    // NOTE: structs and fields are tested together
+    //
+
+    #[test]
+    fn must_write_struct_with_delta_fields() {
+        let (trans, _, mut o_prot) = test_objects();
+
+        // no bytes should be written however
+        assert_success!(o_prot.write_struct_begin(&TStructIdentifier::new("foo")));
+
+        // write three fields with tiny field ids
+        // since they're small the field ids will be encoded as deltas
+
+        // since this is the first field (and it's zero) it gets the full varint write
+        assert_success!(o_prot.write_field_begin(&TFieldIdentifier::new("foo", TType::I08, 0)));
+        assert_success!(o_prot.write_field_end());
+
+        // since this delta > 0 and < 15 it can be encoded as a delta
+        assert_success!(o_prot.write_field_begin(&TFieldIdentifier::new("foo", TType::I16, 4)));
+        assert_success!(o_prot.write_field_end());
+
+        // since this delta > 0 and < 15 it can be encoded as a delta
+        assert_success!(o_prot.write_field_begin(&TFieldIdentifier::new("foo", TType::List, 9)));
+        assert_success!(o_prot.write_field_end());
+
+        // now, finish the struct off
+        assert_success!(o_prot.write_field_stop());
+        assert_success!(o_prot.write_struct_end());
+
+        // get bytes written
+        let buf = trans.borrow_mut().write_buffer_to_vec();
+
+        let expected: [u8; 5] = [0x03 /* field type */, 0x00 /* first field id */,
+                                 0x44 /* field delta (4) | field type */,
+                                 0x59 /* field delta (5) | field type */,
+                                 0x00 /* field stop */];
+
+        assert_eq!(&buf, &expected);
+    }
+
+    #[test]
+    fn must_round_trip_struct_with_delta_fields() {
+        let (trans, mut i_prot, mut o_prot) = test_objects();
+
+        // no bytes should be written however
+        assert_success!(o_prot.write_struct_begin(&TStructIdentifier::new("foo")));
+
+        // write three fields with tiny field ids
+        // since they're small the field ids will be encoded as deltas
+
+        // since this is the first field (and it's zero) it gets the full varint write
+        let field_ident_1 = TFieldIdentifier::new("foo", TType::I08, 0);
+        assert_success!(o_prot.write_field_begin(&field_ident_1));
+        assert_success!(o_prot.write_field_end());
+
+        // since this delta > 0 and < 15 it can be encoded as a delta
+        let field_ident_2 = TFieldIdentifier::new("foo", TType::I16, 4);
+        assert_success!(o_prot.write_field_begin(&field_ident_2));
+        assert_success!(o_prot.write_field_end());
+
+        // since this delta > 0 and < 15 it can be encoded as a delta
+        let field_ident_3 = TFieldIdentifier::new("foo", TType::List, 9);
+        assert_success!(o_prot.write_field_begin(&field_ident_3));
+        assert_success!(o_prot.write_field_end());
+
+        // now, finish the struct off
+        assert_success!(o_prot.write_field_stop());
+        assert_success!(o_prot.write_struct_end());
+
+        trans.borrow_mut().copy_write_buffer_to_read_buffer();
+
+        // read the struct back
+        assert_success!(i_prot.read_struct_begin());
+
+        let read_ident_1 = assert_success!(i_prot.read_field_begin());
+        assert_eq!(read_ident_1,
+                   TFieldIdentifier { name: None, ..field_ident_1 });
+        assert_success!(i_prot.read_field_end());
+
+        let read_ident_2 = assert_success!(i_prot.read_field_begin());
+        assert_eq!(read_ident_2,
+                   TFieldIdentifier { name: None, ..field_ident_2 });
+        assert_success!(i_prot.read_field_end());
+
+        let read_ident_3 = assert_success!(i_prot.read_field_begin());
+        assert_eq!(read_ident_3,
+                   TFieldIdentifier { name: None, ..field_ident_3 });
+        assert_success!(i_prot.read_field_end());
+
+        let read_ident_4 = assert_success!(i_prot.read_field_begin());
+        assert_eq!(read_ident_4,
+                   TFieldIdentifier {
+                       name: None,
+                       field_type: TType::Stop,
+                       id: None,
+                   });
+
+        assert_success!(i_prot.read_struct_end());
+    }
+
+    #[test]
+    fn must_write_struct_with_non_zero_initial_field_and_delta_fields() {
+        let (trans, _, mut o_prot) = test_objects();
+
+        // no bytes should be written however
+        assert_success!(o_prot.write_struct_begin(&TStructIdentifier::new("foo")));
+
+        // write three fields with tiny field ids
+        // since they're small the field ids will be encoded as deltas
+
+        // gets a delta write
+        assert_success!(o_prot.write_field_begin(&TFieldIdentifier::new("foo", TType::I32, 1)));
+        assert_success!(o_prot.write_field_end());
+
+        // since this delta > 0 and < 15 it can be encoded as a delta
+        assert_success!(o_prot.write_field_begin(&TFieldIdentifier::new("foo", TType::Set, 2)));
+        assert_success!(o_prot.write_field_end());
+
+        // since this delta > 0 and < 15 it can be encoded as a delta
+        assert_success!(o_prot.write_field_begin(&TFieldIdentifier::new("foo", TType::String, 6)));
+        assert_success!(o_prot.write_field_end());
+
+        // now, finish the struct off
+        assert_success!(o_prot.write_field_stop());
+        assert_success!(o_prot.write_struct_end());
+
+        // get bytes written
+        let buf = trans.borrow_mut().write_buffer_to_vec();
+
+        let expected: [u8; 4] = [0x15 /* field delta (1) | field type */,
+                                 0x1A /* field delta (1) | field type */,
+                                 0x48 /* field delta (4) | field type */,
+                                 0x00 /* field stop */];
+
+        assert_eq!(&buf, &expected);
+    }
+
+    #[test]
+    fn must_round_trip_struct_with_non_zero_initial_field_and_delta_fields() {
+        let (trans, mut i_prot, mut o_prot) = test_objects();
+
+        // no bytes should be written however
+        assert_success!(o_prot.write_struct_begin(&TStructIdentifier::new("foo")));
+
+        // write three fields with tiny field ids
+        // since they're small the field ids will be encoded as deltas
+
+        // gets a delta write
+        let field_ident_1 = TFieldIdentifier::new("foo", TType::I32, 1);
+        assert_success!(o_prot.write_field_begin(&field_ident_1));
+        assert_success!(o_prot.write_field_end());
+
+        // since this delta > 0 and < 15 it can be encoded as a delta
+        let field_ident_2 = TFieldIdentifier::new("foo", TType::Set, 2);
+        assert_success!(o_prot.write_field_begin(&field_ident_2));
+        assert_success!(o_prot.write_field_end());
+
+        // since this delta > 0 and < 15 it can be encoded as a delta
+        let field_ident_3 = TFieldIdentifier::new("foo", TType::String, 6);
+        assert_success!(o_prot.write_field_begin(&field_ident_3));
+        assert_success!(o_prot.write_field_end());
+
+        // now, finish the struct off
+        assert_success!(o_prot.write_field_stop());
+        assert_success!(o_prot.write_struct_end());
+
+        trans.borrow_mut().copy_write_buffer_to_read_buffer();
+
+        // read the struct back
+        assert_success!(i_prot.read_struct_begin());
+
+        let read_ident_1 = assert_success!(i_prot.read_field_begin());
+        assert_eq!(read_ident_1,
+                   TFieldIdentifier { name: None, ..field_ident_1 });
+        assert_success!(i_prot.read_field_end());
+
+        let read_ident_2 = assert_success!(i_prot.read_field_begin());
+        assert_eq!(read_ident_2,
+                   TFieldIdentifier { name: None, ..field_ident_2 });
+        assert_success!(i_prot.read_field_end());
+
+        let read_ident_3 = assert_success!(i_prot.read_field_begin());
+        assert_eq!(read_ident_3,
+                   TFieldIdentifier { name: None, ..field_ident_3 });
+        assert_success!(i_prot.read_field_end());
+
+        let read_ident_4 = assert_success!(i_prot.read_field_begin());
+        assert_eq!(read_ident_4,
+                   TFieldIdentifier {
+                       name: None,
+                       field_type: TType::Stop,
+                       id: None,
+                   });
+
+        assert_success!(i_prot.read_struct_end());
+    }
+
+    #[test]
+    fn must_write_struct_with_long_fields() {
+        let (trans, _, mut o_prot) = test_objects();
+
+        // no bytes should be written however
+        assert_success!(o_prot.write_struct_begin(&TStructIdentifier::new("foo")));
+
+        // write three fields with field ids that cannot be encoded as deltas
+
+        // since this is the first field (and it's zero) it gets the full varint write
+        assert_success!(o_prot.write_field_begin(&TFieldIdentifier::new("foo", TType::I32, 0)));
+        assert_success!(o_prot.write_field_end());
+
+        // since this delta is > 15 it is encoded as a zig-zag varint
+        assert_success!(o_prot.write_field_begin(&TFieldIdentifier::new("foo", TType::I64, 16)));
+        assert_success!(o_prot.write_field_end());
+
+        // since this delta is > 15 it is encoded as a zig-zag varint
+        assert_success!(o_prot.write_field_begin(&TFieldIdentifier::new("foo", TType::Set, 99)));
+        assert_success!(o_prot.write_field_end());
+
+        // now, finish the struct off
+        assert_success!(o_prot.write_field_stop());
+        assert_success!(o_prot.write_struct_end());
+
+        // get bytes written
+        let buf = trans.borrow_mut().write_buffer_to_vec();
+
+        let expected: [u8; 8] =
+            [0x05 /* field type */, 0x00 /* first field id */,
+             0x06 /* field type */, 0x20 /* zig-zag varint field id */,
+             0x0A /* field type */, 0xC6, 0x01 /* zig-zag varint field id */,
+             0x00 /* field stop */];
+
+        assert_eq!(&buf, &expected);
+    }
+
+    #[test]
+    fn must_round_trip_struct_with_long_fields() {
+        let (trans, mut i_prot, mut o_prot) = test_objects();
+
+        // no bytes should be written however
+        assert_success!(o_prot.write_struct_begin(&TStructIdentifier::new("foo")));
+
+        // write three fields with field ids that cannot be encoded as deltas
+
+        // since this is the first field (and it's zero) it gets the full varint write
+        let field_ident_1 = TFieldIdentifier::new("foo", TType::I32, 0);
+        assert_success!(o_prot.write_field_begin(&field_ident_1));
+        assert_success!(o_prot.write_field_end());
+
+        // since this delta is > 15 it is encoded as a zig-zag varint
+        let field_ident_2 = TFieldIdentifier::new("foo", TType::I64, 16);
+        assert_success!(o_prot.write_field_begin(&field_ident_2));
+        assert_success!(o_prot.write_field_end());
+
+        // since this delta is > 15 it is encoded as a zig-zag varint
+        let field_ident_3 = TFieldIdentifier::new("foo", TType::Set, 99);
+        assert_success!(o_prot.write_field_begin(&field_ident_3));
+        assert_success!(o_prot.write_field_end());
+
+        // now, finish the struct off
+        assert_success!(o_prot.write_field_stop());
+        assert_success!(o_prot.write_struct_end());
+
+        trans.borrow_mut().copy_write_buffer_to_read_buffer();
+
+        // read the struct back
+        assert_success!(i_prot.read_struct_begin());
+
+        let read_ident_1 = assert_success!(i_prot.read_field_begin());
+        assert_eq!(read_ident_1,
+                   TFieldIdentifier { name: None, ..field_ident_1 });
+        assert_success!(i_prot.read_field_end());
+
+        let read_ident_2 = assert_success!(i_prot.read_field_begin());
+        assert_eq!(read_ident_2,
+                   TFieldIdentifier { name: None, ..field_ident_2 });
+        assert_success!(i_prot.read_field_end());
+
+        let read_ident_3 = assert_success!(i_prot.read_field_begin());
+        assert_eq!(read_ident_3,
+                   TFieldIdentifier { name: None, ..field_ident_3 });
+        assert_success!(i_prot.read_field_end());
+
+        let read_ident_4 = assert_success!(i_prot.read_field_begin());
+        assert_eq!(read_ident_4,
+                   TFieldIdentifier {
+                       name: None,
+                       field_type: TType::Stop,
+                       id: None,
+                   });
+
+        assert_success!(i_prot.read_struct_end());
+    }
+
+    #[test]
+    fn must_write_struct_with_mix_of_long_and_delta_fields() {
+        let (trans, _, mut o_prot) = test_objects();
+
+        // no bytes should be written however
+        assert_success!(o_prot.write_struct_begin(&TStructIdentifier::new("foo")));
+
+        // write three fields with field ids that cannot be encoded as deltas
+
+        // since the delta is > 0 and < 15 it gets a delta write
+        assert_success!(o_prot.write_field_begin(&TFieldIdentifier::new("foo", TType::I64, 1)));
+        assert_success!(o_prot.write_field_end());
+
+        // since this delta > 0 and < 15 it gets a delta write
+        assert_success!(o_prot.write_field_begin(&TFieldIdentifier::new("foo", TType::I32, 9)));
+        assert_success!(o_prot.write_field_end());
+
+        // since this delta is > 15 it is encoded as a zig-zag varint
+        assert_success!(o_prot.write_field_begin(&TFieldIdentifier::new("foo", TType::Set, 1000)));
+        assert_success!(o_prot.write_field_end());
+
+        // since this delta is > 15 it is encoded as a zig-zag varint
+        assert_success!(o_prot.write_field_begin(&TFieldIdentifier::new("foo", TType::Set, 2001)));
+        assert_success!(o_prot.write_field_end());
+
+        // since this is only 3 up from the previous it is recorded as a delta
+        assert_success!(o_prot.write_field_begin(&TFieldIdentifier::new("foo", TType::Set, 2004)));
+        assert_success!(o_prot.write_field_end());
+
+        // now, finish the struct off
+        assert_success!(o_prot.write_field_stop());
+        assert_success!(o_prot.write_struct_end());
+
+        // get bytes written
+        let buf = trans.borrow_mut().write_buffer_to_vec();
+
+        let expected: [u8; 10] =
+            [0x16 /* field delta (1) | field type */,
+             0x85 /* field delta (8) | field type */, 0x0A /* field type */, 0xD0,
+             0x0F /* zig-zag varint field id */, 0x0A /* field type */, 0xA2,
+             0x1F /* zig-zag varint field id */,
+             0x3A /* field delta (3) | field type */, 0x00 /* field stop */];
+
+        assert_eq!(&buf, &expected);
+    }
+
+    #[test]
+    fn must_round_trip_struct_with_mix_of_long_and_delta_fields() {
+        let (trans, mut i_prot, mut o_prot) = test_objects();
+
+        // no bytes should be written however
+        let struct_ident = TStructIdentifier::new("foo");
+        assert_success!(o_prot.write_struct_begin(&struct_ident));
+
+        // write three fields with field ids that cannot be encoded as deltas
+
+        // since the delta is > 0 and < 15 it gets a delta write
+        let field_ident_1 = TFieldIdentifier::new("foo", TType::I64, 1);
+        assert_success!(o_prot.write_field_begin(&field_ident_1));
+        assert_success!(o_prot.write_field_end());
+
+        // since this delta > 0 and < 15 it gets a delta write
+        let field_ident_2 = TFieldIdentifier::new("foo", TType::I32, 9);
+        assert_success!(o_prot.write_field_begin(&field_ident_2));
+        assert_success!(o_prot.write_field_end());
+
+        // since this delta is > 15 it is encoded as a zig-zag varint
+        let field_ident_3 = TFieldIdentifier::new("foo", TType::Set, 1000);
+        assert_success!(o_prot.write_field_begin(&field_ident_3));
+        assert_success!(o_prot.write_field_end());
+
+        // since this delta is > 15 it is encoded as a zig-zag varint
+        let field_ident_4 = TFieldIdentifier::new("foo", TType::Set, 2001);
+        assert_success!(o_prot.write_field_begin(&field_ident_4));
+        assert_success!(o_prot.write_field_end());
+
+        // since this is only 3 up from the previous it is recorded as a delta
+        let field_ident_5 = TFieldIdentifier::new("foo", TType::Set, 2004);
+        assert_success!(o_prot.write_field_begin(&field_ident_5));
+        assert_success!(o_prot.write_field_end());
+
+        // now, finish the struct off
+        assert_success!(o_prot.write_field_stop());
+        assert_success!(o_prot.write_struct_end());
+
+        trans.borrow_mut().copy_write_buffer_to_read_buffer();
+
+        // read the struct back
+        assert_success!(i_prot.read_struct_begin());
+
+        let read_ident_1 = assert_success!(i_prot.read_field_begin());
+        assert_eq!(read_ident_1,
+                   TFieldIdentifier { name: None, ..field_ident_1 });
+        assert_success!(i_prot.read_field_end());
+
+        let read_ident_2 = assert_success!(i_prot.read_field_begin());
+        assert_eq!(read_ident_2,
+                   TFieldIdentifier { name: None, ..field_ident_2 });
+        assert_success!(i_prot.read_field_end());
+
+        let read_ident_3 = assert_success!(i_prot.read_field_begin());
+        assert_eq!(read_ident_3,
+                   TFieldIdentifier { name: None, ..field_ident_3 });
+        assert_success!(i_prot.read_field_end());
+
+        let read_ident_4 = assert_success!(i_prot.read_field_begin());
+        assert_eq!(read_ident_4,
+                   TFieldIdentifier { name: None, ..field_ident_4 });
+        assert_success!(i_prot.read_field_end());
+
+        let read_ident_5 = assert_success!(i_prot.read_field_begin());
+        assert_eq!(read_ident_5,
+                   TFieldIdentifier { name: None, ..field_ident_5 });
+        assert_success!(i_prot.read_field_end());
+
+        let read_ident_6 = assert_success!(i_prot.read_field_begin());
+        assert_eq!(read_ident_6,
+                   TFieldIdentifier {
+                       name: None,
+                       field_type: TType::Stop,
+                       id: None,
+                   });
+
+        assert_success!(i_prot.read_struct_end());
+    }
+
+    #[test]
+    fn must_write_nested_structs_0() {
+        // last field of the containing struct is a delta
+        // first field of the the contained struct is a delta
+
+        let (trans, _, mut o_prot) = test_objects();
+
+        // start containing struct
+        assert_success!(o_prot.write_struct_begin(&TStructIdentifier::new("foo")));
+
+        // containing struct
+        // since the delta is > 0 and < 15 it gets a delta write
+        assert_success!(o_prot.write_field_begin(&TFieldIdentifier::new("foo", TType::I64, 1)));
+        assert_success!(o_prot.write_field_end());
+
+        // containing struct
+        // since this delta > 0 and < 15 it gets a delta write
+        assert_success!(o_prot.write_field_begin(&TFieldIdentifier::new("foo", TType::I32, 9)));
+        assert_success!(o_prot.write_field_end());
+
+        // start contained struct
+        assert_success!(o_prot.write_struct_begin(&TStructIdentifier::new("foo")));
+
+        // contained struct
+        // since the delta is > 0 and < 15 it gets a delta write
+        assert_success!(o_prot.write_field_begin(&TFieldIdentifier::new("foo", TType::I08, 7)));
+        assert_success!(o_prot.write_field_end());
+
+        // contained struct
+        // since this delta > 15 it gets a full write
+        assert_success!(o_prot.write_field_begin(&TFieldIdentifier::new("foo", TType::Double, 24)));
+        assert_success!(o_prot.write_field_end());
+
+        // end contained struct
+        assert_success!(o_prot.write_field_stop());
+        assert_success!(o_prot.write_struct_end());
+
+        // end containing struct
+        assert_success!(o_prot.write_field_stop());
+        assert_success!(o_prot.write_struct_end());
+
+        // get bytes written
+        let buf = trans.borrow_mut().write_buffer_to_vec();
+
+        let expected: [u8; 7] =
+            [0x16 /* field delta (1) | field type */,
+             0x85 /* field delta (8) | field type */,
+             0x73 /* field delta (7) | field type */, 0x07 /* field type */,
+             0x30 /* zig-zag varint field id */, 0x00 /* field stop - contained */,
+             0x00 /* field stop - containing */];
+
+        assert_eq!(&buf, &expected);
+    }
+
+    #[test]
+    fn must_round_trip_nested_structs_0() {
+        // last field of the containing struct is a delta
+        // first field of the the contained struct is a delta
+
+        let (trans, mut i_prot, mut o_prot) = test_objects();
+
+        // start containing struct
+        assert_success!(o_prot.write_struct_begin(&TStructIdentifier::new("foo")));
+
+        // containing struct
+        // since the delta is > 0 and < 15 it gets a delta write
+        let field_ident_1 = TFieldIdentifier::new("foo", TType::I64, 1);
+        assert_success!(o_prot.write_field_begin(&field_ident_1));
+        assert_success!(o_prot.write_field_end());
+
+        // containing struct
+        // since this delta > 0 and < 15 it gets a delta write
+        let field_ident_2 = TFieldIdentifier::new("foo", TType::I32, 9);
+        assert_success!(o_prot.write_field_begin(&field_ident_2));
+        assert_success!(o_prot.write_field_end());
+
+        // start contained struct
+        assert_success!(o_prot.write_struct_begin(&TStructIdentifier::new("foo")));
+
+        // contained struct
+        // since the delta is > 0 and < 15 it gets a delta write
+        let field_ident_3 = TFieldIdentifier::new("foo", TType::I08, 7);
+        assert_success!(o_prot.write_field_begin(&field_ident_3));
+        assert_success!(o_prot.write_field_end());
+
+        // contained struct
+        // since this delta > 15 it gets a full write
+        let field_ident_4 = TFieldIdentifier::new("foo", TType::Double, 24);
+        assert_success!(o_prot.write_field_begin(&field_ident_4));
+        assert_success!(o_prot.write_field_end());
+
+        // end contained struct
+        assert_success!(o_prot.write_field_stop());
+        assert_success!(o_prot.write_struct_end());
+
+        // end containing struct
+        assert_success!(o_prot.write_field_stop());
+        assert_success!(o_prot.write_struct_end());
+
+        trans.borrow_mut().copy_write_buffer_to_read_buffer();
+
+        // read containing struct back
+        assert_success!(i_prot.read_struct_begin());
+
+        let read_ident_1 = assert_success!(i_prot.read_field_begin());
+        assert_eq!(read_ident_1,
+                   TFieldIdentifier { name: None, ..field_ident_1 });
+        assert_success!(i_prot.read_field_end());
+
+        let read_ident_2 = assert_success!(i_prot.read_field_begin());
+        assert_eq!(read_ident_2,
+                   TFieldIdentifier { name: None, ..field_ident_2 });
+        assert_success!(i_prot.read_field_end());
+
+        // read contained struct back
+        assert_success!(i_prot.read_struct_begin());
+
+        let read_ident_3 = assert_success!(i_prot.read_field_begin());
+        assert_eq!(read_ident_3,
+                   TFieldIdentifier { name: None, ..field_ident_3 });
+        assert_success!(i_prot.read_field_end());
+
+        let read_ident_4 = assert_success!(i_prot.read_field_begin());
+        assert_eq!(read_ident_4,
+                   TFieldIdentifier { name: None, ..field_ident_4 });
+        assert_success!(i_prot.read_field_end());
+
+        // end contained struct
+        let read_ident_6 = assert_success!(i_prot.read_field_begin());
+        assert_eq!(read_ident_6,
+                   TFieldIdentifier {
+                       name: None,
+                       field_type: TType::Stop,
+                       id: None,
+                   });
+        assert_success!(i_prot.read_struct_end());
+
+        // end containing struct
+        let read_ident_7 = assert_success!(i_prot.read_field_begin());
+        assert_eq!(read_ident_7,
+                   TFieldIdentifier {
+                       name: None,
+                       field_type: TType::Stop,
+                       id: None,
+                   });
+        assert_success!(i_prot.read_struct_end());
+    }
+
+    #[test]
+    fn must_write_nested_structs_1() {
+        // last field of the containing struct is a delta
+        // first field of the the contained struct is a full write
+
+        let (trans, _, mut o_prot) = test_objects();
+
+        // start containing struct
+        assert_success!(o_prot.write_struct_begin(&TStructIdentifier::new("foo")));
+
+        // containing struct
+        // since the delta is > 0 and < 15 it gets a delta write
+        assert_success!(o_prot.write_field_begin(&TFieldIdentifier::new("foo", TType::I64, 1)));
+        assert_success!(o_prot.write_field_end());
+
+        // containing struct
+        // since this delta > 0 and < 15 it gets a delta write
+        assert_success!(o_prot.write_field_begin(&TFieldIdentifier::new("foo", TType::I32, 9)));
+        assert_success!(o_prot.write_field_end());
+
+        // start contained struct
+        assert_success!(o_prot.write_struct_begin(&TStructIdentifier::new("foo")));
+
+        // contained struct
+        // since this delta > 15 it gets a full write
+        assert_success!(o_prot.write_field_begin(&TFieldIdentifier::new("foo", TType::Double, 24)));
+        assert_success!(o_prot.write_field_end());
+
+        // contained struct
+        // since the delta is > 0 and < 15 it gets a delta write
+        assert_success!(o_prot.write_field_begin(&TFieldIdentifier::new("foo", TType::I08, 27)));
+        assert_success!(o_prot.write_field_end());
+
+        // end contained struct
+        assert_success!(o_prot.write_field_stop());
+        assert_success!(o_prot.write_struct_end());
+
+        // end containing struct
+        assert_success!(o_prot.write_field_stop());
+        assert_success!(o_prot.write_struct_end());
+
+        // get bytes written
+        let buf = trans.borrow_mut().write_buffer_to_vec();
+
+        let expected: [u8; 7] =
+            [0x16 /* field delta (1) | field type */,
+             0x85 /* field delta (8) | field type */, 0x07 /* field type */,
+             0x30 /* zig-zag varint field id */,
+             0x33 /* field delta (3) | field type */, 0x00 /* field stop - contained */,
+             0x00 /* field stop - containing */];
+
+        assert_eq!(&buf, &expected);
+    }
+
+    #[test]
+    fn must_round_trip_nested_structs_1() {
+        // last field of the containing struct is a delta
+        // first field of the the contained struct is a full write
+
+        let (trans, mut i_prot, mut o_prot) = test_objects();
+
+        // start containing struct
+        assert_success!(o_prot.write_struct_begin(&TStructIdentifier::new("foo")));
+
+        // containing struct
+        // since the delta is > 0 and < 15 it gets a delta write
+        let field_ident_1 = TFieldIdentifier::new("foo", TType::I64, 1);
+        assert_success!(o_prot.write_field_begin(&field_ident_1));
+        assert_success!(o_prot.write_field_end());
+
+        // containing struct
+        // since this delta > 0 and < 15 it gets a delta write
+        let field_ident_2 = TFieldIdentifier::new("foo", TType::I32, 9);
+        assert_success!(o_prot.write_field_begin(&field_ident_2));
+        assert_success!(o_prot.write_field_end());
+
+        // start contained struct
+        assert_success!(o_prot.write_struct_begin(&TStructIdentifier::new("foo")));
+
+        // contained struct
+        // since this delta > 15 it gets a full write
+        let field_ident_3 = TFieldIdentifier::new("foo", TType::Double, 24);
+        assert_success!(o_prot.write_field_begin(&field_ident_3));
+        assert_success!(o_prot.write_field_end());
+
+        // contained struct
+        // since the delta is > 0 and < 15 it gets a delta write
+        let field_ident_4 = TFieldIdentifier::new("foo", TType::I08, 27);
+        assert_success!(o_prot.write_field_begin(&field_ident_4));
+        assert_success!(o_prot.write_field_end());
+
+        // end contained struct
+        assert_success!(o_prot.write_field_stop());
+        assert_success!(o_prot.write_struct_end());
+
+        // end containing struct
+        assert_success!(o_prot.write_field_stop());
+        assert_success!(o_prot.write_struct_end());
+
+        trans.borrow_mut().copy_write_buffer_to_read_buffer();
+
+        // read containing struct back
+        assert_success!(i_prot.read_struct_begin());
+
+        let read_ident_1 = assert_success!(i_prot.read_field_begin());
+        assert_eq!(read_ident_1,
+                   TFieldIdentifier { name: None, ..field_ident_1 });
+        assert_success!(i_prot.read_field_end());
+
+        let read_ident_2 = assert_success!(i_prot.read_field_begin());
+        assert_eq!(read_ident_2,
+                   TFieldIdentifier { name: None, ..field_ident_2 });
+        assert_success!(i_prot.read_field_end());
+
+        // read contained struct back
+        assert_success!(i_prot.read_struct_begin());
+
+        let read_ident_3 = assert_success!(i_prot.read_field_begin());
+        assert_eq!(read_ident_3,
+                   TFieldIdentifier { name: None, ..field_ident_3 });
+        assert_success!(i_prot.read_field_end());
+
+        let read_ident_4 = assert_success!(i_prot.read_field_begin());
+        assert_eq!(read_ident_4,
+                   TFieldIdentifier { name: None, ..field_ident_4 });
+        assert_success!(i_prot.read_field_end());
+
+        // end contained struct
+        let read_ident_6 = assert_success!(i_prot.read_field_begin());
+        assert_eq!(read_ident_6,
+                   TFieldIdentifier {
+                       name: None,
+                       field_type: TType::Stop,
+                       id: None,
+                   });
+        assert_success!(i_prot.read_struct_end());
+
+        // end containing struct
+        let read_ident_7 = assert_success!(i_prot.read_field_begin());
+        assert_eq!(read_ident_7,
+                   TFieldIdentifier {
+                       name: None,
+                       field_type: TType::Stop,
+                       id: None,
+                   });
+        assert_success!(i_prot.read_struct_end());
+    }
+
+    #[test]
+    fn must_write_nested_structs_2() {
+        // last field of the containing struct is a full write
+        // first field of the the contained struct is a delta write
+
+        let (trans, _, mut o_prot) = test_objects();
+
+        // start containing struct
+        assert_success!(o_prot.write_struct_begin(&TStructIdentifier::new("foo")));
+
+        // containing struct
+        // since the delta is > 0 and < 15 it gets a delta write
+        assert_success!(o_prot.write_field_begin(&TFieldIdentifier::new("foo", TType::I64, 1)));
+        assert_success!(o_prot.write_field_end());
+
+        // containing struct
+        // since this delta > 15 it gets a full write
+        assert_success!(o_prot.write_field_begin(&TFieldIdentifier::new("foo", TType::String, 21)));
+        assert_success!(o_prot.write_field_end());
+
+        // start contained struct
+        assert_success!(o_prot.write_struct_begin(&TStructIdentifier::new("foo")));
+
+        // contained struct
+        // since this delta > 0 and < 15 it gets a delta write
+        assert_success!(o_prot.write_field_begin(&TFieldIdentifier::new("foo", TType::Double, 7)));
+        assert_success!(o_prot.write_field_end());
+
+        // contained struct
+        // since the delta is > 0 and < 15 it gets a delta write
+        assert_success!(o_prot.write_field_begin(&TFieldIdentifier::new("foo", TType::I08, 10)));
+        assert_success!(o_prot.write_field_end());
+
+        // end contained struct
+        assert_success!(o_prot.write_field_stop());
+        assert_success!(o_prot.write_struct_end());
+
+        // end containing struct
+        assert_success!(o_prot.write_field_stop());
+        assert_success!(o_prot.write_struct_end());
+
+        // get bytes written
+        let buf = trans.borrow_mut().write_buffer_to_vec();
+
+        let expected: [u8; 7] =
+            [0x16 /* field delta (1) | field type */, 0x08 /* field type */,
+             0x2A /* zig-zag varint field id */, 0x77 /* field delta(7) | field type */,
+             0x33 /* field delta (3) | field type */, 0x00 /* field stop - contained */,
+             0x00 /* field stop - containing */];
+
+        assert_eq!(&buf, &expected);
+    }
+
+    #[test]
+    fn must_round_trip_nested_structs_2() {
+        let (trans, mut i_prot, mut o_prot) = test_objects();
+
+        // start containing struct
+        assert_success!(o_prot.write_struct_begin(&TStructIdentifier::new("foo")));
+
+        // containing struct
+        // since the delta is > 0 and < 15 it gets a delta write
+        let field_ident_1 = TFieldIdentifier::new("foo", TType::I64, 1);
+        assert_success!(o_prot.write_field_begin(&field_ident_1));
+        assert_success!(o_prot.write_field_end());
+
+        // containing struct
+        // since this delta > 15 it gets a full write
+        let field_ident_2 = TFieldIdentifier::new("foo", TType::String, 21);
+        assert_success!(o_prot.write_field_begin(&field_ident_2));
+        assert_success!(o_prot.write_field_end());
+
+        // start contained struct
+        assert_success!(o_prot.write_struct_begin(&TStructIdentifier::new("foo")));
+
+        // contained struct
+        // since this delta > 0 and < 15 it gets a delta write
+        let field_ident_3 = TFieldIdentifier::new("foo", TType::Double, 7);
+        assert_success!(o_prot.write_field_begin(&field_ident_3));
+        assert_success!(o_prot.write_field_end());
+
+        // contained struct
+        // since the delta is > 0 and < 15 it gets a delta write
+        let field_ident_4 = TFieldIdentifier::new("foo", TType::I08, 10);
+        assert_success!(o_prot.write_field_begin(&field_ident_4));
+        assert_success!(o_prot.write_field_end());
+
+        // end contained struct
+        assert_success!(o_prot.write_field_stop());
+        assert_success!(o_prot.write_struct_end());
+
+        // end containing struct
+        assert_success!(o_prot.write_field_stop());
+        assert_success!(o_prot.write_struct_end());
+
+        trans.borrow_mut().copy_write_buffer_to_read_buffer();
+
+        // read containing struct back
+        assert_success!(i_prot.read_struct_begin());
+
+        let read_ident_1 = assert_success!(i_prot.read_field_begin());
+        assert_eq!(read_ident_1,
+                   TFieldIdentifier { name: None, ..field_ident_1 });
+        assert_success!(i_prot.read_field_end());
+
+        let read_ident_2 = assert_success!(i_prot.read_field_begin());
+        assert_eq!(read_ident_2,
+                   TFieldIdentifier { name: None, ..field_ident_2 });
+        assert_success!(i_prot.read_field_end());
+
+        // read contained struct back
+        assert_success!(i_prot.read_struct_begin());
+
+        let read_ident_3 = assert_success!(i_prot.read_field_begin());
+        assert_eq!(read_ident_3,
+                   TFieldIdentifier { name: None, ..field_ident_3 });
+        assert_success!(i_prot.read_field_end());
+
+        let read_ident_4 = assert_success!(i_prot.read_field_begin());
+        assert_eq!(read_ident_4,
+                   TFieldIdentifier { name: None, ..field_ident_4 });
+        assert_success!(i_prot.read_field_end());
+
+        // end contained struct
+        let read_ident_6 = assert_success!(i_prot.read_field_begin());
+        assert_eq!(read_ident_6,
+                   TFieldIdentifier {
+                       name: None,
+                       field_type: TType::Stop,
+                       id: None,
+                   });
+        assert_success!(i_prot.read_struct_end());
+
+        // end containing struct
+        let read_ident_7 = assert_success!(i_prot.read_field_begin());
+        assert_eq!(read_ident_7,
+                   TFieldIdentifier {
+                       name: None,
+                       field_type: TType::Stop,
+                       id: None,
+                   });
+        assert_success!(i_prot.read_struct_end());
+    }
+
+    #[test]
+    fn must_write_nested_structs_3() {
+        // last field of the containing struct is a full write
+        // first field of the the contained struct is a full write
+
+        let (trans, _, mut o_prot) = test_objects();
+
+        // start containing struct
+        assert_success!(o_prot.write_struct_begin(&TStructIdentifier::new("foo")));
+
+        // containing struct
+        // since the delta is > 0 and < 15 it gets a delta write
+        assert_success!(o_prot.write_field_begin(&TFieldIdentifier::new("foo", TType::I64, 1)));
+        assert_success!(o_prot.write_field_end());
+
+        // containing struct
+        // since this delta > 15 it gets a full write
+        assert_success!(o_prot.write_field_begin(&TFieldIdentifier::new("foo", TType::String, 21)));
+        assert_success!(o_prot.write_field_end());
+
+        // start contained struct
+        assert_success!(o_prot.write_struct_begin(&TStructIdentifier::new("foo")));
+
+        // contained struct
+        // since this delta > 15 it gets a full write
+        assert_success!(o_prot.write_field_begin(&TFieldIdentifier::new("foo", TType::Double, 21)));
+        assert_success!(o_prot.write_field_end());
+
+        // contained struct
+        // since the delta is > 0 and < 15 it gets a delta write
+        assert_success!(o_prot.write_field_begin(&TFieldIdentifier::new("foo", TType::I08, 27)));
+        assert_success!(o_prot.write_field_end());
+
+        // end contained struct
+        assert_success!(o_prot.write_field_stop());
+        assert_success!(o_prot.write_struct_end());
+
+        // end containing struct
+        assert_success!(o_prot.write_field_stop());
+        assert_success!(o_prot.write_struct_end());
+
+        // get bytes written
+        let buf = trans.borrow_mut().write_buffer_to_vec();
+
+        let expected: [u8; 8] =
+            [0x16 /* field delta (1) | field type */, 0x08 /* field type */,
+             0x2A /* zig-zag varint field id */, 0x07 /* field type */,
+             0x2A /* zig-zag varint field id */,
+             0x63 /* field delta (6) | field type */, 0x00 /* field stop - contained */,
+             0x00 /* field stop - containing */];
+
+        assert_eq!(&buf, &expected);
+    }
+
+    #[test]
+    fn must_round_trip_nested_structs_3() {
+        // last field of the containing struct is a full write
+        // first field of the the contained struct is a full write
+
+        let (trans, mut i_prot, mut o_prot) = test_objects();
+
+        // start containing struct
+        assert_success!(o_prot.write_struct_begin(&TStructIdentifier::new("foo")));
+
+        // containing struct
+        // since the delta is > 0 and < 15 it gets a delta write
+        let field_ident_1 = TFieldIdentifier::new("foo", TType::I64, 1);
+        assert_success!(o_prot.write_field_begin(&field_ident_1));
+        assert_success!(o_prot.write_field_end());
+
+        // containing struct
+        // since this delta > 15 it gets a full write
+        let field_ident_2 = TFieldIdentifier::new("foo", TType::String, 21);
+        assert_success!(o_prot.write_field_begin(&field_ident_2));
+        assert_success!(o_prot.write_field_end());
+
+        // start contained struct
+        assert_success!(o_prot.write_struct_begin(&TStructIdentifier::new("foo")));
+
+        // contained struct
+        // since this delta > 15 it gets a full write
+        let field_ident_3 = TFieldIdentifier::new("foo", TType::Double, 21);
+        assert_success!(o_prot.write_field_begin(&field_ident_3));
+        assert_success!(o_prot.write_field_end());
+
+        // contained struct
+        // since the delta is > 0 and < 15 it gets a delta write
+        let field_ident_4 = TFieldIdentifier::new("foo", TType::I08, 27);
+        assert_success!(o_prot.write_field_begin(&field_ident_4));
+        assert_success!(o_prot.write_field_end());
+
+        // end contained struct
+        assert_success!(o_prot.write_field_stop());
+        assert_success!(o_prot.write_struct_end());
+
+        // end containing struct
+        assert_success!(o_prot.write_field_stop());
+        assert_success!(o_prot.write_struct_end());
+
+        trans.borrow_mut().copy_write_buffer_to_read_buffer();
+
+        // read containing struct back
+        assert_success!(i_prot.read_struct_begin());
+
+        let read_ident_1 = assert_success!(i_prot.read_field_begin());
+        assert_eq!(read_ident_1,
+                   TFieldIdentifier { name: None, ..field_ident_1 });
+        assert_success!(i_prot.read_field_end());
+
+        let read_ident_2 = assert_success!(i_prot.read_field_begin());
+        assert_eq!(read_ident_2,
+                   TFieldIdentifier { name: None, ..field_ident_2 });
+        assert_success!(i_prot.read_field_end());
+
+        // read contained struct back
+        assert_success!(i_prot.read_struct_begin());
+
+        let read_ident_3 = assert_success!(i_prot.read_field_begin());
+        assert_eq!(read_ident_3,
+                   TFieldIdentifier { name: None, ..field_ident_3 });
+        assert_success!(i_prot.read_field_end());
+
+        let read_ident_4 = assert_success!(i_prot.read_field_begin());
+        assert_eq!(read_ident_4,
+                   TFieldIdentifier { name: None, ..field_ident_4 });
+        assert_success!(i_prot.read_field_end());
+
+        // end contained struct
+        let read_ident_6 = assert_success!(i_prot.read_field_begin());
+        assert_eq!(read_ident_6,
+                   TFieldIdentifier {
+                       name: None,
+                       field_type: TType::Stop,
+                       id: None,
+                   });
+        assert_success!(i_prot.read_struct_end());
+
+        // end containing struct
+        let read_ident_7 = assert_success!(i_prot.read_field_begin());
+        assert_eq!(read_ident_7,
+                   TFieldIdentifier {
+                       name: None,
+                       field_type: TType::Stop,
+                       id: None,
+                   });
+        assert_success!(i_prot.read_struct_end());
+    }
+
+    #[test]
+    fn must_write_bool_field() {
+        let (trans, _, mut o_prot) = test_objects();
+
+        // no bytes should be written however
+        assert_success!(o_prot.write_struct_begin(&TStructIdentifier::new("foo")));
+
+        // write three fields with field ids that cannot be encoded as deltas
+
+        // since the delta is > 0 and < 16 it gets a delta write
+        assert_success!(o_prot.write_field_begin(&TFieldIdentifier::new("foo", TType::Bool, 1)));
+        assert_success!(o_prot.write_bool(true));
+        assert_success!(o_prot.write_field_end());
+
+        // since this delta > 0 and < 15 it gets a delta write
+        assert_success!(o_prot.write_field_begin(&TFieldIdentifier::new("foo", TType::Bool, 9)));
+        assert_success!(o_prot.write_bool(false));
+        assert_success!(o_prot.write_field_end());
+
+        // since this delta > 15 it gets a full write
+        assert_success!(o_prot.write_field_begin(&TFieldIdentifier::new("foo", TType::Bool, 26)));
+        assert_success!(o_prot.write_bool(true));
+        assert_success!(o_prot.write_field_end());
+
+        // since this delta > 15 it gets a full write
+        assert_success!(o_prot.write_field_begin(&TFieldIdentifier::new("foo", TType::Bool, 45)));
+        assert_success!(o_prot.write_bool(false));
+        assert_success!(o_prot.write_field_end());
+
+        // now, finish the struct off
+        assert_success!(o_prot.write_field_stop());
+        assert_success!(o_prot.write_struct_end());
+
+        // get bytes written
+        let buf = trans.borrow_mut().write_buffer_to_vec();
+
+        let expected: [u8; 7] = [0x11 /* field delta (1) | true */,
+                                 0x82 /* field delta (8) | false */, 0x01 /* true */,
+                                 0x34 /* field id */, 0x02 /* false */,
+                                 0x5A /* field id */, 0x00 /* stop field */];
+
+        assert_eq!(&buf, &expected);
+    }
+
+    #[test]
+    fn must_round_trip_bool_field() {
+        let (trans, mut i_prot, mut o_prot) = test_objects();
+
+        // no bytes should be written however
+        let struct_ident = TStructIdentifier::new("foo");
+        assert_success!(o_prot.write_struct_begin(&struct_ident));
+
+        // write two fields
+
+        // since the delta is > 0 and < 16 it gets a delta write
+        let field_ident_1 = TFieldIdentifier::new("foo", TType::Bool, 1);
+        assert_success!(o_prot.write_field_begin(&field_ident_1));
+        assert_success!(o_prot.write_bool(true));
+        assert_success!(o_prot.write_field_end());
+
+        // since this delta > 0 and < 15 it gets a delta write
+        let field_ident_2 = TFieldIdentifier::new("foo", TType::Bool, 9);
+        assert_success!(o_prot.write_field_begin(&field_ident_2));
+        assert_success!(o_prot.write_bool(false));
+        assert_success!(o_prot.write_field_end());
+
+        // since this delta > 15 it gets a full write
+        let field_ident_3 = TFieldIdentifier::new("foo", TType::Bool, 26);
+        assert_success!(o_prot.write_field_begin(&field_ident_3));
+        assert_success!(o_prot.write_bool(true));
+        assert_success!(o_prot.write_field_end());
+
+        // since this delta > 15 it gets a full write
+        let field_ident_4 = TFieldIdentifier::new("foo", TType::Bool, 45);
+        assert_success!(o_prot.write_field_begin(&field_ident_4));
+        assert_success!(o_prot.write_bool(false));
+        assert_success!(o_prot.write_field_end());
+
+        // now, finish the struct off
+        assert_success!(o_prot.write_field_stop());
+        assert_success!(o_prot.write_struct_end());
+
+        trans.borrow_mut().copy_write_buffer_to_read_buffer();
+
+        // read the struct back
+        assert_success!(i_prot.read_struct_begin());
+
+        let read_ident_1 = assert_success!(i_prot.read_field_begin());
+        assert_eq!(read_ident_1,
+                   TFieldIdentifier { name: None, ..field_ident_1 });
+        let read_value_1 = assert_success!(i_prot.read_bool());
+        assert_eq!(read_value_1, true);
+        assert_success!(i_prot.read_field_end());
+
+        let read_ident_2 = assert_success!(i_prot.read_field_begin());
+        assert_eq!(read_ident_2,
+                   TFieldIdentifier { name: None, ..field_ident_2 });
+        let read_value_2 = assert_success!(i_prot.read_bool());
+        assert_eq!(read_value_2, false);
+        assert_success!(i_prot.read_field_end());
+
+        let read_ident_3 = assert_success!(i_prot.read_field_begin());
+        assert_eq!(read_ident_3,
+                   TFieldIdentifier { name: None, ..field_ident_3 });
+        let read_value_3 = assert_success!(i_prot.read_bool());
+        assert_eq!(read_value_3, true);
+        assert_success!(i_prot.read_field_end());
+
+        let read_ident_4 = assert_success!(i_prot.read_field_begin());
+        assert_eq!(read_ident_4,
+                   TFieldIdentifier { name: None, ..field_ident_4 });
+        let read_value_4 = assert_success!(i_prot.read_bool());
+        assert_eq!(read_value_4, false);
+        assert_success!(i_prot.read_field_end());
+
+        let read_ident_5 = assert_success!(i_prot.read_field_begin());
+        assert_eq!(read_ident_5,
+                   TFieldIdentifier {
+                       name: None,
+                       field_type: TType::Stop,
+                       id: None,
+                   });
+
+        assert_success!(i_prot.read_struct_end());
+    }
+
+    #[test]
+    #[should_panic]
+    fn must_fail_if_write_field_end_without_writing_bool_value() {
+        let (_, _, mut o_prot) = test_objects();
+        assert_success!(o_prot.write_struct_begin(&TStructIdentifier::new("foo")));
+        assert_success!(o_prot.write_field_begin(&TFieldIdentifier::new("foo", TType::Bool, 1)));
+        o_prot.write_field_end().unwrap();
+    }
+
+    #[test]
+    #[should_panic]
+    fn must_fail_if_write_stop_field_without_writing_bool_value() {
+        let (_, _, mut o_prot) = test_objects();
+        assert_success!(o_prot.write_struct_begin(&TStructIdentifier::new("foo")));
+        assert_success!(o_prot.write_field_begin(&TFieldIdentifier::new("foo", TType::Bool, 1)));
+        o_prot.write_field_stop().unwrap();
+    }
+
+    #[test]
+    #[should_panic]
+    fn must_fail_if_write_struct_end_without_writing_bool_value() {
+        let (_, _, mut o_prot) = test_objects();
+        assert_success!(o_prot.write_struct_begin(&TStructIdentifier::new("foo")));
+        assert_success!(o_prot.write_field_begin(&TFieldIdentifier::new("foo", TType::Bool, 1)));
+        o_prot.write_struct_end().unwrap();
+    }
+
+    #[test]
+    #[should_panic]
+    fn must_fail_if_write_struct_end_without_any_fields() {
+        let (_, _, mut o_prot) = test_objects();
+        o_prot.write_struct_end().unwrap();
+    }
+
+    #[test]
+    fn must_write_field_end() {
+        assert_no_write(|o| o.write_field_end());
+    }
+
+    #[test]
+    fn must_write_small_sized_list_begin() {
+        let (trans, _, mut o_prot) = test_objects();
+
+        assert_success!(o_prot.write_list_begin(&TListIdentifier::new(TType::I64, 4)));
+
+        let expected: [u8; 1] = [0x46 /* size | elem_type */];
+
+        assert_eq!(trans.borrow().write_buffer_as_ref(), &expected);
+    }
+
+    #[test]
+    fn must_round_trip_small_sized_list_begin() {
+        let (trans, mut i_prot, mut o_prot) = test_objects();
+
+        let ident = TListIdentifier::new(TType::I08, 10);
+
+        assert_success!(o_prot.write_list_begin(&ident));
+
+        trans.borrow_mut().copy_write_buffer_to_read_buffer();
+
+        let res = assert_success!(i_prot.read_list_begin());
+        assert_eq!(&res, &ident);
+    }
+
+    #[test]
+    fn must_write_large_sized_list_begin() {
+        let (trans, _, mut o_prot) = test_objects();
+
+        let res = o_prot.write_list_begin(&TListIdentifier::new(TType::List, 9999));
+        assert!(res.is_ok());
+
+        let expected: [u8; 3] = [0xF9 /* 0xF0 | elem_type */, 0x8F,
+                                 0x4E /* size as varint */];
+
+        assert_eq!(trans.borrow().write_buffer_as_ref(), &expected);
+    }
+
+    #[test]
+    fn must_round_trip_large_sized_list_begin() {
+        let (trans, mut i_prot, mut o_prot) = test_objects();
+
+        let ident = TListIdentifier::new(TType::Set, 47381);
+
+        assert_success!(o_prot.write_list_begin(&ident));
+
+        trans.borrow_mut().copy_write_buffer_to_read_buffer();
+
+        let res = assert_success!(i_prot.read_list_begin());
+        assert_eq!(&res, &ident);
+    }
+
+    #[test]
+    fn must_write_list_end() {
+        assert_no_write(|o| o.write_list_end());
+    }
+
+    #[test]
+    fn must_write_small_sized_set_begin() {
+        let (trans, _, mut o_prot) = test_objects();
+
+        assert_success!(o_prot.write_set_begin(&TSetIdentifier::new(TType::Struct, 2)));
+
+        let expected: [u8; 1] = [0x2C /* size | elem_type */];
+
+        assert_eq!(trans.borrow().write_buffer_as_ref(), &expected);
+    }
+
+    #[test]
+    fn must_round_trip_small_sized_set_begin() {
+        let (trans, mut i_prot, mut o_prot) = test_objects();
+
+        let ident = TSetIdentifier::new(TType::I16, 7);
+
+        assert_success!(o_prot.write_set_begin(&ident));
+
+        trans.borrow_mut().copy_write_buffer_to_read_buffer();
+
+        let res = assert_success!(i_prot.read_set_begin());
+        assert_eq!(&res, &ident);
+    }
+
+    #[test]
+    fn must_write_large_sized_set_begin() {
+        let (trans, _, mut o_prot) = test_objects();
+
+        assert_success!(o_prot.write_set_begin(&TSetIdentifier::new(TType::Double, 23891)));
+
+        let expected: [u8; 4] = [0xF7 /* 0xF0 | elem_type */, 0xD3, 0xBA,
+                                 0x01 /* size as varint */];
+
+        assert_eq!(trans.borrow().write_buffer_as_ref(), &expected);
+    }
+
+    #[test]
+    fn must_round_trip_large_sized_set_begin() {
+        let (trans, mut i_prot, mut o_prot) = test_objects();
+
+        let ident = TSetIdentifier::new(TType::Map, 3928429);
+
+        assert_success!(o_prot.write_set_begin(&ident));
+
+        trans.borrow_mut().copy_write_buffer_to_read_buffer();
+
+        let res = assert_success!(i_prot.read_set_begin());
+        assert_eq!(&res, &ident);
+    }
+
+    #[test]
+    fn must_write_set_end() {
+        assert_no_write(|o| o.write_set_end());
+    }
+
+    #[test]
+    fn must_write_zero_sized_map_begin() {
+        let (trans, _, mut o_prot) = test_objects();
+
+        assert_success!(o_prot.write_map_begin(&TMapIdentifier::new(TType::String, TType::I32, 0)));
+
+        let expected: [u8; 1] = [0x00]; // since size is zero we don't write anything
+
+        assert_eq!(trans.borrow().write_buffer_as_ref(), &expected);
+    }
+
+    #[test]
+    fn must_read_zero_sized_map_begin() {
+        let (trans, mut i_prot, mut o_prot) = test_objects();
+
+        assert_success!(o_prot.write_map_begin(&TMapIdentifier::new(TType::Double, TType::I32, 0)));
+
+        trans.borrow_mut().copy_write_buffer_to_read_buffer();
+
+        let res = assert_success!(i_prot.read_map_begin());
+        assert_eq!(&res,
+                   &TMapIdentifier {
+                       key_type: None,
+                       value_type: None,
+                       size: 0,
+                   });
+    }
+
+    #[test]
+    fn must_write_map_begin() {
+        let (trans, _, mut o_prot) = test_objects();
+
+        assert_success!(o_prot.write_map_begin(&TMapIdentifier::new(TType::Double, TType::String, 238)));
+
+        let expected: [u8; 3] = [0xEE, 0x01 /* size as varint */,
+                                 0x78 /* key type | val type */];
+
+        assert_eq!(trans.borrow().write_buffer_as_ref(), &expected);
+    }
+
+    #[test]
+    fn must_round_trip_map_begin() {
+        let (trans, mut i_prot, mut o_prot) = test_objects();
+
+        let ident = TMapIdentifier::new(TType::Map, TType::List, 1928349);
+
+        assert_success!(o_prot.write_map_begin(&ident));
+
+        trans.borrow_mut().copy_write_buffer_to_read_buffer();
+
+        let res = assert_success!(i_prot.read_map_begin());
+        assert_eq!(&res, &ident);
+    }
+
+    #[test]
+    fn must_write_map_end() {
+        assert_no_write(|o| o.write_map_end());
+    }
+
+    #[test]
+    fn must_write_map_with_bool_key_and_value() {
+        let (trans, _, mut o_prot) = test_objects();
+
+        assert_success!(o_prot.write_map_begin(&TMapIdentifier::new(TType::Bool, TType::Bool, 1)));
+        assert_success!(o_prot.write_bool(true));
+        assert_success!(o_prot.write_bool(false));
+        assert_success!(o_prot.write_map_end());
+
+        let expected: [u8; 4] = [0x01 /* size as varint */,
+                                 0x11 /* key type | val type */, 0x01 /* key: true */,
+                                 0x02 /* val: false */];
+
+        assert_eq!(trans.borrow().write_buffer_as_ref(), &expected);
+    }
+
+    #[test]
+    fn must_round_trip_map_with_bool_value() {
+        let (trans, mut i_prot, mut o_prot) = test_objects();
+
+        let map_ident = TMapIdentifier::new(TType::Bool, TType::Bool, 2);
+        assert_success!(o_prot.write_map_begin(&map_ident));
+        assert_success!(o_prot.write_bool(true));
+        assert_success!(o_prot.write_bool(false));
+        assert_success!(o_prot.write_bool(false));
+        assert_success!(o_prot.write_bool(true));
+        assert_success!(o_prot.write_map_end());
+
+        trans.borrow_mut().copy_write_buffer_to_read_buffer();
+
+        // map header
+        let rcvd_ident = assert_success!(i_prot.read_map_begin());
+        assert_eq!(&rcvd_ident, &map_ident);
+        // key 1
+        let b = assert_success!(i_prot.read_bool());
+        assert_eq!(b, true);
+        // val 1
+        let b = assert_success!(i_prot.read_bool());
+        assert_eq!(b, false);
+        // key 2
+        let b = assert_success!(i_prot.read_bool());
+        assert_eq!(b, false);
+        // val 2
+        let b = assert_success!(i_prot.read_bool());
+        assert_eq!(b, true);
+        // map end
+        assert_success!(i_prot.read_map_end());
+    }
+
+    #[test]
+    fn must_read_map_end() {
+        let (_, mut i_prot, _) = test_objects();
+        assert!(i_prot.read_map_end().is_ok()); // will blow up if we try to read from empty buffer
+    }
+
+    fn test_objects
+        ()
+        -> (Rc<RefCell<Box<TBufferTransport>>>, TCompactInputProtocol, TCompactOutputProtocol)
+    {
+        let mem = Rc::new(RefCell::new(Box::new(TBufferTransport::with_capacity(80, 80))));
+
+        let inner: Box<TTransport> = Box::new(TPassThruTransport { inner: mem.clone() });
+        let inner = Rc::new(RefCell::new(inner));
+
+        let i_prot = TCompactInputProtocol::new(inner.clone());
+        let o_prot = TCompactOutputProtocol::new(inner.clone());
+
+        (mem, i_prot, o_prot)
+    }
+
+    fn assert_no_write<F: FnMut(&mut TCompactOutputProtocol) -> ::Result<()>>(mut write_fn: F) {
+        let (trans, _, mut o_prot) = test_objects();
+        assert!(write_fn(&mut o_prot).is_ok());
+        assert_eq!(trans.borrow().write_buffer_as_ref().len(), 0);
+    }
+}
diff --git a/lib/rs/src/protocol/mod.rs b/lib/rs/src/protocol/mod.rs
new file mode 100644
index 0000000..b230d63
--- /dev/null
+++ b/lib/rs/src/protocol/mod.rs
@@ -0,0 +1,709 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+//! Types used to send and receive primitives between a Thrift client and server.
+//!
+//! # Examples
+//!
+//! Create and use a `TOutputProtocol`.
+//!
+//! ```no_run
+//! use std::cell::RefCell;
+//! use std::rc::Rc;
+//! use thrift::protocol::{TBinaryOutputProtocol, TFieldIdentifier, TOutputProtocol, TType};
+//! use thrift::transport::{TTcpTransport, TTransport};
+//!
+//! // create the I/O channel
+//! let mut transport = TTcpTransport::new();
+//! transport.open("127.0.0.1:9090").unwrap();
+//! let transport = Rc::new(RefCell::new(Box::new(transport) as Box<TTransport>));
+//!
+//! // create the protocol to encode types into bytes
+//! let mut o_prot = TBinaryOutputProtocol::new(transport.clone(), true);
+//!
+//! // write types
+//! o_prot.write_field_begin(&TFieldIdentifier::new("string_thing", TType::String, 1)).unwrap();
+//! o_prot.write_string("foo").unwrap();
+//! o_prot.write_field_end().unwrap();
+//! ```
+//!
+//! Create and use a `TInputProtocol`.
+//!
+//! ```no_run
+//! use std::cell::RefCell;
+//! use std::rc::Rc;
+//! use thrift::protocol::{TBinaryInputProtocol, TInputProtocol};
+//! use thrift::transport::{TTcpTransport, TTransport};
+//!
+//! // create the I/O channel
+//! let mut transport = TTcpTransport::new();
+//! transport.open("127.0.0.1:9090").unwrap();
+//! let transport = Rc::new(RefCell::new(Box::new(transport) as Box<TTransport>));
+//!
+//! // create the protocol to decode bytes into types
+//! let mut i_prot = TBinaryInputProtocol::new(transport.clone(), true);
+//!
+//! // read types from the wire
+//! let field_identifier = i_prot.read_field_begin().unwrap();
+//! let field_contents = i_prot.read_string().unwrap();
+//! let field_end = i_prot.read_field_end().unwrap();
+//! ```
+
+use std::cell::RefCell;
+use std::fmt;
+use std::fmt::{Display, Formatter};
+use std::convert::From;
+use std::rc::Rc;
+use try_from::TryFrom;
+
+use ::{ProtocolError, ProtocolErrorKind};
+use ::transport::TTransport;
+
+mod binary;
+mod compact;
+mod multiplexed;
+mod stored;
+
+pub use self::binary::{TBinaryInputProtocol, TBinaryInputProtocolFactory, TBinaryOutputProtocol,
+                       TBinaryOutputProtocolFactory};
+pub use self::compact::{TCompactInputProtocol, TCompactInputProtocolFactory,
+                        TCompactOutputProtocol, TCompactOutputProtocolFactory};
+pub use self::multiplexed::TMultiplexedOutputProtocol;
+pub use self::stored::TStoredInputProtocol;
+
+// Default maximum depth to which `TInputProtocol::skip` will skip a Thrift
+// field. A default is necessary because Thrift structs or collections may
+// contain nested structs and collections, which could result in indefinite
+// recursion.
+const MAXIMUM_SKIP_DEPTH: i8 = 64;
+
+/// Converts a stream of bytes into Thrift identifiers, primitives,
+/// containers, or structs.
+///
+/// This trait does not deal with higher-level Thrift concepts like structs or
+/// exceptions - only with primitives and message or container boundaries. Once
+/// bytes are read they are deserialized and an identifier (for example
+/// `TMessageIdentifier`) or a primitive is returned.
+///
+/// All methods return a `thrift::Result`. If an `Err` is returned the protocol
+/// instance and its underlying transport should be terminated.
+///
+/// # Examples
+///
+/// Create and use a `TInputProtocol`
+///
+/// ```no_run
+/// use std::cell::RefCell;
+/// use std::rc::Rc;
+/// use thrift::protocol::{TBinaryInputProtocol, TInputProtocol};
+/// use thrift::transport::{TTcpTransport, TTransport};
+///
+/// let mut transport = TTcpTransport::new();
+/// transport.open("127.0.0.1:9090").unwrap();
+/// let transport = Rc::new(RefCell::new(Box::new(transport) as Box<TTransport>));
+///
+/// let mut i_prot = TBinaryInputProtocol::new(transport.clone(), true);
+///
+/// let field_identifier = i_prot.read_field_begin().unwrap();
+/// let field_contents = i_prot.read_string().unwrap();
+/// let field_end = i_prot.read_field_end().unwrap();
+/// ```
+pub trait TInputProtocol {
+    /// Read the beginning of a Thrift message.
+    fn read_message_begin(&mut self) -> ::Result<TMessageIdentifier>;
+    /// Read the end of a Thrift message.
+    fn read_message_end(&mut self) -> ::Result<()>;
+    /// Read the beginning of a Thrift struct.
+    fn read_struct_begin(&mut self) -> ::Result<Option<TStructIdentifier>>;
+    /// Read the end of a Thrift struct.
+    fn read_struct_end(&mut self) -> ::Result<()>;
+    /// Read the beginning of a Thrift struct field.
+    fn read_field_begin(&mut self) -> ::Result<TFieldIdentifier>;
+    /// Read the end of a Thrift struct field.
+    fn read_field_end(&mut self) -> ::Result<()>;
+    /// Read a bool.
+    fn read_bool(&mut self) -> ::Result<bool>;
+    /// Read a fixed-length byte array.
+    fn read_bytes(&mut self) -> ::Result<Vec<u8>>;
+    /// Read a word.
+    fn read_i8(&mut self) -> ::Result<i8>;
+    /// Read a 16-bit signed integer.
+    fn read_i16(&mut self) -> ::Result<i16>;
+    /// Read a 32-bit signed integer.
+    fn read_i32(&mut self) -> ::Result<i32>;
+    /// Read a 64-bit signed integer.
+    fn read_i64(&mut self) -> ::Result<i64>;
+    /// Read a 64-bit float.
+    fn read_double(&mut self) -> ::Result<f64>;
+    /// Read a fixed-length string (not null terminated).
+    fn read_string(&mut self) -> ::Result<String>;
+    /// Read the beginning of a list.
+    fn read_list_begin(&mut self) -> ::Result<TListIdentifier>;
+    /// Read the end of a list.
+    fn read_list_end(&mut self) -> ::Result<()>;
+    /// Read the beginning of a set.
+    fn read_set_begin(&mut self) -> ::Result<TSetIdentifier>;
+    /// Read the end of a set.
+    fn read_set_end(&mut self) -> ::Result<()>;
+    /// Read the beginning of a map.
+    fn read_map_begin(&mut self) -> ::Result<TMapIdentifier>;
+    /// Read the end of a map.
+    fn read_map_end(&mut self) -> ::Result<()>;
+    /// Skip a field with type `field_type` recursively until the default
+    /// maximum skip depth is reached.
+    fn skip(&mut self, field_type: TType) -> ::Result<()> {
+        self.skip_till_depth(field_type, MAXIMUM_SKIP_DEPTH)
+    }
+    /// Skip a field with type `field_type` recursively up to `depth` levels.
+    fn skip_till_depth(&mut self, field_type: TType, depth: i8) -> ::Result<()> {
+        if depth == 0 {
+            return Err(::Error::Protocol(ProtocolError {
+                kind: ProtocolErrorKind::DepthLimit,
+                message: format!("cannot parse past {:?}", field_type),
+            }));
+        }
+
+        match field_type {
+            TType::Bool => self.read_bool().map(|_| ()),
+            TType::I08 => self.read_i8().map(|_| ()),
+            TType::I16 => self.read_i16().map(|_| ()),
+            TType::I32 => self.read_i32().map(|_| ()),
+            TType::I64 => self.read_i64().map(|_| ()),
+            TType::Double => self.read_double().map(|_| ()),
+            TType::String => self.read_string().map(|_| ()),
+            TType::Struct => {
+                self.read_struct_begin()?;
+                loop {
+                    let field_ident = self.read_field_begin()?;
+                    if field_ident.field_type == TType::Stop {
+                        break;
+                    }
+                    self.skip_till_depth(field_ident.field_type, depth - 1)?;
+                }
+                self.read_struct_end()
+            }
+            TType::List => {
+                let list_ident = self.read_list_begin()?;
+                for _ in 0..list_ident.size {
+                    self.skip_till_depth(list_ident.element_type, depth - 1)?;
+                }
+                self.read_list_end()
+            }
+            TType::Set => {
+                let set_ident = self.read_set_begin()?;
+                for _ in 0..set_ident.size {
+                    self.skip_till_depth(set_ident.element_type, depth - 1)?;
+                }
+                self.read_set_end()
+            }
+            TType::Map => {
+                let map_ident = self.read_map_begin()?;
+                for _ in 0..map_ident.size {
+                    let key_type = map_ident.key_type
+                        .expect("non-zero sized map should contain key type");
+                    let val_type = map_ident.value_type
+                        .expect("non-zero sized map should contain value type");
+                    self.skip_till_depth(key_type, depth - 1)?;
+                    self.skip_till_depth(val_type, depth - 1)?;
+                }
+                self.read_map_end()
+            }
+            u => {
+                Err(::Error::Protocol(ProtocolError {
+                    kind: ProtocolErrorKind::Unknown,
+                    message: format!("cannot skip field type {:?}", &u),
+                }))
+            }
+        }
+    }
+
+    // utility (DO NOT USE IN GENERATED CODE!!!!)
+    //
+
+    /// Read an unsigned byte.
+    ///
+    /// This method should **never** be used in generated code.
+    fn read_byte(&mut self) -> ::Result<u8>;
+}
+
+/// Converts Thrift identifiers, primitives, containers or structs into a
+/// stream of bytes.
+///
+/// This trait does not deal with higher-level Thrift concepts like structs or
+/// exceptions - only with primitives and message or container boundaries.
+/// Write methods take an identifier (for example, `TMessageIdentifier`) or a
+/// primitive. Any or all of the fields in an identifier may be omitted when
+/// writing to the transport. Write methods may even be noops. All of this is
+/// transparent to the caller; as long as a matching `TInputProtocol`
+/// implementation is used, received messages will be decoded correctly.
+///
+/// All methods return a `thrift::Result`. If an `Err` is returned the protocol
+/// instance and its underlying transport should be terminated.
+///
+/// # Examples
+///
+/// Create and use a `TOutputProtocol`
+///
+/// ```no_run
+/// use std::cell::RefCell;
+/// use std::rc::Rc;
+/// use thrift::protocol::{TBinaryOutputProtocol, TFieldIdentifier, TOutputProtocol, TType};
+/// use thrift::transport::{TTcpTransport, TTransport};
+///
+/// let mut transport = TTcpTransport::new();
+/// transport.open("127.0.0.1:9090").unwrap();
+/// let transport = Rc::new(RefCell::new(Box::new(transport) as Box<TTransport>));
+///
+/// let mut o_prot = TBinaryOutputProtocol::new(transport.clone(), true);
+///
+/// o_prot.write_field_begin(&TFieldIdentifier::new("string_thing", TType::String, 1)).unwrap();
+/// o_prot.write_string("foo").unwrap();
+/// o_prot.write_field_end().unwrap();
+/// ```
+pub trait TOutputProtocol {
+    /// Write the beginning of a Thrift message.
+    fn write_message_begin(&mut self, identifier: &TMessageIdentifier) -> ::Result<()>;
+    /// Write the end of a Thrift message.
+    fn write_message_end(&mut self) -> ::Result<()>;
+    /// Write the beginning of a Thrift struct.
+    fn write_struct_begin(&mut self, identifier: &TStructIdentifier) -> ::Result<()>;
+    /// Write the end of a Thrift struct.
+    fn write_struct_end(&mut self) -> ::Result<()>;
+    /// Write the beginning of a Thrift field.
+    fn write_field_begin(&mut self, identifier: &TFieldIdentifier) -> ::Result<()>;
+    /// Write the end of a Thrift field.
+    fn write_field_end(&mut self) -> ::Result<()>;
+    /// Write a STOP field indicating that all the fields in a struct have been
+    /// written.
+    fn write_field_stop(&mut self) -> ::Result<()>;
+    /// Write a bool.
+    fn write_bool(&mut self, b: bool) -> ::Result<()>;
+    /// Write a fixed-length byte array.
+    fn write_bytes(&mut self, b: &[u8]) -> ::Result<()>;
+    /// Write an 8-bit signed integer.
+    fn write_i8(&mut self, i: i8) -> ::Result<()>;
+    /// Write a 16-bit signed integer.
+    fn write_i16(&mut self, i: i16) -> ::Result<()>;
+    /// Write a 32-bit signed integer.
+    fn write_i32(&mut self, i: i32) -> ::Result<()>;
+    /// Write a 64-bit signed integer.
+    fn write_i64(&mut self, i: i64) -> ::Result<()>;
+    /// Write a 64-bit float.
+    fn write_double(&mut self, d: f64) -> ::Result<()>;
+    /// Write a fixed-length string.
+    fn write_string(&mut self, s: &str) -> ::Result<()>;
+    /// Write the beginning of a list.
+    fn write_list_begin(&mut self, identifier: &TListIdentifier) -> ::Result<()>;
+    /// Write the end of a list.
+    fn write_list_end(&mut self) -> ::Result<()>;
+    /// Write the beginning of a set.
+    fn write_set_begin(&mut self, identifier: &TSetIdentifier) -> ::Result<()>;
+    /// Write the end of a set.
+    fn write_set_end(&mut self) -> ::Result<()>;
+    /// Write the beginning of a map.
+    fn write_map_begin(&mut self, identifier: &TMapIdentifier) -> ::Result<()>;
+    /// Write the end of a map.
+    fn write_map_end(&mut self) -> ::Result<()>;
+    /// Flush buffered bytes to the underlying transport.
+    fn flush(&mut self) -> ::Result<()>;
+
+    // utility (DO NOT USE IN GENERATED CODE!!!!)
+    //
+
+    /// Write an unsigned byte.
+    ///
+    /// This method should **never** be used in generated code.
+    fn write_byte(&mut self, b: u8) -> ::Result<()>; // FIXME: REMOVE
+}
+
+/// Helper type used by servers to create `TInputProtocol` instances for
+/// accepted client connections.
+///
+/// # Examples
+///
+/// Create a `TInputProtocolFactory` and use it to create a `TInputProtocol`.
+///
+/// ```no_run
+/// use std::cell::RefCell;
+/// use std::rc::Rc;
+/// use thrift::protocol::{TBinaryInputProtocolFactory, TInputProtocolFactory};
+/// use thrift::transport::{TTcpTransport, TTransport};
+///
+/// let mut transport = TTcpTransport::new();
+/// transport.open("127.0.0.1:9090").unwrap();
+/// let transport = Rc::new(RefCell::new(Box::new(transport) as Box<TTransport>));
+///
+/// let mut i_proto_factory = TBinaryInputProtocolFactory::new();
+/// let i_prot = i_proto_factory.create(transport);
+/// ```
+pub trait TInputProtocolFactory {
+    /// Create a `TInputProtocol` that reads bytes from `transport`.
+    fn create(&mut self, transport: Rc<RefCell<Box<TTransport>>>) -> Box<TInputProtocol>;
+}
+
+/// Helper type used by servers to create `TOutputProtocol` instances for
+/// accepted client connections.
+///
+/// # Examples
+///
+/// Create a `TOutputProtocolFactory` and use it to create a `TOutputProtocol`.
+///
+/// ```no_run
+/// use std::cell::RefCell;
+/// use std::rc::Rc;
+/// use thrift::protocol::{TBinaryOutputProtocolFactory, TOutputProtocolFactory};
+/// use thrift::transport::{TTcpTransport, TTransport};
+///
+/// let mut transport = TTcpTransport::new();
+/// transport.open("127.0.0.1:9090").unwrap();
+/// let transport = Rc::new(RefCell::new(Box::new(transport) as Box<TTransport>));
+///
+/// let mut o_proto_factory = TBinaryOutputProtocolFactory::new();
+/// let o_prot = o_proto_factory.create(transport);
+/// ```
+pub trait TOutputProtocolFactory {
+    /// Create a `TOutputProtocol` that writes bytes to `transport`.
+    fn create(&mut self, transport: Rc<RefCell<Box<TTransport>>>) -> Box<TOutputProtocol>;
+}
+
+/// Thrift message identifier.
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub struct TMessageIdentifier {
+    /// Service call the message is associated with.
+    pub name: String,
+    /// Message type.
+    pub message_type: TMessageType,
+    /// Ordered sequence number identifying the message.
+    pub sequence_number: i32,
+}
+
+impl TMessageIdentifier {
+    /// Create a `TMessageIdentifier` for a Thrift service-call named `name`
+    /// with message type `message_type` and sequence number `sequence_number`.
+    pub fn new<S: Into<String>>(name: S,
+                                message_type: TMessageType,
+                                sequence_number: i32)
+                                -> TMessageIdentifier {
+        TMessageIdentifier {
+            name: name.into(),
+            message_type: message_type,
+            sequence_number: sequence_number,
+        }
+    }
+}
+
+/// Thrift struct identifier.
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub struct TStructIdentifier {
+    /// Name of the encoded Thrift struct.
+    pub name: String,
+}
+
+impl TStructIdentifier {
+    /// Create a `TStructIdentifier` for a struct named `name`.
+    pub fn new<S: Into<String>>(name: S) -> TStructIdentifier {
+        TStructIdentifier { name: name.into() }
+    }
+}
+
+/// Thrift field identifier.
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub struct TFieldIdentifier {
+    /// Name of the Thrift field.
+    ///
+    /// `None` if it's not sent over the wire.
+    pub name: Option<String>,
+    /// Field type.
+    ///
+    /// This may be a primitive, container, or a struct.
+    pub field_type: TType,
+    /// Thrift field id.
+    ///
+    /// `None` only if `field_type` is `TType::Stop`.
+    pub id: Option<i16>,
+}
+
+impl TFieldIdentifier {
+    /// Create a `TFieldIdentifier` for a field named `name` with type
+    /// `field_type` and field id `id`.
+    ///
+    /// `id` should be `None` if `field_type` is `TType::Stop`.
+    pub fn new<N, S, I>(name: N, field_type: TType, id: I) -> TFieldIdentifier
+        where N: Into<Option<S>>,
+              S: Into<String>,
+              I: Into<Option<i16>>
+    {
+        TFieldIdentifier {
+            name: name.into().map(|n| n.into()),
+            field_type: field_type,
+            id: id.into(),
+        }
+    }
+}
+
+/// Thrift list identifier.
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub struct TListIdentifier {
+    /// Type of the elements in the list.
+    pub element_type: TType,
+    /// Number of elements in the list.
+    pub size: i32,
+}
+
+impl TListIdentifier {
+    /// Create a `TListIdentifier` for a list with `size` elements of type
+    /// `element_type`.
+    pub fn new(element_type: TType, size: i32) -> TListIdentifier {
+        TListIdentifier {
+            element_type: element_type,
+            size: size,
+        }
+    }
+}
+
+/// Thrift set identifier.
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub struct TSetIdentifier {
+    /// Type of the elements in the set.
+    pub element_type: TType,
+    /// Number of elements in the set.
+    pub size: i32,
+}
+
+impl TSetIdentifier {
+    /// Create a `TSetIdentifier` for a set with `size` elements of type
+    /// `element_type`.
+    pub fn new(element_type: TType, size: i32) -> TSetIdentifier {
+        TSetIdentifier {
+            element_type: element_type,
+            size: size,
+        }
+    }
+}
+
+/// Thrift map identifier.
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub struct TMapIdentifier {
+    /// Map key type.
+    pub key_type: Option<TType>,
+    /// Map value type.
+    pub value_type: Option<TType>,
+    /// Number of entries in the map.
+    pub size: i32,
+}
+
+impl TMapIdentifier {
+    /// Create a `TMapIdentifier` for a map with `size` entries of type
+    /// `key_type -> value_type`.
+    pub fn new<K, V>(key_type: K, value_type: V, size: i32) -> TMapIdentifier
+        where K: Into<Option<TType>>,
+              V: Into<Option<TType>>
+    {
+        TMapIdentifier {
+            key_type: key_type.into(),
+            value_type: value_type.into(),
+            size: size,
+        }
+    }
+}
+
+/// Thrift message types.
+#[derive(Clone, Copy, Debug, Eq, PartialEq)]
+pub enum TMessageType {
+    /// Service-call request.
+    Call,
+    /// Service-call response.
+    Reply,
+    /// Unexpected error in the remote service.
+    Exception,
+    /// One-way service-call request (no response is expected).
+    OneWay,
+}
+
+impl Display for TMessageType {
+    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
+        match *self {
+            TMessageType::Call => write!(f, "Call"),
+            TMessageType::Reply => write!(f, "Reply"),
+            TMessageType::Exception => write!(f, "Exception"),
+            TMessageType::OneWay => write!(f, "OneWay"),
+        }
+    }
+}
+
+impl From<TMessageType> for u8 {
+    fn from(message_type: TMessageType) -> Self {
+        match message_type {
+            TMessageType::Call => 0x01,
+            TMessageType::Reply => 0x02,
+            TMessageType::Exception => 0x03,
+            TMessageType::OneWay => 0x04,
+        }
+    }
+}
+
+impl TryFrom<u8> for TMessageType {
+    type Err = ::Error;
+    fn try_from(b: u8) -> ::Result<Self> {
+        match b {
+            0x01 => Ok(TMessageType::Call),
+            0x02 => Ok(TMessageType::Reply),
+            0x03 => Ok(TMessageType::Exception),
+            0x04 => Ok(TMessageType::OneWay),
+            unkn => {
+                Err(::Error::Protocol(ProtocolError {
+                    kind: ProtocolErrorKind::InvalidData,
+                    message: format!("cannot convert {} to TMessageType", unkn),
+                }))
+            }
+        }
+    }
+}
+
+/// Thrift struct-field types.
+#[derive(Clone, Copy, Debug, Eq, PartialEq)]
+pub enum TType {
+    /// Indicates that there are no more serialized fields in this Thrift struct.
+    Stop,
+    /// Void (`()`) field.
+    Void,
+    /// Boolean.
+    Bool,
+    /// Signed 8-bit int.
+    I08,
+    /// Double-precision number.
+    Double,
+    /// Signed 16-bit int.
+    I16,
+    /// Signed 32-bit int.
+    I32,
+    /// Signed 64-bit int.
+    I64,
+    /// UTF-8 string.
+    String,
+    /// UTF-7 string. *Unsupported*.
+    Utf7,
+    /// Thrift struct.
+    Struct,
+    /// Map.
+    Map,
+    /// Set.
+    Set,
+    /// List.
+    List,
+    /// UTF-8 string.
+    Utf8,
+    /// UTF-16 string. *Unsupported*.
+    Utf16,
+}
+
+impl Display for TType {
+    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
+        match *self {
+            TType::Stop => write!(f, "STOP"),
+            TType::Void => write!(f, "void"),
+            TType::Bool => write!(f, "bool"),
+            TType::I08 => write!(f, "i08"),
+            TType::Double => write!(f, "double"),
+            TType::I16 => write!(f, "i16"),
+            TType::I32 => write!(f, "i32"),
+            TType::I64 => write!(f, "i64"),
+            TType::String => write!(f, "string"),
+            TType::Utf7 => write!(f, "UTF7"),
+            TType::Struct => write!(f, "struct"),
+            TType::Map => write!(f, "map"),
+            TType::Set => write!(f, "set"),
+            TType::List => write!(f, "list"),
+            TType::Utf8 => write!(f, "UTF8"),
+            TType::Utf16 => write!(f, "UTF16"),
+        }
+    }
+}
+
+/// Compare the expected message sequence number `expected` with the received
+/// message sequence number `actual`.
+///
+/// Return `()` if `actual == expected`, `Err` otherwise.
+pub fn verify_expected_sequence_number(expected: i32, actual: i32) -> ::Result<()> {
+    if expected == actual {
+        Ok(())
+    } else {
+        Err(::Error::Application(::ApplicationError {
+            kind: ::ApplicationErrorKind::BadSequenceId,
+            message: format!("expected {} got {}", expected, actual),
+        }))
+    }
+}
+
+/// Compare the expected service-call name `expected` with the received
+/// service-call name `actual`.
+///
+/// Return `()` if `actual == expected`, `Err` otherwise.
+pub fn verify_expected_service_call(expected: &str, actual: &str) -> ::Result<()> {
+    if expected == actual {
+        Ok(())
+    } else {
+        Err(::Error::Application(::ApplicationError {
+            kind: ::ApplicationErrorKind::WrongMethodName,
+            message: format!("expected {} got {}", expected, actual),
+        }))
+    }
+}
+
+/// Compare the expected message type `expected` with the received message type
+/// `actual`.
+///
+/// Return `()` if `actual == expected`, `Err` otherwise.
+pub fn verify_expected_message_type(expected: TMessageType, actual: TMessageType) -> ::Result<()> {
+    if expected == actual {
+        Ok(())
+    } else {
+        Err(::Error::Application(::ApplicationError {
+            kind: ::ApplicationErrorKind::InvalidMessageType,
+            message: format!("expected {} got {}", expected, actual),
+        }))
+    }
+}
+
+/// Check if a required Thrift struct field exists.
+///
+/// Return `()` if it does, `Err` otherwise.
+pub fn verify_required_field_exists<T>(field_name: &str, field: &Option<T>) -> ::Result<()> {
+    match *field {
+        Some(_) => Ok(()),
+        None => {
+            Err(::Error::Protocol(::ProtocolError {
+                kind: ::ProtocolErrorKind::Unknown,
+                message: format!("missing required field {}", field_name),
+            }))
+        }
+    }
+}
+
+/// Extract the field id from a Thrift field identifier.
+///
+/// `field_ident` must *not* have `TFieldIdentifier.field_type` of type `TType::Stop`.
+///
+/// Return `TFieldIdentifier.id` if an id exists, `Err` otherwise.
+pub fn field_id(field_ident: &TFieldIdentifier) -> ::Result<i16> {
+    field_ident.id.ok_or_else(|| {
+        ::Error::Protocol(::ProtocolError {
+            kind: ::ProtocolErrorKind::Unknown,
+            message: format!("missing field in in {:?}", field_ident),
+        })
+    })
+}
diff --git a/lib/rs/src/protocol/multiplexed.rs b/lib/rs/src/protocol/multiplexed.rs
new file mode 100644
index 0000000..15fe608
--- /dev/null
+++ b/lib/rs/src/protocol/multiplexed.rs
@@ -0,0 +1,219 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+use super::{TFieldIdentifier, TListIdentifier, TMapIdentifier, TMessageIdentifier, TMessageType,
+            TOutputProtocol, TSetIdentifier, TStructIdentifier};
+
+/// `TOutputProtocol` that prefixes the service name to all outgoing Thrift
+/// messages.
+///
+/// A `TMultiplexedOutputProtocol` should be used when multiple Thrift services
+/// send messages over a single I/O channel. By prefixing service identifiers
+/// to outgoing messages receivers are able to demux them and route them to the
+/// appropriate service processor. Rust receivers must use a `TMultiplexedProcessor`
+/// to process incoming messages, while other languages must use their
+/// corresponding multiplexed processor implementations.
+///
+/// For example, given a service `TestService` and a service call `test_call`,
+/// this implementation would identify messages as originating from
+/// `TestService:test_call`.
+///
+/// # Examples
+///
+/// Create and use a `TMultiplexedOutputProtocol`.
+///
+/// ```no_run
+/// use std::cell::RefCell;
+/// use std::rc::Rc;
+/// use thrift::protocol::{TMessageIdentifier, TMessageType, TOutputProtocol};
+/// use thrift::protocol::{TBinaryOutputProtocol, TMultiplexedOutputProtocol};
+/// use thrift::transport::{TTcpTransport, TTransport};
+///
+/// let mut transport = TTcpTransport::new();
+/// transport.open("localhost:9090").unwrap();
+/// let transport = Rc::new(RefCell::new(Box::new(transport) as Box<TTransport>));
+///
+/// let o_prot = TBinaryOutputProtocol::new(transport, true);
+/// let mut o_prot = TMultiplexedOutputProtocol::new("service_name", Box::new(o_prot));
+///
+/// let ident = TMessageIdentifier::new("svc_call", TMessageType::Call, 1);
+/// o_prot.write_message_begin(&ident).unwrap();
+/// ```
+pub struct TMultiplexedOutputProtocol {
+    service_name: String,
+    inner: Box<TOutputProtocol>,
+}
+
+impl TMultiplexedOutputProtocol {
+    /// Create a `TMultiplexedOutputProtocol` that identifies outgoing messages
+    /// as originating from a service named `service_name` and sends them over
+    /// the `wrapped` `TOutputProtocol`. Outgoing messages are encoded and sent
+    /// by `wrapped`, not by this instance.
+    pub fn new(service_name: &str, wrapped: Box<TOutputProtocol>) -> TMultiplexedOutputProtocol {
+        TMultiplexedOutputProtocol {
+            service_name: service_name.to_owned(),
+            inner: wrapped,
+        }
+    }
+}
+
+// FIXME: avoid passthrough methods
+impl TOutputProtocol for TMultiplexedOutputProtocol {
+    fn write_message_begin(&mut self, identifier: &TMessageIdentifier) -> ::Result<()> {
+        match identifier.message_type { // FIXME: is there a better way to override identifier here?
+            TMessageType::Call | TMessageType::OneWay => {
+                let identifier = TMessageIdentifier {
+                    name: format!("{}:{}", self.service_name, identifier.name),
+                    ..*identifier
+                };
+                self.inner.write_message_begin(&identifier)
+            }
+            _ => self.inner.write_message_begin(identifier),
+        }
+    }
+
+    fn write_message_end(&mut self) -> ::Result<()> {
+        self.inner.write_message_end()
+    }
+
+    fn write_struct_begin(&mut self, identifier: &TStructIdentifier) -> ::Result<()> {
+        self.inner.write_struct_begin(identifier)
+    }
+
+    fn write_struct_end(&mut self) -> ::Result<()> {
+        self.inner.write_struct_end()
+    }
+
+    fn write_field_begin(&mut self, identifier: &TFieldIdentifier) -> ::Result<()> {
+        self.inner.write_field_begin(identifier)
+    }
+
+    fn write_field_end(&mut self) -> ::Result<()> {
+        self.inner.write_field_end()
+    }
+
+    fn write_field_stop(&mut self) -> ::Result<()> {
+        self.inner.write_field_stop()
+    }
+
+    fn write_bytes(&mut self, b: &[u8]) -> ::Result<()> {
+        self.inner.write_bytes(b)
+    }
+
+    fn write_bool(&mut self, b: bool) -> ::Result<()> {
+        self.inner.write_bool(b)
+    }
+
+    fn write_i8(&mut self, i: i8) -> ::Result<()> {
+        self.inner.write_i8(i)
+    }
+
+    fn write_i16(&mut self, i: i16) -> ::Result<()> {
+        self.inner.write_i16(i)
+    }
+
+    fn write_i32(&mut self, i: i32) -> ::Result<()> {
+        self.inner.write_i32(i)
+    }
+
+    fn write_i64(&mut self, i: i64) -> ::Result<()> {
+        self.inner.write_i64(i)
+    }
+
+    fn write_double(&mut self, d: f64) -> ::Result<()> {
+        self.inner.write_double(d)
+    }
+
+    fn write_string(&mut self, s: &str) -> ::Result<()> {
+        self.inner.write_string(s)
+    }
+
+    fn write_list_begin(&mut self, identifier: &TListIdentifier) -> ::Result<()> {
+        self.inner.write_list_begin(identifier)
+    }
+
+    fn write_list_end(&mut self) -> ::Result<()> {
+        self.inner.write_list_end()
+    }
+
+    fn write_set_begin(&mut self, identifier: &TSetIdentifier) -> ::Result<()> {
+        self.inner.write_set_begin(identifier)
+    }
+
+    fn write_set_end(&mut self) -> ::Result<()> {
+        self.inner.write_set_end()
+    }
+
+    fn write_map_begin(&mut self, identifier: &TMapIdentifier) -> ::Result<()> {
+        self.inner.write_map_begin(identifier)
+    }
+
+    fn write_map_end(&mut self) -> ::Result<()> {
+        self.inner.write_map_end()
+    }
+
+    fn flush(&mut self) -> ::Result<()> {
+        self.inner.flush()
+    }
+
+    // utility
+    //
+
+    fn write_byte(&mut self, b: u8) -> ::Result<()> {
+        self.inner.write_byte(b)
+    }
+}
+
+#[cfg(test)]
+mod tests {
+
+    use std::cell::RefCell;
+    use std::rc::Rc;
+
+    use ::protocol::{TBinaryOutputProtocol, TMessageIdentifier, TMessageType, TOutputProtocol};
+    use ::transport::{TPassThruTransport, TTransport};
+    use ::transport::mem::TBufferTransport;
+
+    use super::*;
+
+    #[test]
+    fn must_write_message_begin_with_prefixed_service_name() {
+        let (trans, mut o_prot) = test_objects();
+
+        let ident = TMessageIdentifier::new("bar", TMessageType::Call, 2);
+        assert_success!(o_prot.write_message_begin(&ident));
+
+        let expected: [u8; 19] =
+            [0x80, 0x01 /* protocol identifier */, 0x00, 0x01 /* message type */, 0x00,
+             0x00, 0x00, 0x07, 0x66, 0x6F, 0x6F /* "foo" */, 0x3A /* ":" */, 0x62, 0x61,
+             0x72 /* "bar" */, 0x00, 0x00, 0x00, 0x02 /* sequence number */];
+
+        assert_eq!(&trans.borrow().write_buffer_to_vec(), &expected);
+    }
+
+    fn test_objects() -> (Rc<RefCell<Box<TBufferTransport>>>, TMultiplexedOutputProtocol) {
+        let mem = Rc::new(RefCell::new(Box::new(TBufferTransport::with_capacity(40, 40))));
+
+        let inner: Box<TTransport> = Box::new(TPassThruTransport { inner: mem.clone() });
+        let inner = Rc::new(RefCell::new(inner));
+
+        let o_prot = TBinaryOutputProtocol::new(inner.clone(), true);
+        let o_prot = TMultiplexedOutputProtocol::new("foo", Box::new(o_prot));
+
+        (mem, o_prot)
+    }
+}
diff --git a/lib/rs/src/protocol/stored.rs b/lib/rs/src/protocol/stored.rs
new file mode 100644
index 0000000..6826c00
--- /dev/null
+++ b/lib/rs/src/protocol/stored.rs
@@ -0,0 +1,191 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+use std::convert::Into;
+
+use ::ProtocolErrorKind;
+use super::{TFieldIdentifier, TListIdentifier, TMapIdentifier, TMessageIdentifier, TInputProtocol,
+            TSetIdentifier, TStructIdentifier};
+
+/// `TInputProtocol` required to use a `TMultiplexedProcessor`.
+///
+/// A `TMultiplexedProcessor` reads incoming message identifiers to determine to
+/// which `TProcessor` requests should be forwarded. However, once read, those
+/// message identifier bytes are no longer on the wire. Since downstream
+/// processors expect to read message identifiers from the given input protocol
+/// we need some way of supplying a `TMessageIdentifier` with the service-name
+/// stripped. This implementation stores the received `TMessageIdentifier`
+/// (without the service name) and passes it to the wrapped `TInputProtocol`
+/// when `TInputProtocol::read_message_begin(...)` is called. It delegates all
+/// other calls directly to the wrapped `TInputProtocol`.
+///
+/// This type **should not** be used by application code.
+///
+/// # Examples
+///
+/// Create and use a `TStoredInputProtocol`.
+///
+/// ```no_run
+/// use std::cell::RefCell;
+/// use std::rc::Rc;
+/// use thrift;
+/// use thrift::protocol::{TInputProtocol, TMessageIdentifier, TMessageType, TOutputProtocol};
+/// use thrift::protocol::{TBinaryInputProtocol, TBinaryOutputProtocol, TStoredInputProtocol};
+/// use thrift::server::TProcessor;
+/// use thrift::transport::{TTcpTransport, TTransport};
+///
+/// // sample processor
+/// struct ActualProcessor;
+/// impl TProcessor for ActualProcessor {
+///     fn process(
+///         &mut self,
+///         _: &mut TInputProtocol,
+///         _: &mut TOutputProtocol
+///     ) -> thrift::Result<()> {
+///         unimplemented!()
+///     }
+/// }
+/// let mut processor = ActualProcessor {};
+///
+/// // construct the shared transport
+/// let mut transport = TTcpTransport::new();
+/// transport.open("localhost:9090").unwrap();
+/// let transport = Rc::new(RefCell::new(Box::new(transport) as Box<TTransport>));
+///
+/// // construct the actual input and output protocols
+/// let mut i_prot = TBinaryInputProtocol::new(transport.clone(), true);
+/// let mut o_prot = TBinaryOutputProtocol::new(transport.clone(), true);
+///
+/// // message identifier received from remote and modified to remove the service name
+/// let new_msg_ident = TMessageIdentifier::new("service_call", TMessageType::Call, 1);
+///
+/// // construct the proxy input protocol
+/// let mut proxy_i_prot = TStoredInputProtocol::new(&mut i_prot, new_msg_ident);
+/// let res = processor.process(&mut proxy_i_prot, &mut o_prot);
+/// ```
+pub struct TStoredInputProtocol<'a> {
+    inner: &'a mut TInputProtocol,
+    message_ident: Option<TMessageIdentifier>,
+}
+
+impl<'a> TStoredInputProtocol<'a> {
+    /// Create a `TStoredInputProtocol` that delegates all calls other than
+    /// `TInputProtocol::read_message_begin(...)` to a `wrapped`
+    /// `TInputProtocol`. `message_ident` is the modified message identifier -
+    /// with service name stripped - that will be passed to
+    /// `wrapped.read_message_begin(...)`.
+    pub fn new(wrapped: &mut TInputProtocol,
+               message_ident: TMessageIdentifier)
+               -> TStoredInputProtocol {
+        TStoredInputProtocol {
+            inner: wrapped,
+            message_ident: message_ident.into(),
+        }
+    }
+}
+
+impl<'a> TInputProtocol for TStoredInputProtocol<'a> {
+    fn read_message_begin(&mut self) -> ::Result<TMessageIdentifier> {
+        self.message_ident.take().ok_or_else(|| {
+            ::errors::new_protocol_error(ProtocolErrorKind::Unknown,
+                                         "message identifier already read")
+        })
+    }
+
+    fn read_message_end(&mut self) -> ::Result<()> {
+        self.inner.read_message_end()
+    }
+
+    fn read_struct_begin(&mut self) -> ::Result<Option<TStructIdentifier>> {
+        self.inner.read_struct_begin()
+    }
+
+    fn read_struct_end(&mut self) -> ::Result<()> {
+        self.inner.read_struct_end()
+    }
+
+    fn read_field_begin(&mut self) -> ::Result<TFieldIdentifier> {
+        self.inner.read_field_begin()
+    }
+
+    fn read_field_end(&mut self) -> ::Result<()> {
+        self.inner.read_field_end()
+    }
+
+    fn read_bytes(&mut self) -> ::Result<Vec<u8>> {
+        self.inner.read_bytes()
+    }
+
+    fn read_bool(&mut self) -> ::Result<bool> {
+        self.inner.read_bool()
+    }
+
+    fn read_i8(&mut self) -> ::Result<i8> {
+        self.inner.read_i8()
+    }
+
+    fn read_i16(&mut self) -> ::Result<i16> {
+        self.inner.read_i16()
+    }
+
+    fn read_i32(&mut self) -> ::Result<i32> {
+        self.inner.read_i32()
+    }
+
+    fn read_i64(&mut self) -> ::Result<i64> {
+        self.inner.read_i64()
+    }
+
+    fn read_double(&mut self) -> ::Result<f64> {
+        self.inner.read_double()
+    }
+
+    fn read_string(&mut self) -> ::Result<String> {
+        self.inner.read_string()
+    }
+
+    fn read_list_begin(&mut self) -> ::Result<TListIdentifier> {
+        self.inner.read_list_begin()
+    }
+
+    fn read_list_end(&mut self) -> ::Result<()> {
+        self.inner.read_list_end()
+    }
+
+    fn read_set_begin(&mut self) -> ::Result<TSetIdentifier> {
+        self.inner.read_set_begin()
+    }
+
+    fn read_set_end(&mut self) -> ::Result<()> {
+        self.inner.read_set_end()
+    }
+
+    fn read_map_begin(&mut self) -> ::Result<TMapIdentifier> {
+        self.inner.read_map_begin()
+    }
+
+    fn read_map_end(&mut self) -> ::Result<()> {
+        self.inner.read_map_end()
+    }
+
+    // utility
+    //
+
+    fn read_byte(&mut self) -> ::Result<u8> {
+        self.inner.read_byte()
+    }
+}