diff --git a/README.md b/README.md index 84ebc1b662d6d42dc2dd4c712dc30b8e14bf4a2e..725b3742f0042e0e7527cdcbbbe8f727571c57c7 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ LevelDB for rust #### 参与贡献 1. Fork 本仓库 -2. 新建 Feat_xxx 分支 +2. 新建 feat/1.0.0_util_xxx 分支 3. 提交代码 4. 新建 Pull Request @@ -37,18 +37,18 @@ LevelDB for rust 1. 1.0.0 版本, 完成 util 相关的内容 -| 功能模块 | 完成人 | -|-------------------------------|--------------| -| Arena (Memory Management) | wangboo | -| Slice | wangboo | -| BloomFilter | colagy | -| Cache | colagy | -| Coding (Primitive Type SerDe) | colagy | -| Comparator | fengyang | -| Status | fengyang | -| Random | fengyang | -| CRC | lxd5866 | -| Env | lxd5866 | -| Hash | lxd5866 | -| MutexLock | kazeseiriou | -| Histgram | kazeseiriou | \ No newline at end of file +| 功能模块 | 完成人 | +|-------------------------------|-------------| +| Arena (Memory Management) | wangboo | +| Slice | wangboo | +| Random | colagy | +| Cache | colagy | +| Coding (Primitive Type SerDe) | colagy | +| Comparator | fengyang | +| Status | fengyang | +| BloomFilter | fengyang | +| CRC | lxd5866 | +| Env | lxd5866 | +| Hash | lxd5866 | +| MutexLock | kazeseiriou | +| Histgram | kazeseiriou | \ No newline at end of file diff --git a/doc/TODOList.md b/doc/TODOList.md index 5b36362a43d56b3b687efe29b4cf9a0c8569572a..e0b7c83c8b4ed8fc20fcda3342b6a4d5a2eab59a 100644 --- a/doc/TODOList.md +++ b/doc/TODOList.md @@ -16,6 +16,7 @@ 5. Table Build 6. Recovery 7. SStable +8. # Util 1. Arena (Memory Management) 2. BloomFilter @@ -31,6 +32,7 @@ 12. Status 13. Random 14. Slice + ## Traits 1. public trait defined in leveldb include dir diff --git a/src/traits/iterator.rs b/src/traits/iterator.rs index 319650a7a237f22507e4ef62f6347fac70f37f12..a985e81ad68b49c971c249bca5fefd5ae1908854 100644 --- a/src/traits/iterator.rs +++ b/src/traits/iterator.rs @@ -1,7 +1,6 @@ -use crate::util::Slice; +use crate::util::slice::Slice; pub trait Iterator { - fn valid(&self) -> bool; fn seek_to_first(&mut self); @@ -19,6 +18,4 @@ pub trait Iterator { fn value(&self) -> &Slice; } -trait AAA { - -} +trait AAA {} diff --git a/src/traits/mod.rs b/src/traits/mod.rs index fb4a7b14ad387496c2539be1c2670821968f4824..74f6e800347c1bb0251c7f5bbabbba7ddd13c936 100644 --- a/src/traits/mod.rs +++ b/src/traits/mod.rs @@ -1,4 +1,4 @@ -use crate::util::Slice; +use crate::util::slice::Slice; mod iterator; mod comparator; diff --git a/src/util/arena.rs b/src/util/arena.rs new file mode 100644 index 0000000000000000000000000000000000000000..611053b5857101b48ef6e2666fa008335fc84bdf --- /dev/null +++ b/src/util/arena.rs @@ -0,0 +1,104 @@ +use std::alloc::{alloc, dealloc, Layout}; +use std::ptr::NonNull; +use std::slice; + +// Arena block size +const ARENA_BLOCK_SIZE: usize = 4096; + +pub struct Arena { + alloc_ptr: Option>, + alloc_bytes_remaining: usize, + blocks: Vec<(NonNull, Layout)>, + memory_usage: usize, +} + +impl Default for Arena { + fn default() -> Self { + Self { + alloc_ptr: None, + alloc_bytes_remaining: 0, + blocks: vec![], + memory_usage: 0, + } + } +} + +impl Arena { + + /// 申请一块内存 + /// + /// # Arguments + /// + /// * `bytes`: 申请内存大小(byte) + /// + /// returns: &mut [u8] + /// 内存的 byte 数组 + /// # Examples + /// + /// ``` + ///let arena = Arena::default(); + /// // 申请 12 字节大小的内存 + /// let buf = arena.allocate(12); + /// ``` + #[inline] + pub fn allocate(&mut self, bytes: usize) -> &mut [u8] { + self.allocate_align(bytes, 1) + } + + pub fn allocate_align(&mut self, bytes: usize, align: usize) -> &mut [u8] { + if bytes <= self.alloc_bytes_remaining { + self.alloc_bytes_remaining -= bytes; + let result = unsafe { slice::from_raw_parts_mut(self.alloc_ptr.unwrap().as_ptr(), bytes) }; + unsafe { + let new_ptr = self.alloc_ptr.unwrap().as_ptr().offset(bytes as isize); + self.alloc_ptr = Some(NonNull::new_unchecked(new_ptr)); + }; + return result; + } + return self.allocate_fallback(bytes, align); + } + + pub fn memory_usage(&self) -> usize { + self.memory_usage + } + + fn allocate_fallback(&mut self, bytes: usize, align: usize) -> &mut [u8] { + if bytes > ARENA_BLOCK_SIZE / 4 { + unsafe { + let layout = Layout::from_size_align_unchecked(bytes, align); + return self.allocate_new_block(layout); + } + } + unsafe { + self.alloc_bytes_remaining = ARENA_BLOCK_SIZE - bytes; + let layout = Layout::from_size_align_unchecked(ARENA_BLOCK_SIZE, align); + let new_block = self.allocate_new_block(layout); + let ptr = new_block.as_ptr() as *mut u8; + let result = slice::from_raw_parts_mut(ptr, bytes); + self.alloc_ptr = Some(NonNull::new_unchecked(ptr.offset(bytes as isize))); + result + } + } + + + /// 分配一块新的内存 + fn allocate_new_block(&mut self, layout: Layout) -> &mut [u8] { + unsafe { + let data = alloc(layout); + self.memory_usage += layout.size(); + self.blocks.push((NonNull::new_unchecked(data), layout)); + slice::from_raw_parts_mut(data, layout.size()) + } + } +} + +impl Drop for Arena { + /// 释放内存 + fn drop(&mut self) { + for (block, layout) in self.blocks.iter() { + unsafe { + dealloc(block.as_ptr(), *layout) + } + } + } +} \ No newline at end of file diff --git a/src/util/arena_test.rs b/src/util/arena_test.rs new file mode 100644 index 0000000000000000000000000000000000000000..dbc8cf12ac94c5b61572f5b0f410a2c09f9a3fa3 --- /dev/null +++ b/src/util/arena_test.rs @@ -0,0 +1,35 @@ +use crate::util::Arena; + +#[test] +fn test_memory_usage() { + let mut arena = Arena::default(); + let _buf0 = arena.allocate(12); + let _buf1 = arena.allocate(16); + assert_eq!(4096, arena.memory_usage()); + let _buf2 = arena.allocate(3900); + assert_eq!(4096, arena.memory_usage()); + let _buf3 = arena.allocate(1200); + assert_eq!(4096 + 1200, arena.memory_usage()); +} + +#[test] +fn test_allocate() { + let mut arena = Arena::default(); + for i in 0..=12 { + let byte_size = 1 << i; + let buf = arena.allocate(byte_size); + assert_eq!(byte_size, buf.len()); + } + assert_eq!(8192, arena.memory_usage()); +} + +#[test] +fn test_allocate_align() { + let mut arena = Arena::default(); + for i in 0..=12 { + let byte_size = 1 << i; + let buf = arena.allocate_align(byte_size, 8); + assert_eq!(byte_size, buf.len()); + } + assert_eq!(4096 * 2, arena.memory_usage()); +} \ No newline at end of file diff --git a/src/util/coding.rs b/src/util/coding.rs new file mode 100644 index 0000000000000000000000000000000000000000..11709506e4f9a5e44ee5547313e51f383cd1485e --- /dev/null +++ b/src/util/coding.rs @@ -0,0 +1,3 @@ +pub struct Coding { + +} \ No newline at end of file diff --git a/src/util/coding_test.rs b/src/util/coding_test.rs new file mode 100644 index 0000000000000000000000000000000000000000..ed0356514f7193fe33373cd1c9afc4120ae4903f --- /dev/null +++ b/src/util/coding_test.rs @@ -0,0 +1,4 @@ +mod test { + use crate::util::coding::Coding; + +} diff --git a/src/util/mod.rs b/src/util/mod.rs index 3f159e66e099d362139eb5513fe2506f8eab5d8b..d2fb80b901654ad86738f0da8b90bb5f116bbffe 100644 --- a/src/util/mod.rs +++ b/src/util/mod.rs @@ -1,3 +1,17 @@ -mod slice; -mod status; -mod slice_test; \ No newline at end of file +use crate::util::status::LevelError; +use std::result; + +pub mod slice; +mod slice_test; +pub mod coding; +mod coding_test; +pub mod arena; +mod arena_test; + +pub use arena::Arena; + +pub mod status; +mod status_test; + +/// 定义别名 +pub type Result = result::Result; diff --git a/src/util/slice.rs b/src/util/slice.rs index da59ff75bef4fbc5153c226c93fc48d672f639ba..7616c134dc7191a61ba52a24406194f08176edcf 100644 --- a/src/util/slice.rs +++ b/src/util/slice.rs @@ -1,11 +1,170 @@ +use std::mem; +use std::borrow::Cow; +use std::cmp::Ordering; +use std::ops::Deref; pub struct Slice { + data: Vec, +} +#[allow(improper_ctypes)] +extern { + fn memcmp(s1: *const i8, s2: *const i8, n: usize) -> i32; } impl Default for Slice { + /// 构造一个空的 Slice fn default() -> Self { - todo!() + Self { + data: Vec::new() + } + } +} + +impl Slice { + /// 从 &mut [u8] 转到 Slice + /// # Unsafe + /// 这里目前存在内存泄漏和double free问题, 先别用 + pub unsafe fn from_buf(buf: &mut [u8]) -> Self { + Self { + data: Vec::from_raw_parts(buf.as_mut_ptr(), buf.len(), buf.len()) + } + } + /// 获取 slice 长度 + #[inline] + pub fn size(&self) -> usize { + self.data.len() + } + + /// 判断 slice 是否为空 + #[inline] + pub fn empty(&self) -> bool { + self.data.is_empty() + } + + /// 移除头部 n 个元素 + pub fn remove_prefix(&self, n: usize) -> Slice { + assert!(self.size() >= n); + if self.size() == 0 { + return Slice::default(); + } + let sub_data = &(*self.data)[n..self.size()]; + Self { + data: Vec::from(sub_data) + } + } + + /// 判断本 Slice 是否以 other 为开始 + pub fn starts_with(&self, other: &Self) -> bool { + assert!(other.size() <= self.size()); + if other.size() == 0 { + return true; + } + return self.size() >= other.size() && unsafe { + memcmp( + self.data.as_ptr() as *const i8, + other.data.as_ptr() as *const i8, + other.size()) == 0 + }; + } + + pub fn merge(&mut self, mut other: Self, joiner: Option) { + if other.empty() { + return; + } + match joiner { + None => self.data.append(&mut other.data), + Some(mut j) => unsafe { + self.data.append(j.as_mut_vec()); + self.data.append(&mut other.data); + } + } + } + +} + +impl<'a> Slice { + /// 借取 Slice 中的数据, 调用方只拥有读权限 + pub fn borrow_data(&mut self) -> Cow<'a, String> { + unsafe { + // String & Vec has the same layout + let s: &String = mem::transmute(&self.data); + Cow::Borrowed(s) + } + } +} + +impl From for String { + /// 将 Slice 内数据的所有权移交给 String + fn from(s: Slice) -> Self { + unsafe { + String::from_utf8_unchecked(s.data) + } + } +} + +impl > From for Slice { + fn from(r: R) -> Self { + Self { + data: Vec::from(r.as_ref()) + } + } +} + +impl PartialEq for Slice { + /// 判断两个 Slice 是否相同 + fn eq(&self, other: &Self) -> bool { + return self.size() == other.size() && unsafe { + memcmp( + self.data.as_ptr() as *const i8, + other.data.as_ptr() as *const i8, + self.size(), + ) == 0 + }; + } +} + +impl PartialOrd for Slice { + /// 判断两个 slice 的大小关系 + fn partial_cmp(&self, other: &Self) -> Option { + match self.size().partial_cmp(&other.size()) { + Some(Ordering::Equal) => { + let cmp = unsafe { + memcmp( + self.data.as_ptr() as *const i8, + other.data.as_ptr() as *const i8, + self.size(), + ) + }; + if cmp == 0 { + Some(Ordering::Equal) + } else if cmp > 0 { + Some(Ordering::Greater) + } else { + Some(Ordering::Less) + } + } + op => op + } + } +} + +impl core::ops::Index for Slice { + type Output = u8; + + /// 获取某个下标的数据 + fn index(&self, index: usize) -> &Self::Output { + assert!(index < self.size()); + &(**self)[index] + } +} + +impl Deref for Slice { + type Target = [u8]; + + /// Slice 解引用到 &[u8] + fn deref(&self) -> &Self::Target { + &*self.data } } diff --git a/src/util/slice_test.rs b/src/util/slice_test.rs index 02d05e289bd0ea11c838bd5810af8d336aceb0b2..06591c98ac4cb9155e2dbaf54f33e54f6938f984 100644 --- a/src/util/slice_test.rs +++ b/src/util/slice_test.rs @@ -1,10 +1,128 @@ - mod test { + use std::cmp::Ordering; + use std::mem::ManuallyDrop; use crate::util::slice::Slice; #[test] - fn test() { - let slice = Slice::default(); + fn test_from() { + // from &str + let a0 = Slice::from("123"); + assert_eq!(String::from("123"), String::from(a0)); + // from String + let a1 = Slice::from(String::from("123")); + assert_eq!(String::from("123"), String::from(a1)); + // from buf + // let mut data = ManuallyDrop::new([48_u8, 49, 50]); + // let slice = data.as_mut_slice(); + // let a2 = Slice::from_buf(slice); + // assert_eq!(String::from("012"), String::from(a2)); + } + + #[test] + fn test_empty() { + let a0 = Slice::default(); + assert_eq!(true, a0.empty()); + + let a1 = Slice::from("123"); + assert_eq!(false, a1.empty()); + } + + #[test] + fn test_remove_prefix() { + let a0 = Slice::from("123"); + let a1 = a0.remove_prefix(1); + assert_eq!(2, a1.len()); + } + + #[test] + fn test_starts_with() { + let a0 = Slice::from("12345"); + let a1 = a0.remove_prefix(2); + assert_eq!(String::from("345"), String::from(a1)); + } + + #[test] + fn test_borrow_data() { + let mut a0 = Slice::from("123"); + let borrowed = a0.borrow_data(); + assert_eq!(3, borrowed.len()); + let owned = borrowed.to_owned(); + assert_eq!(3, owned.len()); + } + + #[test] + fn test_partial_eq() { + let a0 = Slice::from("123"); + let a1 = Slice::from("123"); + let a2 = Slice::from("234"); + let a3 = Slice::from("012"); + assert_eq!(true, a0 == a1); + assert_eq!(true, a0 < a2); + assert_eq!(true, a0 > a3); } + #[test] + fn test_partial_ord() { + let a0 = Slice::from("123"); + let a1 = Slice::from("123"); + let a2 = Slice::from("234"); + let a3 = Slice::from("012"); + assert_eq!(Ordering::Equal, a0.partial_cmp(&a1).unwrap()); + assert_eq!(Ordering::Less, a0.partial_cmp(&a2).unwrap()); + assert_eq!(Ordering::Greater, a0.partial_cmp(&a3).unwrap()); + } + + #[test] + fn test_merge() { + let mut a0 = Slice::from("123"); + let a1 = Slice::default(); + a0.merge(a1, None); + assert_eq!(String::from("123"), String::from(a0)); + } + + #[test] + fn test_merge1() { + let mut a0 = Slice::from("123"); + let a1 = Slice::default(); + a0.merge(a1, Some(String::from("ok"))); + assert_eq!(String::from("123"), String::from(a0)); + } + + #[test] + fn test_merge2() { + let mut a0 = Slice::from("123"); + let mut a2 = Slice::from("456"); + a0.merge(a2, None); + assert_eq!(String::from("123456"), String::from(a0)); + } + + #[test] + fn test_merge3() { + let mut a0 = Slice::from("123"); + let a2 = Slice::from("456"); + a0.merge(a2, Some(String::from("ok"))); + assert_eq!(String::from("123ok456"), String::from(a0)); + } + + // #[test] + fn test_memory_leak() { + // 申请 100G 内存, 查看是否内存泄漏。如果内存泄漏,程序会OOM + (0..100_000_000).for_each(|_| { + // 1k + let str = "0123012301230123012301230123012301230123012301230123012301230123012\ + 301230123012301230123012301230123012301230123012301230123012301230123012301230123012\ + 301230123012301230123012301230123012301230123012301230123012301230123012301230123012\ + 301230123012301230123012301230123012301230123012301230123012301230123012301230123012\ + 301230123012301230123012301230123012301230123012301230123012301230123012301230123012\ + 301230123012301230123012301230123012301230123012301230123012301230123012301230123012\ + 301230123012301230123012301230123012301230123012301230123012301230123012301230123012\ + 301230123012301230123012301230123012301230123012301230123012301230123012301230123012\ + 301230123012301230123012301230123012301230123012301230123012301230123012301230123012\ + 301230123012301230123012301230123012301230123012301230123012301230123012301230123012\ + 301230123012301230123012301230123012301230123012301230123012301230123012301230123012\ + 301230123012301230123012301230123012301230123012301230123012301230123012301230123012\ + 301230123012301230123012301230123"; + let _: Slice = Slice::from(str); + }) + } } \ No newline at end of file diff --git a/src/util/status.rs b/src/util/status.rs index ae9e509f916cf277e9a414a6ec40278a0e76f793..c5761269edc3c970303e36002b446e32c72a35fa 100644 --- a/src/util/status.rs +++ b/src/util/status.rs @@ -1,4 +1,186 @@ +/// 使用 Status 这个类来得到你的函数的返回的状态 +/// +use std::borrow::{Borrow, Cow}; +use std::fmt::Debug; +use std::ptr; +use std::ptr::{NonNull, null}; +use crate::util::slice::Slice; +use crate::util::status::LevelError::{kCorruption, kInvalidArgument, kIOError, kNotFound, kNotSupported, kOk}; -pub enum Status { +/// Status 的状态 +pub enum LevelError { + kOk, + kNotFound(Option), + kCorruption(Option), + kNotSupported(Option), + kInvalidArgument(Option), + kIOError(Option), -} \ No newline at end of file +} + +impl Default for LevelError { + fn default() -> Self { + kOk + } +} + +impl LevelError { + pub fn get_code(&self) -> u32 { + match self { + kOk => {0}, + kNotFound(_) => {1}, + kCorruption(_) => {2}, + kNotSupported(_) => {3}, + kInvalidArgument(_) => {4}, + kIOError(_) => {5}, + } + } + + /// 得到 error 中的 slice 信息 + pub fn into_msg(self) -> Option { + match self { + kOk => {None}, + kNotFound(slice) => { + slice + }, + kCorruption(slice) => { + // println!("The slice to be {:?}", slice); + slice + }, + kNotSupported(slice) => { + // println!("The slice to be {:?}", slice); + slice + }, + kInvalidArgument(slice) => { + // println!("The slice to be {:?}", slice); + slice + }, + kIOError(slice) => { + // println!("The slice to be {:?}", slice); + slice + }, + } + } + + /// 判断 状态是否为默认值 + fn is_default(&self) -> bool { + self.ok() + } + + /// Returns true iff the status indicates success. + pub fn ok(&self) -> bool { + match self { + kOk => true, + _ => false + } + } + + /// Returns true iff the status indicates a NotFound error. + pub fn is_not_found(&self) -> bool { + match self { + kNotFound(_) => true, + _ => false + } + } + + /// Returns true iff the status indicates a Corruption error. + pub fn is_corruption(&self) -> bool { + match self { + kCorruption(_) => true, + _ => false + } + } + + /// Returns true iff the status indicates an IOError. + pub fn is_io_error(&self) -> bool { + match self { + kIOError(_) => true, + _ => false + } + } + + /// Returns true iff the status indicates a NotSupportedError. + pub fn is_not_supported_error(&self) -> bool { + match self { + kNotSupported(_) => true, + _ => false + } + } + + /// Returns true iff the status indicates an InvalidArgument. + pub fn is_invalid_argument(&self) -> bool { + match self { + kInvalidArgument(_) => true, + _ => false + } + } + + /// Return a string representation of this status suitable for printing. + /// Returns the string "OK" for success. + pub fn to_string(self) -> String { + if self.is_default() { + return String::from("OK") + } + + let _tmp:Vec = Vec::with_capacity(30); + let mut _type: &str = ""; + + match self { + kOk => { + _type = "OK"; + }, + kNotFound(_) => { + _type = "NotFound: "; + }, + kCorruption(_) => { + _type = "Corruption: "; + }, + kNotSupported(_) => { + _type = "Not implemented: "; + }, + kInvalidArgument(_) => { + _type = "Invalid argument: "; + }, + kIOError(_) => { + _type = "IO error: "; + } + } + + // todo + + String::from(_type) + } +} + +// 这一组函数用来组合指定的状态信息 +impl<'a> LevelError { + /// 返回 ok 的 Status + pub fn OK() -> LevelError { + kOk + } + + /// 返回 not_found 的 Status + pub fn not_found(msg: Slice, msg2: Slice) -> LevelError { + kNotFound(Some(msg)) + } + + /// 返回 Corruption 的 Status + pub fn corruption(msg: Slice, msg2: Slice) -> LevelError { + kCorruption(Some(msg)) + } + + /// 返回 NotSupported 的 Status + pub fn not_supportedfound(msg: Slice, msg2: Slice) -> LevelError { + LevelError::kNotSupported(Some(msg)) + } + + /// 返回 InvalidArgument 的 Status + pub fn invalid_argument(msg: Slice, msg2: Slice) -> LevelError { + LevelError::kInvalidArgument(Some(msg)) + } + + /// 返回 IOError 的 Status + pub fn io_error(msg: Slice, msg2: Slice) -> LevelError { + LevelError::kIOError(Some(msg)) + } +} diff --git a/src/util/status_test.rs b/src/util/status_test.rs new file mode 100644 index 0000000000000000000000000000000000000000..c15f2769bbd68756b5d7b03af64dfc3a1be65190 --- /dev/null +++ b/src/util/status_test.rs @@ -0,0 +1,48 @@ + +mod test { + use crate::util::slice::Slice; + use crate::util::status::LevelError; + + #[test] + fn test_of() { + let msg1 = "abcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabc"; + let msg2 = "456456456456456456456456456456456456456456456456"; + + let err: LevelError = LevelError::io_error(String::from(msg1).into(), + String::from(msg2).into()); + let slice: Option = err.into_msg(); + assert_eq!("abcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabc", + String::from(slice.unwrap())); + + let err: LevelError = LevelError::OK(); + let slice: Option = err.into_msg(); + assert!(Option::None == slice); + } + + // #[test] + // fn test_toString() { + // // ok + // let status: LevelError = LevelError::OK(); + // assert_eq!("OK", status.to_string()); + // + // let msg1 = "abcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabc\ + // abcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabc\ + // abcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabc\ + // abcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabc\ + // abcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabc"; + // let msg2 = "456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456\ + // 456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456\ + // 456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456\ + // 456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456\ + // 456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456456"; + // + // let status: Status = Status::of(Code::kIOError, + // String::from(msg1).into(), + // String::from(msg2).into()); + // + // let expectString: String = String::from("".to_owned() + msg1 + ""); + // assert_eq!("IO error: 101".to_owned() + msg1 + ": " + msg2, + // status.to_string()); + // } + +}