Fenrir
8 years ago
14 changed files with 5510 additions and 2 deletions
@ -0,0 +1,788 @@ |
|||||||
|
// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
|
||||||
|
// file at the top-level directory of this distribution and at
|
||||||
|
// http://rust-lang.org/COPYRIGHT.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||||
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||||
|
// option. This file may not be copied, modified, or distributed
|
||||||
|
// except according to those terms.
|
||||||
|
|
||||||
|
use ascii; |
||||||
|
use collections::borrow::{Cow, Borrow, ToOwned}; |
||||||
|
use core::cmp::Ordering; |
||||||
|
use error::Error; |
||||||
|
use core::fmt::{self, Write}; |
||||||
|
use io; |
||||||
|
use libctru::libc::{self, c_char}; |
||||||
|
use core::mem; |
||||||
|
use memchr; |
||||||
|
use core::ops; |
||||||
|
use core::ptr; |
||||||
|
use core::slice; |
||||||
|
use core::str::{self, Utf8Error}; |
||||||
|
use alloc::boxed::Box; |
||||||
|
use collections::Vec; |
||||||
|
use collections::String; |
||||||
|
|
||||||
|
/// A type representing an owned C-compatible string
|
||||||
|
///
|
||||||
|
/// This type serves the primary purpose of being able to safely generate a
|
||||||
|
/// C-compatible string from a Rust byte slice or vector. An instance of this
|
||||||
|
/// type is a static guarantee that the underlying bytes contain no interior 0
|
||||||
|
/// bytes and the final byte is 0.
|
||||||
|
///
|
||||||
|
/// A `CString` is created from either a byte slice or a byte vector. After
|
||||||
|
/// being created, a `CString` predominately inherits all of its methods from
|
||||||
|
/// the `Deref` implementation to `[c_char]`. Note that the underlying array
|
||||||
|
/// is represented as an array of `c_char` as opposed to `u8`. A `u8` slice
|
||||||
|
/// can be obtained with the `as_bytes` method. Slices produced from a `CString`
|
||||||
|
/// do *not* contain the trailing nul terminator unless otherwise specified.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```no_run
|
||||||
|
/// # fn main() {
|
||||||
|
/// use std::ffi::CString;
|
||||||
|
/// use std::os::raw::c_char;
|
||||||
|
///
|
||||||
|
/// extern {
|
||||||
|
/// fn my_printer(s: *const c_char);
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// let c_to_print = CString::new("Hello, world!").unwrap();
|
||||||
|
/// unsafe {
|
||||||
|
/// my_printer(c_to_print.as_ptr());
|
||||||
|
/// }
|
||||||
|
/// # }
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// `CString` is intended for working with traditional C-style strings
|
||||||
|
/// (a sequence of non-null bytes terminated by a single null byte); the
|
||||||
|
/// primary use case for these kinds of strings is interoperating with C-like
|
||||||
|
/// code. Often you will need to transfer ownership to/from that external
|
||||||
|
/// code. It is strongly recommended that you thoroughly read through the
|
||||||
|
/// documentation of `CString` before use, as improper ownership management
|
||||||
|
/// of `CString` instances can lead to invalid memory accesses, memory leaks,
|
||||||
|
/// and other memory errors.
|
||||||
|
|
||||||
|
#[derive(PartialEq, PartialOrd, Eq, Ord, Hash, Clone)] |
||||||
|
pub struct CString { |
||||||
|
// Invariant 1: the slice ends with a zero byte and has a length of at least one.
|
||||||
|
// Invariant 2: the slice contains only one zero byte.
|
||||||
|
// Improper usage of unsafe function can break Invariant 2, but not Invariant 1.
|
||||||
|
inner: Box<[u8]>, |
||||||
|
} |
||||||
|
|
||||||
|
/// Representation of a borrowed C string.
|
||||||
|
///
|
||||||
|
/// This dynamically sized type is only safely constructed via a borrowed
|
||||||
|
/// version of an instance of `CString`. This type can be constructed from a raw
|
||||||
|
/// C string as well and represents a C string borrowed from another location.
|
||||||
|
///
|
||||||
|
/// Note that this structure is **not** `repr(C)` and is not recommended to be
|
||||||
|
/// placed in the signatures of FFI functions. Instead safe wrappers of FFI
|
||||||
|
/// functions may leverage the unsafe `from_ptr` constructor to provide a safe
|
||||||
|
/// interface to other consumers.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// Inspecting a foreign C string
|
||||||
|
///
|
||||||
|
/// ```no_run
|
||||||
|
/// use std::ffi::CStr;
|
||||||
|
/// use std::os::raw::c_char;
|
||||||
|
///
|
||||||
|
/// extern { fn my_string() -> *const c_char; }
|
||||||
|
///
|
||||||
|
/// unsafe {
|
||||||
|
/// let slice = CStr::from_ptr(my_string());
|
||||||
|
/// println!("string length: {}", slice.to_bytes().len());
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// Passing a Rust-originating C string
|
||||||
|
///
|
||||||
|
/// ```no_run
|
||||||
|
/// use std::ffi::{CString, CStr};
|
||||||
|
/// use std::os::raw::c_char;
|
||||||
|
///
|
||||||
|
/// fn work(data: &CStr) {
|
||||||
|
/// extern { fn work_with(data: *const c_char); }
|
||||||
|
///
|
||||||
|
/// unsafe { work_with(data.as_ptr()) }
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// let s = CString::new("data data data data").unwrap();
|
||||||
|
/// work(&s);
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// Converting a foreign C string into a Rust `String`
|
||||||
|
///
|
||||||
|
/// ```no_run
|
||||||
|
/// use std::ffi::CStr;
|
||||||
|
/// use std::os::raw::c_char;
|
||||||
|
///
|
||||||
|
/// extern { fn my_string() -> *const c_char; }
|
||||||
|
///
|
||||||
|
/// fn my_string_safe() -> String {
|
||||||
|
/// unsafe {
|
||||||
|
/// CStr::from_ptr(my_string()).to_string_lossy().into_owned()
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// println!("string: {}", my_string_safe());
|
||||||
|
/// ```
|
||||||
|
#[derive(Hash)] |
||||||
|
pub struct CStr { |
||||||
|
// FIXME: this should not be represented with a DST slice but rather with
|
||||||
|
// just a raw `c_char` along with some form of marker to make
|
||||||
|
// this an unsized type. Essentially `sizeof(&CStr)` should be the
|
||||||
|
// same as `sizeof(&c_char)` but `CStr` should be an unsized type.
|
||||||
|
inner: [c_char] |
||||||
|
} |
||||||
|
|
||||||
|
/// An error returned from `CString::new` to indicate that a nul byte was found
|
||||||
|
/// in the vector provided.
|
||||||
|
#[derive(Clone, PartialEq, Eq, Debug)] |
||||||
|
pub struct NulError(usize, Vec<u8>); |
||||||
|
|
||||||
|
/// An error returned from `CStr::from_bytes_with_nul` to indicate that a nul
|
||||||
|
/// byte was found too early in the slice provided or one wasn't found at all.
|
||||||
|
#[derive(Clone, PartialEq, Eq, Debug)] |
||||||
|
pub struct FromBytesWithNulError { _a: () } |
||||||
|
|
||||||
|
/// An error returned from `CString::into_string` to indicate that a UTF-8 error
|
||||||
|
/// was encountered during the conversion.
|
||||||
|
#[derive(Clone, PartialEq, Eq, Debug)] |
||||||
|
pub struct IntoStringError { |
||||||
|
inner: CString, |
||||||
|
error: Utf8Error, |
||||||
|
} |
||||||
|
|
||||||
|
impl CString { |
||||||
|
/// Creates a new C-compatible string from a container of bytes.
|
||||||
|
///
|
||||||
|
/// This method will consume the provided data and use the underlying bytes
|
||||||
|
/// to construct a new string, ensuring that there is a trailing 0 byte.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```no_run
|
||||||
|
/// use std::ffi::CString;
|
||||||
|
/// use std::os::raw::c_char;
|
||||||
|
///
|
||||||
|
/// extern { fn puts(s: *const c_char); }
|
||||||
|
///
|
||||||
|
/// let to_print = CString::new("Hello!").unwrap();
|
||||||
|
/// unsafe {
|
||||||
|
/// puts(to_print.as_ptr());
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// This function will return an error if the bytes yielded contain an
|
||||||
|
/// internal 0 byte. The error returned will contain the bytes as well as
|
||||||
|
/// the position of the nul byte.
|
||||||
|
pub fn new<T: Into<Vec<u8>>>(t: T) -> Result<CString, NulError> { |
||||||
|
Self::_new(t.into()) |
||||||
|
} |
||||||
|
|
||||||
|
fn _new(bytes: Vec<u8>) -> Result<CString, NulError> { |
||||||
|
match memchr::memchr(0, &bytes) { |
||||||
|
Some(i) => Err(NulError(i, bytes)), |
||||||
|
None => Ok(unsafe { CString::from_vec_unchecked(bytes) }), |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// Creates a C-compatible string from a byte vector without checking for
|
||||||
|
/// interior 0 bytes.
|
||||||
|
///
|
||||||
|
/// This method is equivalent to `new` except that no runtime assertion
|
||||||
|
/// is made that `v` contains no 0 bytes, and it requires an actual
|
||||||
|
/// byte vector, not anything that can be converted to one with Into.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use std::ffi::CString;
|
||||||
|
///
|
||||||
|
/// let raw = b"foo".to_vec();
|
||||||
|
/// unsafe {
|
||||||
|
/// let c_string = CString::from_vec_unchecked(raw);
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
pub unsafe fn from_vec_unchecked(mut v: Vec<u8>) -> CString { |
||||||
|
v.reserve_exact(1); |
||||||
|
v.push(0); |
||||||
|
CString { inner: v.into_boxed_slice() } |
||||||
|
} |
||||||
|
|
||||||
|
/// Retakes ownership of a `CString` that was transferred to C.
|
||||||
|
///
|
||||||
|
/// Additionally, the length of the string will be recalculated from the pointer.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// This should only ever be called with a pointer that was earlier
|
||||||
|
/// obtained by calling `into_raw` on a `CString`. Other usage (e.g. trying to take
|
||||||
|
/// ownership of a string that was allocated by foreign code) is likely to lead
|
||||||
|
/// to undefined behavior or allocator corruption.
|
||||||
|
pub unsafe fn from_raw(ptr: *mut c_char) -> CString { |
||||||
|
let len = libc::strlen(ptr) + 1; // Including the NUL byte
|
||||||
|
let slice = slice::from_raw_parts(ptr, len as usize); |
||||||
|
CString { inner: mem::transmute(slice) } |
||||||
|
} |
||||||
|
|
||||||
|
/// Transfers ownership of the string to a C caller.
|
||||||
|
///
|
||||||
|
/// The pointer must be returned to Rust and reconstituted using
|
||||||
|
/// `from_raw` to be properly deallocated. Specifically, one
|
||||||
|
/// should *not* use the standard C `free` function to deallocate
|
||||||
|
/// this string.
|
||||||
|
///
|
||||||
|
/// Failure to call `from_raw` will lead to a memory leak.
|
||||||
|
pub fn into_raw(self) -> *mut c_char { |
||||||
|
Box::into_raw(self.into_inner()) as *mut c_char |
||||||
|
} |
||||||
|
|
||||||
|
/// Converts the `CString` into a `String` if it contains valid Unicode data.
|
||||||
|
///
|
||||||
|
/// On failure, ownership of the original `CString` is returned.
|
||||||
|
pub fn into_string(self) -> Result<String, IntoStringError> { |
||||||
|
String::from_utf8(self.into_bytes()) |
||||||
|
.map_err(|e| IntoStringError { |
||||||
|
error: e.utf8_error(), |
||||||
|
inner: unsafe { CString::from_vec_unchecked(e.into_bytes()) }, |
||||||
|
}) |
||||||
|
} |
||||||
|
|
||||||
|
/// Returns the underlying byte buffer.
|
||||||
|
///
|
||||||
|
/// The returned buffer does **not** contain the trailing nul separator and
|
||||||
|
/// it is guaranteed to not have any interior nul bytes.
|
||||||
|
pub fn into_bytes(self) -> Vec<u8> { |
||||||
|
let mut vec = self.into_inner().into_vec(); |
||||||
|
let _nul = vec.pop(); |
||||||
|
debug_assert_eq!(_nul, Some(0u8)); |
||||||
|
vec |
||||||
|
} |
||||||
|
|
||||||
|
/// Equivalent to the `into_bytes` function except that the returned vector
|
||||||
|
/// includes the trailing nul byte.
|
||||||
|
pub fn into_bytes_with_nul(self) -> Vec<u8> { |
||||||
|
self.into_inner().into_vec() |
||||||
|
} |
||||||
|
|
||||||
|
/// Returns the contents of this `CString` as a slice of bytes.
|
||||||
|
///
|
||||||
|
/// The returned slice does **not** contain the trailing nul separator and
|
||||||
|
/// it is guaranteed to not have any interior nul bytes.
|
||||||
|
pub fn as_bytes(&self) -> &[u8] { |
||||||
|
&self.inner[..self.inner.len() - 1] |
||||||
|
} |
||||||
|
|
||||||
|
/// Equivalent to the `as_bytes` function except that the returned slice
|
||||||
|
/// includes the trailing nul byte.
|
||||||
|
pub fn as_bytes_with_nul(&self) -> &[u8] { |
||||||
|
&self.inner |
||||||
|
} |
||||||
|
|
||||||
|
// Bypass "move out of struct which implements `Drop` trait" restriction.
|
||||||
|
fn into_inner(self) -> Box<[u8]> { |
||||||
|
unsafe { |
||||||
|
let result = ptr::read(&self.inner); |
||||||
|
mem::forget(self); |
||||||
|
result |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// Turns this `CString` into an empty string to prevent
|
||||||
|
// memory unsafe code from working by accident. Inline
|
||||||
|
// to prevent LLVM from optimizing it away in debug builds.
|
||||||
|
impl Drop for CString { |
||||||
|
#[inline] |
||||||
|
fn drop(&mut self) { |
||||||
|
unsafe { *self.inner.get_unchecked_mut(0) = 0; } |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl ops::Deref for CString { |
||||||
|
type Target = CStr; |
||||||
|
|
||||||
|
fn deref(&self) -> &CStr { |
||||||
|
unsafe { mem::transmute(self.as_bytes_with_nul()) } |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl fmt::Debug for CString { |
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
||||||
|
fmt::Debug::fmt(&**self, f) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl From<CString> for Vec<u8> { |
||||||
|
fn from(s: CString) -> Vec<u8> { |
||||||
|
s.into_bytes() |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl fmt::Debug for CStr { |
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
||||||
|
write!(f, "\"")?; |
||||||
|
for byte in self.to_bytes().iter().flat_map(|&b| ascii::escape_default(b)) { |
||||||
|
f.write_char(byte as char)?; |
||||||
|
} |
||||||
|
write!(f, "\"") |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl<'a> Default for &'a CStr { |
||||||
|
fn default() -> &'a CStr { |
||||||
|
static SLICE: &'static [c_char] = &[0]; |
||||||
|
unsafe { CStr::from_ptr(SLICE.as_ptr()) } |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl Default for CString { |
||||||
|
/// Creates an empty `CString`.
|
||||||
|
fn default() -> CString { |
||||||
|
let a: &CStr = Default::default(); |
||||||
|
a.to_owned() |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl Borrow<CStr> for CString { |
||||||
|
fn borrow(&self) -> &CStr { self } |
||||||
|
} |
||||||
|
|
||||||
|
impl NulError { |
||||||
|
/// Returns the position of the nul byte in the slice that was provided to
|
||||||
|
/// `CString::new`.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use std::ffi::CString;
|
||||||
|
///
|
||||||
|
/// let nul_error = CString::new("foo\0bar").unwrap_err();
|
||||||
|
/// assert_eq!(nul_error.nul_position(), 3);
|
||||||
|
///
|
||||||
|
/// let nul_error = CString::new("foo bar\0").unwrap_err();
|
||||||
|
/// assert_eq!(nul_error.nul_position(), 7);
|
||||||
|
/// ```
|
||||||
|
pub fn nul_position(&self) -> usize { self.0 } |
||||||
|
|
||||||
|
/// Consumes this error, returning the underlying vector of bytes which
|
||||||
|
/// generated the error in the first place.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use std::ffi::CString;
|
||||||
|
///
|
||||||
|
/// let nul_error = CString::new("foo\0bar").unwrap_err();
|
||||||
|
/// assert_eq!(nul_error.into_vec(), b"foo\0bar");
|
||||||
|
/// ```
|
||||||
|
pub fn into_vec(self) -> Vec<u8> { self.1 } |
||||||
|
} |
||||||
|
|
||||||
|
impl Error for NulError { |
||||||
|
fn description(&self) -> &str { "nul byte found in data" } |
||||||
|
} |
||||||
|
|
||||||
|
impl fmt::Display for NulError { |
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
||||||
|
write!(f, "nul byte found in provided data at position: {}", self.0) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl From<NulError> for io::Error { |
||||||
|
fn from(_: NulError) -> io::Error { |
||||||
|
io::Error::new(io::ErrorKind::InvalidInput, |
||||||
|
"data provided contains a nul byte") |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl IntoStringError { |
||||||
|
/// Consumes this error, returning original `CString` which generated the
|
||||||
|
/// error.
|
||||||
|
pub fn into_cstring(self) -> CString { |
||||||
|
self.inner |
||||||
|
} |
||||||
|
|
||||||
|
/// Access the underlying UTF-8 error that was the cause of this error.
|
||||||
|
pub fn utf8_error(&self) -> Utf8Error { |
||||||
|
self.error |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl Error for IntoStringError { |
||||||
|
fn description(&self) -> &str { |
||||||
|
"C string contained non-utf8 bytes" |
||||||
|
} |
||||||
|
|
||||||
|
fn cause(&self) -> Option<&Error> { |
||||||
|
Some(&self.error) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl fmt::Display for IntoStringError { |
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
||||||
|
self.description().fmt(f) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl CStr { |
||||||
|
/// Casts a raw C string to a safe C string wrapper.
|
||||||
|
///
|
||||||
|
/// This function will cast the provided `ptr` to the `CStr` wrapper which
|
||||||
|
/// allows inspection and interoperation of non-owned C strings. This method
|
||||||
|
/// is unsafe for a number of reasons:
|
||||||
|
///
|
||||||
|
/// * There is no guarantee to the validity of `ptr`
|
||||||
|
/// * The returned lifetime is not guaranteed to be the actual lifetime of
|
||||||
|
/// `ptr`
|
||||||
|
/// * There is no guarantee that the memory pointed to by `ptr` contains a
|
||||||
|
/// valid nul terminator byte at the end of the string.
|
||||||
|
///
|
||||||
|
/// > **Note**: This operation is intended to be a 0-cost cast but it is
|
||||||
|
/// > currently implemented with an up-front calculation of the length of
|
||||||
|
/// > the string. This is not guaranteed to always be the case.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```no_run
|
||||||
|
/// # fn main() {
|
||||||
|
/// use std::ffi::CStr;
|
||||||
|
/// use std::os::raw::c_char;
|
||||||
|
///
|
||||||
|
/// extern {
|
||||||
|
/// fn my_string() -> *const c_char;
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// unsafe {
|
||||||
|
/// let slice = CStr::from_ptr(my_string());
|
||||||
|
/// println!("string returned: {}", slice.to_str().unwrap());
|
||||||
|
/// }
|
||||||
|
/// # }
|
||||||
|
/// ```
|
||||||
|
pub unsafe fn from_ptr<'a>(ptr: *const c_char) -> &'a CStr { |
||||||
|
let len = libc::strlen(ptr); |
||||||
|
mem::transmute(slice::from_raw_parts(ptr, len as usize + 1)) |
||||||
|
} |
||||||
|
|
||||||
|
/// Creates a C string wrapper from a byte slice.
|
||||||
|
///
|
||||||
|
/// This function will cast the provided `bytes` to a `CStr` wrapper after
|
||||||
|
/// ensuring that it is null terminated and does not contain any interior
|
||||||
|
/// nul bytes.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use std::ffi::CStr;
|
||||||
|
///
|
||||||
|
/// let cstr = CStr::from_bytes_with_nul(b"hello\0");
|
||||||
|
/// assert!(cstr.is_ok());
|
||||||
|
/// ```
|
||||||
|
pub fn from_bytes_with_nul(bytes: &[u8]) |
||||||
|
-> Result<&CStr, FromBytesWithNulError> { |
||||||
|
if bytes.is_empty() || memchr::memchr(0, &bytes) != Some(bytes.len() - 1) { |
||||||
|
Err(FromBytesWithNulError { _a: () }) |
||||||
|
} else { |
||||||
|
Ok(unsafe { Self::from_bytes_with_nul_unchecked(bytes) }) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// Unsafely creates a C string wrapper from a byte slice.
|
||||||
|
///
|
||||||
|
/// This function will cast the provided `bytes` to a `CStr` wrapper without
|
||||||
|
/// performing any sanity checks. The provided slice must be null terminated
|
||||||
|
/// and not contain any interior nul bytes.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use std::ffi::{CStr, CString};
|
||||||
|
///
|
||||||
|
/// unsafe {
|
||||||
|
/// let cstring = CString::new("hello").unwrap();
|
||||||
|
/// let cstr = CStr::from_bytes_with_nul_unchecked(cstring.to_bytes_with_nul());
|
||||||
|
/// assert_eq!(cstr, &*cstring);
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
pub unsafe fn from_bytes_with_nul_unchecked(bytes: &[u8]) -> &CStr { |
||||||
|
mem::transmute(bytes) |
||||||
|
} |
||||||
|
|
||||||
|
/// Returns the inner pointer to this C string.
|
||||||
|
///
|
||||||
|
/// The returned pointer will be valid for as long as `self` is and points
|
||||||
|
/// to a contiguous region of memory terminated with a 0 byte to represent
|
||||||
|
/// the end of the string.
|
||||||
|
///
|
||||||
|
/// **WARNING**
|
||||||
|
///
|
||||||
|
/// It is your responsibility to make sure that the underlying memory is not
|
||||||
|
/// freed too early. For example, the following code will cause undefined
|
||||||
|
/// behaviour when `ptr` is used inside the `unsafe` block:
|
||||||
|
///
|
||||||
|
/// ```no_run
|
||||||
|
/// use std::ffi::{CString};
|
||||||
|
///
|
||||||
|
/// let ptr = CString::new("Hello").unwrap().as_ptr();
|
||||||
|
/// unsafe {
|
||||||
|
/// // `ptr` is dangling
|
||||||
|
/// *ptr;
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// This happens because the pointer returned by `as_ptr` does not carry any
|
||||||
|
/// lifetime information and the string is deallocated immediately after
|
||||||
|
/// the `CString::new("Hello").unwrap().as_ptr()` expression is evaluated.
|
||||||
|
/// To fix the problem, bind the string to a local variable:
|
||||||
|
///
|
||||||
|
/// ```no_run
|
||||||
|
/// use std::ffi::{CString};
|
||||||
|
///
|
||||||
|
/// let hello = CString::new("Hello").unwrap();
|
||||||
|
/// let ptr = hello.as_ptr();
|
||||||
|
/// unsafe {
|
||||||
|
/// // `ptr` is valid because `hello` is in scope
|
||||||
|
/// *ptr;
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
pub fn as_ptr(&self) -> *const c_char { |
||||||
|
self.inner.as_ptr() |
||||||
|
} |
||||||
|
|
||||||
|
/// Converts this C string to a byte slice.
|
||||||
|
///
|
||||||
|
/// This function will calculate the length of this string (which normally
|
||||||
|
/// requires a linear amount of work to be done) and then return the
|
||||||
|
/// resulting slice of `u8` elements.
|
||||||
|
///
|
||||||
|
/// The returned slice will **not** contain the trailing nul that this C
|
||||||
|
/// string has.
|
||||||
|
///
|
||||||
|
/// > **Note**: This method is currently implemented as a 0-cost cast, but
|
||||||
|
/// > it is planned to alter its definition in the future to perform the
|
||||||
|
/// > length calculation whenever this method is called.
|
||||||
|
pub fn to_bytes(&self) -> &[u8] { |
||||||
|
let bytes = self.to_bytes_with_nul(); |
||||||
|
&bytes[..bytes.len() - 1] |
||||||
|
} |
||||||
|
|
||||||
|
/// Converts this C string to a byte slice containing the trailing 0 byte.
|
||||||
|
///
|
||||||
|
/// This function is the equivalent of `to_bytes` except that it will retain
|
||||||
|
/// the trailing nul instead of chopping it off.
|
||||||
|
///
|
||||||
|
/// > **Note**: This method is currently implemented as a 0-cost cast, but
|
||||||
|
/// > it is planned to alter its definition in the future to perform the
|
||||||
|
/// > length calculation whenever this method is called.
|
||||||
|
pub fn to_bytes_with_nul(&self) -> &[u8] { |
||||||
|
unsafe { mem::transmute(&self.inner) } |
||||||
|
} |
||||||
|
|
||||||
|
/// Yields a `&str` slice if the `CStr` contains valid UTF-8.
|
||||||
|
///
|
||||||
|
/// This function will calculate the length of this string and check for
|
||||||
|
/// UTF-8 validity, and then return the `&str` if it's valid.
|
||||||
|
///
|
||||||
|
/// > **Note**: This method is currently implemented to check for validity
|
||||||
|
/// > after a 0-cost cast, but it is planned to alter its definition in the
|
||||||
|
/// > future to perform the length calculation in addition to the UTF-8
|
||||||
|
/// > check whenever this method is called.
|
||||||
|
pub fn to_str(&self) -> Result<&str, str::Utf8Error> { |
||||||
|
// NB: When CStr is changed to perform the length check in .to_bytes()
|
||||||
|
// instead of in from_ptr(), it may be worth considering if this should
|
||||||
|
// be rewritten to do the UTF-8 check inline with the length calculation
|
||||||
|
// instead of doing it afterwards.
|
||||||
|
str::from_utf8(self.to_bytes()) |
||||||
|
} |
||||||
|
|
||||||
|
/// Converts a `CStr` into a `Cow<str>`.
|
||||||
|
///
|
||||||
|
/// This function will calculate the length of this string (which normally
|
||||||
|
/// requires a linear amount of work to be done) and then return the
|
||||||
|
/// resulting slice as a `Cow<str>`, replacing any invalid UTF-8 sequences
|
||||||
|
/// with `U+FFFD REPLACEMENT CHARACTER`.
|
||||||
|
///
|
||||||
|
/// > **Note**: This method is currently implemented to check for validity
|
||||||
|
/// > after a 0-cost cast, but it is planned to alter its definition in the
|
||||||
|
/// > future to perform the length calculation in addition to the UTF-8
|
||||||
|
/// > check whenever this method is called.
|
||||||
|
pub fn to_string_lossy(&self) -> Cow<str> { |
||||||
|
String::from_utf8_lossy(self.to_bytes()) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl PartialEq for CStr { |
||||||
|
fn eq(&self, other: &CStr) -> bool { |
||||||
|
self.to_bytes().eq(other.to_bytes()) |
||||||
|
} |
||||||
|
} |
||||||
|
impl Eq for CStr {} |
||||||
|
impl PartialOrd for CStr { |
||||||
|
fn partial_cmp(&self, other: &CStr) -> Option<Ordering> { |
||||||
|
self.to_bytes().partial_cmp(&other.to_bytes()) |
||||||
|
} |
||||||
|
} |
||||||
|
impl Ord for CStr { |
||||||
|
fn cmp(&self, other: &CStr) -> Ordering { |
||||||
|
self.to_bytes().cmp(&other.to_bytes()) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl ToOwned for CStr { |
||||||
|
type Owned = CString; |
||||||
|
|
||||||
|
fn to_owned(&self) -> CString { |
||||||
|
unsafe { CString::from_vec_unchecked(self.to_bytes().to_vec()) } |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl<'a> From<&'a CStr> for CString { |
||||||
|
fn from(s: &'a CStr) -> CString { |
||||||
|
s.to_owned() |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl ops::Index<ops::RangeFull> for CString { |
||||||
|
type Output = CStr; |
||||||
|
|
||||||
|
#[inline] |
||||||
|
fn index(&self, _index: ops::RangeFull) -> &CStr { |
||||||
|
self |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl AsRef<CStr> for CStr { |
||||||
|
fn as_ref(&self) -> &CStr { |
||||||
|
self |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl AsRef<CStr> for CString { |
||||||
|
fn as_ref(&self) -> &CStr { |
||||||
|
self |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
#[cfg(test)] |
||||||
|
mod tests { |
||||||
|
use super::*; |
||||||
|
use libc::c_char; |
||||||
|
use collections::borrow::Cow::{Borrowed, Owned}; |
||||||
|
use collections::borrow::ToOwned; |
||||||
|
use core::hash::{Hash, Hasher}; |
||||||
|
|
||||||
|
#[test] |
||||||
|
fn c_to_rust() { |
||||||
|
let data = b"123\0"; |
||||||
|
let ptr = data.as_ptr() as *const c_char; |
||||||
|
unsafe { |
||||||
|
assert_eq!(CStr::from_ptr(ptr).to_bytes(), b"123"); |
||||||
|
assert_eq!(CStr::from_ptr(ptr).to_bytes_with_nul(), b"123\0"); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
#[test] |
||||||
|
fn simple() { |
||||||
|
let s = CString::new("1234").unwrap(); |
||||||
|
assert_eq!(s.as_bytes(), b"1234"); |
||||||
|
assert_eq!(s.as_bytes_with_nul(), b"1234\0"); |
||||||
|
} |
||||||
|
|
||||||
|
#[test] |
||||||
|
fn build_with_zero1() { |
||||||
|
assert!(CString::new(&b"\0"[..]).is_err()); |
||||||
|
} |
||||||
|
#[test] |
||||||
|
fn build_with_zero2() { |
||||||
|
assert!(CString::new(vec![0]).is_err()); |
||||||
|
} |
||||||
|
|
||||||
|
#[test] |
||||||
|
fn build_with_zero3() { |
||||||
|
unsafe { |
||||||
|
let s = CString::from_vec_unchecked(vec![0]); |
||||||
|
assert_eq!(s.as_bytes(), b"\0"); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
#[test] |
||||||
|
fn formatted() { |
||||||
|
let s = CString::new(&b"abc\x01\x02\n\xE2\x80\xA6\xFF"[..]).unwrap(); |
||||||
|
assert_eq!(format!("{:?}", s), r#""abc\x01\x02\n\xe2\x80\xa6\xff""#); |
||||||
|
} |
||||||
|
|
||||||
|
#[test] |
||||||
|
fn borrowed() { |
||||||
|
unsafe { |
||||||
|
let s = CStr::from_ptr(b"12\0".as_ptr() as *const _); |
||||||
|
assert_eq!(s.to_bytes(), b"12"); |
||||||
|
assert_eq!(s.to_bytes_with_nul(), b"12\0"); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
#[test] |
||||||
|
fn to_str() { |
||||||
|
let data = b"123\xE2\x80\xA6\0"; |
||||||
|
let ptr = data.as_ptr() as *const c_char; |
||||||
|
unsafe { |
||||||
|
assert_eq!(CStr::from_ptr(ptr).to_str(), Ok("123…")); |
||||||
|
assert_eq!(CStr::from_ptr(ptr).to_string_lossy(), Borrowed("123…")); |
||||||
|
} |
||||||
|
let data = b"123\xE2\0"; |
||||||
|
let ptr = data.as_ptr() as *const c_char; |
||||||
|
unsafe { |
||||||
|
assert!(CStr::from_ptr(ptr).to_str().is_err()); |
||||||
|
assert_eq!(CStr::from_ptr(ptr).to_string_lossy(), Owned::<str>(format!("123\u{FFFD}"))); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
#[test] |
||||||
|
fn to_owned() { |
||||||
|
let data = b"123\0"; |
||||||
|
let ptr = data.as_ptr() as *const c_char; |
||||||
|
|
||||||
|
let owned = unsafe { CStr::from_ptr(ptr).to_owned() }; |
||||||
|
assert_eq!(owned.as_bytes_with_nul(), data); |
||||||
|
} |
||||||
|
|
||||||
|
#[test] |
||||||
|
fn from_bytes_with_nul() { |
||||||
|
let data = b"123\0"; |
||||||
|
let cstr = CStr::from_bytes_with_nul(data); |
||||||
|
assert_eq!(cstr.map(CStr::to_bytes), Ok(&b"123"[..])); |
||||||
|
let cstr = CStr::from_bytes_with_nul(data); |
||||||
|
assert_eq!(cstr.map(CStr::to_bytes_with_nul), Ok(&b"123\0"[..])); |
||||||
|
|
||||||
|
unsafe { |
||||||
|
let cstr = CStr::from_bytes_with_nul(data); |
||||||
|
let cstr_unchecked = CStr::from_bytes_with_nul_unchecked(data); |
||||||
|
assert_eq!(cstr, Ok(cstr_unchecked)); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
#[test] |
||||||
|
fn from_bytes_with_nul_unterminated() { |
||||||
|
let data = b"123"; |
||||||
|
let cstr = CStr::from_bytes_with_nul(data); |
||||||
|
assert!(cstr.is_err()); |
||||||
|
} |
||||||
|
|
||||||
|
#[test] |
||||||
|
fn from_bytes_with_nul_interior() { |
||||||
|
let data = b"1\023\0"; |
||||||
|
let cstr = CStr::from_bytes_with_nul(data); |
||||||
|
assert!(cstr.is_err()); |
||||||
|
} |
||||||
|
} |
@ -1,3 +1,5 @@ |
|||||||
|
pub use self::c_str::{CString, CStr}; |
||||||
pub use self::os_str::{OsString, OsStr}; |
pub use self::os_str::{OsString, OsStr}; |
||||||
|
|
||||||
|
mod c_str; |
||||||
mod os_str; |
mod os_str; |
||||||
|
@ -0,0 +1,575 @@ |
|||||||
|
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
|
||||||
|
// file at the top-level directory of this distribution and at
|
||||||
|
// http://rust-lang.org/COPYRIGHT.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||||
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||||
|
// option. This file may not be copied, modified, or distributed
|
||||||
|
// except according to those terms.
|
||||||
|
|
||||||
|
use io::prelude::*; |
||||||
|
|
||||||
|
use core::cmp; |
||||||
|
use io::{self, SeekFrom, Error, ErrorKind}; |
||||||
|
|
||||||
|
use collections::boxed::Box; |
||||||
|
use collections::Vec; |
||||||
|
|
||||||
|
/// A `Cursor` wraps another type and provides it with a
|
||||||
|
/// [`Seek`](trait.Seek.html) implementation.
|
||||||
|
///
|
||||||
|
/// Cursors are typically used with in-memory buffers to allow them to
|
||||||
|
/// implement `Read` and/or `Write`, allowing these buffers to be used
|
||||||
|
/// anywhere you might use a reader or writer that does actual I/O.
|
||||||
|
///
|
||||||
|
/// The standard library implements some I/O traits on various types which
|
||||||
|
/// are commonly used as a buffer, like `Cursor<Vec<u8>>` and `Cursor<&[u8]>`.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// We may want to write bytes to a [`File`][file] in our production
|
||||||
|
/// code, but use an in-memory buffer in our tests. We can do this with
|
||||||
|
/// `Cursor`:
|
||||||
|
///
|
||||||
|
/// [file]: ../fs/struct.File.html
|
||||||
|
///
|
||||||
|
/// ```no_run
|
||||||
|
/// use std::io::prelude::*;
|
||||||
|
/// use std::io::{self, SeekFrom};
|
||||||
|
/// use std::fs::File;
|
||||||
|
///
|
||||||
|
/// // a library function we've written
|
||||||
|
/// fn write_ten_bytes_at_end<W: Write + Seek>(writer: &mut W) -> io::Result<()> {
|
||||||
|
/// try!(writer.seek(SeekFrom::End(-10)));
|
||||||
|
///
|
||||||
|
/// for i in 0..10 {
|
||||||
|
/// try!(writer.write(&[i]));
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// // all went well
|
||||||
|
/// Ok(())
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// # fn foo() -> io::Result<()> {
|
||||||
|
/// // Here's some code that uses this library function.
|
||||||
|
/// //
|
||||||
|
/// // We might want to use a BufReader here for efficiency, but let's
|
||||||
|
/// // keep this example focused.
|
||||||
|
/// let mut file = try!(File::create("foo.txt"));
|
||||||
|
///
|
||||||
|
/// try!(write_ten_bytes_at_end(&mut file));
|
||||||
|
/// # Ok(())
|
||||||
|
/// # }
|
||||||
|
///
|
||||||
|
/// // now let's write a test
|
||||||
|
/// #[test]
|
||||||
|
/// fn test_writes_bytes() {
|
||||||
|
/// // setting up a real File is much more slow than an in-memory buffer,
|
||||||
|
/// // let's use a cursor instead
|
||||||
|
/// use std::io::Cursor;
|
||||||
|
/// let mut buff = Cursor::new(vec![0; 15]);
|
||||||
|
///
|
||||||
|
/// write_ten_bytes_at_end(&mut buff).unwrap();
|
||||||
|
///
|
||||||
|
/// assert_eq!(&buff.get_ref()[5..15], &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
#[derive(Clone, Debug)] |
||||||
|
pub struct Cursor<T> { |
||||||
|
inner: T, |
||||||
|
pos: u64, |
||||||
|
} |
||||||
|
|
||||||
|
impl<T> Cursor<T> { |
||||||
|
/// Creates a new cursor wrapping the provided underlying I/O object.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use std::io::Cursor;
|
||||||
|
///
|
||||||
|
/// let buff = Cursor::new(Vec::new());
|
||||||
|
/// # fn force_inference(_: &Cursor<Vec<u8>>) {}
|
||||||
|
/// # force_inference(&buff);
|
||||||
|
/// ```
|
||||||
|
pub fn new(inner: T) -> Cursor<T> { |
||||||
|
Cursor { pos: 0, inner: inner } |
||||||
|
} |
||||||
|
|
||||||
|
/// Consumes this cursor, returning the underlying value.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use std::io::Cursor;
|
||||||
|
///
|
||||||
|
/// let buff = Cursor::new(Vec::new());
|
||||||
|
/// # fn force_inference(_: &Cursor<Vec<u8>>) {}
|
||||||
|
/// # force_inference(&buff);
|
||||||
|
///
|
||||||
|
/// let vec = buff.into_inner();
|
||||||
|
/// ```
|
||||||
|
pub fn into_inner(self) -> T { self.inner } |
||||||
|
|
||||||
|
/// Gets a reference to the underlying value in this cursor.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use std::io::Cursor;
|
||||||
|
///
|
||||||
|
/// let buff = Cursor::new(Vec::new());
|
||||||
|
/// # fn force_inference(_: &Cursor<Vec<u8>>) {}
|
||||||
|
/// # force_inference(&buff);
|
||||||
|
///
|
||||||
|
/// let reference = buff.get_ref();
|
||||||
|
/// ```
|
||||||
|
pub fn get_ref(&self) -> &T { &self.inner } |
||||||
|
|
||||||
|
/// Gets a mutable reference to the underlying value in this cursor.
|
||||||
|
///
|
||||||
|
/// Care should be taken to avoid modifying the internal I/O state of the
|
||||||
|
/// underlying value as it may corrupt this cursor's position.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use std::io::Cursor;
|
||||||
|
///
|
||||||
|
/// let mut buff = Cursor::new(Vec::new());
|
||||||
|
/// # fn force_inference(_: &Cursor<Vec<u8>>) {}
|
||||||
|
/// # force_inference(&buff);
|
||||||
|
///
|
||||||
|
/// let reference = buff.get_mut();
|
||||||
|
/// ```
|
||||||
|
pub fn get_mut(&mut self) -> &mut T { &mut self.inner } |
||||||
|
|
||||||
|
/// Returns the current position of this cursor.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use std::io::Cursor;
|
||||||
|
/// use std::io::prelude::*;
|
||||||
|
/// use std::io::SeekFrom;
|
||||||
|
///
|
||||||
|
/// let mut buff = Cursor::new(vec![1, 2, 3, 4, 5]);
|
||||||
|
///
|
||||||
|
/// assert_eq!(buff.position(), 0);
|
||||||
|
///
|
||||||
|
/// buff.seek(SeekFrom::Current(2)).unwrap();
|
||||||
|
/// assert_eq!(buff.position(), 2);
|
||||||
|
///
|
||||||
|
/// buff.seek(SeekFrom::Current(-1)).unwrap();
|
||||||
|
/// assert_eq!(buff.position(), 1);
|
||||||
|
/// ```
|
||||||
|
pub fn position(&self) -> u64 { self.pos } |
||||||
|
|
||||||
|
/// Sets the position of this cursor.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use std::io::Cursor;
|
||||||
|
///
|
||||||
|
/// let mut buff = Cursor::new(vec![1, 2, 3, 4, 5]);
|
||||||
|
///
|
||||||
|
/// assert_eq!(buff.position(), 0);
|
||||||
|
///
|
||||||
|
/// buff.set_position(2);
|
||||||
|
/// assert_eq!(buff.position(), 2);
|
||||||
|
///
|
||||||
|
/// buff.set_position(4);
|
||||||
|
/// assert_eq!(buff.position(), 4);
|
||||||
|
/// ```
|
||||||
|
pub fn set_position(&mut self, pos: u64) { self.pos = pos; } |
||||||
|
} |
||||||
|
|
||||||
|
impl<T> io::Seek for Cursor<T> where T: AsRef<[u8]> { |
||||||
|
fn seek(&mut self, style: SeekFrom) -> io::Result<u64> { |
||||||
|
let pos = match style { |
||||||
|
SeekFrom::Start(n) => { self.pos = n; return Ok(n) } |
||||||
|
SeekFrom::End(n) => self.inner.as_ref().len() as i64 + n, |
||||||
|
SeekFrom::Current(n) => self.pos as i64 + n, |
||||||
|
}; |
||||||
|
|
||||||
|
if pos < 0 { |
||||||
|
Err(Error::new(ErrorKind::InvalidInput, |
||||||
|
"invalid seek to a negative position")) |
||||||
|
} else { |
||||||
|
self.pos = pos as u64; |
||||||
|
Ok(self.pos) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl<T> Read for Cursor<T> where T: AsRef<[u8]> { |
||||||
|
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { |
||||||
|
let n = Read::read(&mut self.fill_buf()?, buf)?; |
||||||
|
self.pos += n as u64; |
||||||
|
Ok(n) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl<T> BufRead for Cursor<T> where T: AsRef<[u8]> { |
||||||
|
fn fill_buf(&mut self) -> io::Result<&[u8]> { |
||||||
|
let amt = cmp::min(self.pos, self.inner.as_ref().len() as u64); |
||||||
|
Ok(&self.inner.as_ref()[(amt as usize)..]) |
||||||
|
} |
||||||
|
fn consume(&mut self, amt: usize) { self.pos += amt as u64; } |
||||||
|
} |
||||||
|
|
||||||
|
impl<'a> Write for Cursor<&'a mut [u8]> { |
||||||
|
#[inline] |
||||||
|
fn write(&mut self, data: &[u8]) -> io::Result<usize> { |
||||||
|
let pos = cmp::min(self.pos, self.inner.len() as u64); |
||||||
|
let amt = (&mut self.inner[(pos as usize)..]).write(data)?; |
||||||
|
self.pos += amt as u64; |
||||||
|
Ok(amt) |
||||||
|
} |
||||||
|
fn flush(&mut self) -> io::Result<()> { Ok(()) } |
||||||
|
} |
||||||
|
|
||||||
|
impl Write for Cursor<Vec<u8>> { |
||||||
|
fn write(&mut self, buf: &[u8]) -> io::Result<usize> { |
||||||
|
// Make sure the internal buffer is as least as big as where we
|
||||||
|
// currently are
|
||||||
|
let pos = self.position(); |
||||||
|
let amt = pos.saturating_sub(self.inner.len() as u64); |
||||||
|
// use `resize` so that the zero filling is as efficient as possible
|
||||||
|
let len = self.inner.len(); |
||||||
|
self.inner.resize(len + amt as usize, 0); |
||||||
|
|
||||||
|
// Figure out what bytes will be used to overwrite what's currently
|
||||||
|
// there (left), and what will be appended on the end (right)
|
||||||
|
{ |
||||||
|
let pos = pos as usize; |
||||||
|
let space = self.inner.len() - pos; |
||||||
|
let (left, right) = buf.split_at(cmp::min(space, buf.len())); |
||||||
|
self.inner[pos..pos + left.len()].copy_from_slice(left); |
||||||
|
self.inner.extend_from_slice(right); |
||||||
|
} |
||||||
|
|
||||||
|
// Bump us forward
|
||||||
|
self.set_position(pos + buf.len() as u64); |
||||||
|
Ok(buf.len()) |
||||||
|
} |
||||||
|
fn flush(&mut self) -> io::Result<()> { Ok(()) } |
||||||
|
} |
||||||
|
|
||||||
|
impl Write for Cursor<Box<[u8]>> { |
||||||
|
#[inline] |
||||||
|
fn write(&mut self, buf: &[u8]) -> io::Result<usize> { |
||||||
|
let pos = cmp::min(self.pos, self.inner.len() as u64); |
||||||
|
let amt = (&mut self.inner[(pos as usize)..]).write(buf)?; |
||||||
|
self.pos += amt as u64; |
||||||
|
Ok(amt) |
||||||
|
} |
||||||
|
fn flush(&mut self) -> io::Result<()> { Ok(()) } |
||||||
|
} |
||||||
|
|
||||||
|
#[cfg(test)] |
||||||
|
mod tests { |
||||||
|
use io::prelude::*; |
||||||
|
use io::{Cursor, SeekFrom}; |
||||||
|
|
||||||
|
use collections::Vec; |
||||||
|
|
||||||
|
#[test] |
||||||
|
fn test_vec_writer() { |
||||||
|
let mut writer = Vec::new(); |
||||||
|
assert_eq!(writer.write(&[0]).unwrap(), 1); |
||||||
|
assert_eq!(writer.write(&[1, 2, 3]).unwrap(), 3); |
||||||
|
assert_eq!(writer.write(&[4, 5, 6, 7]).unwrap(), 4); |
||||||
|
let b: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7]; |
||||||
|
assert_eq!(writer, b); |
||||||
|
} |
||||||
|
|
||||||
|
#[test] |
||||||
|
fn test_mem_writer() { |
||||||
|
let mut writer = Cursor::new(Vec::new()); |
||||||
|
assert_eq!(writer.write(&[0]).unwrap(), 1); |
||||||
|
assert_eq!(writer.write(&[1, 2, 3]).unwrap(), 3); |
||||||
|
assert_eq!(writer.write(&[4, 5, 6, 7]).unwrap(), 4); |
||||||
|
let b: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7]; |
||||||
|
assert_eq!(&writer.get_ref()[..], b); |
||||||
|
} |
||||||
|
|
||||||
|
#[test] |
||||||
|
fn test_box_slice_writer() { |
||||||
|
let mut writer = Cursor::new(vec![0u8; 9].into_boxed_slice()); |
||||||
|
assert_eq!(writer.position(), 0); |
||||||
|
assert_eq!(writer.write(&[0]).unwrap(), 1); |
||||||
|
assert_eq!(writer.position(), 1); |
||||||
|
assert_eq!(writer.write(&[1, 2, 3]).unwrap(), 3); |
||||||
|
assert_eq!(writer.write(&[4, 5, 6, 7]).unwrap(), 4); |
||||||
|
assert_eq!(writer.position(), 8); |
||||||
|
assert_eq!(writer.write(&[]).unwrap(), 0); |
||||||
|
assert_eq!(writer.position(), 8); |
||||||
|
|
||||||
|
assert_eq!(writer.write(&[8, 9]).unwrap(), 1); |
||||||
|
assert_eq!(writer.write(&[10]).unwrap(), 0); |
||||||
|
let b: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7, 8]; |
||||||
|
assert_eq!(&**writer.get_ref(), b); |
||||||
|
} |
||||||
|
|
||||||
|
#[test] |
||||||
|
fn test_buf_writer() { |
||||||
|
let mut buf = [0 as u8; 9]; |
||||||
|
{ |
||||||
|
let mut writer = Cursor::new(&mut buf[..]); |
||||||
|
assert_eq!(writer.position(), 0); |
||||||
|
assert_eq!(writer.write(&[0]).unwrap(), 1); |
||||||
|
assert_eq!(writer.position(), 1); |
||||||
|
assert_eq!(writer.write(&[1, 2, 3]).unwrap(), 3); |
||||||
|
assert_eq!(writer.write(&[4, 5, 6, 7]).unwrap(), 4); |
||||||
|
assert_eq!(writer.position(), 8); |
||||||
|
assert_eq!(writer.write(&[]).unwrap(), 0); |
||||||
|
assert_eq!(writer.position(), 8); |
||||||
|
|
||||||
|
assert_eq!(writer.write(&[8, 9]).unwrap(), 1); |
||||||
|
assert_eq!(writer.write(&[10]).unwrap(), 0); |
||||||
|
} |
||||||
|
let b: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7, 8]; |
||||||
|
assert_eq!(buf, b); |
||||||
|
} |
||||||
|
|
||||||
|
#[test] |
||||||
|
fn test_buf_writer_seek() { |
||||||
|
let mut buf = [0 as u8; 8]; |
||||||
|
{ |
||||||
|
let mut writer = Cursor::new(&mut buf[..]); |
||||||
|
assert_eq!(writer.position(), 0); |
||||||
|
assert_eq!(writer.write(&[1]).unwrap(), 1); |
||||||
|
assert_eq!(writer.position(), 1); |
||||||
|
|
||||||
|
assert_eq!(writer.seek(SeekFrom::Start(2)).unwrap(), 2); |
||||||
|
assert_eq!(writer.position(), 2); |
||||||
|
assert_eq!(writer.write(&[2]).unwrap(), 1); |
||||||
|
assert_eq!(writer.position(), 3); |
||||||
|
|
||||||
|
assert_eq!(writer.seek(SeekFrom::Current(-2)).unwrap(), 1); |
||||||
|
assert_eq!(writer.position(), 1); |
||||||
|
assert_eq!(writer.write(&[3]).unwrap(), 1); |
||||||
|
assert_eq!(writer.position(), 2); |
||||||
|
|
||||||
|
assert_eq!(writer.seek(SeekFrom::End(-1)).unwrap(), 7); |
||||||
|
assert_eq!(writer.position(), 7); |
||||||
|
assert_eq!(writer.write(&[4]).unwrap(), 1); |
||||||
|
assert_eq!(writer.position(), 8); |
||||||
|
|
||||||
|
} |
||||||
|
let b: &[_] = &[1, 3, 2, 0, 0, 0, 0, 4]; |
||||||
|
assert_eq!(buf, b); |
||||||
|
} |
||||||
|
|
||||||
|
#[test] |
||||||
|
fn test_buf_writer_error() { |
||||||
|
let mut buf = [0 as u8; 2]; |
||||||
|
let mut writer = Cursor::new(&mut buf[..]); |
||||||
|
assert_eq!(writer.write(&[0]).unwrap(), 1); |
||||||
|
assert_eq!(writer.write(&[0, 0]).unwrap(), 1); |
||||||
|
assert_eq!(writer.write(&[0, 0]).unwrap(), 0); |
||||||
|
} |
||||||
|
|
||||||
|
#[test] |
||||||
|
fn test_mem_reader() { |
||||||
|
let mut reader = Cursor::new(vec!(0, 1, 2, 3, 4, 5, 6, 7)); |
||||||
|
let mut buf = []; |
||||||
|
assert_eq!(reader.read(&mut buf).unwrap(), 0); |
||||||
|
assert_eq!(reader.position(), 0); |
||||||
|
let mut buf = [0]; |
||||||
|
assert_eq!(reader.read(&mut buf).unwrap(), 1); |
||||||
|
assert_eq!(reader.position(), 1); |
||||||
|
let b: &[_] = &[0]; |
||||||
|
assert_eq!(buf, b); |
||||||
|
let mut buf = [0; 4]; |
||||||
|
assert_eq!(reader.read(&mut buf).unwrap(), 4); |
||||||
|
assert_eq!(reader.position(), 5); |
||||||
|
let b: &[_] = &[1, 2, 3, 4]; |
||||||
|
assert_eq!(buf, b); |
||||||
|
assert_eq!(reader.read(&mut buf).unwrap(), 3); |
||||||
|
let b: &[_] = &[5, 6, 7]; |
||||||
|
assert_eq!(&buf[..3], b); |
||||||
|
assert_eq!(reader.read(&mut buf).unwrap(), 0); |
||||||
|
} |
||||||
|
|
||||||
|
#[test] |
||||||
|
fn test_boxed_slice_reader() { |
||||||
|
let mut reader = Cursor::new(vec!(0, 1, 2, 3, 4, 5, 6, 7).into_boxed_slice()); |
||||||
|
let mut buf = []; |
||||||
|
assert_eq!(reader.read(&mut buf).unwrap(), 0); |
||||||
|
assert_eq!(reader.position(), 0); |
||||||
|
let mut buf = [0]; |
||||||
|
assert_eq!(reader.read(&mut buf).unwrap(), 1); |
||||||
|
assert_eq!(reader.position(), 1); |
||||||
|
let b: &[_] = &[0]; |
||||||
|
assert_eq!(buf, b); |
||||||
|
let mut buf = [0; 4]; |
||||||
|
assert_eq!(reader.read(&mut buf).unwrap(), 4); |
||||||
|
assert_eq!(reader.position(), 5); |
||||||
|
let b: &[_] = &[1, 2, 3, 4]; |
||||||
|
assert_eq!(buf, b); |
||||||
|
assert_eq!(reader.read(&mut buf).unwrap(), 3); |
||||||
|
let b: &[_] = &[5, 6, 7]; |
||||||
|
assert_eq!(&buf[..3], b); |
||||||
|
assert_eq!(reader.read(&mut buf).unwrap(), 0); |
||||||
|
} |
||||||
|
|
||||||
|
#[test] |
||||||
|
fn read_to_end() { |
||||||
|
let mut reader = Cursor::new(vec!(0, 1, 2, 3, 4, 5, 6, 7)); |
||||||
|
let mut v = Vec::new(); |
||||||
|
reader.read_to_end(&mut v).unwrap(); |
||||||
|
assert_eq!(v, [0, 1, 2, 3, 4, 5, 6, 7]); |
||||||
|
} |
||||||
|
|
||||||
|
#[test] |
||||||
|
fn test_slice_reader() { |
||||||
|
let in_buf = vec![0, 1, 2, 3, 4, 5, 6, 7]; |
||||||
|
let mut reader = &mut &in_buf[..]; |
||||||
|
let mut buf = []; |
||||||
|
assert_eq!(reader.read(&mut buf).unwrap(), 0); |
||||||
|
let mut buf = [0]; |
||||||
|
assert_eq!(reader.read(&mut buf).unwrap(), 1); |
||||||
|
assert_eq!(reader.len(), 7); |
||||||
|
let b: &[_] = &[0]; |
||||||
|
assert_eq!(&buf[..], b); |
||||||
|
let mut buf = [0; 4]; |
||||||
|
assert_eq!(reader.read(&mut buf).unwrap(), 4); |
||||||
|
assert_eq!(reader.len(), 3); |
||||||
|
let b: &[_] = &[1, 2, 3, 4]; |
||||||
|
assert_eq!(&buf[..], b); |
||||||
|
assert_eq!(reader.read(&mut buf).unwrap(), 3); |
||||||
|
let b: &[_] = &[5, 6, 7]; |
||||||
|
assert_eq!(&buf[..3], b); |
||||||
|
assert_eq!(reader.read(&mut buf).unwrap(), 0); |
||||||
|
} |
||||||
|
|
||||||
|
#[test] |
||||||
|
fn test_buf_reader() { |
||||||
|
let in_buf = vec![0, 1, 2, 3, 4, 5, 6, 7]; |
||||||
|
let mut reader = Cursor::new(&in_buf[..]); |
||||||
|
let mut buf = []; |
||||||
|
assert_eq!(reader.read(&mut buf).unwrap(), 0); |
||||||
|
assert_eq!(reader.position(), 0); |
||||||
|
let mut buf = [0]; |
||||||
|
assert_eq!(reader.read(&mut buf).unwrap(), 1); |
||||||
|
assert_eq!(reader.position(), 1); |
||||||
|
let b: &[_] = &[0]; |
||||||
|
assert_eq!(buf, b); |
||||||
|
let mut buf = [0; 4]; |
||||||
|
assert_eq!(reader.read(&mut buf).unwrap(), 4); |
||||||
|
assert_eq!(reader.position(), 5); |
||||||
|
let b: &[_] = &[1, 2, 3, 4]; |
||||||
|
assert_eq!(buf, b); |
||||||
|
assert_eq!(reader.read(&mut buf).unwrap(), 3); |
||||||
|
let b: &[_] = &[5, 6, 7]; |
||||||
|
assert_eq!(&buf[..3], b); |
||||||
|
assert_eq!(reader.read(&mut buf).unwrap(), 0); |
||||||
|
} |
||||||
|
|
||||||
|
#[test] |
||||||
|
fn test_read_char() { |
||||||
|
let b = &b"Vi\xE1\xBB\x87t"[..]; |
||||||
|
let mut c = Cursor::new(b).chars(); |
||||||
|
assert_eq!(c.next().unwrap().unwrap(), 'V'); |
||||||
|
assert_eq!(c.next().unwrap().unwrap(), 'i'); |
||||||
|
assert_eq!(c.next().unwrap().unwrap(), 'ệ'); |
||||||
|
assert_eq!(c.next().unwrap().unwrap(), 't'); |
||||||
|
assert!(c.next().is_none()); |
||||||
|
} |
||||||
|
|
||||||
|
#[test] |
||||||
|
fn test_read_bad_char() { |
||||||
|
let b = &b"\x80"[..]; |
||||||
|
let mut c = Cursor::new(b).chars(); |
||||||
|
assert!(c.next().unwrap().is_err()); |
||||||
|
} |
||||||
|
|
||||||
|
#[test] |
||||||
|
fn seek_past_end() { |
||||||
|
let buf = [0xff]; |
||||||
|
let mut r = Cursor::new(&buf[..]); |
||||||
|
assert_eq!(r.seek(SeekFrom::Start(10)).unwrap(), 10); |
||||||
|
assert_eq!(r.read(&mut [0]).unwrap(), 0); |
||||||
|
|
||||||
|
let mut r = Cursor::new(vec!(10)); |
||||||
|
assert_eq!(r.seek(SeekFrom::Start(10)).unwrap(), 10); |
||||||
|
assert_eq!(r.read(&mut [0]).unwrap(), 0); |
||||||
|
|
||||||
|
let mut buf = [0]; |
||||||
|
let mut r = Cursor::new(&mut buf[..]); |
||||||
|
assert_eq!(r.seek(SeekFrom::Start(10)).unwrap(), 10); |
||||||
|
assert_eq!(r.write(&[3]).unwrap(), 0); |
||||||
|
|
||||||
|
let mut r = Cursor::new(vec![10].into_boxed_slice()); |
||||||
|
assert_eq!(r.seek(SeekFrom::Start(10)).unwrap(), 10); |
||||||
|
assert_eq!(r.write(&[3]).unwrap(), 0); |
||||||
|
} |
||||||
|
|
||||||
|
#[test] |
||||||
|
fn seek_before_0() { |
||||||
|
let buf = [0xff]; |
||||||
|
let mut r = Cursor::new(&buf[..]); |
||||||
|
assert!(r.seek(SeekFrom::End(-2)).is_err()); |
||||||
|
|
||||||
|
let mut r = Cursor::new(vec!(10)); |
||||||
|
assert!(r.seek(SeekFrom::End(-2)).is_err()); |
||||||
|
|
||||||
|
let mut buf = [0]; |
||||||
|
let mut r = Cursor::new(&mut buf[..]); |
||||||
|
assert!(r.seek(SeekFrom::End(-2)).is_err()); |
||||||
|
|
||||||
|
let mut r = Cursor::new(vec!(10).into_boxed_slice()); |
||||||
|
assert!(r.seek(SeekFrom::End(-2)).is_err()); |
||||||
|
} |
||||||
|
|
||||||
|
#[test] |
||||||
|
fn test_seekable_mem_writer() { |
||||||
|
let mut writer = Cursor::new(Vec::<u8>::new()); |
||||||
|
assert_eq!(writer.position(), 0); |
||||||
|
assert_eq!(writer.write(&[0]).unwrap(), 1); |
||||||
|
assert_eq!(writer.position(), 1); |
||||||
|
assert_eq!(writer.write(&[1, 2, 3]).unwrap(), 3); |
||||||
|
assert_eq!(writer.write(&[4, 5, 6, 7]).unwrap(), 4); |
||||||
|
assert_eq!(writer.position(), 8); |
||||||
|
let b: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7]; |
||||||
|
assert_eq!(&writer.get_ref()[..], b); |
||||||
|
|
||||||
|
assert_eq!(writer.seek(SeekFrom::Start(0)).unwrap(), 0); |
||||||
|
assert_eq!(writer.position(), 0); |
||||||
|
assert_eq!(writer.write(&[3, 4]).unwrap(), 2); |
||||||
|
let b: &[_] = &[3, 4, 2, 3, 4, 5, 6, 7]; |
||||||
|
assert_eq!(&writer.get_ref()[..], b); |
||||||
|
|
||||||
|
assert_eq!(writer.seek(SeekFrom::Current(1)).unwrap(), 3); |
||||||
|
assert_eq!(writer.write(&[0, 1]).unwrap(), 2); |
||||||
|
let b: &[_] = &[3, 4, 2, 0, 1, 5, 6, 7]; |
||||||
|
assert_eq!(&writer.get_ref()[..], b); |
||||||
|
|
||||||
|
assert_eq!(writer.seek(SeekFrom::End(-1)).unwrap(), 7); |
||||||
|
assert_eq!(writer.write(&[1, 2]).unwrap(), 2); |
||||||
|
let b: &[_] = &[3, 4, 2, 0, 1, 5, 6, 1, 2]; |
||||||
|
assert_eq!(&writer.get_ref()[..], b); |
||||||
|
|
||||||
|
assert_eq!(writer.seek(SeekFrom::End(1)).unwrap(), 10); |
||||||
|
assert_eq!(writer.write(&[1]).unwrap(), 1); |
||||||
|
let b: &[_] = &[3, 4, 2, 0, 1, 5, 6, 1, 2, 0, 1]; |
||||||
|
assert_eq!(&writer.get_ref()[..], b); |
||||||
|
} |
||||||
|
|
||||||
|
#[test] |
||||||
|
fn vec_seek_past_end() { |
||||||
|
let mut r = Cursor::new(Vec::new()); |
||||||
|
assert_eq!(r.seek(SeekFrom::Start(10)).unwrap(), 10); |
||||||
|
assert_eq!(r.write(&[3]).unwrap(), 1); |
||||||
|
} |
||||||
|
|
||||||
|
#[test] |
||||||
|
fn vec_seek_before_0() { |
||||||
|
let mut r = Cursor::new(Vec::new()); |
||||||
|
assert!(r.seek(SeekFrom::End(-2)).is_err()); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,529 @@ |
|||||||
|
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
|
||||||
|
// file at the top-level directory of this distribution and at
|
||||||
|
// http://rust-lang.org/COPYRIGHT.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||||
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||||
|
// option. This file may not be copied, modified, or distributed
|
||||||
|
// except according to those terms.
|
||||||
|
|
||||||
|
use error; |
||||||
|
use core::fmt; |
||||||
|
use core::result; |
||||||
|
use sys; |
||||||
|
|
||||||
|
use collections::boxed::Box; |
||||||
|
|
||||||
|
/// A specialized [`Result`](../result/enum.Result.html) type for I/O
|
||||||
|
/// operations.
|
||||||
|
///
|
||||||
|
/// This type is broadly used across `std::io` for any operation which may
|
||||||
|
/// produce an error.
|
||||||
|
///
|
||||||
|
/// This typedef is generally used to avoid writing out `io::Error` directly and
|
||||||
|
/// is otherwise a direct mapping to `Result`.
|
||||||
|
///
|
||||||
|
/// While usual Rust style is to import types directly, aliases of `Result`
|
||||||
|
/// often are not, to make it easier to distinguish between them. `Result` is
|
||||||
|
/// generally assumed to be `std::result::Result`, and so users of this alias
|
||||||
|
/// will generally use `io::Result` instead of shadowing the prelude's import
|
||||||
|
/// of `std::result::Result`.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// A convenience function that bubbles an `io::Result` to its caller:
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use std::io;
|
||||||
|
///
|
||||||
|
/// fn get_string() -> io::Result<String> {
|
||||||
|
/// let mut buffer = String::new();
|
||||||
|
///
|
||||||
|
/// try!(io::stdin().read_line(&mut buffer));
|
||||||
|
///
|
||||||
|
/// Ok(buffer)
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
pub type Result<T> = result::Result<T, Error>; |
||||||
|
|
||||||
|
/// The error type for I/O operations of the `Read`, `Write`, `Seek`, and
|
||||||
|
/// associated traits.
|
||||||
|
///
|
||||||
|
/// Errors mostly originate from the underlying OS, but custom instances of
|
||||||
|
/// `Error` can be created with crafted error messages and a particular value of
|
||||||
|
/// [`ErrorKind`].
|
||||||
|
///
|
||||||
|
/// [`ErrorKind`]: enum.ErrorKind.html
|
||||||
|
#[derive(Debug)] |
||||||
|
pub struct Error { |
||||||
|
repr: Repr, |
||||||
|
} |
||||||
|
|
||||||
|
enum Repr { |
||||||
|
Os(i32), |
||||||
|
Custom(Box<Custom>), |
||||||
|
} |
||||||
|
|
||||||
|
#[derive(Debug)] |
||||||
|
struct Custom { |
||||||
|
kind: ErrorKind, |
||||||
|
error: Box<error::Error+Send+Sync>, |
||||||
|
} |
||||||
|
|
||||||
|
/// A list specifying general categories of I/O error.
|
||||||
|
///
|
||||||
|
/// This list is intended to grow over time and it is not recommended to
|
||||||
|
/// exhaustively match against it.
|
||||||
|
///
|
||||||
|
/// It is used with the [`io::Error`] type.
|
||||||
|
///
|
||||||
|
/// [`io::Error`]: struct.Error.html
|
||||||
|
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] |
||||||
|
#[allow(deprecated)] |
||||||
|
pub enum ErrorKind { |
||||||
|
/// An entity was not found, often a file.
|
||||||
|
NotFound, |
||||||
|
/// The operation lacked the necessary privileges to complete.
|
||||||
|
PermissionDenied, |
||||||
|
/// The connection was refused by the remote server.
|
||||||
|
ConnectionRefused, |
||||||
|
/// The connection was reset by the remote server.
|
||||||
|
ConnectionReset, |
||||||
|
/// The connection was aborted (terminated) by the remote server.
|
||||||
|
ConnectionAborted, |
||||||
|
/// The network operation failed because it was not connected yet.
|
||||||
|
NotConnected, |
||||||
|
/// A socket address could not be bound because the address is already in
|
||||||
|
/// use elsewhere.
|
||||||
|
AddrInUse, |
||||||
|
/// A nonexistent interface was requested or the requested address was not
|
||||||
|
/// local.
|
||||||
|
AddrNotAvailable, |
||||||
|
/// The operation failed because a pipe was closed.
|
||||||
|
BrokenPipe, |
||||||
|
/// An entity already exists, often a file.
|
||||||
|
AlreadyExists, |
||||||
|
/// The operation needs to block to complete, but the blocking operation was
|
||||||
|
/// requested to not occur.
|
||||||
|
WouldBlock, |
||||||
|
/// A parameter was incorrect.
|
||||||
|
InvalidInput, |
||||||
|
/// Data not valid for the operation were encountered.
|
||||||
|
///
|
||||||
|
/// Unlike `InvalidInput`, this typically means that the operation
|
||||||
|
/// parameters were valid, however the error was caused by malformed
|
||||||
|
/// input data.
|
||||||
|
///
|
||||||
|
/// For example, a function that reads a file into a string will error with
|
||||||
|
/// `InvalidData` if the file's contents are not valid UTF-8.
|
||||||
|
InvalidData, |
||||||
|
/// The I/O operation's timeout expired, causing it to be canceled.
|
||||||
|
TimedOut, |
||||||
|
/// An error returned when an operation could not be completed because a
|
||||||
|
/// call to `write` returned `Ok(0)`.
|
||||||
|
///
|
||||||
|
/// This typically means that an operation could only succeed if it wrote a
|
||||||
|
/// particular number of bytes but only a smaller number of bytes could be
|
||||||
|
/// written.
|
||||||
|
WriteZero, |
||||||
|
/// This operation was interrupted.
|
||||||
|
///
|
||||||
|
/// Interrupted operations can typically be retried.
|
||||||
|
Interrupted, |
||||||
|
/// Any I/O error not part of this list.
|
||||||
|
Other, |
||||||
|
|
||||||
|
/// An error returned when an operation could not be completed because an
|
||||||
|
/// "end of file" was reached prematurely.
|
||||||
|
///
|
||||||
|
/// This typically means that an operation could only succeed if it read a
|
||||||
|
/// particular number of bytes but only a smaller number of bytes could be
|
||||||
|
/// read.
|
||||||
|
UnexpectedEof, |
||||||
|
|
||||||
|
/// A marker variant that tells the compiler that users of this enum cannot
|
||||||
|
/// match it exhaustively.
|
||||||
|
#[doc(hidden)] |
||||||
|
__Nonexhaustive, |
||||||
|
} |
||||||
|
|
||||||
|
impl Error { |
||||||
|
/// Creates a new I/O error from a known kind of error as well as an
|
||||||
|
/// arbitrary error payload.
|
||||||
|
///
|
||||||
|
/// This function is used to generically create I/O errors which do not
|
||||||
|
/// originate from the OS itself. The `error` argument is an arbitrary
|
||||||
|
/// payload which will be contained in this `Error`.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use std::io::{Error, ErrorKind};
|
||||||
|
///
|
||||||
|
/// // errors can be created from strings
|
||||||
|
/// let custom_error = Error::new(ErrorKind::Other, "oh no!");
|
||||||
|
///
|
||||||
|
/// // errors can also be created from other errors
|
||||||
|
/// let custom_error2 = Error::new(ErrorKind::Interrupted, custom_error);
|
||||||
|
/// ```
|
||||||
|
pub fn new<E>(kind: ErrorKind, error: E) -> Error |
||||||
|
where E: Into<Box<error::Error+Send+Sync>> |
||||||
|
{ |
||||||
|
Self::_new(kind, error.into()) |
||||||
|
} |
||||||
|
|
||||||
|
fn _new(kind: ErrorKind, error: Box<error::Error+Send+Sync>) -> Error { |
||||||
|
Error { |
||||||
|
repr: Repr::Custom(Box::new(Custom { |
||||||
|
kind: kind, |
||||||
|
error: error, |
||||||
|
})) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// Returns an error representing the last OS error which occurred.
|
||||||
|
///
|
||||||
|
/// This function reads the value of `errno` for the target platform (e.g.
|
||||||
|
/// `GetLastError` on Windows) and will return a corresponding instance of
|
||||||
|
/// `Error` for the error code.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use std::io::Error;
|
||||||
|
///
|
||||||
|
/// println!("last OS error: {:?}", Error::last_os_error());
|
||||||
|
/// ```
|
||||||
|
pub fn last_os_error() -> Error { |
||||||
|
Error::from_raw_os_error(sys::os::errno() as i32) |
||||||
|
} |
||||||
|
|
||||||
|
/// Creates a new instance of an `Error` from a particular OS error code.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// On Linux:
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # if cfg!(target_os = "linux") {
|
||||||
|
/// use std::io;
|
||||||
|
///
|
||||||
|
/// let error = io::Error::from_raw_os_error(98);
|
||||||
|
/// assert_eq!(error.kind(), io::ErrorKind::AddrInUse);
|
||||||
|
/// # }
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// On Windows:
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # if cfg!(windows) {
|
||||||
|
/// use std::io;
|
||||||
|
///
|
||||||
|
/// let error = io::Error::from_raw_os_error(10048);
|
||||||
|
/// assert_eq!(error.kind(), io::ErrorKind::AddrInUse);
|
||||||
|
/// # }
|
||||||
|
/// ```
|
||||||
|
pub fn from_raw_os_error(code: i32) -> Error { |
||||||
|
Error { repr: Repr::Os(code) } |
||||||
|
} |
||||||
|
|
||||||
|
/// Returns the OS error that this error represents (if any).
|
||||||
|
///
|
||||||
|
/// If this `Error` was constructed via `last_os_error` or
|
||||||
|
/// `from_raw_os_error`, then this function will return `Some`, otherwise
|
||||||
|
/// it will return `None`.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use std::io::{Error, ErrorKind};
|
||||||
|
///
|
||||||
|
/// fn print_os_error(err: &Error) {
|
||||||
|
/// if let Some(raw_os_err) = err.raw_os_error() {
|
||||||
|
/// println!("raw OS error: {:?}", raw_os_err);
|
||||||
|
/// } else {
|
||||||
|
/// println!("Not an OS error");
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// fn main() {
|
||||||
|
/// // Will print "raw OS error: ...".
|
||||||
|
/// print_os_error(&Error::last_os_error());
|
||||||
|
/// // Will print "Not an OS error".
|
||||||
|
/// print_os_error(&Error::new(ErrorKind::Other, "oh no!"));
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
pub fn raw_os_error(&self) -> Option<i32> { |
||||||
|
match self.repr { |
||||||
|
Repr::Os(i) => Some(i), |
||||||
|
Repr::Custom(..) => None, |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// Returns a reference to the inner error wrapped by this error (if any).
|
||||||
|
///
|
||||||
|
/// If this `Error` was constructed via `new` then this function will
|
||||||
|
/// return `Some`, otherwise it will return `None`.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use std::io::{Error, ErrorKind};
|
||||||
|
///
|
||||||
|
/// fn print_error(err: &Error) {
|
||||||
|
/// if let Some(inner_err) = err.get_ref() {
|
||||||
|
/// println!("Inner error: {:?}", inner_err);
|
||||||
|
/// } else {
|
||||||
|
/// println!("No inner error");
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// fn main() {
|
||||||
|
/// // Will print "No inner error".
|
||||||
|
/// print_error(&Error::last_os_error());
|
||||||
|
/// // Will print "Inner error: ...".
|
||||||
|
/// print_error(&Error::new(ErrorKind::Other, "oh no!"));
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
pub fn get_ref(&self) -> Option<&(error::Error+Send+Sync+'static)> { |
||||||
|
match self.repr { |
||||||
|
Repr::Os(..) => None, |
||||||
|
Repr::Custom(ref c) => Some(&*c.error), |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// Returns a mutable reference to the inner error wrapped by this error
|
||||||
|
/// (if any).
|
||||||
|
///
|
||||||
|
/// If this `Error` was constructed via `new` then this function will
|
||||||
|
/// return `Some`, otherwise it will return `None`.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use std::io::{Error, ErrorKind};
|
||||||
|
/// use std::{error, fmt};
|
||||||
|
/// use std::fmt::Display;
|
||||||
|
///
|
||||||
|
/// #[derive(Debug)]
|
||||||
|
/// struct MyError {
|
||||||
|
/// v: String,
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// impl MyError {
|
||||||
|
/// fn new() -> MyError {
|
||||||
|
/// MyError {
|
||||||
|
/// v: "oh no!".to_owned()
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// fn change_message(&mut self, new_message: &str) {
|
||||||
|
/// self.v = new_message.to_owned();
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// impl error::Error for MyError {
|
||||||
|
/// fn description(&self) -> &str { &self.v }
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// impl Display for MyError {
|
||||||
|
/// fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
/// write!(f, "MyError: {}", &self.v)
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// fn change_error(mut err: Error) -> Error {
|
||||||
|
/// if let Some(inner_err) = err.get_mut() {
|
||||||
|
/// inner_err.downcast_mut::<MyError>().unwrap().change_message("I've been changed!");
|
||||||
|
/// }
|
||||||
|
/// err
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// fn print_error(err: &Error) {
|
||||||
|
/// if let Some(inner_err) = err.get_ref() {
|
||||||
|
/// println!("Inner error: {}", inner_err);
|
||||||
|
/// } else {
|
||||||
|
/// println!("No inner error");
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// fn main() {
|
||||||
|
/// // Will print "No inner error".
|
||||||
|
/// print_error(&change_error(Error::last_os_error()));
|
||||||
|
/// // Will print "Inner error: ...".
|
||||||
|
/// print_error(&change_error(Error::new(ErrorKind::Other, MyError::new())));
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
pub fn get_mut(&mut self) -> Option<&mut (error::Error+Send+Sync+'static)> { |
||||||
|
match self.repr { |
||||||
|
Repr::Os(..) => None, |
||||||
|
Repr::Custom(ref mut c) => Some(&mut *c.error), |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// Consumes the `Error`, returning its inner error (if any).
|
||||||
|
///
|
||||||
|
/// If this `Error` was constructed via `new` then this function will
|
||||||
|
/// return `Some`, otherwise it will return `None`.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use std::io::{Error, ErrorKind};
|
||||||
|
///
|
||||||
|
/// fn print_error(err: Error) {
|
||||||
|
/// if let Some(inner_err) = err.into_inner() {
|
||||||
|
/// println!("Inner error: {}", inner_err);
|
||||||
|
/// } else {
|
||||||
|
/// println!("No inner error");
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// fn main() {
|
||||||
|
/// // Will print "No inner error".
|
||||||
|
/// print_error(Error::last_os_error());
|
||||||
|
/// // Will print "Inner error: ...".
|
||||||
|
/// print_error(Error::new(ErrorKind::Other, "oh no!"));
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
pub fn into_inner(self) -> Option<Box<error::Error+Send+Sync>> { |
||||||
|
match self.repr { |
||||||
|
Repr::Os(..) => None, |
||||||
|
Repr::Custom(c) => Some(c.error) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// Returns the corresponding `ErrorKind` for this error.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use std::io::{Error, ErrorKind};
|
||||||
|
///
|
||||||
|
/// fn print_error(err: Error) {
|
||||||
|
/// println!("{:?}", err.kind());
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// fn main() {
|
||||||
|
/// // Will print "No inner error".
|
||||||
|
/// print_error(Error::last_os_error());
|
||||||
|
/// // Will print "Inner error: ...".
|
||||||
|
/// print_error(Error::new(ErrorKind::AddrInUse, "oh no!"));
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
pub fn kind(&self) -> ErrorKind { |
||||||
|
match self.repr { |
||||||
|
Repr::Os(code) => sys::os::decode_error_kind(code), |
||||||
|
Repr::Custom(ref c) => c.kind, |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl fmt::Debug for Repr { |
||||||
|
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { |
||||||
|
match *self { |
||||||
|
Repr::Os(ref code) => |
||||||
|
fmt.debug_struct("Os").field("code", code) |
||||||
|
.field("message", &sys::os::error_string(*code)).finish(), |
||||||
|
Repr::Custom(ref c) => fmt.debug_tuple("Custom").field(c).finish(), |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl fmt::Display for Error { |
||||||
|
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { |
||||||
|
match self.repr { |
||||||
|
Repr::Os(code) => { |
||||||
|
let detail = sys::os::error_string(code); |
||||||
|
write!(fmt, "{} (os error {})", detail, code) |
||||||
|
} |
||||||
|
Repr::Custom(ref c) => c.error.fmt(fmt), |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl error::Error for Error { |
||||||
|
fn description(&self) -> &str { |
||||||
|
match self.repr { |
||||||
|
Repr::Os(..) => match self.kind() { |
||||||
|
ErrorKind::NotFound => "entity not found", |
||||||
|
ErrorKind::PermissionDenied => "permission denied", |
||||||
|
ErrorKind::ConnectionRefused => "connection refused", |
||||||
|
ErrorKind::ConnectionReset => "connection reset", |
||||||
|
ErrorKind::ConnectionAborted => "connection aborted", |
||||||
|
ErrorKind::NotConnected => "not connected", |
||||||
|
ErrorKind::AddrInUse => "address in use", |
||||||
|
ErrorKind::AddrNotAvailable => "address not available", |
||||||
|
ErrorKind::BrokenPipe => "broken pipe", |
||||||
|
ErrorKind::AlreadyExists => "entity already exists", |
||||||
|
ErrorKind::WouldBlock => "operation would block", |
||||||
|
ErrorKind::InvalidInput => "invalid input parameter", |
||||||
|
ErrorKind::InvalidData => "invalid data", |
||||||
|
ErrorKind::TimedOut => "timed out", |
||||||
|
ErrorKind::WriteZero => "write zero", |
||||||
|
ErrorKind::Interrupted => "operation interrupted", |
||||||
|
ErrorKind::Other => "other os error", |
||||||
|
ErrorKind::UnexpectedEof => "unexpected end of file", |
||||||
|
ErrorKind::__Nonexhaustive => unreachable!() |
||||||
|
}, |
||||||
|
Repr::Custom(ref c) => c.error.description(), |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
fn cause(&self) -> Option<&error::Error> { |
||||||
|
match self.repr { |
||||||
|
Repr::Os(..) => None, |
||||||
|
Repr::Custom(ref c) => c.error.cause(), |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
fn _assert_error_is_sync_send() { |
||||||
|
fn _is_sync_send<T: Sync+Send>() {} |
||||||
|
_is_sync_send::<Error>(); |
||||||
|
} |
||||||
|
|
||||||
|
#[cfg(test)] |
||||||
|
mod test { |
||||||
|
use super::{Error, ErrorKind}; |
||||||
|
use error; |
||||||
|
use core::fmt; |
||||||
|
use sys::os::error_string; |
||||||
|
|
||||||
|
#[test] |
||||||
|
fn test_debug_error() { |
||||||
|
let code = 6; |
||||||
|
let msg = error_string(code); |
||||||
|
let err = Error { repr: super::Repr::Os(code) }; |
||||||
|
let expected = format!("Error {{ repr: Os {{ code: {:?}, message: {:?} }} }}", code, msg); |
||||||
|
assert_eq!(format!("{:?}", err), expected); |
||||||
|
} |
||||||
|
|
||||||
|
#[test] |
||||||
|
fn test_downcasting() { |
||||||
|
#[derive(Debug)] |
||||||
|
struct TestError; |
||||||
|
|
||||||
|
impl fmt::Display for TestError { |
||||||
|
fn fmt(&self, _: &mut fmt::Formatter) -> fmt::Result { |
||||||
|
Ok(()) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl error::Error for TestError { |
||||||
|
fn description(&self) -> &str { |
||||||
|
"asdf" |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// we have to call all of these UFCS style right now since method
|
||||||
|
// resolution won't implicitly drop the Send+Sync bounds
|
||||||
|
let mut err = Error::new(ErrorKind::Other, TestError); |
||||||
|
assert!(err.get_ref().unwrap().is::<TestError>()); |
||||||
|
assert_eq!("asdf", err.get_ref().unwrap().description()); |
||||||
|
assert!(err.get_mut().unwrap().is::<TestError>()); |
||||||
|
let extracted = err.into_inner().unwrap(); |
||||||
|
extracted.downcast::<TestError>().unwrap(); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,279 @@ |
|||||||
|
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
|
||||||
|
// file at the top-level directory of this distribution and at
|
||||||
|
// http://rust-lang.org/COPYRIGHT.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||||
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||||
|
// option. This file may not be copied, modified, or distributed
|
||||||
|
// except according to those terms.
|
||||||
|
|
||||||
|
use core::cmp; |
||||||
|
use io::{self, SeekFrom, Read, Write, Seek, BufRead, Error, ErrorKind}; |
||||||
|
use core::fmt; |
||||||
|
use core::mem; |
||||||
|
|
||||||
|
use collections::boxed::Box; |
||||||
|
use collections::Vec; |
||||||
|
use collections::String; |
||||||
|
|
||||||
|
// =============================================================================
|
||||||
|
// Forwarding implementations
|
||||||
|
|
||||||
|
impl<'a, R: Read + ?Sized> Read for &'a mut R { |
||||||
|
#[inline] |
||||||
|
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { |
||||||
|
(**self).read(buf) |
||||||
|
} |
||||||
|
|
||||||
|
#[inline] |
||||||
|
fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> { |
||||||
|
(**self).read_to_end(buf) |
||||||
|
} |
||||||
|
|
||||||
|
#[inline] |
||||||
|
fn read_to_string(&mut self, buf: &mut String) -> io::Result<usize> { |
||||||
|
(**self).read_to_string(buf) |
||||||
|
} |
||||||
|
|
||||||
|
#[inline] |
||||||
|
fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> { |
||||||
|
(**self).read_exact(buf) |
||||||
|
} |
||||||
|
} |
||||||
|
impl<'a, W: Write + ?Sized> Write for &'a mut W { |
||||||
|
#[inline] |
||||||
|
fn write(&mut self, buf: &[u8]) -> io::Result<usize> { (**self).write(buf) } |
||||||
|
|
||||||
|
#[inline] |
||||||
|
fn flush(&mut self) -> io::Result<()> { (**self).flush() } |
||||||
|
|
||||||
|
#[inline] |
||||||
|
fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { |
||||||
|
(**self).write_all(buf) |
||||||
|
} |
||||||
|
|
||||||
|
#[inline] |
||||||
|
fn write_fmt(&mut self, fmt: fmt::Arguments) -> io::Result<()> { |
||||||
|
(**self).write_fmt(fmt) |
||||||
|
} |
||||||
|
} |
||||||
|
impl<'a, S: Seek + ?Sized> Seek for &'a mut S { |
||||||
|
#[inline] |
||||||
|
fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> { (**self).seek(pos) } |
||||||
|
} |
||||||
|
impl<'a, B: BufRead + ?Sized> BufRead for &'a mut B { |
||||||
|
#[inline] |
||||||
|
fn fill_buf(&mut self) -> io::Result<&[u8]> { (**self).fill_buf() } |
||||||
|
|
||||||
|
#[inline] |
||||||
|
fn consume(&mut self, amt: usize) { (**self).consume(amt) } |
||||||
|
|
||||||
|
#[inline] |
||||||
|
fn read_until(&mut self, byte: u8, buf: &mut Vec<u8>) -> io::Result<usize> { |
||||||
|
(**self).read_until(byte, buf) |
||||||
|
} |
||||||
|
|
||||||
|
#[inline] |
||||||
|
fn read_line(&mut self, buf: &mut String) -> io::Result<usize> { |
||||||
|
(**self).read_line(buf) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl<R: Read + ?Sized> Read for Box<R> { |
||||||
|
#[inline] |
||||||
|
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { |
||||||
|
(**self).read(buf) |
||||||
|
} |
||||||
|
|
||||||
|
#[inline] |
||||||
|
fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> { |
||||||
|
(**self).read_to_end(buf) |
||||||
|
} |
||||||
|
|
||||||
|
#[inline] |
||||||
|
fn read_to_string(&mut self, buf: &mut String) -> io::Result<usize> { |
||||||
|
(**self).read_to_string(buf) |
||||||
|
} |
||||||
|
|
||||||
|
#[inline] |
||||||
|
fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> { |
||||||
|
(**self).read_exact(buf) |
||||||
|
} |
||||||
|
} |
||||||
|
impl<W: Write + ?Sized> Write for Box<W> { |
||||||
|
#[inline] |
||||||
|
fn write(&mut self, buf: &[u8]) -> io::Result<usize> { (**self).write(buf) } |
||||||
|
|
||||||
|
#[inline] |
||||||
|
fn flush(&mut self) -> io::Result<()> { (**self).flush() } |
||||||
|
|
||||||
|
#[inline] |
||||||
|
fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { |
||||||
|
(**self).write_all(buf) |
||||||
|
} |
||||||
|
|
||||||
|
#[inline] |
||||||
|
fn write_fmt(&mut self, fmt: fmt::Arguments) -> io::Result<()> { |
||||||
|
(**self).write_fmt(fmt) |
||||||
|
} |
||||||
|
} |
||||||
|
impl<S: Seek + ?Sized> Seek for Box<S> { |
||||||
|
#[inline] |
||||||
|
fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> { (**self).seek(pos) } |
||||||
|
} |
||||||
|
impl<B: BufRead + ?Sized> BufRead for Box<B> { |
||||||
|
#[inline] |
||||||
|
fn fill_buf(&mut self) -> io::Result<&[u8]> { (**self).fill_buf() } |
||||||
|
|
||||||
|
#[inline] |
||||||
|
fn consume(&mut self, amt: usize) { (**self).consume(amt) } |
||||||
|
|
||||||
|
#[inline] |
||||||
|
fn read_until(&mut self, byte: u8, buf: &mut Vec<u8>) -> io::Result<usize> { |
||||||
|
(**self).read_until(byte, buf) |
||||||
|
} |
||||||
|
|
||||||
|
#[inline] |
||||||
|
fn read_line(&mut self, buf: &mut String) -> io::Result<usize> { |
||||||
|
(**self).read_line(buf) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// =============================================================================
|
||||||
|
// In-memory buffer implementations
|
||||||
|
|
||||||
|
impl<'a> Read for &'a [u8] { |
||||||
|
#[inline] |
||||||
|
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { |
||||||
|
let amt = cmp::min(buf.len(), self.len()); |
||||||
|
let (a, b) = self.split_at(amt); |
||||||
|
buf[..amt].copy_from_slice(a); |
||||||
|
*self = b; |
||||||
|
Ok(amt) |
||||||
|
} |
||||||
|
|
||||||
|
#[inline] |
||||||
|
fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> { |
||||||
|
if buf.len() > self.len() { |
||||||
|
return Err(Error::new(ErrorKind::UnexpectedEof, |
||||||
|
"failed to fill whole buffer")); |
||||||
|
} |
||||||
|
let (a, b) = self.split_at(buf.len()); |
||||||
|
buf.copy_from_slice(a); |
||||||
|
*self = b; |
||||||
|
Ok(()) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl<'a> BufRead for &'a [u8] { |
||||||
|
#[inline] |
||||||
|
fn fill_buf(&mut self) -> io::Result<&[u8]> { Ok(*self) } |
||||||
|
|
||||||
|
#[inline] |
||||||
|
fn consume(&mut self, amt: usize) { *self = &self[amt..]; } |
||||||
|
} |
||||||
|
|
||||||
|
impl<'a> Write for &'a mut [u8] { |
||||||
|
#[inline] |
||||||
|
fn write(&mut self, data: &[u8]) -> io::Result<usize> { |
||||||
|
let amt = cmp::min(data.len(), self.len()); |
||||||
|
let (a, b) = mem::replace(self, &mut []).split_at_mut(amt); |
||||||
|
a.copy_from_slice(&data[..amt]); |
||||||
|
*self = b; |
||||||
|
Ok(amt) |
||||||
|
} |
||||||
|
|
||||||
|
#[inline] |
||||||
|
fn write_all(&mut self, data: &[u8]) -> io::Result<()> { |
||||||
|
if self.write(data)? == data.len() { |
||||||
|
Ok(()) |
||||||
|
} else { |
||||||
|
Err(Error::new(ErrorKind::WriteZero, "failed to write whole buffer")) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
#[inline] |
||||||
|
fn flush(&mut self) -> io::Result<()> { Ok(()) } |
||||||
|
} |
||||||
|
|
||||||
|
impl Write for Vec<u8> { |
||||||
|
#[inline] |
||||||
|
fn write(&mut self, buf: &[u8]) -> io::Result<usize> { |
||||||
|
self.extend_from_slice(buf); |
||||||
|
Ok(buf.len()) |
||||||
|
} |
||||||
|
|
||||||
|
#[inline] |
||||||
|
fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { |
||||||
|
self.extend_from_slice(buf); |
||||||
|
Ok(()) |
||||||
|
} |
||||||
|
|
||||||
|
#[inline] |
||||||
|
fn flush(&mut self) -> io::Result<()> { Ok(()) } |
||||||
|
} |
||||||
|
|
||||||
|
#[cfg(test)] |
||||||
|
mod tests { |
||||||
|
use io::prelude::*; |
||||||
|
use test; |
||||||
|
|
||||||
|
use collections::Vec; |
||||||
|
|
||||||
|
#[bench] |
||||||
|
fn bench_read_slice(b: &mut test::Bencher) { |
||||||
|
let buf = [5; 1024]; |
||||||
|
let mut dst = [0; 128]; |
||||||
|
|
||||||
|
b.iter(|| { |
||||||
|
let mut rd = &buf[..]; |
||||||
|
for _ in 0..8 { |
||||||
|
let _ = rd.read(&mut dst); |
||||||
|
test::black_box(&dst); |
||||||
|
} |
||||||
|
}) |
||||||
|
} |
||||||
|
|
||||||
|
#[bench] |
||||||
|
fn bench_write_slice(b: &mut test::Bencher) { |
||||||
|
let mut buf = [0; 1024]; |
||||||
|
let src = [5; 128]; |
||||||
|
|
||||||
|
b.iter(|| { |
||||||
|
let mut wr = &mut buf[..]; |
||||||
|
for _ in 0..8 { |
||||||
|
let _ = wr.write_all(&src); |
||||||
|
test::black_box(&wr); |
||||||
|
} |
||||||
|
}) |
||||||
|
} |
||||||
|
|
||||||
|
#[bench] |
||||||
|
fn bench_read_vec(b: &mut test::Bencher) { |
||||||
|
let buf = vec![5; 1024]; |
||||||
|
let mut dst = [0; 128]; |
||||||
|
|
||||||
|
b.iter(|| { |
||||||
|
let mut rd = &buf[..]; |
||||||
|
for _ in 0..8 { |
||||||
|
let _ = rd.read(&mut dst); |
||||||
|
test::black_box(&dst); |
||||||
|
} |
||||||
|
}) |
||||||
|
} |
||||||
|
|
||||||
|
#[bench] |
||||||
|
fn bench_write_vec(b: &mut test::Bencher) { |
||||||
|
let mut buf = Vec::with_capacity(1024); |
||||||
|
let src = [5; 128]; |
||||||
|
|
||||||
|
b.iter(|| { |
||||||
|
let mut wr = &mut buf[..]; |
||||||
|
for _ in 0..8 { |
||||||
|
let _ = wr.write_all(&src); |
||||||
|
test::black_box(&wr); |
||||||
|
} |
||||||
|
}) |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,22 @@ |
|||||||
|
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
|
||||||
|
// file at the top-level directory of this distribution and at
|
||||||
|
// http://rust-lang.org/COPYRIGHT.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||||
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||||
|
// option. This file may not be copied, modified, or distributed
|
||||||
|
// except according to those terms.
|
||||||
|
|
||||||
|
//! The I/O Prelude
|
||||||
|
//!
|
||||||
|
//! The purpose of this module is to alleviate imports of many common I/O traits
|
||||||
|
//! by adding a glob import to the top of I/O heavy modules:
|
||||||
|
//!
|
||||||
|
//! ```
|
||||||
|
//! # #![allow(unused_imports)]
|
||||||
|
//! use std::io::prelude::*;
|
||||||
|
//! ```
|
||||||
|
|
||||||
|
|
||||||
|
pub use super::{Read, Write, BufRead, Seek}; |
@ -0,0 +1,204 @@ |
|||||||
|
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
|
||||||
|
// file at the top-level directory of this distribution and at
|
||||||
|
// http://rust-lang.org/COPYRIGHT.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||||
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||||
|
// option. This file may not be copied, modified, or distributed
|
||||||
|
// except according to those terms.
|
||||||
|
|
||||||
|
#![allow(missing_copy_implementations)] |
||||||
|
|
||||||
|
use io::{self, Read, Write, ErrorKind, BufRead}; |
||||||
|
|
||||||
|
/// Copies the entire contents of a reader into a writer.
|
||||||
|
///
|
||||||
|
/// This function will continuously read data from `reader` and then
|
||||||
|
/// write it into `writer` in a streaming fashion until `reader`
|
||||||
|
/// returns EOF.
|
||||||
|
///
|
||||||
|
/// On success, the total number of bytes that were copied from
|
||||||
|
/// `reader` to `writer` is returned.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// This function will return an error immediately if any call to `read` or
|
||||||
|
/// `write` returns an error. All instances of `ErrorKind::Interrupted` are
|
||||||
|
/// handled by this function and the underlying operation is retried.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use std::io;
|
||||||
|
///
|
||||||
|
/// # fn foo() -> io::Result<()> {
|
||||||
|
/// let mut reader: &[u8] = b"hello";
|
||||||
|
/// let mut writer: Vec<u8> = vec![];
|
||||||
|
///
|
||||||
|
/// try!(io::copy(&mut reader, &mut writer));
|
||||||
|
///
|
||||||
|
/// assert_eq!(reader, &writer[..]);
|
||||||
|
/// # Ok(())
|
||||||
|
/// # }
|
||||||
|
/// ```
|
||||||
|
pub fn copy<R: ?Sized, W: ?Sized>(reader: &mut R, writer: &mut W) -> io::Result<u64> |
||||||
|
where R: Read, W: Write |
||||||
|
{ |
||||||
|
let mut buf = [0; super::DEFAULT_BUF_SIZE]; |
||||||
|
let mut written = 0; |
||||||
|
loop { |
||||||
|
let len = match reader.read(&mut buf) { |
||||||
|
Ok(0) => return Ok(written), |
||||||
|
Ok(len) => len, |
||||||
|
Err(ref e) if e.kind() == ErrorKind::Interrupted => continue, |
||||||
|
Err(e) => return Err(e), |
||||||
|
}; |
||||||
|
writer.write_all(&buf[..len])?; |
||||||
|
written += len as u64; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// A reader which is always at EOF.
|
||||||
|
///
|
||||||
|
/// This struct is generally created by calling [`empty()`][empty]. Please see
|
||||||
|
/// the documentation of `empty()` for more details.
|
||||||
|
///
|
||||||
|
/// [empty]: fn.empty.html
|
||||||
|
pub struct Empty { _priv: () } |
||||||
|
|
||||||
|
/// Constructs a new handle to an empty reader.
|
||||||
|
///
|
||||||
|
/// All reads from the returned reader will return `Ok(0)`.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// A slightly sad example of not reading anything into a buffer:
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use std::io::{self, Read};
|
||||||
|
///
|
||||||
|
/// let mut buffer = String::new();
|
||||||
|
/// io::empty().read_to_string(&mut buffer).unwrap();
|
||||||
|
/// assert!(buffer.is_empty());
|
||||||
|
/// ```
|
||||||
|
pub fn empty() -> Empty { Empty { _priv: () } } |
||||||
|
|
||||||
|
impl Read for Empty { |
||||||
|
fn read(&mut self, _buf: &mut [u8]) -> io::Result<usize> { Ok(0) } |
||||||
|
} |
||||||
|
impl BufRead for Empty { |
||||||
|
fn fill_buf(&mut self) -> io::Result<&[u8]> { Ok(&[]) } |
||||||
|
fn consume(&mut self, _n: usize) {} |
||||||
|
} |
||||||
|
|
||||||
|
/// A reader which yields one byte over and over and over and over and over and...
|
||||||
|
///
|
||||||
|
/// This struct is generally created by calling [`repeat()`][repeat]. Please
|
||||||
|
/// see the documentation of `repeat()` for more details.
|
||||||
|
///
|
||||||
|
/// [repeat]: fn.repeat.html
|
||||||
|
pub struct Repeat { byte: u8 } |
||||||
|
|
||||||
|
/// Creates an instance of a reader that infinitely repeats one byte.
|
||||||
|
///
|
||||||
|
/// All reads from this reader will succeed by filling the specified buffer with
|
||||||
|
/// the given byte.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use std::io::{self, Read};
|
||||||
|
///
|
||||||
|
/// let mut buffer = [0; 3];
|
||||||
|
/// io::repeat(0b101).read_exact(&mut buffer).unwrap();
|
||||||
|
/// assert_eq!(buffer, [0b101, 0b101, 0b101]);
|
||||||
|
/// ```
|
||||||
|
pub fn repeat(byte: u8) -> Repeat { Repeat { byte: byte } } |
||||||
|
|
||||||
|
impl Read for Repeat { |
||||||
|
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { |
||||||
|
for slot in &mut *buf { |
||||||
|
*slot = self.byte; |
||||||
|
} |
||||||
|
Ok(buf.len()) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// A writer which will move data into the void.
|
||||||
|
///
|
||||||
|
/// This struct is generally created by calling [`sink()`][sink]. Please
|
||||||
|
/// see the documentation of `sink()` for more details.
|
||||||
|
///
|
||||||
|
/// [sink]: fn.sink.html
|
||||||
|
pub struct Sink { _priv: () } |
||||||
|
|
||||||
|
/// Creates an instance of a writer which will successfully consume all data.
|
||||||
|
///
|
||||||
|
/// All calls to `write` on the returned instance will return `Ok(buf.len())`
|
||||||
|
/// and the contents of the buffer will not be inspected.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// use std::io::{self, Write};
|
||||||
|
///
|
||||||
|
/// let buffer = vec![1, 2, 3, 5, 8];
|
||||||
|
/// let num_bytes = io::sink().write(&buffer).unwrap();
|
||||||
|
/// assert_eq!(num_bytes, 5);
|
||||||
|
/// ```
|
||||||
|
pub fn sink() -> Sink { Sink { _priv: () } } |
||||||
|
|
||||||
|
impl Write for Sink { |
||||||
|
fn write(&mut self, buf: &[u8]) -> io::Result<usize> { Ok(buf.len()) } |
||||||
|
fn flush(&mut self) -> io::Result<()> { Ok(()) } |
||||||
|
} |
||||||
|
|
||||||
|
#[cfg(test)] |
||||||
|
mod tests { |
||||||
|
use io::prelude::*; |
||||||
|
use io::{copy, sink, empty, repeat}; |
||||||
|
|
||||||
|
#[test] |
||||||
|
fn copy_copies() { |
||||||
|
let mut r = repeat(0).take(4); |
||||||
|
let mut w = sink(); |
||||||
|
assert_eq!(copy(&mut r, &mut w).unwrap(), 4); |
||||||
|
|
||||||
|
let mut r = repeat(0).take(1 << 17); |
||||||
|
assert_eq!(copy(&mut r as &mut Read, &mut w as &mut Write).unwrap(), 1 << 17); |
||||||
|
} |
||||||
|
|
||||||
|
#[test] |
||||||
|
fn sink_sinks() { |
||||||
|
let mut s = sink(); |
||||||
|
assert_eq!(s.write(&[]).unwrap(), 0); |
||||||
|
assert_eq!(s.write(&[0]).unwrap(), 1); |
||||||
|
assert_eq!(s.write(&[0; 1024]).unwrap(), 1024); |
||||||
|
assert_eq!(s.by_ref().write(&[0; 1024]).unwrap(), 1024); |
||||||
|
} |
||||||
|
|
||||||
|
#[test] |
||||||
|
fn empty_reads() { |
||||||
|
let mut e = empty(); |
||||||
|
assert_eq!(e.read(&mut []).unwrap(), 0); |
||||||
|
assert_eq!(e.read(&mut [0]).unwrap(), 0); |
||||||
|
assert_eq!(e.read(&mut [0; 1024]).unwrap(), 0); |
||||||
|
assert_eq!(e.by_ref().read(&mut [0; 1024]).unwrap(), 0); |
||||||
|
} |
||||||
|
|
||||||
|
#[test] |
||||||
|
fn repeat_repeats() { |
||||||
|
let mut r = repeat(4); |
||||||
|
let mut b = [0; 1024]; |
||||||
|
assert_eq!(r.read(&mut b).unwrap(), 1024); |
||||||
|
assert!(b.iter().all(|b| *b == 4)); |
||||||
|
} |
||||||
|
|
||||||
|
#[test] |
||||||
|
fn take_some_bytes() { |
||||||
|
assert_eq!(repeat(4).take(100).bytes().count(), 100); |
||||||
|
assert_eq!(repeat(4).take(100).bytes().next().unwrap().unwrap(), 4); |
||||||
|
assert_eq!(repeat(1).take(10).chain(repeat(2).take(10)).bytes().count(), 20); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,78 @@ |
|||||||
|
use libctru::libc; |
||||||
|
use io::ErrorKind; |
||||||
|
use collections::{str, String}; |
||||||
|
use collections::borrow::ToOwned; |
||||||
|
use ffi::CStr; |
||||||
|
|
||||||
|
const TMPBUF_SZ: usize = 128; |
||||||
|
|
||||||
|
pub fn decode_error_kind(errno: i32) -> ErrorKind { |
||||||
|
match errno as libc::c_int { |
||||||
|
libc::ECONNREFUSED => ErrorKind::ConnectionRefused, |
||||||
|
libc::ECONNRESET => ErrorKind::ConnectionReset, |
||||||
|
libc::EPERM | libc::EACCES => ErrorKind::PermissionDenied, |
||||||
|
libc::EPIPE => ErrorKind::BrokenPipe, |
||||||
|
libc::ENOTCONN => ErrorKind::NotConnected, |
||||||
|
libc::ECONNABORTED => ErrorKind::ConnectionAborted, |
||||||
|
libc::EADDRNOTAVAIL => ErrorKind::AddrNotAvailable, |
||||||
|
libc::EADDRINUSE => ErrorKind::AddrInUse, |
||||||
|
libc::ENOENT => ErrorKind::NotFound, |
||||||
|
libc::EINTR => ErrorKind::Interrupted, |
||||||
|
libc::EINVAL => ErrorKind::InvalidInput, |
||||||
|
libc::ETIMEDOUT => ErrorKind::TimedOut, |
||||||
|
libc::EEXIST => ErrorKind::AlreadyExists, |
||||||
|
|
||||||
|
// These two constants can have the same value on some systems,
|
||||||
|
// but different values on others, so we can't use a match
|
||||||
|
// clause
|
||||||
|
x if x == libc::EAGAIN || x == libc::EWOULDBLOCK => ErrorKind::WouldBlock, |
||||||
|
|
||||||
|
_ => ErrorKind::Other, |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
extern "C" { |
||||||
|
#[cfg(not(target_os = "dragonfly"))] |
||||||
|
#[cfg_attr(any(target_os = "linux", target_os = "emscripten"),
|
||||||
|
link_name = "__errno_location")] |
||||||
|
#[cfg_attr(any(target_os = "bitrig",
|
||||||
|
target_os = "netbsd", |
||||||
|
target_os = "openbsd", |
||||||
|
target_os = "android", |
||||||
|
target_env = "newlib"), |
||||||
|
link_name = "__errno")] |
||||||
|
#[cfg_attr(target_os = "solaris", link_name = "___errno")] |
||||||
|
#[cfg_attr(any(target_os = "macos",
|
||||||
|
target_os = "ios", |
||||||
|
target_os = "freebsd"), |
||||||
|
link_name = "__error")] |
||||||
|
fn errno_location() -> *mut libc::c_int; |
||||||
|
} |
||||||
|
|
||||||
|
pub fn errno() -> i32 { |
||||||
|
unsafe { (*errno_location()) as i32 } |
||||||
|
} |
||||||
|
|
||||||
|
/// Gets a detailed string description for the given error number.
|
||||||
|
pub fn error_string(errno: i32) -> String { |
||||||
|
extern "C" { |
||||||
|
#[cfg_attr(any(target_os = "linux", target_env = "newlib"),
|
||||||
|
link_name = "__xpg_strerror_r")] |
||||||
|
fn strerror_r(errnum: libc::c_int, |
||||||
|
buf: *mut libc::c_char, |
||||||
|
buflen: libc::size_t) |
||||||
|
-> libc::c_int; |
||||||
|
} |
||||||
|
|
||||||
|
let mut buf = [0 as libc::c_char; TMPBUF_SZ]; |
||||||
|
|
||||||
|
let p = buf.as_mut_ptr(); |
||||||
|
unsafe { |
||||||
|
if strerror_r(errno as libc::c_int, p, buf.len() as libc::size_t) < 0 { |
||||||
|
panic!("strerror_r failure"); |
||||||
|
} |
||||||
|
|
||||||
|
let p = p as *const _; |
||||||
|
str::from_utf8(CStr::from_ptr(p).to_bytes()).unwrap().to_owned() |
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue