diff --git a/compiler/rustc_incremental/src/persist/file_format.rs b/compiler/rustc_incremental/src/persist/file_format.rs index 68180a2214a50..baac2e2cd7d6f 100644 --- a/compiler/rustc_incremental/src/persist/file_format.rs +++ b/compiler/rustc_incremental/src/persist/file_format.rs @@ -15,8 +15,7 @@ use std::io::{self, Read}; use std::path::{Path, PathBuf}; use rustc_data_structures::memmap::Mmap; -use rustc_serialize::opaque::{FileEncodeResult, FileEncoder}; -use rustc_serialize::Encoder; +use rustc_serialize::{opaque, raw, Encoder}; use rustc_session::Session; /// The first few bytes of files generated by incremental compilation. @@ -30,7 +29,26 @@ const HEADER_FORMAT_VERSION: u16 = 0; /// the Git commit hash. const RUSTC_VERSION: Option<&str> = option_env!("CFG_VERSION"); -pub(crate) fn write_file_header(stream: &mut FileEncoder, nightly_build: bool) -> FileEncodeResult { +pub(crate) fn write_file_header( + stream: &mut opaque::FileEncoder, + nightly_build: bool, +) -> opaque::FileEncodeResult { + stream.emit_raw_bytes(FILE_MAGIC)?; + stream.emit_raw_bytes(&[ + (HEADER_FORMAT_VERSION >> 0) as u8, + (HEADER_FORMAT_VERSION >> 8) as u8, + ])?; + + let rustc_version = rustc_version(nightly_build); + assert_eq!(rustc_version.len(), (rustc_version.len() as u8) as usize); + stream.emit_raw_bytes(&[rustc_version.len() as u8])?; + stream.emit_raw_bytes(rustc_version.as_bytes()) +} + +pub(crate) fn write_file_header_raw( + stream: &mut raw::FileEncoder, + nightly_build: bool, +) -> raw::FileEncodeResult { stream.emit_raw_bytes(FILE_MAGIC)?; stream.emit_raw_bytes(&[ (HEADER_FORMAT_VERSION >> 0) as u8, @@ -45,7 +63,7 @@ pub(crate) fn write_file_header(stream: &mut FileEncoder, nightly_build: bool) - pub(crate) fn save_in(sess: &Session, path_buf: PathBuf, name: &str, encode: F) where - F: FnOnce(&mut FileEncoder) -> FileEncodeResult, + F: FnOnce(&mut opaque::FileEncoder) -> opaque::FileEncodeResult, { debug!("save: storing data in {}", path_buf.display()); @@ -72,7 +90,7 @@ where } } - let mut encoder = match FileEncoder::new(&path_buf) { + let mut encoder = match opaque::FileEncoder::new(&path_buf) { Ok(encoder) => encoder, Err(err) => { sess.err(&format!("failed to create {} at `{}`: {}", name, path_buf.display(), err)); @@ -104,6 +122,61 @@ where debug!("save: data written to disk successfully"); } +pub(crate) fn save_in_raw(sess: &Session, path_buf: PathBuf, name: &str, encode: F) +where + F: FnOnce(&mut raw::FileEncoder) -> raw::FileEncodeResult, +{ + debug!("save: storing data in {}", path_buf.display()); + + // Delete the old file, if any. + // Note: It's important that we actually delete the old file and not just + // truncate and overwrite it, since it might be a shared hard-link, the + // underlying data of which we don't want to modify. + // + // We have to ensure we have dropped the memory maps to this file + // before performing this removal. + match fs::remove_file(&path_buf) { + Ok(()) => { + debug!("save: remove old file"); + } + Err(err) if err.kind() == io::ErrorKind::NotFound => (), + Err(err) => { + sess.err(&format!( + "unable to delete old {} at `{}`: {}", + name, + path_buf.display(), + err + )); + return; + } + } + + let mut encoder = match raw::FileEncoder::new(&path_buf) { + Ok(encoder) => encoder, + Err(err) => { + sess.err(&format!("failed to create {} at `{}`: {}", name, path_buf.display(), err)); + return; + } + }; + + if let Err(err) = write_file_header_raw(&mut encoder, sess.is_nightly_build()) { + sess.err(&format!("failed to write {} header to `{}`: {}", name, path_buf.display(), err)); + return; + } + + if let Err(err) = encode(&mut encoder) { + sess.err(&format!("failed to write {} to `{}`: {}", name, path_buf.display(), err)); + return; + } + + if let Err(err) = encoder.flush() { + sess.err(&format!("failed to flush {} to `{}`: {}", name, path_buf.display(), err)); + return; + } + + debug!("save: data written to disk successfully"); +} + /// Reads the contents of a file with a file header as defined in this module. /// /// - Returns `Ok(Some(data, pos))` if the file existed and was generated by a diff --git a/compiler/rustc_incremental/src/persist/load.rs b/compiler/rustc_incremental/src/persist/load.rs index 908a936142475..23a61188d959a 100644 --- a/compiler/rustc_incremental/src/persist/load.rs +++ b/compiler/rustc_incremental/src/persist/load.rs @@ -4,8 +4,7 @@ use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::memmap::Mmap; use rustc_middle::dep_graph::{SerializedDepGraph, WorkProduct, WorkProductId}; use rustc_middle::ty::OnDiskCache; -use rustc_serialize::opaque::Decoder; -use rustc_serialize::Decodable; +use rustc_serialize::{raw, Decodable}; use rustc_session::config::IncrementalStateAssertion; use rustc_session::Session; use std::path::Path; @@ -156,7 +155,7 @@ pub fn load_dep_graph(sess: &Session) -> DepGraphFuture { if let LoadResult::Ok { data: (work_products_data, start_pos) } = load_result { // Decode the list of work_products - let mut work_product_decoder = Decoder::new(&work_products_data[..], start_pos); + let mut work_product_decoder = raw::Decoder::new(&work_products_data[..], start_pos); let work_products: Vec = Decodable::decode(&mut work_product_decoder); @@ -195,7 +194,7 @@ pub fn load_dep_graph(sess: &Session) -> DepGraphFuture { LoadResult::DataOutOfDate => LoadResult::DataOutOfDate, LoadResult::Error { message } => LoadResult::Error { message }, LoadResult::Ok { data: (bytes, start_pos) } => { - let mut decoder = Decoder::new(&bytes, start_pos); + let mut decoder = raw::Decoder::new(&bytes, start_pos); let prev_commandline_args_hash = u64::decode(&mut decoder); if prev_commandline_args_hash != expected_hash { diff --git a/compiler/rustc_incremental/src/persist/save.rs b/compiler/rustc_incremental/src/persist/save.rs index 9601a49267f08..506869e1d1eff 100644 --- a/compiler/rustc_incremental/src/persist/save.rs +++ b/compiler/rustc_incremental/src/persist/save.rs @@ -2,7 +2,8 @@ use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::sync::join; use rustc_middle::dep_graph::{DepGraph, SerializedDepGraph, WorkProduct, WorkProductId}; use rustc_middle::ty::TyCtxt; -use rustc_serialize::opaque::{FileEncodeResult, FileEncoder}; +use rustc_serialize::opaque; +use rustc_serialize::raw; use rustc_serialize::Encodable as RustcEncodable; use rustc_session::Session; use std::fs; @@ -96,7 +97,7 @@ pub fn save_work_product_index( debug!("save_work_product_index()"); dep_graph.assert_ignored(); let path = work_products_path(sess); - file_format::save_in(sess, path, "work product index", |e| { + file_format::save_in_raw(sess, path, "work product index", |e| { encode_work_product_index(&new_work_products, e) }); @@ -127,8 +128,8 @@ pub fn save_work_product_index( fn encode_work_product_index( work_products: &FxHashMap, - encoder: &mut FileEncoder, -) -> FileEncodeResult { + encoder: &mut raw::FileEncoder, +) -> raw::FileEncodeResult { let serialized_products: Vec<_> = work_products .iter() .map(|(id, work_product)| SerializedWorkProduct { @@ -140,7 +141,10 @@ fn encode_work_product_index( serialized_products.encode(encoder) } -fn encode_query_cache(tcx: TyCtxt<'_>, encoder: &mut FileEncoder) -> FileEncodeResult { +fn encode_query_cache( + tcx: TyCtxt<'_>, + encoder: &mut opaque::FileEncoder, +) -> opaque::FileEncodeResult { tcx.sess.time("incr_comp_serialize_result_cache", || tcx.serialize_query_result_cache(encoder)) } @@ -163,7 +167,7 @@ pub fn build_dep_graph( // Stream the dep-graph to an alternate file, to avoid overwriting anything in case of errors. let path_buf = staging_dep_graph_path(sess); - let mut encoder = match FileEncoder::new(&path_buf) { + let mut encoder = match raw::FileEncoder::new(&path_buf) { Ok(encoder) => encoder, Err(err) => { sess.err(&format!( @@ -175,7 +179,7 @@ pub fn build_dep_graph( } }; - if let Err(err) = file_format::write_file_header(&mut encoder, sess.is_nightly_build()) { + if let Err(err) = file_format::write_file_header_raw(&mut encoder, sess.is_nightly_build()) { sess.err(&format!( "failed to write dependency graph header to `{}`: {}", path_buf.display(), diff --git a/compiler/rustc_query_impl/src/on_disk_cache.rs b/compiler/rustc_query_impl/src/on_disk_cache.rs index f2f895367ff82..2f38a81cb0184 100644 --- a/compiler/rustc_query_impl/src/on_disk_cache.rs +++ b/compiler/rustc_query_impl/src/on_disk_cache.rs @@ -15,8 +15,8 @@ use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_query_system::dep_graph::DepContext; use rustc_query_system::query::{QueryCache, QueryContext, QuerySideEffects}; use rustc_serialize::{ - opaque::{self, FileEncodeResult, FileEncoder, IntEncodedWithFixedSize}, - Decodable, Decoder, Encodable, Encoder, + opaque::{self, FileEncodeResult, FileEncoder}, + Decodable, Decoder, Encodable, Encoder, IntEncodedWithFixedSize, }; use rustc_session::Session; use rustc_span::hygiene::{ diff --git a/compiler/rustc_query_system/src/dep_graph/graph.rs b/compiler/rustc_query_system/src/dep_graph/graph.rs index a080b4a3e9ab5..9a901d88da9b8 100644 --- a/compiler/rustc_query_system/src/dep_graph/graph.rs +++ b/compiler/rustc_query_system/src/dep_graph/graph.rs @@ -7,7 +7,7 @@ use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_data_structures::steal::Steal; use rustc_data_structures::sync::{AtomicU32, AtomicU64, Lock, Lrc, Ordering}; use rustc_index::vec::IndexVec; -use rustc_serialize::opaque::{FileEncodeResult, FileEncoder}; +use rustc_serialize::raw::{FileEncodeResult, FileEncoder}; use smallvec::{smallvec, SmallVec}; use std::assert_matches::assert_matches; use std::collections::hash_map::Entry; diff --git a/compiler/rustc_query_system/src/dep_graph/mod.rs b/compiler/rustc_query_system/src/dep_graph/mod.rs index 5907ae309ca37..9624aa5a7882f 100644 --- a/compiler/rustc_query_system/src/dep_graph/mod.rs +++ b/compiler/rustc_query_system/src/dep_graph/mod.rs @@ -13,7 +13,7 @@ pub use serialized::{SerializedDepGraph, SerializedDepNodeIndex}; use crate::ich::StableHashingContext; use rustc_data_structures::profiling::SelfProfilerRef; -use rustc_serialize::{opaque::FileEncoder, Encodable}; +use rustc_serialize::{raw::FileEncoder, Encodable}; use rustc_session::Session; use std::fmt; diff --git a/compiler/rustc_query_system/src/dep_graph/serialized.rs b/compiler/rustc_query_system/src/dep_graph/serialized.rs index 9778f77384cf9..190a8712caa6e 100644 --- a/compiler/rustc_query_system/src/dep_graph/serialized.rs +++ b/compiler/rustc_query_system/src/dep_graph/serialized.rs @@ -19,8 +19,8 @@ use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::profiling::SelfProfilerRef; use rustc_data_structures::sync::Lock; use rustc_index::vec::{Idx, IndexVec}; -use rustc_serialize::opaque::{self, FileEncodeResult, FileEncoder, IntEncodedWithFixedSize}; -use rustc_serialize::{Decodable, Decoder, Encodable}; +use rustc_serialize::raw::{self, FileEncodeResult, FileEncoder}; +use rustc_serialize::{Decodable, Decoder, Encodable, IntEncodedWithFixedSize}; use smallvec::SmallVec; use std::convert::TryInto; @@ -96,11 +96,11 @@ impl SerializedDepGraph { } } -impl<'a, K: DepKind + Decodable>> Decodable> +impl<'a, K: DepKind + Decodable>> Decodable> for SerializedDepGraph { #[instrument(level = "debug", skip(d))] - fn decode(d: &mut opaque::Decoder<'a>) -> SerializedDepGraph { + fn decode(d: &mut raw::Decoder<'a>) -> SerializedDepGraph { let start_position = d.position(); // The last 16 bytes are the node count and edge count. diff --git a/compiler/rustc_serialize/src/lib.rs b/compiler/rustc_serialize/src/lib.rs index bd257dc64646a..57dcd940c99f7 100644 --- a/compiler/rustc_serialize/src/lib.rs +++ b/compiler/rustc_serialize/src/lib.rs @@ -20,6 +20,8 @@ Core encoding and decoding interfaces. #![cfg_attr(test, feature(test))] #![allow(rustc::internal)] +use std::convert::TryInto; + pub use self::serialize::{Decodable, Decoder, Encodable, Encoder}; mod collection_impls; @@ -29,3 +31,82 @@ pub mod json; pub mod leb128; pub mod opaque; + +pub mod raw; + +// An integer that will always encode to 8 bytes. +pub struct IntEncodedWithFixedSize(pub u64); + +impl IntEncodedWithFixedSize { + pub const ENCODED_SIZE: usize = 8; +} + +impl Encodable for IntEncodedWithFixedSize { + #[inline] + fn encode(&self, e: &mut opaque::Encoder) -> opaque::EncodeResult { + let _start_pos = e.position(); + e.emit_raw_bytes(&self.0.to_le_bytes())?; + let _end_pos = e.position(); + debug_assert_eq!((_end_pos - _start_pos), IntEncodedWithFixedSize::ENCODED_SIZE); + Ok(()) + } +} + +impl Encodable for IntEncodedWithFixedSize { + #[inline] + fn encode(&self, e: &mut opaque::FileEncoder) -> opaque::FileEncodeResult { + let _start_pos = e.position(); + e.emit_raw_bytes(&self.0.to_le_bytes())?; + let _end_pos = e.position(); + debug_assert_eq!((_end_pos - _start_pos), IntEncodedWithFixedSize::ENCODED_SIZE); + Ok(()) + } +} + +impl<'a> Decodable> for IntEncodedWithFixedSize { + #[inline] + fn decode(decoder: &mut opaque::Decoder<'a>) -> IntEncodedWithFixedSize { + let _start_pos = decoder.position(); + let bytes = decoder.read_raw_bytes(IntEncodedWithFixedSize::ENCODED_SIZE); + let value = u64::from_le_bytes(bytes.try_into().unwrap()); + let _end_pos = decoder.position(); + debug_assert_eq!((_end_pos - _start_pos), IntEncodedWithFixedSize::ENCODED_SIZE); + + IntEncodedWithFixedSize(value) + } +} + +impl Encodable for IntEncodedWithFixedSize { + #[inline] + fn encode(&self, e: &mut raw::Encoder) -> raw::EncodeResult { + let _start_pos = e.position(); + e.emit_raw_bytes(&self.0.to_le_bytes())?; + let _end_pos = e.position(); + debug_assert_eq!((_end_pos - _start_pos), IntEncodedWithFixedSize::ENCODED_SIZE); + Ok(()) + } +} + +impl Encodable for IntEncodedWithFixedSize { + #[inline] + fn encode(&self, e: &mut raw::FileEncoder) -> raw::FileEncodeResult { + let _start_pos = e.position(); + e.emit_raw_bytes(&self.0.to_le_bytes())?; + let _end_pos = e.position(); + debug_assert_eq!((_end_pos - _start_pos), IntEncodedWithFixedSize::ENCODED_SIZE); + Ok(()) + } +} + +impl<'a> Decodable> for IntEncodedWithFixedSize { + #[inline] + fn decode(decoder: &mut raw::Decoder<'a>) -> IntEncodedWithFixedSize { + let _start_pos = decoder.position(); + let bytes = decoder.read_raw_bytes(IntEncodedWithFixedSize::ENCODED_SIZE); + let _end_pos = decoder.position(); + debug_assert_eq!((_end_pos - _start_pos), IntEncodedWithFixedSize::ENCODED_SIZE); + + let value = u64::from_le_bytes(bytes.try_into().unwrap()); + IntEncodedWithFixedSize(value) + } +} diff --git a/compiler/rustc_serialize/src/opaque.rs b/compiler/rustc_serialize/src/opaque.rs index 5e5cbacbcff1a..78a100dcb6b6f 100644 --- a/compiler/rustc_serialize/src/opaque.rs +++ b/compiler/rustc_serialize/src/opaque.rs @@ -1,6 +1,5 @@ use crate::leb128::{self, max_leb128_len}; use crate::serialize::{self, Decoder as _, Encoder as _}; -use std::convert::TryInto; use std::fs::File; use std::io::{self, Write}; use std::mem::MaybeUninit; @@ -698,45 +697,3 @@ impl<'a> serialize::Decodable> for Vec { d.read_raw_bytes(len).to_owned() } } - -// An integer that will always encode to 8 bytes. -pub struct IntEncodedWithFixedSize(pub u64); - -impl IntEncodedWithFixedSize { - pub const ENCODED_SIZE: usize = 8; -} - -impl serialize::Encodable for IntEncodedWithFixedSize { - #[inline] - fn encode(&self, e: &mut Encoder) -> EncodeResult { - let _start_pos = e.position(); - e.emit_raw_bytes(&self.0.to_le_bytes())?; - let _end_pos = e.position(); - debug_assert_eq!((_end_pos - _start_pos), IntEncodedWithFixedSize::ENCODED_SIZE); - Ok(()) - } -} - -impl serialize::Encodable for IntEncodedWithFixedSize { - #[inline] - fn encode(&self, e: &mut FileEncoder) -> FileEncodeResult { - let _start_pos = e.position(); - e.emit_raw_bytes(&self.0.to_le_bytes())?; - let _end_pos = e.position(); - debug_assert_eq!((_end_pos - _start_pos), IntEncodedWithFixedSize::ENCODED_SIZE); - Ok(()) - } -} - -impl<'a> serialize::Decodable> for IntEncodedWithFixedSize { - #[inline] - fn decode(decoder: &mut Decoder<'a>) -> IntEncodedWithFixedSize { - let _start_pos = decoder.position(); - let bytes = decoder.read_raw_bytes(IntEncodedWithFixedSize::ENCODED_SIZE); - let value = u64::from_le_bytes(bytes.try_into().unwrap()); - let _end_pos = decoder.position(); - debug_assert_eq!((_end_pos - _start_pos), IntEncodedWithFixedSize::ENCODED_SIZE); - - IntEncodedWithFixedSize(value) - } -} diff --git a/compiler/rustc_serialize/src/raw.rs b/compiler/rustc_serialize/src/raw.rs new file mode 100644 index 0000000000000..8b5a79232e5d3 --- /dev/null +++ b/compiler/rustc_serialize/src/raw.rs @@ -0,0 +1,662 @@ +use crate::serialize::{self, Encoder as _}; +use std::convert::TryInto; +use std::fs::File; +use std::io::{self, Write}; +use std::mem::MaybeUninit; +use std::path::Path; +use std::ptr; + +// ----------------------------------------------------------------------------- +// Encoder +// ----------------------------------------------------------------------------- + +pub type EncodeResult = Result<(), !>; + +pub struct Encoder { + pub data: Vec, +} + +impl Encoder { + pub fn new(data: Vec) -> Encoder { + Encoder { data } + } + + pub fn into_inner(self) -> Vec { + self.data + } + + #[inline] + pub fn position(&self) -> usize { + self.data.len() + } +} + +macro_rules! write_raw { + ($enc:expr, $value:expr, $int_ty:ty) => {{ + let bytes = $value.to_ne_bytes(); + $enc.emit_raw_bytes(&bytes) + }}; +} + +/// A byte that [cannot occur in UTF8 sequences][utf8]. Used to mark the end of a string. +/// This way we can skip validation and still be relatively sure that deserialization +/// did not desynchronize. +/// +/// [utf8]: https://en.wikipedia.org/w/index.php?title=UTF-8&oldid=1058865525#Codepage_layout +const STR_SENTINEL: u8 = 0xC1; + +impl serialize::Encoder for Encoder { + type Error = !; + + #[inline] + fn emit_unit(&mut self) -> EncodeResult { + Ok(()) + } + + #[inline] + fn emit_usize(&mut self, v: usize) -> EncodeResult { + write_raw!(self, v, usize) + } + + #[inline] + fn emit_u128(&mut self, v: u128) -> EncodeResult { + write_raw!(self, v, u128) + } + + #[inline] + fn emit_u64(&mut self, v: u64) -> EncodeResult { + write_raw!(self, v, u64) + } + + #[inline] + fn emit_u32(&mut self, v: u32) -> EncodeResult { + write_raw!(self, v, u32) + } + + #[inline] + fn emit_u16(&mut self, v: u16) -> EncodeResult { + write_raw!(self, v, u16) + } + + #[inline] + fn emit_u8(&mut self, v: u8) -> EncodeResult { + self.data.push(v); + Ok(()) + } + + #[inline] + fn emit_isize(&mut self, v: isize) -> EncodeResult { + write_raw!(self, v, isize) + } + + #[inline] + fn emit_i128(&mut self, v: i128) -> EncodeResult { + write_raw!(self, v, i128) + } + + #[inline] + fn emit_i64(&mut self, v: i64) -> EncodeResult { + write_raw!(self, v, i64) + } + + #[inline] + fn emit_i32(&mut self, v: i32) -> EncodeResult { + write_raw!(self, v, i32) + } + + #[inline] + fn emit_i16(&mut self, v: i16) -> EncodeResult { + write_raw!(self, v, i16) + } + + #[inline] + fn emit_i8(&mut self, v: i8) -> EncodeResult { + let as_u8: u8 = unsafe { std::mem::transmute(v) }; + self.emit_u8(as_u8) + } + + #[inline] + fn emit_bool(&mut self, v: bool) -> EncodeResult { + self.emit_u8(if v { 1 } else { 0 }) + } + + #[inline] + fn emit_f64(&mut self, v: f64) -> EncodeResult { + let as_u64: u64 = v.to_bits(); + self.emit_u64(as_u64) + } + + #[inline] + fn emit_f32(&mut self, v: f32) -> EncodeResult { + let as_u32: u32 = v.to_bits(); + self.emit_u32(as_u32) + } + + #[inline] + fn emit_char(&mut self, v: char) -> EncodeResult { + self.emit_u32(v as u32) + } + + #[inline] + fn emit_str(&mut self, v: &str) -> EncodeResult { + self.emit_usize(v.len())?; + self.emit_raw_bytes(v.as_bytes())?; + self.emit_u8(STR_SENTINEL) + } + + #[inline] + fn emit_raw_bytes(&mut self, s: &[u8]) -> EncodeResult { + self.data.extend_from_slice(s); + Ok(()) + } +} + +pub type FileEncodeResult = Result<(), io::Error>; + +// `FileEncoder` encodes data to file via fixed-size buffer. +// +// When encoding large amounts of data to a file, using `FileEncoder` may be +// preferred over using `Encoder` to encode to a `Vec`, and then writing the +// `Vec` to file, as the latter uses as much memory as there is encoded data, +// while the former uses the fixed amount of memory allocated to the buffer. +// `FileEncoder` also has the advantage of not needing to reallocate as data +// is appended to it, but the disadvantage of requiring more error handling, +// which has some runtime overhead. +pub struct FileEncoder { + // The input buffer. For adequate performance, we need more control over + // buffering than `BufWriter` offers. If `BufWriter` ever offers a raw + // buffer access API, we can use it, and remove `buf` and `buffered`. + buf: Box<[MaybeUninit]>, + buffered: usize, + flushed: usize, + file: File, +} + +impl FileEncoder { + pub fn new>(path: P) -> io::Result { + const DEFAULT_BUF_SIZE: usize = 8192; + FileEncoder::with_capacity(path, DEFAULT_BUF_SIZE) + } + + pub fn with_capacity>(path: P, capacity: usize) -> io::Result { + // Require capacity at least as large as the largest LEB128 encoding + // here, so that we don't have to check or handle this on every write. + assert!(capacity >= 16); + + // Require capacity small enough such that some capacity checks can be + // done using guaranteed non-overflowing add rather than sub, which + // shaves an instruction off those code paths (on x86 at least). + assert!(capacity <= usize::MAX - 16); + + let file = File::create(path)?; + + Ok(FileEncoder { buf: Box::new_uninit_slice(capacity), buffered: 0, flushed: 0, file }) + } + + #[inline] + pub fn position(&self) -> usize { + // Tracking position this way instead of having a `self.position` field + // means that we don't have to update the position on every write call. + self.flushed + self.buffered + } + + pub fn flush(&mut self) -> FileEncodeResult { + // This is basically a copy of `BufWriter::flush`. If `BufWriter` ever + // offers a raw buffer access API, we can use it, and remove this. + + /// Helper struct to ensure the buffer is updated after all the writes + /// are complete. It tracks the number of written bytes and drains them + /// all from the front of the buffer when dropped. + struct BufGuard<'a> { + buffer: &'a mut [u8], + encoder_buffered: &'a mut usize, + encoder_flushed: &'a mut usize, + flushed: usize, + } + + impl<'a> BufGuard<'a> { + fn new( + buffer: &'a mut [u8], + encoder_buffered: &'a mut usize, + encoder_flushed: &'a mut usize, + ) -> Self { + assert_eq!(buffer.len(), *encoder_buffered); + Self { buffer, encoder_buffered, encoder_flushed, flushed: 0 } + } + + /// The unwritten part of the buffer + fn remaining(&self) -> &[u8] { + &self.buffer[self.flushed..] + } + + /// Flag some bytes as removed from the front of the buffer + fn consume(&mut self, amt: usize) { + self.flushed += amt; + } + + /// true if all of the bytes have been written + fn done(&self) -> bool { + self.flushed >= *self.encoder_buffered + } + } + + impl Drop for BufGuard<'_> { + fn drop(&mut self) { + if self.flushed > 0 { + if self.done() { + *self.encoder_flushed += *self.encoder_buffered; + *self.encoder_buffered = 0; + } else { + self.buffer.copy_within(self.flushed.., 0); + *self.encoder_flushed += self.flushed; + *self.encoder_buffered -= self.flushed; + } + } + } + } + + let mut guard = BufGuard::new( + unsafe { MaybeUninit::slice_assume_init_mut(&mut self.buf[..self.buffered]) }, + &mut self.buffered, + &mut self.flushed, + ); + + while !guard.done() { + match self.file.write(guard.remaining()) { + Ok(0) => { + return Err(io::Error::new( + io::ErrorKind::WriteZero, + "failed to write the buffered data", + )); + } + Ok(n) => guard.consume(n), + Err(ref e) if e.kind() == io::ErrorKind::Interrupted => {} + Err(e) => return Err(e), + } + } + + Ok(()) + } + + #[inline] + fn capacity(&self) -> usize { + self.buf.len() + } + + #[inline] + fn write_one(&mut self, value: u8) -> FileEncodeResult { + // We ensure this during `FileEncoder` construction. + debug_assert!(self.capacity() >= 1); + + let mut buffered = self.buffered; + + if std::intrinsics::unlikely(buffered >= self.capacity()) { + self.flush()?; + buffered = 0; + } + + // SAFETY: The above check and `flush` ensures that there is enough + // room to write the input to the buffer. + unsafe { + *MaybeUninit::slice_as_mut_ptr(&mut self.buf).add(buffered) = value; + } + + self.buffered = buffered + 1; + + Ok(()) + } + + #[inline] + fn write_all(&mut self, buf: &[u8]) -> FileEncodeResult { + let capacity = self.capacity(); + let buf_len = buf.len(); + + if std::intrinsics::likely(buf_len <= capacity) { + let mut buffered = self.buffered; + + if std::intrinsics::unlikely(buf_len > capacity - buffered) { + self.flush()?; + buffered = 0; + } + + // SAFETY: The above check and `flush` ensures that there is enough + // room to write the input to the buffer. + unsafe { + let src = buf.as_ptr(); + let dst = MaybeUninit::slice_as_mut_ptr(&mut self.buf).add(buffered); + ptr::copy_nonoverlapping(src, dst, buf_len); + } + + self.buffered = buffered + buf_len; + + Ok(()) + } else { + self.write_all_unbuffered(buf) + } + } + + fn write_all_unbuffered(&mut self, mut buf: &[u8]) -> FileEncodeResult { + if self.buffered > 0 { + self.flush()?; + } + + // This is basically a copy of `Write::write_all` but also updates our + // `self.flushed`. It's necessary because `Write::write_all` does not + // return the number of bytes written when an error is encountered, and + // without that, we cannot accurately update `self.flushed` on error. + while !buf.is_empty() { + match self.file.write(buf) { + Ok(0) => { + return Err(io::Error::new( + io::ErrorKind::WriteZero, + "failed to write whole buffer", + )); + } + Ok(n) => { + buf = &buf[n..]; + self.flushed += n; + } + Err(ref e) if e.kind() == io::ErrorKind::Interrupted => {} + Err(e) => return Err(e), + } + } + + Ok(()) + } +} + +impl Drop for FileEncoder { + fn drop(&mut self) { + let _result = self.flush(); + } +} + +impl serialize::Encoder for FileEncoder { + type Error = io::Error; + + #[inline] + fn emit_unit(&mut self) -> FileEncodeResult { + Ok(()) + } + + #[inline] + fn emit_usize(&mut self, v: usize) -> FileEncodeResult { + write_raw!(self, v, usize) + } + + #[inline] + fn emit_u128(&mut self, v: u128) -> FileEncodeResult { + write_raw!(self, v, u128) + } + + #[inline] + fn emit_u64(&mut self, v: u64) -> FileEncodeResult { + write_raw!(self, v, u64) + } + + #[inline] + fn emit_u32(&mut self, v: u32) -> FileEncodeResult { + write_raw!(self, v, u32) + } + + #[inline] + fn emit_u16(&mut self, v: u16) -> FileEncodeResult { + write_raw!(self, v, u16) + } + + #[inline] + fn emit_u8(&mut self, v: u8) -> FileEncodeResult { + self.write_one(v) + } + + #[inline] + fn emit_isize(&mut self, v: isize) -> FileEncodeResult { + write_raw!(self, v, isize) + } + + #[inline] + fn emit_i128(&mut self, v: i128) -> FileEncodeResult { + write_raw!(self, v, i128) + } + + #[inline] + fn emit_i64(&mut self, v: i64) -> FileEncodeResult { + write_raw!(self, v, i64) + } + + #[inline] + fn emit_i32(&mut self, v: i32) -> FileEncodeResult { + write_raw!(self, v, i32) + } + + #[inline] + fn emit_i16(&mut self, v: i16) -> FileEncodeResult { + write_raw!(self, v, i16) + } + + #[inline] + fn emit_i8(&mut self, v: i8) -> FileEncodeResult { + let as_u8: u8 = unsafe { std::mem::transmute(v) }; + self.emit_u8(as_u8) + } + + #[inline] + fn emit_bool(&mut self, v: bool) -> FileEncodeResult { + self.emit_u8(if v { 1 } else { 0 }) + } + + #[inline] + fn emit_f64(&mut self, v: f64) -> FileEncodeResult { + let as_u64: u64 = v.to_bits(); + self.emit_u64(as_u64) + } + + #[inline] + fn emit_f32(&mut self, v: f32) -> FileEncodeResult { + let as_u32: u32 = v.to_bits(); + self.emit_u32(as_u32) + } + + #[inline] + fn emit_char(&mut self, v: char) -> FileEncodeResult { + self.emit_u32(v as u32) + } + + #[inline] + fn emit_str(&mut self, v: &str) -> FileEncodeResult { + self.emit_usize(v.len())?; + self.emit_raw_bytes(v.as_bytes()) + self.emit_u8(STR_SENTINEL) + } + + #[inline] + fn emit_raw_bytes(&mut self, s: &[u8]) -> FileEncodeResult { + self.write_all(s) + } +} + +// ----------------------------------------------------------------------------- +// Decoder +// ----------------------------------------------------------------------------- + +pub struct Decoder<'a> { + pub data: &'a [u8], + position: usize, +} + +impl<'a> Decoder<'a> { + #[inline] + pub fn new(data: &'a [u8], position: usize) -> Decoder<'a> { + Decoder { data, position } + } + + #[inline] + pub fn position(&self) -> usize { + self.position + } + + #[inline] + pub fn set_position(&mut self, pos: usize) { + self.position = pos + } + + #[inline] + pub fn advance(&mut self, bytes: usize) { + self.position += bytes; + } + + #[inline] + pub fn read_raw_bytes(&mut self, bytes: usize) -> &'a [u8] { + let start = self.position; + self.position += bytes; + &self.data[start..self.position] + } +} + +macro_rules! read_raw { + ($dec:expr, $int_ty:ty) => {{ + const LEN: usize = std::mem::size_of::<$int_ty>(); + let bytes = $dec.data[$dec.position..$dec.position + LEN].try_into().unwrap(); + $dec.position += LEN; + <$int_ty>::from_ne_bytes(bytes) + }}; +} + +impl<'a> serialize::Decoder for Decoder<'a> { + #[inline] + fn read_u128(&mut self) -> u128 { + read_raw!(self, u128) + } + + #[inline] + fn read_u64(&mut self) -> u64 { + read_raw!(self, u64) + } + + #[inline] + fn read_u32(&mut self) -> u32 { + read_raw!(self, u32) + } + + #[inline] + fn read_u16(&mut self) -> u16 { + read_raw!(self, u16) + } + + #[inline] + fn read_u8(&mut self) -> u8 { + let value = self.data[self.position]; + self.position += 1; + value + } + + #[inline] + fn read_usize(&mut self) -> usize { + read_raw!(self, usize) + } + + #[inline] + fn read_i128(&mut self) -> i128 { + read_raw!(self, i128) + } + + #[inline] + fn read_i64(&mut self) -> i64 { + read_raw!(self, i64) + } + + #[inline] + fn read_i32(&mut self) -> i32 { + read_raw!(self, i32) + } + + #[inline] + fn read_i16(&mut self) -> i16 { + read_raw!(self, i16) + } + + #[inline] + fn read_i8(&mut self) -> i8 { + let as_u8 = self.data[self.position]; + self.position += 1; + unsafe { ::std::mem::transmute(as_u8) } + } + + #[inline] + fn read_isize(&mut self) -> isize { + read_raw!(self, isize) + } + + #[inline] + fn read_bool(&mut self) -> bool { + let value = self.read_u8(); + value != 0 + } + + #[inline] + fn read_f64(&mut self) -> f64 { + let bits = self.read_u64(); + f64::from_bits(bits) + } + + #[inline] + fn read_f32(&mut self) -> f32 { + let bits = self.read_u32(); + f32::from_bits(bits) + } + + #[inline] + fn read_char(&mut self) -> char { + let bits = self.read_u32(); + std::char::from_u32(bits).unwrap() + } + + #[inline] + fn read_str(&mut self) -> &'a str { + let len = self.read_usize(); + let sentinel = self.data[self.position + len]; + assert!(sentinel == STR_SENTINEL); + let s = unsafe { + std::str::from_utf8_unchecked(&self.data[self.position..self.position + len]) + }; + self.position += len + 1; + s + } + + #[inline] + fn read_raw_bytes(&mut self, bytes: usize) -> &'a [u8] { + let start = self.position; + self.position += bytes; + &self.data[start..self.position] + } +} + +// Specializations for contiguous byte sequences follow. The default implementations for slices +// encode and decode each element individually. This isn't necessary for `u8` slices when using +// opaque encoders and decoders, because each `u8` is unchanged by encoding and decoding. +// Therefore, we can use more efficient implementations that process the entire sequence at once. + +// Specialize encoding byte slices. This specialization also applies to encoding `Vec`s, etc., +// since the default implementations call `encode` on their slices internally. +impl serialize::Encodable for [u8] { + fn encode(&self, e: &mut Encoder) -> EncodeResult { + serialize::Encoder::emit_usize(e, self.len())?; + e.emit_raw_bytes(self) + } +} + +impl serialize::Encodable for [u8] { + fn encode(&self, e: &mut FileEncoder) -> FileEncodeResult { + serialize::Encoder::emit_usize(e, self.len())?; + e.emit_raw_bytes(self) + } +} + +// Specialize decoding `Vec`. This specialization also applies to decoding `Box<[u8]>`s, etc., +// since the default implementations call `decode` to produce a `Vec` internally. +impl<'a> serialize::Decodable> for Vec { + fn decode(d: &mut Decoder<'a>) -> Self { + let len = serialize::Decoder::read_usize(d); + d.read_raw_bytes(len).to_owned() + } +} diff --git a/compiler/rustc_serialize/src/serialize.rs b/compiler/rustc_serialize/src/serialize.rs index 42bf6ff2a9852..f97f89ed6a440 100644 --- a/compiler/rustc_serialize/src/serialize.rs +++ b/compiler/rustc_serialize/src/serialize.rs @@ -44,6 +44,7 @@ pub trait Encoder { f(self) } + #[inline] fn emit_enum_variant( &mut self, _v_name: &str, @@ -113,6 +114,7 @@ pub trait Encoder { } // Specialized types: + #[inline] fn emit_option(&mut self, f: F) -> Result<(), Self::Error> where F: FnOnce(&mut Self) -> Result<(), Self::Error>, @@ -125,6 +127,7 @@ pub trait Encoder { self.emit_enum_variant("None", 0, 0, |_| Ok(())) } + #[inline] fn emit_option_some(&mut self, f: F) -> Result<(), Self::Error> where F: FnOnce(&mut Self) -> Result<(), Self::Error>, @@ -132,6 +135,7 @@ pub trait Encoder { self.emit_enum_variant("Some", 1, 1, f) } + #[inline] fn emit_seq(&mut self, len: usize, f: F) -> Result<(), Self::Error> where F: FnOnce(&mut Self) -> Result<(), Self::Error>, @@ -148,6 +152,7 @@ pub trait Encoder { f(self) } + #[inline] fn emit_map(&mut self, len: usize, f: F) -> Result<(), Self::Error> where F: FnOnce(&mut Self) -> Result<(), Self::Error>, @@ -235,12 +240,14 @@ macro_rules! direct_serialize_impls { ($($ty:ident $emit_method:ident $read_method:ident),*) => { $( impl Encodable for $ty { + #[inline] fn encode(&self, s: &mut S) -> Result<(), S::Error> { s.$emit_method(*self) } } impl Decodable for $ty { + #[inline] fn decode(d: &mut D) -> $ty { d.$read_method() } @@ -281,64 +288,75 @@ impl Decodable for ! { } impl Encodable for ::std::num::NonZeroU32 { + #[inline] fn encode(&self, s: &mut S) -> Result<(), S::Error> { s.emit_u32(self.get()) } } impl Decodable for ::std::num::NonZeroU32 { + #[inline] fn decode(d: &mut D) -> Self { ::std::num::NonZeroU32::new(d.read_u32()).unwrap() } } impl Encodable for str { + #[inline] fn encode(&self, s: &mut S) -> Result<(), S::Error> { s.emit_str(self) } } impl Encodable for &str { + #[inline] fn encode(&self, s: &mut S) -> Result<(), S::Error> { s.emit_str(self) } } impl Encodable for String { + #[inline] fn encode(&self, s: &mut S) -> Result<(), S::Error> { s.emit_str(&self[..]) } } impl Decodable for String { + #[inline] fn decode(d: &mut D) -> String { d.read_str().to_owned() } } impl Encodable for () { + #[inline] fn encode(&self, s: &mut S) -> Result<(), S::Error> { s.emit_unit() } } impl Decodable for () { + #[inline] fn decode(_: &mut D) -> () {} } impl Encodable for PhantomData { + #[inline] fn encode(&self, s: &mut S) -> Result<(), S::Error> { s.emit_unit() } } impl Decodable for PhantomData { + #[inline] fn decode(_: &mut D) -> PhantomData { PhantomData } } impl> Decodable for Box<[T]> { + #[inline] fn decode(d: &mut D) -> Box<[T]> { let v: Vec = Decodable::decode(d); v.into_boxed_slice() @@ -346,12 +364,14 @@ impl> Decodable for Box<[T]> { } impl> Encodable for Rc { + #[inline] fn encode(&self, s: &mut S) -> Result<(), S::Error> { (**self).encode(s) } } impl> Decodable for Rc { + #[inline] fn decode(d: &mut D) -> Rc { Rc::new(Decodable::decode(d)) } @@ -369,6 +389,7 @@ impl> Encodable for [T] { } impl> Encodable for Vec { + #[inline] fn encode(&self, s: &mut S) -> Result<(), S::Error> { let slice: &[T] = self; slice.encode(s) @@ -393,6 +414,7 @@ impl> Decodable for Vec { } impl, const N: usize> Encodable for [T; N] { + #[inline] fn encode(&self, s: &mut S) -> Result<(), S::Error> { let slice: &[T] = self; slice.encode(s) @@ -400,6 +422,7 @@ impl, const N: usize> Encodable for [T; N] { } impl Decodable for [u8; N] { + #[inline] fn decode(d: &mut D) -> [u8; N] { let len = d.read_usize(); assert!(len == N); @@ -415,6 +438,7 @@ impl<'a, S: Encoder, T: Encodable> Encodable for Cow<'a, [T]> where [T]: ToOwned>, { + #[inline] fn encode(&self, s: &mut S) -> Result<(), S::Error> { let slice: &[T] = self; slice.encode(s) @@ -425,6 +449,7 @@ impl + ToOwned> Decodable for Cow<'static, [T]> where [T]: ToOwned>, { + #[inline] fn decode(d: &mut D) -> Cow<'static, [T]> { let v: Vec = Decodable::decode(d); Cow::Owned(v) @@ -432,6 +457,7 @@ where } impl> Encodable for Option { + #[inline] fn encode(&self, s: &mut S) -> Result<(), S::Error> { s.emit_option(|s| match *self { None => s.emit_option_none(), @@ -441,6 +467,7 @@ impl> Encodable for Option { } impl> Decodable for Option { + #[inline] fn decode(d: &mut D) -> Option { match d.read_usize() { 0 => None, @@ -451,6 +478,7 @@ impl> Decodable for Option { } impl, T2: Encodable> Encodable for Result { + #[inline] fn encode(&self, s: &mut S) -> Result<(), S::Error> { s.emit_enum(|s| match *self { Ok(ref v) => { @@ -464,6 +492,7 @@ impl, T2: Encodable> Encodable for Result, T2: Decodable> Decodable for Result { + #[inline] fn decode(d: &mut D) -> Result { match d.read_usize() { 0 => Ok(T1::decode(d)), @@ -494,12 +523,14 @@ macro_rules! tuple { () => (); ( $($name:ident,)+ ) => ( impl),+> Decodable for ($($name,)+) { + #[inline] fn decode(d: &mut D) -> ($($name,)+) { ($({ let element: $name = Decodable::decode(d); element },)+) } } impl),+> Encodable for ($($name,)+) { #[allow(non_snake_case)] + #[inline] fn encode(&self, s: &mut S) -> Result<(), S::Error> { let ($(ref $name,)+) = *self; let len: usize = count!($($name)+); @@ -517,18 +548,21 @@ macro_rules! tuple { tuple! { T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, } impl Encodable for path::Path { + #[inline] fn encode(&self, e: &mut S) -> Result<(), S::Error> { self.to_str().unwrap().encode(e) } } impl Encodable for path::PathBuf { + #[inline] fn encode(&self, e: &mut S) -> Result<(), S::Error> { path::Path::encode(self, e) } } impl Decodable for path::PathBuf { + #[inline] fn decode(d: &mut D) -> path::PathBuf { let bytes: String = Decodable::decode(d); path::PathBuf::from(bytes) @@ -536,12 +570,14 @@ impl Decodable for path::PathBuf { } impl + Copy> Encodable for Cell { + #[inline] fn encode(&self, s: &mut S) -> Result<(), S::Error> { self.get().encode(s) } } impl + Copy> Decodable for Cell { + #[inline] fn decode(d: &mut D) -> Cell { Cell::new(Decodable::decode(d)) } @@ -553,35 +589,41 @@ impl + Copy> Decodable for Cell { // from `encode` when `try_borrow` returns `None`. impl> Encodable for RefCell { + #[inline] fn encode(&self, s: &mut S) -> Result<(), S::Error> { self.borrow().encode(s) } } impl> Decodable for RefCell { + #[inline] fn decode(d: &mut D) -> RefCell { RefCell::new(Decodable::decode(d)) } } impl> Encodable for Arc { + #[inline] fn encode(&self, s: &mut S) -> Result<(), S::Error> { (**self).encode(s) } } impl> Decodable for Arc { + #[inline] fn decode(d: &mut D) -> Arc { Arc::new(Decodable::decode(d)) } } impl> Encodable for Box { + #[inline] fn encode(&self, s: &mut S) -> Result<(), S::Error> { (**self).encode(s) } } impl> Decodable for Box { + #[inline] fn decode(d: &mut D) -> Box { Box::new(Decodable::decode(d)) } diff --git a/compiler/rustc_serialize/tests/raw.rs b/compiler/rustc_serialize/tests/raw.rs new file mode 100644 index 0000000000000..7596724b10d56 --- /dev/null +++ b/compiler/rustc_serialize/tests/raw.rs @@ -0,0 +1,276 @@ +#![allow(rustc::internal)] + +use rustc_macros::{Decodable, Encodable}; +use rustc_serialize::raw::{Decoder, Encoder}; +use rustc_serialize::{Decodable, Encodable}; +use std::fmt::Debug; + +#[derive(PartialEq, Clone, Debug, Encodable, Decodable)] +struct Struct { + a: (), + b: u8, + c: u16, + d: u32, + e: u64, + f: usize, + + g: i8, + h: i16, + i: i32, + j: i64, + k: isize, + + l: char, + m: String, + n: f32, + o: f64, + p: bool, + q: Option, +} + +fn check_round_trip + for<'a> Decodable> + PartialEq + Debug>( + values: Vec, +) { + let mut encoder = Encoder::new(Vec::new()); + + for value in &values { + Encodable::encode(value, &mut encoder).unwrap(); + } + + let data = encoder.into_inner(); + let mut decoder = Decoder::new(&data[..], 0); + + for value in values { + let decoded = Decodable::decode(&mut decoder); + assert_eq!(value, decoded); + } +} + +#[test] +fn test_unit() { + check_round_trip(vec![(), (), (), ()]); +} + +#[test] +fn test_u8() { + let mut vec = vec![]; + for i in u8::MIN..u8::MAX { + vec.push(i); + } + check_round_trip(vec); +} + +#[test] +fn test_u16() { + for i in u16::MIN..u16::MAX { + check_round_trip(vec![1, 2, 3, i, i, i]); + } +} + +#[test] +fn test_u32() { + check_round_trip(vec![1, 2, 3, u32::MIN, 0, 1, u32::MAX, 2, 1]); +} + +#[test] +fn test_u64() { + check_round_trip(vec![1, 2, 3, u64::MIN, 0, 1, u64::MAX, 2, 1]); +} + +#[test] +fn test_usize() { + check_round_trip(vec![1, 2, 3, usize::MIN, 0, 1, usize::MAX, 2, 1]); +} + +#[test] +fn test_i8() { + let mut vec = vec![]; + for i in i8::MIN..i8::MAX { + vec.push(i); + } + check_round_trip(vec); +} + +#[test] +fn test_i16() { + for i in i16::MIN..i16::MAX { + check_round_trip(vec![-1, 2, -3, i, i, i, 2]); + } +} + +#[test] +fn test_i32() { + check_round_trip(vec![-1, 2, -3, i32::MIN, 0, 1, i32::MAX, 2, 1]); +} + +#[test] +fn test_i64() { + check_round_trip(vec![-1, 2, -3, i64::MIN, 0, 1, i64::MAX, 2, 1]); +} + +#[test] +fn test_isize() { + check_round_trip(vec![-1, 2, -3, isize::MIN, 0, 1, isize::MAX, 2, 1]); +} + +#[test] +fn test_bool() { + check_round_trip(vec![false, true, true, false, false]); +} + +#[test] +fn test_f32() { + let mut vec = vec![]; + for i in -100..100 { + vec.push((i as f32) / 3.0); + } + check_round_trip(vec); +} + +#[test] +fn test_f64() { + let mut vec = vec![]; + for i in -100..100 { + vec.push((i as f64) / 3.0); + } + check_round_trip(vec); +} + +#[test] +fn test_char() { + let vec = vec!['a', 'b', 'c', 'd', 'A', 'X', ' ', '#', 'Ö', 'Ä', 'µ', '€']; + check_round_trip(vec); +} + +#[test] +fn test_string() { + let vec = vec![ + "abcbuÖeiovÄnameÜavmpßvmea€µsbpnvapeapmaebn".to_string(), + "abcbuÖganeiovÄnameÜavmpßvmea€µsbpnvapeapmaebn".to_string(), + "abcbuÖganeiovÄnameÜavmpßvmea€µsbpapmaebn".to_string(), + "abcbuÖganeiovÄnameÜavmpßvmeabpnvapeapmaebn".to_string(), + "abcbuÖganeiÄnameÜavmpßvmea€µsbpnvapeapmaebn".to_string(), + "abcbuÖganeiovÄnameÜavmpßvmea€µsbpmaebn".to_string(), + "abcbuÖganeiovÄnameÜavmpßvmea€µnvapeapmaebn".to_string(), + ]; + + check_round_trip(vec); +} + +#[test] +fn test_option() { + check_round_trip(vec![Some(-1i8)]); + check_round_trip(vec![Some(-2i16)]); + check_round_trip(vec![Some(-3i32)]); + check_round_trip(vec![Some(-4i64)]); + check_round_trip(vec![Some(-5isize)]); + + let none_i8: Option = None; + check_round_trip(vec![none_i8]); + + let none_i16: Option = None; + check_round_trip(vec![none_i16]); + + let none_i32: Option = None; + check_round_trip(vec![none_i32]); + + let none_i64: Option = None; + check_round_trip(vec![none_i64]); + + let none_isize: Option = None; + check_round_trip(vec![none_isize]); +} + +#[test] +fn test_struct() { + check_round_trip(vec![Struct { + a: (), + b: 10, + c: 11, + d: 12, + e: 13, + f: 14, + + g: 15, + h: 16, + i: 17, + j: 18, + k: 19, + + l: 'x', + m: "abc".to_string(), + n: 20.5, + o: 21.5, + p: false, + q: None, + }]); + + check_round_trip(vec![Struct { + a: (), + b: 101, + c: 111, + d: 121, + e: 131, + f: 141, + + g: -15, + h: -16, + i: -17, + j: -18, + k: -19, + + l: 'y', + m: "def".to_string(), + n: -20.5, + o: -21.5, + p: true, + q: Some(1234567), + }]); +} + +#[derive(PartialEq, Clone, Debug, Encodable, Decodable)] +enum Enum { + Variant1, + Variant2(usize, f32), + Variant3 { a: i32, b: char, c: bool }, +} + +#[test] +fn test_enum() { + check_round_trip(vec![ + Enum::Variant1, + Enum::Variant2(1, 2.5), + Enum::Variant3 { a: 3, b: 'b', c: false }, + Enum::Variant3 { a: -4, b: 'f', c: true }, + ]); +} + +#[test] +fn test_sequence() { + let mut vec = vec![]; + for i in -100i64..100i64 { + vec.push(i * 100000); + } + + check_round_trip(vec![vec]); +} + +#[test] +fn test_hash_map() { + use std::collections::HashMap; + let mut map = HashMap::new(); + for i in -100i64..100i64 { + map.insert(i * 100000, i * 10000); + } + + check_round_trip(vec![map]); +} + +#[test] +fn test_tuples() { + check_round_trip(vec![('x', (), false, 0.5f32)]); + check_round_trip(vec![(9i8, 10u16, 1.5f64)]); + check_round_trip(vec![(-12i16, 11u8, 12usize)]); + check_round_trip(vec![(1234567isize, 100000000000000u64, 99999999999999i64)]); + check_round_trip(vec![(String::new(), "some string".to_string())]); +}