]> git.proxmox.com Git - proxmox.git/commitdiff
add proxmox-io crate
authorWolfgang Bumiller <w.bumiller@proxmox.com>
Wed, 6 Oct 2021 12:52:59 +0000 (14:52 +0200)
committerWolfgang Bumiller <w.bumiller@proxmox.com>
Mon, 11 Oct 2021 08:07:53 +0000 (10:07 +0200)
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
21 files changed:
Cargo.toml
Makefile
proxmox-io/Cargo.toml [new file with mode: 0644]
proxmox-io/debian/changelog [new file with mode: 0644]
proxmox-io/debian/control [new file with mode: 0644]
proxmox-io/debian/copyright [new file with mode: 0644]
proxmox-io/debian/debcargo.toml [new file with mode: 0644]
proxmox-io/src/byte_buffer.rs [new file with mode: 0644]
proxmox-io/src/lib.rs [new file with mode: 0644]
proxmox-io/src/read.rs [new file with mode: 0644]
proxmox-io/src/sparse_copy.rs [new file with mode: 0644]
proxmox-io/src/vec/byte_vec.rs [new file with mode: 0644]
proxmox-io/src/vec/mod.rs [new file with mode: 0644]
proxmox-io/src/write.rs [new file with mode: 0644]
proxmox/src/tools/byte_buffer.rs [deleted file]
proxmox/src/tools/io/mod.rs [deleted file]
proxmox/src/tools/io/read.rs [deleted file]
proxmox/src/tools/io/write.rs [deleted file]
proxmox/src/tools/mod.rs
proxmox/src/tools/vec/byte_vec.rs [deleted file]
proxmox/src/tools/vec/mod.rs [deleted file]

index 47e2c7b2bdca9a7a4ef7fa4533ae8ddaccba2bc2..e8666910b0b1f2838d7a8673f6e3c497ab0f6bd0 100644 (file)
@@ -4,6 +4,7 @@ members = [
     "proxmox-api-macro",
     "proxmox-borrow",
     "proxmox-http",
+    "proxmox-io",
     "proxmox-lang",
     "proxmox-sortable-macro",
     "proxmox-tfa",
index 205528b2242d72391ca3cf6aed338f220f6f7e6e..a29553ddc57c832a41dffbdad862837925c57e63 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -5,6 +5,7 @@ CRATES = \
         proxmox-api-macro \
         proxmox-borrow \
         proxmox-http \
+        proxmox-io \
         proxmox-lang \
         proxmox-sortable-macro \
         proxmox-tfa \
diff --git a/proxmox-io/Cargo.toml b/proxmox-io/Cargo.toml
new file mode 100644 (file)
index 0000000..55938dc
--- /dev/null
@@ -0,0 +1,14 @@
+[package]
+name = "proxmox-io"
+version = "1.0.0"
+authors = ["Proxmox Support Team <support@proxmox.com>"]
+edition = "2018"
+license = "AGPL-3"
+description = "extension traits for Read and Write"
+
+exclude = [ "debian" ]
+
+[dependencies]
+endian_trait = { version = "0.6", features = ["arrays"] }
+# this is also the feature name:
+tokio = { version = "1.0", features = [ "io-util" ], optional = true }
diff --git a/proxmox-io/debian/changelog b/proxmox-io/debian/changelog
new file mode 100644 (file)
index 0000000..06acaa9
--- /dev/null
@@ -0,0 +1,5 @@
+rust-proxmox-io (1.0.0-1) stable; urgency=medium
+
+  * initial split out of `librust-proxmox-dev`
+
+ -- Proxmox Support Team <support@proxmox.com>  Wed, 06 Oct 2021 11:04:36 +0200
diff --git a/proxmox-io/debian/control b/proxmox-io/debian/control
new file mode 100644 (file)
index 0000000..0923d0e
--- /dev/null
@@ -0,0 +1,52 @@
+Source: rust-proxmox-io
+Section: rust
+Priority: optional
+Build-Depends: debhelper (>= 12),
+ dh-cargo (>= 24),
+ cargo:native <!nocheck>,
+ rustc:native <!nocheck>,
+ libstd-rust-dev <!nocheck>,
+ librust-endian-trait-0.6+arrays-dev <!nocheck>,
+ librust-endian-trait-0.6+default-dev <!nocheck>
+Maintainer: Proxmox Support Team <support@proxmox.com>
+Standards-Version: 4.5.1
+Vcs-Git: git://git.proxmox.com/git/proxmox.git
+Vcs-Browser: https://git.proxmox.com/?p=proxmox.git
+Rules-Requires-Root: no
+
+Package: librust-proxmox-io-dev
+Architecture: any
+Multi-Arch: same
+Depends:
+ ${misc:Depends},
+ librust-endian-trait-0.6+arrays-dev,
+ librust-endian-trait-0.6+default-dev
+Suggests:
+ librust-proxmox-io+tokio-dev (= ${binary:Version})
+Provides:
+ librust-proxmox-io+default-dev (= ${binary:Version}),
+ librust-proxmox-io-1-dev (= ${binary:Version}),
+ librust-proxmox-io-1+default-dev (= ${binary:Version}),
+ librust-proxmox-io-1.0-dev (= ${binary:Version}),
+ librust-proxmox-io-1.0+default-dev (= ${binary:Version}),
+ librust-proxmox-io-1.0.0-dev (= ${binary:Version}),
+ librust-proxmox-io-1.0.0+default-dev (= ${binary:Version})
+Description: Extension traits for Read and Write - Rust source code
+ This package contains the source for the Rust proxmox-io crate, packaged by
+ debcargo for use with cargo and dh-cargo.
+
+Package: librust-proxmox-io+tokio-dev
+Architecture: any
+Multi-Arch: same
+Depends:
+ ${misc:Depends},
+ librust-proxmox-io-dev (= ${binary:Version}),
+ librust-tokio-1+default-dev,
+ librust-tokio-1+io-util-dev
+Provides:
+ librust-proxmox-io-1+tokio-dev (= ${binary:Version}),
+ librust-proxmox-io-1.0+tokio-dev (= ${binary:Version}),
+ librust-proxmox-io-1.0.0+tokio-dev (= ${binary:Version})
+Description: Extension traits for Read and Write - feature "tokio"
+ This metapackage enables feature "tokio" for the Rust proxmox-io crate, by
+ pulling in any additional dependencies needed by that feature.
diff --git a/proxmox-io/debian/copyright b/proxmox-io/debian/copyright
new file mode 100644 (file)
index 0000000..5661ef6
--- /dev/null
@@ -0,0 +1,16 @@
+Copyright (C) 2021 Proxmox Server Solutions GmbH
+
+This software is written by Proxmox Server Solutions GmbH <support@proxmox.com>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU Affero General Public License for more details.
+
+You should have received a copy of the GNU Affero General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
diff --git a/proxmox-io/debian/debcargo.toml b/proxmox-io/debian/debcargo.toml
new file mode 100644 (file)
index 0000000..b7864cd
--- /dev/null
@@ -0,0 +1,7 @@
+overlay = "."
+crate_src_path = ".."
+maintainer = "Proxmox Support Team <support@proxmox.com>"
+
+[source]
+vcs_git = "git://git.proxmox.com/git/proxmox.git"
+vcs_browser = "https://git.proxmox.com/?p=proxmox.git"
diff --git a/proxmox-io/src/byte_buffer.rs b/proxmox-io/src/byte_buffer.rs
new file mode 100644 (file)
index 0000000..5e94414
--- /dev/null
@@ -0,0 +1,227 @@
+//! ByteBuffer
+//!
+//! a simple buffer for u8 with a practical api for reading and appending
+//! and consuming from the front
+//! Example:
+//! ```
+//! # use std::io::Read;
+//! # use proxmox_io::ByteBuffer;
+//! fn code<T: Read + ?Sized>(input: &mut T) -> std::io::Result<Box<[u8]>> {
+//!     let mut buffer = ByteBuffer::new();
+//!     let amount = buffer.read_from(input)?;
+//!     let data = buffer.remove_data(amount);
+//!     assert_eq!(data.len(), amount);
+//!     Ok(data)
+//! }
+//! # code(&mut &b"testdata"[..]).expect("byte buffer test failed");
+//! ```
+
+use std::io::{Read, Result};
+
+use crate::vec;
+
+#[cfg(feature = "tokio")]
+use tokio::io::{AsyncRead, AsyncReadExt};
+
+/// A Buffer that holds bytes (u8)
+/// has convenience methods for reading (with any reader that
+/// implements std::io::Read or tokio::io::AsyncRead) onto the back
+/// and consuming from the front
+pub struct ByteBuffer {
+    buf: Box<[u8]>,
+    data_size: usize,
+    capacity: usize,
+}
+
+impl ByteBuffer {
+    /// Creates a new instance with a default capacity of 4096
+    pub fn new() -> Self {
+        Self::with_capacity(4096)
+    }
+
+    pub fn with_capacity(capacity: usize) -> Self {
+        Self {
+            buf: vec::undefined(capacity).into_boxed_slice(),
+            data_size: 0,
+            capacity,
+        }
+    }
+
+    pub fn free_size(&self) -> usize {
+        self.capacity - self.data_size
+    }
+
+    pub fn is_full(&self) -> bool {
+        self.data_size >= self.capacity
+    }
+
+    pub fn clear(&mut self) {
+        self.data_size = 0
+    }
+
+    /// Sets the length of the data. Useful if data was manually added
+    /// with a mutable slice (e.g. from [get_free_mut_slice](#method.get_free_mut_slice)).
+    ///
+    /// Panics when new size would be greater than capacity.
+    ///
+    /// Example:
+    /// ```
+    /// # use proxmox_io::ByteBuffer;
+    /// let mut buf = ByteBuffer::new();
+    /// buf.get_free_mut_slice()[..1].copy_from_slice(&[1u8]);
+    /// buf.add_size(1);
+    /// ```
+    ///
+    /// This code will panic:
+    /// ```should_panic
+    /// # use proxmox_io::ByteBuffer;
+    /// let mut buf = ByteBuffer::with_capacity(128);
+    /// buf.add_size(256);
+    /// ```
+    pub fn add_size(&mut self, size: usize) {
+        if self.data_size + size > self.capacity {
+            panic!("size bigger than capacity!");
+        }
+        self.data_size += size;
+    }
+
+    /// Returns a mutable reference to the free section of the
+    /// Buffer. There are no guarantees about the content of the
+    /// free part of the Buffer (may even be uninitialized).
+    /// see [add_size](#method.add_size) for a usage example.
+    pub fn get_free_mut_slice(&mut self) -> &mut [u8] {
+        &mut self.buf[self.data_size..self.capacity]
+    }
+
+    /// Removes up to max_amount of data from the front
+    /// of the buffer and returns. If there was less than max_amount present,
+    /// it will return as much data as there was in the buffer.
+    /// The rest of the data will be moved to the front, and
+    /// the data size will be updated accordingly.
+    ///
+    /// Example:
+    /// ```
+    /// # use proxmox_io::ByteBuffer;
+    /// let mut buf = ByteBuffer::new();
+    /// buf.get_free_mut_slice()[..2].copy_from_slice(&[1u8, 2u8]);
+    /// buf.add_size(2);
+    /// assert_eq!(buf.len(), 2);
+    ///
+    /// let data = buf.remove_data(100);
+    /// assert_eq!(&data[..], &[1u8, 2u8]);
+    /// assert!(buf.is_empty());
+    /// ```
+    #[must_use]
+    pub fn remove_data(&mut self, max_amount: usize) -> Box<[u8]> {
+        let size = max_amount.min(self.data_size);
+        let tmp: Box<[u8]> = self.buf[..size].into();
+        self.buf.copy_within(size..self.capacity, 0);
+        self.data_size -= size;
+        tmp
+    }
+
+    /// Removes up to max_amount of data from the front and returns
+    /// the amount of data removed. If there was less than max_amount present,
+    /// it will empty out the buffer and return the amount removed.
+    ///
+    /// Example:
+    /// ```
+    /// # use proxmox_io::ByteBuffer;
+    /// let mut buf = ByteBuffer::new();
+    /// buf.get_free_mut_slice()[..2].copy_from_slice(&[1u8, 2u8]);
+    /// buf.add_size(2);
+    /// assert_eq!(buf.len(), 2);
+    ///
+    /// let amount = buf.consume(1);
+    /// assert_eq!(amount, 1);
+    /// let amount = buf.consume(100);
+    /// assert_eq!(amount, 1);
+    /// assert!(buf.is_empty());
+    /// ```
+    pub fn consume(&mut self, max_amount: usize) -> usize {
+        let size = max_amount.min(self.data_size);
+        if size < max_amount {
+            self.clear()
+        } else {
+            self.buf.copy_within(size..self.capacity, 0);
+            self.data_size -= size;
+        }
+        size
+    }
+
+    /// Takes a reader and reads into the back of the buffer (up to the
+    /// free space in the buffer) and updates its size accordingly.
+    ///
+    /// Example:
+    /// ```
+    /// # use std::io;
+    /// # use proxmox_io::ByteBuffer;
+    /// fn code<R: io::Read>(mut reader: R) -> io::Result<()> {
+    ///     let mut buf = ByteBuffer::new();
+    ///     let res = buf.read_from(&mut reader)?;
+    ///     // do something with the buffer
+    ///     Ok(())
+    /// }
+    /// ```
+    pub fn read_from<T: Read + ?Sized>(&mut self, input: &mut T) -> Result<usize> {
+        let amount = input.read(self.get_free_mut_slice())?;
+        self.add_size(amount);
+        Ok(amount)
+    }
+
+    /// Same as read_from, but for reader that implement tokio::io::AsyncRead.
+    /// See [read_from](#method.read_from) for an example
+    #[cfg(feature = "tokio")]
+    pub async fn read_from_async<T: AsyncRead + Unpin>(&mut self, input: &mut T) -> Result<usize> {
+        let amount = input.read(self.get_free_mut_slice()).await?;
+        self.add_size(amount);
+        Ok(amount)
+    }
+}
+
+impl Default for ByteBuffer {
+    fn default() -> Self {
+        Self::new()
+    }
+}
+
+impl std::ops::Deref for ByteBuffer {
+    type Target = [u8];
+
+    fn deref(&self) -> &Self::Target {
+        &self.buf[..self.data_size]
+    }
+}
+
+impl std::ops::DerefMut for ByteBuffer {
+    fn deref_mut(&mut self) -> &mut Self::Target {
+        &mut self.buf[..self.data_size]
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use super::ByteBuffer;
+
+    #[test]
+    fn test1() {
+        let mut buffer = ByteBuffer::new();
+        let slice = buffer.get_free_mut_slice();
+        for i in 0..slice.len() {
+            slice[i] = (i % 255) as u8;
+        }
+        buffer.add_size(5);
+
+        let slice2 = &buffer[..];
+
+        assert_eq!(slice2, &[0, 1, 2, 3, 4]);
+    }
+
+    #[test]
+    fn test2() {
+        let mut buffer = ByteBuffer::with_capacity(1024);
+        let size = buffer.read_from(&mut std::io::repeat(54)).unwrap();
+        assert_eq!(buffer.len(), size);
+        assert_eq!(buffer[0], 54);
+    }
+}
diff --git a/proxmox-io/src/lib.rs b/proxmox-io/src/lib.rs
new file mode 100644 (file)
index 0000000..7e3e138
--- /dev/null
@@ -0,0 +1,21 @@
+//! Module providing I/O helpers (sync and async).
+//!
+//! The [`ReadExt`] trait provides additional operations for handling byte buffers for types
+//! implementing [`Read`](std::io::Read).
+
+mod read;
+pub use read::ReadExt;
+
+mod write;
+pub use write::WriteExt;
+
+mod sparse_copy;
+pub use sparse_copy::{buffer_is_zero, sparse_copy, SparseCopyResult};
+
+#[cfg(feature = "tokio")]
+pub use sparse_copy::sparse_copy_async;
+
+mod byte_buffer;
+pub use byte_buffer::ByteBuffer;
+
+pub mod vec;
diff --git a/proxmox-io/src/read.rs b/proxmox-io/src/read.rs
new file mode 100644 (file)
index 0000000..d24a32f
--- /dev/null
@@ -0,0 +1,335 @@
+//! Helpers for `Read`.
+
+use std::io;
+use std::mem;
+
+use endian_trait::Endian;
+
+use crate::vec::{self, ByteVecExt};
+
+/// Adds some additional related functionality for types implementing [`Read`](std::io::Read).
+///
+/// Particularly for reading into a newly allocated buffer, appending to a `Vec<u8>` or reading
+/// values of a specific endianess (types implementing [`Endian`]).
+///
+/// Examples:
+/// ```no_run
+/// use proxmox_io::ReadExt;
+///
+/// # fn code() -> std::io::Result<()> {
+/// let mut file = std::fs::File::open("some.data")?;
+///
+/// // read some bytes into a newly allocated Vec<u8>:
+/// let mut data = file.read_exact_allocated(64)?;
+///
+/// // appending data to a vector:
+/// let actually_appended = file.append_to_vec(&mut data, 64)?; // .read() version
+/// file.append_exact_to_vec(&mut data, 64)?; // .read_exact() version
+/// # Ok(())
+/// # }
+/// ```
+///
+/// Or for reading values of a defined representation and endianess:
+///
+/// ```no_run
+/// # use endian_trait::Endian;
+/// # use proxmox_io::ReadExt;
+///
+/// #[derive(Endian)]
+/// #[repr(C)]
+/// struct Header {
+///     version: u16,
+///     data_size: u16,
+/// }
+///
+/// # fn code(mut file: std::fs::File) -> std::io::Result<()> {
+/// // We have given `Header` a proper binary representation via `#[repr]`, so this is safe:
+/// let header: Header = unsafe { file.read_le_value()? };
+/// let mut blob = file.read_exact_allocated(header.data_size as usize)?;
+/// # Ok(())
+/// # }
+/// ```
+///
+/// [`Endian`]: https://docs.rs/endian_trait/0.6/endian_trait/trait.Endian.html
+pub trait ReadExt {
+    /// Read data into a newly allocated vector. This is a shortcut for:
+    /// ```ignore
+    /// let mut data = Vec::with_capacity(len);
+    /// unsafe {
+    ///     data.set_len(len);
+    /// }
+    /// reader.read_exact(&mut data)?;
+    /// ```
+    ///
+    /// With this trait, we just use:
+    /// ```no_run
+    /// use proxmox_io::ReadExt;
+    /// # fn code(mut reader: std::fs::File, len: usize) -> std::io::Result<()> {
+    /// let data = reader.read_exact_allocated(len)?;
+    /// # Ok(())
+    /// # }
+    /// ```
+    fn read_exact_allocated(&mut self, size: usize) -> io::Result<Vec<u8>>;
+
+    /// Append data to a vector, growing it as necessary. Returns the amount of data appended.
+    fn append_to_vec(&mut self, out: &mut Vec<u8>, size: usize) -> io::Result<usize>;
+
+    /// Append an exact amount of data to a vector, growing it as necessary.
+    fn append_exact_to_vec(&mut self, out: &mut Vec<u8>, size: usize) -> io::Result<()>;
+
+    /// Read a value with host endianess.
+    ///
+    /// This is limited to types implementing the [`Endian`] trait under the assumption that
+    /// this is only done for types which are supposed to be read/writable directly.
+    ///
+    /// There's no way to directly depend on a type having a specific `#[repr(...)]`, therefore
+    /// this is considered unsafe.
+    ///
+    /// ```no_run
+    /// # use endian_trait::Endian;
+    /// use proxmox_io::ReadExt;
+    ///
+    /// #[derive(Endian)]
+    /// #[repr(C, packed)]
+    /// struct Data {
+    ///     value: u16,
+    ///     count: u32,
+    /// }
+    ///
+    /// # fn code() -> std::io::Result<()> {
+    /// let mut file = std::fs::File::open("my-raw.dat")?;
+    /// // We know `Data` has a safe binary representation (#[repr(C, packed)]), so we can
+    /// // safely use our helper:
+    /// let data: Data = unsafe { file.read_host_value()? };
+    /// # Ok(())
+    /// # }
+    /// ```
+    ///
+    /// # Safety
+    ///
+    /// This should only used for types with a defined storage representation, usually
+    /// `#[repr(C)]`, otherwise the results may be inconsistent.
+    ///
+    /// [`Endian`]: https://docs.rs/endian_trait/0.6/endian_trait/trait.Endian.html
+    unsafe fn read_host_value<T: Endian>(&mut self) -> io::Result<T>;
+
+    /// Read a little endian value.
+    ///
+    /// The return type is required to implement the [`Endian`] trait, and we make the
+    /// assumption that this is only done for types which are supposed to be read/writable
+    /// directly.
+    ///
+    /// There's no way to directly depend on a type having a specific `#[repr(...)]`, therefore
+    /// this is considered unsafe.
+    ///
+    /// ```no_run
+    /// # use endian_trait::Endian;
+    /// use proxmox_io::ReadExt;
+    ///
+    /// #[derive(Endian)]
+    /// #[repr(C, packed)]
+    /// struct Data {
+    ///     value: u16,
+    ///     count: u32,
+    /// }
+    ///
+    /// # fn code() -> std::io::Result<()> {
+    /// let mut file = std::fs::File::open("my-little-endian.dat")?;
+    /// // We know `Data` has a safe binary representation (#[repr(C, packed)]), so we can
+    /// // safely use our helper:
+    /// let data: Data = unsafe { file.read_le_value()? };
+    /// # Ok(())
+    /// # }
+    /// ```
+    ///
+    /// # Safety
+    ///
+    /// This should only used for types with a defined storage representation, usually
+    /// `#[repr(C)]`, otherwise the results may be inconsistent.
+    ///
+    /// [`Endian`]: https://docs.rs/endian_trait/0.6/endian_trait/trait.Endian.html
+    unsafe fn read_le_value<T: Endian>(&mut self) -> io::Result<T>;
+
+    /// Read a big endian value.
+    ///
+    /// The return type is required to implement the [`Endian`] trait, and we make the
+    /// assumption that this is only done for types which are supposed to be read/writable
+    /// directly.
+    ///
+    /// There's no way to directly depend on a type having a specific `#[repr(...)]`, therefore
+    /// this is considered unsafe.
+    ///
+    /// ```no_run
+    /// # use endian_trait::Endian;
+    /// use proxmox_io::ReadExt;
+    ///
+    /// #[derive(Endian)]
+    /// #[repr(C, packed)]
+    /// struct Data {
+    ///     value: u16,
+    ///     count: u32,
+    /// }
+    ///
+    /// # fn code() -> std::io::Result<()> {
+    /// let mut file = std::fs::File::open("my-big-endian.dat")?;
+    /// // We know `Data` has a safe binary representation (#[repr(C, packed)]), so we can
+    /// // safely use our helper:
+    /// let data: Data = unsafe { file.read_be_value()? };
+    /// # Ok(())
+    /// # }
+    /// ```
+    ///
+    /// # Safety
+    ///
+    /// This should only used for types with a defined storage representation, usually
+    /// `#[repr(C)]`, otherwise the results may be inconsistent.
+    ///
+    /// [`Endian`]: https://docs.rs/endian_trait/0.6/endian_trait/trait.Endian.html
+    unsafe fn read_be_value<T: Endian>(&mut self) -> io::Result<T>;
+
+    /// Read a boxed value with host endianess.
+    ///
+    /// This is currently not limited to types implementing the [`Endian`] trait as in our use
+    /// cases we use this for types which are too big to want to always perform endian swaps
+    /// immediately on all values.
+    ///
+    /// ```
+    /// # use proxmox_io::vec;
+    /// use proxmox_io::ReadExt;
+    ///
+    /// #[repr(C)]
+    /// struct Data {
+    ///     v1: u64,
+    ///     buf: [u8; 4088],
+    /// }
+    ///
+    /// # let mut input = [0u8; 4096];
+    /// # use proxmox_io::WriteExt;
+    /// # let mut writer = &mut input[..];
+    /// # unsafe { writer.write_host_value(32u64).unwrap() };
+    /// # let mut file = &input[..];
+    ///
+    /// # fn code<T: std::io::Read>(mut file: T) -> std::io::Result<()> {
+    /// let data: Box<Data> = unsafe { file.read_host_value_boxed()? };
+    /// assert_eq!(data.v1, 32);
+    /// # Ok(())
+    /// # }
+    /// # code(&input[..]).unwrap();
+    /// ```
+    ///
+    /// # Safety
+    ///
+    /// This should only used for types with a defined storage representation, usually
+    /// `#[repr(C)]`, otherwise the results may be inconsistent.
+    unsafe fn read_host_value_boxed<T>(&mut self) -> io::Result<Box<T>>;
+
+    /// Try to read the exact number of bytes required to fill buf.
+    ///
+    /// This function reads as many bytes as necessary to completely
+    /// fill the specified buffer buf. If this function encounters an
+    /// "end of file" before getting any data, it returns Ok(false).
+    /// If there is some data, but not enough, it return an error of
+    /// the kind ErrorKind::UnexpectedEof. The contents of buf are
+    /// unspecified in this case.
+    fn read_exact_or_eof(&mut self, buf: &mut [u8]) -> io::Result<bool>;
+
+    /// Read until EOF
+    fn skip_to_end(&mut self) -> io::Result<usize>;
+}
+
+impl<R: io::Read> ReadExt for R {
+    fn read_exact_allocated(&mut self, size: usize) -> io::Result<Vec<u8>> {
+        let mut out = unsafe { vec::uninitialized(size) };
+        self.read_exact(&mut out)?;
+        Ok(out)
+    }
+
+    fn append_to_vec(&mut self, out: &mut Vec<u8>, size: usize) -> io::Result<usize> {
+        let pos = out.len();
+        unsafe {
+            out.grow_uninitialized(size);
+        }
+        let got = self.read(&mut out[pos..])?;
+        unsafe {
+            out.set_len(pos + got);
+        }
+        Ok(got)
+    }
+
+    fn append_exact_to_vec(&mut self, out: &mut Vec<u8>, size: usize) -> io::Result<()> {
+        let pos = out.len();
+        unsafe {
+            out.grow_uninitialized(size);
+        }
+        self.read_exact(&mut out[pos..])?;
+        Ok(())
+    }
+
+    unsafe fn read_host_value<T: Endian>(&mut self) -> io::Result<T> {
+        let mut value = std::mem::MaybeUninit::<T>::uninit();
+        self.read_exact(std::slice::from_raw_parts_mut(
+            value.as_mut_ptr() as *mut u8,
+            mem::size_of::<T>(),
+        ))?;
+        Ok(value.assume_init())
+    }
+
+    unsafe fn read_le_value<T: Endian>(&mut self) -> io::Result<T> {
+        Ok(self.read_host_value::<T>()?.from_le())
+    }
+
+    unsafe fn read_be_value<T: Endian>(&mut self) -> io::Result<T> {
+        Ok(self.read_host_value::<T>()?.from_be())
+    }
+
+    unsafe fn read_host_value_boxed<T>(&mut self) -> io::Result<Box<T>> {
+        // FIXME: Change this once #![feature(new_uninit)] lands for Box<T>!
+
+        let ptr = std::alloc::alloc(std::alloc::Layout::new::<T>()) as *mut T;
+        self.read_exact(std::slice::from_raw_parts_mut(
+            ptr as *mut u8,
+            mem::size_of::<T>(),
+        ))?;
+        Ok(Box::from_raw(ptr))
+    }
+
+    fn read_exact_or_eof(&mut self, mut buf: &mut [u8]) -> io::Result<bool> {
+        let mut read_bytes = 0;
+        loop {
+            match self.read(&mut buf) {
+                Ok(0) => {
+                    if read_bytes == 0 {
+                        return Ok(false);
+                    }
+                    return Err(io::Error::new(
+                        io::ErrorKind::UnexpectedEof,
+                        "failed to fill whole buffer",
+                    ));
+                }
+                Ok(n) => {
+                    let tmp = buf;
+                    buf = &mut tmp[n..];
+                    read_bytes += n;
+                    if buf.is_empty() {
+                        return Ok(true);
+                    }
+                }
+                Err(ref e) if e.kind() == std::io::ErrorKind::Interrupted => {}
+                Err(e) => return Err(e),
+            }
+        }
+    }
+
+    fn skip_to_end(&mut self) -> io::Result<usize> {
+        let mut skipped_bytes = 0;
+        let mut buf = unsafe { vec::uninitialized(32 * 1024) };
+        loop {
+            match self.read(&mut buf) {
+                Ok(0) => return Ok(skipped_bytes),
+                Ok(n) => skipped_bytes += n,
+                Err(ref e) if e.kind() == std::io::ErrorKind::Interrupted => {}
+                Err(e) => return Err(e),
+            }
+        }
+    }
+}
diff --git a/proxmox-io/src/sparse_copy.rs b/proxmox-io/src/sparse_copy.rs
new file mode 100644 (file)
index 0000000..60c87ea
--- /dev/null
@@ -0,0 +1,264 @@
+use std::io::{self, ErrorKind, Read, Seek, SeekFrom, Write};
+
+#[cfg(feature = "tokio")]
+use tokio::io::{AsyncRead, AsyncSeek, AsyncSeekExt, AsyncWrite, AsyncWriteExt};
+
+/// Efficiently check whether a byte slice contains only zeroes.
+///
+/// This is implemented in a way which allows the compiler to utilize SIMD instructions.
+pub fn buffer_is_zero(buf: &[u8]) -> bool {
+    !buf.chunks(128)
+        .map(|aa| aa.iter().fold(0, |a, b| a | b) != 0)
+        .any(|a| a)
+}
+
+/// Result of a sparse copy call.
+///
+/// Contains the amount of written/seeked bytes and if the last operation was a seek.
+#[must_use = "if sparse_copy ended with a seek(), the output file's size may need to be adjusted"]
+pub struct SparseCopyResult {
+    pub written: u64,
+    pub seeked_last: bool,
+}
+
+/// Copy similar to [`io::copy`](std::io::copy), but seeks the target when encountering zero bytes
+/// instead of writing them.
+///
+/// Example use:
+/// ```
+/// # use std::io;
+/// # use proxmox_io::sparse_copy;
+/// fn code<R, W>(mut reader: R, mut writer: W) -> io::Result<()>
+/// where
+///     R: io::Read,
+///     W: io::Write + io::Seek,
+/// {
+///     let res = sparse_copy(&mut reader, &mut writer)?;
+///
+///     println!("last part was seeked: {}", res.seeked_last);
+///     println!("written: {}", res.written);
+///
+///     Ok(())
+/// }
+/// ```
+pub fn sparse_copy<R: Read + ?Sized, W: Write + Seek + ?Sized>(
+    reader: &mut R,
+    writer: &mut W,
+) -> Result<SparseCopyResult, io::Error> {
+    let mut buf = crate::byte_buffer::ByteBuffer::with_capacity(4096);
+    let mut written = 0;
+    let mut seek_amount: i64 = 0;
+    let mut seeked_last = false;
+    loop {
+        buf.clear();
+        let len = match buf.read_from(reader) {
+            Ok(len) => len,
+            Err(ref e) if e.kind() == ErrorKind::Interrupted => continue,
+            Err(e) => return Err(e),
+        };
+
+        if len > 0 && buffer_is_zero(&buf[..]) {
+            seek_amount += len as i64;
+            continue;
+        }
+
+        if seek_amount > 0 {
+            writer.seek(SeekFrom::Current(seek_amount))?;
+            written += seek_amount as u64;
+            seek_amount = 0;
+            seeked_last = true;
+        }
+
+        if len > 0 {
+            writer.write_all(&buf[..])?;
+            written += len as u64;
+            seeked_last = false;
+        } else {
+            return Ok(SparseCopyResult {
+                written,
+                seeked_last,
+            });
+        }
+    }
+}
+
+/// copy similar to tokio::io::copy, but seeks the target when encountering
+/// zero bytes instead of writing them
+///
+/// Example:
+/// ```no_run
+/// # use std::io;
+/// # use tokio::io::{AsyncRead, AsyncWrite, AsyncSeek};
+/// # use proxmox_io::sparse_copy_async;
+/// async fn code<R, W>(mut reader: R, mut writer: W) -> io::Result<()>
+/// where
+///     R: AsyncRead + Unpin,
+///     W: AsyncWrite + AsyncSeek + Unpin,
+/// {
+///     let res = sparse_copy_async(&mut reader, &mut writer).await?;
+///
+///     println!("last part was seeked: {}", res.seeked_last);
+///     println!("written: {}", res.written);
+///
+///     Ok(())
+/// }
+/// ```
+#[cfg(feature = "tokio")]
+pub async fn sparse_copy_async<R, W>(
+    reader: &mut R,
+    writer: &mut W,
+) -> Result<SparseCopyResult, io::Error>
+where
+    R: AsyncRead + Unpin,
+    W: AsyncWrite + AsyncSeek + Unpin,
+{
+    let mut buf = crate::byte_buffer::ByteBuffer::with_capacity(4096);
+    let mut written = 0;
+    let mut seek_amount: i64 = 0;
+    let mut seeked_last = false;
+    loop {
+        buf.clear();
+        let len = match buf.read_from_async(reader).await {
+            Ok(len) => len,
+            Err(ref e) if e.kind() == ErrorKind::Interrupted => continue,
+            Err(e) => return Err(e),
+        };
+
+        if len > 0 && buffer_is_zero(&buf[..]) {
+            seek_amount += len as i64;
+            continue;
+        }
+
+        if seek_amount > 0 {
+            writer.seek(SeekFrom::Current(seek_amount)).await?;
+            written += seek_amount as u64;
+            seek_amount = 0;
+            seeked_last = true;
+        }
+
+        if len > 0 {
+            writer.write_all(&buf[..]).await?;
+            written += len as u64;
+            seeked_last = false;
+        } else {
+            return Ok(SparseCopyResult {
+                written,
+                seeked_last,
+            });
+        }
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use std::io::Cursor;
+
+    use super::sparse_copy;
+
+    const LEN: usize = 10000;
+
+    #[test]
+    fn test_sparse_copy() {
+        // test sparse
+        let mut test_data = Vec::new();
+        for _ in 0..LEN / 2 {
+            test_data.push(1u8);
+        }
+        for _ in 0..LEN / 2 {
+            test_data.push(0u8);
+        }
+        let mut test_data = Cursor::new(test_data);
+        let mut result_data = Cursor::new(vec![0; LEN]);
+
+        let result =
+            sparse_copy(&mut test_data, &mut result_data).expect("error during sparse copy");
+        assert_eq!(result.written, LEN as u64);
+        assert_eq!(result.seeked_last, true);
+        for i in 0..LEN {
+            if i < LEN / 2 {
+                assert_eq!(result_data.get_ref()[i], 1);
+            } else {
+                assert_eq!(result_data.get_ref()[i], 0);
+            }
+        }
+
+        // test non sparse
+        let mut test_data = Cursor::new(vec![1; LEN]);
+        let mut result_data = Cursor::new(vec![0; LEN]);
+
+        let result =
+            sparse_copy(&mut test_data, &mut result_data).expect("error during sparse copy");
+        assert_eq!(result.written, LEN as u64);
+        assert_eq!(result.seeked_last, false);
+        for i in 0..LEN {
+            assert_eq!(result_data.get_ref()[i], 1);
+        }
+    }
+
+    #[cfg(feature = "tokio")]
+    #[test]
+    fn test_sparse_copy_async() {
+        use std::future::Future;
+        use std::sync::Arc;
+        use std::task::{Context, Poll};
+
+        use super::sparse_copy_async;
+
+        struct PanicWaker;
+        impl std::task::Wake for PanicWaker {
+            fn wake(self: Arc<Self>) {
+                panic!("this test should not block");
+            }
+        }
+
+        let mut fut = async {
+            // test sparse
+            let mut test_data = Vec::new();
+            for _ in 0..LEN / 2 {
+                test_data.push(1u8);
+            }
+            for _ in 0..LEN / 2 {
+                test_data.push(0u8);
+            }
+            let mut test_data = Cursor::new(test_data);
+            let mut result_data = Cursor::new(vec![0; LEN]);
+
+            let result = sparse_copy_async(&mut test_data, &mut result_data)
+                .await
+                .expect("error during sparse copy");
+
+            assert_eq!(result.written, LEN as u64);
+            assert_eq!(result.seeked_last, true);
+            for i in 0..LEN {
+                if i < LEN / 2 {
+                    assert_eq!(result_data.get_ref()[i], 1);
+                } else {
+                    assert_eq!(result_data.get_ref()[i], 0);
+                }
+            }
+
+            // test non sparse
+            let mut test_data = Cursor::new(vec![1; LEN]);
+            let mut result_data = Cursor::new(vec![0; LEN]);
+
+            let result = sparse_copy_async(&mut test_data, &mut result_data)
+                .await
+                .expect("error during sparse copy");
+
+            assert_eq!(result.written, LEN as u64);
+            assert_eq!(result.seeked_last, false);
+            for i in 0..LEN {
+                assert_eq!(result_data.get_ref()[i], 1);
+            }
+            Ok::<(), std::io::Error>(())
+        };
+
+        let fut = unsafe { std::pin::Pin::new_unchecked(&mut fut) };
+        let waker = std::task::Waker::from(Arc::new(PanicWaker));
+        let mut context = Context::from_waker(&waker);
+        match fut.poll(&mut context) {
+            Poll::Ready(res) => res.expect("expected ok"),
+            Poll::Pending => panic!("yielded on blocking implementation"),
+        }
+    }
+}
diff --git a/proxmox-io/src/vec/byte_vec.rs b/proxmox-io/src/vec/byte_vec.rs
new file mode 100644 (file)
index 0000000..0c0c899
--- /dev/null
@@ -0,0 +1,111 @@
+//! This module provides additional operations for `Vec<u8>`.
+//!
+//! Example:
+//! ```
+//! # use std::io::Read;
+//! use proxmox_io::vec::{self, ByteVecExt};
+//!
+//! fn append_1024_to_vec<T: Read>(mut input: T, buffer: &mut Vec<u8>) -> std::io::Result<()> {
+//!     input.read_exact(unsafe { buffer.grow_uninitialized(1024) })
+//! }
+//! ```
+
+/// Some additional byte vector operations useful for I/O code.
+/// Example:
+/// ```
+/// # use std::io::Read;
+/// # use proxmox_io::ReadExt;
+/// use proxmox_io::vec::{self, ByteVecExt};
+///
+/// # fn code(mut file: std::fs::File, mut data: Vec<u8>) -> std::io::Result<()> {
+/// file.read_exact(unsafe {
+///     data.grow_uninitialized(1024)
+/// })?;
+/// # Ok(())
+/// # }
+/// ```
+///
+/// Note that this module also provides a safe alternative for the case where
+/// `grow_uninitialized()` is directly followed by a `read_exact()` call via the [`ReadExt`]
+/// trait:
+/// ```ignore
+/// file.append_to_vec(&mut data, 1024)?;
+/// ```
+///
+/// [`ReadExt`]: crate::ReadExt
+pub trait ByteVecExt {
+    /// Grow a vector without initializing its elements. The difference to simply using `reserve`
+    /// is that it also updates the actual length, making the newly allocated data part of the
+    /// slice.
+    ///
+    /// This is a shortcut for:
+    /// ```ignore
+    /// vec.reserve(more);
+    /// let total = vec.len() + more;
+    /// unsafe {
+    ///     vec.set_len(total);
+    /// }
+    /// ```
+    ///
+    /// This returns a mutable slice to the newly allocated space, so it can be used inline:
+    /// ```
+    /// # use std::io::Read;
+    /// # use proxmox_io::vec::ByteVecExt;
+    /// # fn test(mut file: std::fs::File, buffer: &mut Vec<u8>) -> std::io::Result<()> {
+    /// file.read_exact(unsafe { buffer.grow_uninitialized(1024) })?;
+    /// # Ok(())
+    /// # }
+    /// ```
+    ///
+    /// Although for the above case it is recommended to use the even shorter version from the
+    /// [`ReadExt`] trait:
+    /// ```ignore
+    /// // use crate::tools::vec::ByteVecExt;
+    /// file.append_to_vec(&mut buffer, 1024)?;
+    /// ```
+    ///
+    /// # Safety
+    ///
+    /// When increasing the size, the new contents are uninitialized and have nothing to do with
+    /// the previously contained content. Since we cannot track this state through the type system,
+    /// this method is marked as an unsafe API for good measure.
+    ///
+    /// [`ReadExt`]: crate::ReadExt
+    unsafe fn grow_uninitialized(&mut self, more: usize) -> &mut [u8];
+
+    /// Resize a vector to a specific size without initializing its data. This is a shortcut for:
+    /// ```ignore
+    /// if new_size <= vec.len() {
+    ///     vec.truncate(new_size);
+    /// } else {
+    ///     unsafe {
+    ///         vec.grow_uninitialized(new_size - vec.len());
+    ///     }
+    /// }
+    /// ```
+    ///
+    /// # Safety
+    ///
+    /// When increasing the size, the new contents are uninitialized and have nothing to do with
+    /// the previously contained content. Since we cannot track this state through the type system,
+    /// this method is marked as an unsafe API for good measure.
+    unsafe fn resize_uninitialized(&mut self, total: usize);
+}
+
+impl ByteVecExt for Vec<u8> {
+    unsafe fn grow_uninitialized(&mut self, more: usize) -> &mut [u8] {
+        let old_len = self.len();
+        self.reserve(more);
+        let total = old_len + more;
+        self.set_len(total);
+        &mut self[old_len..]
+    }
+
+    unsafe fn resize_uninitialized(&mut self, new_size: usize) {
+        if new_size <= self.len() {
+            self.truncate(new_size);
+        } else {
+            self.grow_uninitialized(new_size - self.len());
+        }
+    }
+}
diff --git a/proxmox-io/src/vec/mod.rs b/proxmox-io/src/vec/mod.rs
new file mode 100644 (file)
index 0000000..730dbb3
--- /dev/null
@@ -0,0 +1,130 @@
+//! Byte vector helpers.
+//!
+//! We have a lot of I/O code such as:
+//! ```ignore
+//! let mut buffer = vec![0u8; header_size];
+//! file.read_exact(&mut buffer)?;
+//! ```
+//! (We even have this case with a 4M buffer!)
+//!
+//! This needlessly initializes the buffer to zero (which not only wastes time (an insane amount of
+//! time on debug builds, actually) but also prevents tools such as valgrind from pointing out
+//! access to actually uninitialized data, which may hide bugs...)
+//!
+//! This module provides some helpers for this kind of code. Many of these are supposed to stay on
+//! a lower level, with I/O helpers for types implementing [`Read`](std::io::Read) being available
+//! in this module.
+//!
+//! Examples:
+//! ```no_run
+//! use proxmox_io::vec::{self, ByteVecExt};
+//!
+//! # let size = 64usize;
+//! # let more = 64usize;
+//! let mut buffer = vec::undefined(size); // A zero-initialized buffer with valgrind support
+//!
+//! let mut buffer = unsafe { vec::uninitialized(size) }; // an actually uninitialized buffer
+//! vec::clear(&mut buffer); // zero out an &mut [u8]
+//!
+//! vec::clear(unsafe {
+//!     buffer.grow_uninitialized(more) // grow the buffer with uninitialized bytes
+//! });
+//! ```
+
+mod byte_vec;
+pub use byte_vec::ByteVecExt;
+
+/// Create an uninitialized byte vector of a specific size.
+///
+/// This is just a shortcut for:
+/// ```no_run
+/// # let len = 64usize;
+/// let mut v = Vec::<u8>::with_capacity(len);
+/// unsafe {
+///     v.set_len(len);
+/// }
+/// ```
+///
+/// # Safety
+///
+/// It's generally not unsafe to use this method, but the contents are uninitialized, and since
+/// this does not return a `MaybeUninit` type to track the initialization state, this is simply
+/// marked as unsafe for good measure.
+#[inline]
+pub unsafe fn uninitialized(len: usize) -> Vec<u8> {
+    let mut out = Vec::with_capacity(len);
+    out.set_len(len);
+    out
+}
+
+/// Shortcut to zero out a slice of bytes.
+#[inline]
+pub fn clear(data: &mut [u8]) {
+    unsafe {
+        std::ptr::write_bytes(data.as_mut_ptr(), 0, data.len());
+    }
+}
+
+/// Create a newly allocated, zero initialized byte vector.
+#[inline]
+pub fn zeroed(len: usize) -> Vec<u8> {
+    unsafe {
+        let mut out = uninitialized(len);
+        clear(&mut out);
+        out
+    }
+}
+
+/// Create a newly allocated byte vector of a specific size with "undefined" content.
+///
+/// The data will be zero initialized, but, if the `valgrind` feature is activated, it will be
+/// marked as uninitialized for debugging.
+#[inline]
+pub fn undefined(len: usize) -> Vec<u8> {
+    undefined_impl(len)
+}
+
+#[cfg(not(feature = "valgrind"))]
+fn undefined_impl(len: usize) -> Vec<u8> {
+    zeroed(len)
+}
+
+#[cfg(feature = "valgrind")]
+fn undefined_impl(len: usize) -> Vec<u8> {
+    let out = zeroed(len);
+    vg::make_slice_undefined(&out[..]);
+    out
+}
+
+#[cfg(feature = "valgrind")]
+mod vg {
+    type ValgrindValue = valgrind_request::Value;
+
+    /// Mark a memory region as undefined when using valgrind, causing it to treat read access to
+    /// it as error.
+    #[inline]
+    pub(crate) fn make_mem_undefined(addr: *const u8, len: usize) -> ValgrindValue {
+        const MAKE_MEM_UNDEFINED: ValgrindValue =
+            (((b'M' as ValgrindValue) << 24) | ((b'C' as ValgrindValue) << 16)) + 1;
+        unsafe {
+            valgrind_request::do_client_request(
+                0,
+                &[
+                    MAKE_MEM_UNDEFINED,
+                    addr as usize as ValgrindValue,
+                    len as ValgrindValue,
+                    0,
+                    0,
+                    0,
+                ],
+            )
+        }
+    }
+
+    /// Mark a slice of bytes as undefined when using valgrind, causing it to treat read access to
+    /// it as error.
+    #[inline]
+    pub(crate) fn make_slice_undefined(data: &[u8]) -> ValgrindValue {
+        make_mem_undefined(data.as_ptr(), data.len())
+    }
+}
diff --git a/proxmox-io/src/write.rs b/proxmox-io/src/write.rs
new file mode 100644 (file)
index 0000000..a07a4fb
--- /dev/null
@@ -0,0 +1,183 @@
+//! Helpers for `Write`.
+
+use std::io;
+
+use endian_trait::Endian;
+
+/// Adds some additional related functionality for types implementing [`Write`](std::io::Write).
+///
+/// Particularly for writing values of a specific endianess (types implementing [`Endian`]).
+///
+/// Examples:
+/// ```no_run
+/// # use endian_trait::Endian;
+/// # use proxmox_io::WriteExt;
+///
+/// #[derive(Endian)]
+/// #[repr(C)]
+/// struct Header {
+///     version: u16,
+///     data_size: u16,
+/// }
+///
+/// # fn code(mut file: std::fs::File) -> std::io::Result<()> {
+/// let header = Header {
+///     version: 1,
+///     data_size: 16,
+/// };
+/// // We have given `Header` a proper binary representation via `#[repr]`, so this is safe:
+/// unsafe {
+///     file.write_le_value(header)?;
+/// }
+/// # Ok(())
+/// # }
+/// ```
+///
+/// [`Endian`]: https://docs.rs/endian_trait/0.6/endian_trait/trait.Endian.html
+pub trait WriteExt {
+    /// Write a value with host endianess.
+    ///
+    /// This is limited to types implementing the [`Endian`] trait under the assumption that this
+    /// is only done for types which are supposed to be read/writable directly.
+    ///
+    /// There's no way to directly depend on a type having a specific `#[repr(...)]`, therefore
+    /// this is considered unsafe.
+    ///
+    /// The underlying write call is `.write_all()`, so there are no partial writes.
+    ///
+    /// ```no_run
+    /// # use endian_trait::Endian;
+    /// use proxmox_io::WriteExt;
+    ///
+    /// #[derive(Endian)]
+    /// #[repr(C, packed)]
+    /// struct Data {
+    ///     value: u16,
+    ///     count: u32,
+    /// }
+    ///
+    /// # fn code() -> std::io::Result<()> {
+    /// let mut file = std::fs::File::create("my-raw.dat")?;
+    /// // We know `Data` has a safe binary representation (#[repr(C, packed)]), so we can
+    /// // safely use our helper:
+    /// unsafe {
+    ///     file.write_host_value(Data {
+    ///         value: 1,
+    ///         count: 2,
+    ///     })?;
+    /// }
+    /// # Ok(())
+    /// # }
+    /// ```
+    ///
+    /// # Safety
+    ///
+    /// This should only used for types with a defined storage representation, usually
+    /// `#[repr(C)]`, otherwise the results may be inconsistent.
+    ///
+    /// [`Endian`]: https://docs.rs/endian_trait/0.6/endian_trait/trait.Endian.html
+    unsafe fn write_host_value<T: Endian>(&mut self, value: T) -> io::Result<()>;
+
+    /// Write a little endian value.
+    ///
+    /// The input type is required to implement the [`Endian`] trait, and we make the assumption
+    /// that this is only done for types which are supposed to be read/writable directly.
+    ///
+    /// There's no way to directly depend on a type having a specific `#[repr(...)]`, therefore
+    /// this is considered unsafe.
+    ///
+    /// The underlying write call is `.write_all()`, so there are no partial writes.
+    ///
+    /// ```no_run
+    /// # use endian_trait::Endian;
+    /// use proxmox_io::WriteExt;
+    ///
+    /// #[derive(Endian)]
+    /// #[repr(C, packed)]
+    /// struct Data {
+    ///     value: u16,
+    ///     count: u32,
+    /// }
+    ///
+    /// # fn code() -> std::io::Result<()> {
+    /// let mut file = std::fs::File::create("my-raw.dat")?;
+    /// // We know `Data` has a safe binary representation (#[repr(C, packed)]), so we can
+    /// // safely use our helper:
+    /// unsafe {
+    ///     file.write_le_value(Data {
+    ///         value: 1,
+    ///         count: 2,
+    ///     })?;
+    /// }
+    /// # Ok(())
+    /// # }
+    /// ```
+    ///
+    /// # Safety
+    ///
+    /// This should only used for types with a defined storage representation, usually
+    /// `#[repr(C)]`, otherwise the results may be inconsistent.
+    ///
+    /// [`Endian`]: https://docs.rs/endian_trait/0.6/endian_trait/trait.Endian.html
+    unsafe fn write_le_value<T: Endian>(&mut self, value: T) -> io::Result<()>;
+
+    /// Read a big endian value.
+    ///
+    /// The input type is required to implement the [`Endian`] trait, and we make the assumption
+    /// that this is only done for types which are supposed to be read/writable directly.
+    ///
+    /// There's no way to directly depend on a type having a specific `#[repr(...)]`, therefore
+    /// this is considered unsafe.
+    ///
+    /// The underlying write call is `.write_all()`, so there are no partial writes.
+    ///
+    /// ```no_run
+    /// # use endian_trait::Endian;
+    /// use proxmox_io::WriteExt;
+    ///
+    /// #[derive(Endian)]
+    /// #[repr(C, packed)]
+    /// struct Data {
+    ///     value: u16,
+    ///     count: u32,
+    /// }
+    ///
+    /// # fn code() -> std::io::Result<()> {
+    /// let mut file = std::fs::File::create("my-raw.dat")?;
+    /// // We know `Data` has a safe binary representation (#[repr(C, packed)]), so we can
+    /// // safely use our helper:
+    /// unsafe {
+    ///     file.write_be_value(Data {
+    ///         value: 1,
+    ///         count: 2,
+    ///     })?;
+    /// }
+    /// # Ok(())
+    /// # }
+    /// ```
+    ///
+    /// # Safety
+    ///
+    /// This should only used for types with a defined storage representation, usually
+    /// `#[repr(C)]`, otherwise the results may be inconsistent.
+    ///
+    /// [`Endian`]: https://docs.rs/endian_trait/0.6/endian_trait/trait.Endian.html
+    unsafe fn write_be_value<T: Endian>(&mut self, value: T) -> io::Result<()>;
+}
+
+impl<W: io::Write> WriteExt for W {
+    unsafe fn write_host_value<T: Endian>(&mut self, value: T) -> io::Result<()> {
+        self.write_all(std::slice::from_raw_parts(
+            &value as *const T as *const u8,
+            std::mem::size_of::<T>(),
+        ))
+    }
+
+    unsafe fn write_le_value<T: Endian>(&mut self, value: T) -> io::Result<()> {
+        self.write_host_value::<T>(value.to_le())
+    }
+
+    unsafe fn write_be_value<T: Endian>(&mut self, value: T) -> io::Result<()> {
+        self.write_host_value::<T>(value.to_be())
+    }
+}
diff --git a/proxmox/src/tools/byte_buffer.rs b/proxmox/src/tools/byte_buffer.rs
deleted file mode 100644 (file)
index 9cffcfe..0000000
+++ /dev/null
@@ -1,227 +0,0 @@
-//! ByteBuffer
-//!
-//! a simple buffer for u8 with a practical api for reading and appending
-//! and consuming from the front
-//! Example:
-//! ```
-//! # use std::io::Read;
-//! # use proxmox::tools::byte_buffer::ByteBuffer;
-//! fn code<T: Read + ?Sized>(input: &mut T) -> std::io::Result<Box<[u8]>> {
-//!     let mut buffer = ByteBuffer::new();
-//!     let amount = buffer.read_from(input)?;
-//!     let data = buffer.remove_data(amount);
-//!     assert_eq!(data.len(), amount);
-//!     Ok(data)
-//! }
-//! # code(&mut &b"testdata"[..]).expect("byte buffer test failed");
-//! ```
-
-use std::io::{Read, Result};
-
-use crate::tools::vec;
-
-#[cfg(feature = "tokio")]
-use tokio::io::{AsyncRead, AsyncReadExt};
-
-/// A Buffer that holds bytes (u8)
-/// has convenience methods for reading (with any reader that
-/// implements std::io::Read or tokio::io::AsyncRead) onto the back
-/// and consuming from the front
-pub struct ByteBuffer {
-    buf: Box<[u8]>,
-    data_size: usize,
-    capacity: usize,
-}
-
-impl ByteBuffer {
-    /// Creates a new instance with a default capacity of 4096
-    pub fn new() -> Self {
-        Self::with_capacity(4096)
-    }
-
-    pub fn with_capacity(capacity: usize) -> Self {
-        Self {
-            buf: vec::undefined(capacity).into_boxed_slice(),
-            data_size: 0,
-            capacity,
-        }
-    }
-
-    pub fn free_size(&self) -> usize {
-        self.capacity - self.data_size
-    }
-
-    pub fn is_full(&self) -> bool {
-        self.data_size >= self.capacity
-    }
-
-    pub fn clear(&mut self) {
-        self.data_size = 0
-    }
-
-    /// Sets the length of the data. Useful if data was manually added
-    /// with a mutable slice (e.g. from [get_free_mut_slice](#method.get_free_mut_slice)).
-    ///
-    /// Panics when new size would be greater than capacity.
-    ///
-    /// Example:
-    /// ```
-    /// # use proxmox::tools::byte_buffer::ByteBuffer;
-    /// let mut buf = ByteBuffer::new();
-    /// buf.get_free_mut_slice()[..1].copy_from_slice(&[1u8]);
-    /// buf.add_size(1);
-    /// ```
-    ///
-    /// This code will panic:
-    /// ```should_panic
-    /// # use proxmox::tools::byte_buffer::ByteBuffer;
-    /// let mut buf = ByteBuffer::with_capacity(128);
-    /// buf.add_size(256);
-    /// ```
-    pub fn add_size(&mut self, size: usize) {
-        if self.data_size + size > self.capacity {
-            panic!("size bigger than capacity!");
-        }
-        self.data_size += size;
-    }
-
-    /// Returns a mutable reference to the free section of the
-    /// Buffer. There are no guarantees about the content of the
-    /// free part of the Buffer (may even be uninitialized).
-    /// see [add_size](#method.add_size) for a usage example.
-    pub fn get_free_mut_slice(&mut self) -> &mut [u8] {
-        &mut self.buf[self.data_size..self.capacity]
-    }
-
-    /// Removes up to max_amount of data from the front
-    /// of the buffer and returns. If there was less than max_amount present,
-    /// it will return as much data as there was in the buffer.
-    /// The rest of the data will be moved to the front, and
-    /// the data size will be updated accordingly.
-    ///
-    /// Example:
-    /// ```
-    /// # use proxmox::tools::byte_buffer::ByteBuffer;
-    /// let mut buf = ByteBuffer::new();
-    /// buf.get_free_mut_slice()[..2].copy_from_slice(&[1u8, 2u8]);
-    /// buf.add_size(2);
-    /// assert_eq!(buf.len(), 2);
-    ///
-    /// let data = buf.remove_data(100);
-    /// assert_eq!(&data[..], &[1u8, 2u8]);
-    /// assert!(buf.is_empty());
-    /// ```
-    #[must_use]
-    pub fn remove_data(&mut self, max_amount: usize) -> Box<[u8]> {
-        let size = max_amount.min(self.data_size);
-        let tmp: Box<[u8]> = self.buf[..size].into();
-        self.buf.copy_within(size..self.capacity, 0);
-        self.data_size -= size;
-        tmp
-    }
-
-    /// Removes up to max_amount of data from the front and returns
-    /// the amount of data removed. If there was less than max_amount present,
-    /// it will empty out the buffer and return the amount removed.
-    ///
-    /// Example:
-    /// ```
-    /// # use proxmox::tools::byte_buffer::ByteBuffer;
-    /// let mut buf = ByteBuffer::new();
-    /// buf.get_free_mut_slice()[..2].copy_from_slice(&[1u8, 2u8]);
-    /// buf.add_size(2);
-    /// assert_eq!(buf.len(), 2);
-    ///
-    /// let amount = buf.consume(1);
-    /// assert_eq!(amount, 1);
-    /// let amount = buf.consume(100);
-    /// assert_eq!(amount, 1);
-    /// assert!(buf.is_empty());
-    /// ```
-    pub fn consume(&mut self, max_amount: usize) -> usize {
-        let size = max_amount.min(self.data_size);
-        if size < max_amount {
-            self.clear()
-        } else {
-            self.buf.copy_within(size..self.capacity, 0);
-            self.data_size -= size;
-        }
-        size
-    }
-
-    /// Takes a reader and reads into the back of the buffer (up to the
-    /// free space in the buffer) and updates its size accordingly.
-    ///
-    /// Example:
-    /// ```
-    /// # use std::io;
-    /// # use proxmox::tools::byte_buffer::ByteBuffer;
-    /// fn code<R: io::Read>(mut reader: R) -> io::Result<()> {
-    ///     let mut buf = ByteBuffer::new();
-    ///     let res = buf.read_from(&mut reader)?;
-    ///     // do something with the buffer
-    ///     Ok(())
-    /// }
-    /// ```
-    pub fn read_from<T: Read + ?Sized>(&mut self, input: &mut T) -> Result<usize> {
-        let amount = input.read(self.get_free_mut_slice())?;
-        self.add_size(amount);
-        Ok(amount)
-    }
-
-    /// Same as read_from, but for reader that implement tokio::io::AsyncRead.
-    /// See [read_from](#method.read_from) for an example
-    #[cfg(feature = "tokio")]
-    pub async fn read_from_async<T: AsyncRead + Unpin>(&mut self, input: &mut T) -> Result<usize> {
-        let amount = input.read(self.get_free_mut_slice()).await?;
-        self.add_size(amount);
-        Ok(amount)
-    }
-}
-
-impl Default for ByteBuffer {
-    fn default() -> Self {
-        Self::new()
-    }
-}
-
-impl std::ops::Deref for ByteBuffer {
-    type Target = [u8];
-
-    fn deref(&self) -> &Self::Target {
-        &self.buf[..self.data_size]
-    }
-}
-
-impl std::ops::DerefMut for ByteBuffer {
-    fn deref_mut(&mut self) -> &mut Self::Target {
-        &mut self.buf[..self.data_size]
-    }
-}
-
-#[cfg(test)]
-mod test {
-    use crate::tools::byte_buffer::ByteBuffer;
-
-    #[test]
-    fn test1() {
-        let mut buffer = ByteBuffer::new();
-        let slice = buffer.get_free_mut_slice();
-        for i in 0..slice.len() {
-            slice[i] = (i % 255) as u8;
-        }
-        buffer.add_size(5);
-
-        let slice2 = &buffer[..];
-
-        assert_eq!(slice2, &[0, 1, 2, 3, 4]);
-    }
-
-    #[test]
-    fn test2() {
-        let mut buffer = ByteBuffer::with_capacity(1024);
-        let size = buffer.read_from(&mut std::io::repeat(54)).unwrap();
-        assert_eq!(buffer.len(), size);
-        assert_eq!(buffer[0], 54);
-    }
-}
diff --git a/proxmox/src/tools/io/mod.rs b/proxmox/src/tools/io/mod.rs
deleted file mode 100644 (file)
index d6af309..0000000
+++ /dev/null
@@ -1,252 +0,0 @@
-//! Module providing I/O helpers (sync and async).
-//!
-//! The [`ReadExt`] trait provides additional operations for handling byte buffers for types
-//! implementing [`Read`](std::io::Read).
-
-use std::io::{self, ErrorKind, Read, Seek, SeekFrom, Write};
-
-mod read;
-pub use read::*;
-
-mod write;
-pub use write::*;
-
-fn buffer_is_zero(buf: &[u8]) -> bool {
-    !buf.chunks(128)
-        .map(|aa| aa.iter().fold(0, |a, b| a | b) != 0)
-        .any(|a| a)
-}
-
-/// Result of a sparse copy call
-/// contains the amount of written/seeked bytes
-/// and if the last operation was a seek
-pub struct SparseCopyResult {
-    pub written: u64,
-    pub seeked_last: bool,
-}
-
-/// copy similar to io::copy, but seeks the target when encountering
-/// zero bytes instead of writing them
-///
-/// Example use:
-/// ```
-/// # use std::io;
-/// # use proxmox::tools::io::sparse_copy;
-/// fn code<R, W>(mut reader: R, mut writer: W) -> io::Result<()>
-/// where
-///     R: io::Read,
-///     W: io::Write + io::Seek,
-/// {
-///     let res = sparse_copy(&mut reader, &mut writer)?;
-///
-///     println!("last part was seeked: {}", res.seeked_last);
-///     println!("written: {}", res.written);
-///
-///     Ok(())
-/// }
-/// ```
-pub fn sparse_copy<R: Read + ?Sized, W: Write + Seek + ?Sized>(
-    reader: &mut R,
-    writer: &mut W,
-) -> Result<SparseCopyResult, io::Error> {
-    let mut buf = crate::tools::byte_buffer::ByteBuffer::with_capacity(4096);
-    let mut written = 0;
-    let mut seek_amount: i64 = 0;
-    let mut seeked_last = false;
-    loop {
-        buf.clear();
-        let len = match buf.read_from(reader) {
-            Ok(len) => len,
-            Err(ref e) if e.kind() == ErrorKind::Interrupted => continue,
-            Err(e) => return Err(e),
-        };
-
-        if len > 0 && buffer_is_zero(&buf[..]) {
-            seek_amount += len as i64;
-            continue;
-        }
-
-        if seek_amount > 0 {
-            writer.seek(SeekFrom::Current(seek_amount))?;
-            written += seek_amount as u64;
-            seek_amount = 0;
-            seeked_last = true;
-        }
-
-        if len > 0 {
-            writer.write_all(&buf[..])?;
-            written += len as u64;
-            seeked_last = false;
-        } else {
-            return Ok(SparseCopyResult {
-                written,
-                seeked_last,
-            });
-        }
-    }
-}
-
-#[cfg(feature = "tokio")]
-use tokio::io::{AsyncRead, AsyncSeek, AsyncSeekExt, AsyncWrite, AsyncWriteExt};
-
-#[cfg(feature = "tokio")]
-/// copy similar to tokio::io::copy, but seeks the target when encountering
-/// zero bytes instead of writing them
-///
-/// Example:
-/// ```no_run
-/// # use std::io;
-/// # use tokio::io::{AsyncRead, AsyncWrite, AsyncSeek};
-/// # use proxmox::tools::io::sparse_copy_async;
-/// async fn code<R, W>(mut reader: R, mut writer: W) -> io::Result<()>
-/// where
-///     R: AsyncRead + Unpin,
-///     W: AsyncWrite + AsyncSeek + Unpin,
-/// {
-///     let res = sparse_copy_async(&mut reader, &mut writer).await?;
-///
-///     println!("last part was seeked: {}", res.seeked_last);
-///     println!("written: {}", res.written);
-///
-///     Ok(())
-/// }
-/// ```
-pub async fn sparse_copy_async<R, W>(
-    reader: &mut R,
-    writer: &mut W,
-) -> Result<SparseCopyResult, io::Error>
-where
-    R: AsyncRead + Unpin,
-    W: AsyncWrite + AsyncSeek + Unpin,
-{
-    let mut buf = crate::tools::byte_buffer::ByteBuffer::with_capacity(4096);
-    let mut written = 0;
-    let mut seek_amount: i64 = 0;
-    let mut seeked_last = false;
-    loop {
-        buf.clear();
-        let len = match buf.read_from_async(reader).await {
-            Ok(len) => len,
-            Err(ref e) if e.kind() == ErrorKind::Interrupted => continue,
-            Err(e) => return Err(e),
-        };
-
-        if len > 0 && buffer_is_zero(&buf[..]) {
-            seek_amount += len as i64;
-            continue;
-        }
-
-        if seek_amount > 0 {
-            writer.seek(SeekFrom::Current(seek_amount)).await?;
-            written += seek_amount as u64;
-            seek_amount = 0;
-            seeked_last = true;
-        }
-
-        if len > 0 {
-            writer.write_all(&buf[..]).await?;
-            written += len as u64;
-            seeked_last = false;
-        } else {
-            return Ok(SparseCopyResult {
-                written,
-                seeked_last,
-            });
-        }
-    }
-}
-
-#[cfg(test)]
-mod test {
-    use std::io::Cursor;
-
-    use crate::test::io::{AsyncBlockingReader, AsyncBlockingWriter};
-    use crate::tools::io::{sparse_copy, sparse_copy_async};
-
-    const LEN: usize = 10000;
-
-    #[test]
-    fn test_sparse_copy() {
-        // test sparse
-        let mut test_data = Vec::new();
-        for _ in 0..LEN / 2 {
-            test_data.push(1u8);
-        }
-        for _ in 0..LEN / 2 {
-            test_data.push(0u8);
-        }
-        let mut test_data = Cursor::new(test_data);
-        let mut result_data = Cursor::new(vec![0; LEN]);
-
-        let result =
-            sparse_copy(&mut test_data, &mut result_data).expect("error during sparse copy");
-        assert_eq!(result.written, LEN as u64);
-        assert_eq!(result.seeked_last, true);
-        for i in 0..LEN {
-            if i < LEN / 2 {
-                assert_eq!(result_data.get_ref()[i], 1);
-            } else {
-                assert_eq!(result_data.get_ref()[i], 0);
-            }
-        }
-
-        // test non sparse
-        let mut test_data = Cursor::new(vec![1; LEN]);
-        let mut result_data = Cursor::new(vec![0; LEN]);
-
-        let result =
-            sparse_copy(&mut test_data, &mut result_data).expect("error during sparse copy");
-        assert_eq!(result.written, LEN as u64);
-        assert_eq!(result.seeked_last, false);
-        for i in 0..LEN {
-            assert_eq!(result_data.get_ref()[i], 1);
-        }
-    }
-
-    #[test]
-    fn test_sparse_copy_async() {
-        let fut = async {
-            // test sparse
-            let mut test_data = Vec::new();
-            for _ in 0..LEN / 2 {
-                test_data.push(1u8);
-            }
-            for _ in 0..LEN / 2 {
-                test_data.push(0u8);
-            }
-            let mut test_data = AsyncBlockingReader::new(Cursor::new(test_data));
-            let mut result_data = AsyncBlockingWriter::new(Cursor::new(vec![0; LEN]));
-
-            let result = sparse_copy_async(&mut test_data, &mut result_data)
-                .await
-                .expect("error during sparse copy");
-
-            assert_eq!(result.written, LEN as u64);
-            assert_eq!(result.seeked_last, true);
-            for i in 0..LEN {
-                if i < LEN / 2 {
-                    assert_eq!(result_data.inner().get_ref()[i], 1);
-                } else {
-                    assert_eq!(result_data.inner().get_ref()[i], 0);
-                }
-            }
-
-            // test non sparse
-            let mut test_data = AsyncBlockingReader::new(Cursor::new(vec![1; LEN]));
-            let mut result_data = AsyncBlockingWriter::new(Cursor::new(vec![0; LEN]));
-
-            let result = sparse_copy_async(&mut test_data, &mut result_data)
-                .await
-                .expect("error during sparse copy");
-
-            assert_eq!(result.written, LEN as u64);
-            assert_eq!(result.seeked_last, false);
-            for i in 0..LEN {
-                assert_eq!(result_data.inner().get_ref()[i], 1);
-            }
-            Ok(())
-        };
-
-        crate::test::task::poll_result_once(fut).expect("ok")
-    }
-}
diff --git a/proxmox/src/tools/io/read.rs b/proxmox/src/tools/io/read.rs
deleted file mode 100644 (file)
index eed96b7..0000000
+++ /dev/null
@@ -1,335 +0,0 @@
-//! Helpers for `Read`.
-
-use std::io;
-use std::mem;
-
-use endian_trait::Endian;
-
-use crate::tools::vec::{self, ByteVecExt};
-
-/// Adds some additional related functionality for types implementing [`Read`](std::io::Read).
-///
-/// Particularly for reading into a newly allocated buffer, appending to a `Vec<u8>` or reading
-/// values of a specific endianess (types implementing [`Endian`]).
-///
-/// Examples:
-/// ```no_run
-/// use proxmox::tools::io::ReadExt;
-///
-/// # fn code() -> std::io::Result<()> {
-/// let mut file = std::fs::File::open("some.data")?;
-///
-/// // read some bytes into a newly allocated Vec<u8>:
-/// let mut data = file.read_exact_allocated(64)?;
-///
-/// // appending data to a vector:
-/// let actually_appended = file.append_to_vec(&mut data, 64)?; // .read() version
-/// file.append_exact_to_vec(&mut data, 64)?; // .read_exact() version
-/// # Ok(())
-/// # }
-/// ```
-///
-/// Or for reading values of a defined representation and endianess:
-///
-/// ```no_run
-/// # use endian_trait::Endian;
-/// # use proxmox::tools::io::ReadExt;
-///
-/// #[derive(Endian)]
-/// #[repr(C)]
-/// struct Header {
-///     version: u16,
-///     data_size: u16,
-/// }
-///
-/// # fn code(mut file: std::fs::File) -> std::io::Result<()> {
-/// // We have given `Header` a proper binary representation via `#[repr]`, so this is safe:
-/// let header: Header = unsafe { file.read_le_value()? };
-/// let mut blob = file.read_exact_allocated(header.data_size as usize)?;
-/// # Ok(())
-/// # }
-/// ```
-///
-/// [`Endian`]: https://docs.rs/endian_trait/0.6/endian_trait/trait.Endian.html
-pub trait ReadExt {
-    /// Read data into a newly allocated vector. This is a shortcut for:
-    /// ```ignore
-    /// let mut data = Vec::with_capacity(len);
-    /// unsafe {
-    ///     data.set_len(len);
-    /// }
-    /// reader.read_exact(&mut data)?;
-    /// ```
-    ///
-    /// With this trait, we just use:
-    /// ```no_run
-    /// use proxmox::tools::io::ReadExt;
-    /// # fn code(mut reader: std::fs::File, len: usize) -> std::io::Result<()> {
-    /// let data = reader.read_exact_allocated(len)?;
-    /// # Ok(())
-    /// # }
-    /// ```
-    fn read_exact_allocated(&mut self, size: usize) -> io::Result<Vec<u8>>;
-
-    /// Append data to a vector, growing it as necessary. Returns the amount of data appended.
-    fn append_to_vec(&mut self, out: &mut Vec<u8>, size: usize) -> io::Result<usize>;
-
-    /// Append an exact amount of data to a vector, growing it as necessary.
-    fn append_exact_to_vec(&mut self, out: &mut Vec<u8>, size: usize) -> io::Result<()>;
-
-    /// Read a value with host endianess.
-    ///
-    /// This is limited to types implementing the [`Endian`] trait under the assumption that
-    /// this is only done for types which are supposed to be read/writable directly.
-    ///
-    /// There's no way to directly depend on a type having a specific `#[repr(...)]`, therefore
-    /// this is considered unsafe.
-    ///
-    /// ```no_run
-    /// # use endian_trait::Endian;
-    /// use proxmox::tools::io::ReadExt;
-    ///
-    /// #[derive(Endian)]
-    /// #[repr(C, packed)]
-    /// struct Data {
-    ///     value: u16,
-    ///     count: u32,
-    /// }
-    ///
-    /// # fn code() -> std::io::Result<()> {
-    /// let mut file = std::fs::File::open("my-raw.dat")?;
-    /// // We know `Data` has a safe binary representation (#[repr(C, packed)]), so we can
-    /// // safely use our helper:
-    /// let data: Data = unsafe { file.read_host_value()? };
-    /// # Ok(())
-    /// # }
-    /// ```
-    ///
-    /// # Safety
-    ///
-    /// This should only used for types with a defined storage representation, usually
-    /// `#[repr(C)]`, otherwise the results may be inconsistent.
-    ///
-    /// [`Endian`]: https://docs.rs/endian_trait/0.6/endian_trait/trait.Endian.html
-    unsafe fn read_host_value<T: Endian>(&mut self) -> io::Result<T>;
-
-    /// Read a little endian value.
-    ///
-    /// The return type is required to implement the [`Endian`] trait, and we make the
-    /// assumption that this is only done for types which are supposed to be read/writable
-    /// directly.
-    ///
-    /// There's no way to directly depend on a type having a specific `#[repr(...)]`, therefore
-    /// this is considered unsafe.
-    ///
-    /// ```no_run
-    /// # use endian_trait::Endian;
-    /// use proxmox::tools::io::ReadExt;
-    ///
-    /// #[derive(Endian)]
-    /// #[repr(C, packed)]
-    /// struct Data {
-    ///     value: u16,
-    ///     count: u32,
-    /// }
-    ///
-    /// # fn code() -> std::io::Result<()> {
-    /// let mut file = std::fs::File::open("my-little-endian.dat")?;
-    /// // We know `Data` has a safe binary representation (#[repr(C, packed)]), so we can
-    /// // safely use our helper:
-    /// let data: Data = unsafe { file.read_le_value()? };
-    /// # Ok(())
-    /// # }
-    /// ```
-    ///
-    /// # Safety
-    ///
-    /// This should only used for types with a defined storage representation, usually
-    /// `#[repr(C)]`, otherwise the results may be inconsistent.
-    ///
-    /// [`Endian`]: https://docs.rs/endian_trait/0.6/endian_trait/trait.Endian.html
-    unsafe fn read_le_value<T: Endian>(&mut self) -> io::Result<T>;
-
-    /// Read a big endian value.
-    ///
-    /// The return type is required to implement the [`Endian`] trait, and we make the
-    /// assumption that this is only done for types which are supposed to be read/writable
-    /// directly.
-    ///
-    /// There's no way to directly depend on a type having a specific `#[repr(...)]`, therefore
-    /// this is considered unsafe.
-    ///
-    /// ```no_run
-    /// # use endian_trait::Endian;
-    /// use proxmox::tools::io::ReadExt;
-    ///
-    /// #[derive(Endian)]
-    /// #[repr(C, packed)]
-    /// struct Data {
-    ///     value: u16,
-    ///     count: u32,
-    /// }
-    ///
-    /// # fn code() -> std::io::Result<()> {
-    /// let mut file = std::fs::File::open("my-big-endian.dat")?;
-    /// // We know `Data` has a safe binary representation (#[repr(C, packed)]), so we can
-    /// // safely use our helper:
-    /// let data: Data = unsafe { file.read_be_value()? };
-    /// # Ok(())
-    /// # }
-    /// ```
-    ///
-    /// # Safety
-    ///
-    /// This should only used for types with a defined storage representation, usually
-    /// `#[repr(C)]`, otherwise the results may be inconsistent.
-    ///
-    /// [`Endian`]: https://docs.rs/endian_trait/0.6/endian_trait/trait.Endian.html
-    unsafe fn read_be_value<T: Endian>(&mut self) -> io::Result<T>;
-
-    /// Read a boxed value with host endianess.
-    ///
-    /// This is currently not limited to types implementing the [`Endian`] trait as in our use
-    /// cases we use this for types which are too big to want to always perform endian swaps
-    /// immediately on all values.
-    ///
-    /// ```
-    /// # use proxmox::tools::vec;
-    /// use proxmox::tools::io::ReadExt;
-    ///
-    /// #[repr(C)]
-    /// struct Data {
-    ///     v1: u64,
-    ///     buf: [u8; 4088],
-    /// }
-    ///
-    /// # let mut input = [0u8; 4096];
-    /// # use proxmox::tools::io::WriteExt;
-    /// # let mut writer = &mut input[..];
-    /// # unsafe { writer.write_host_value(32u64).unwrap() };
-    /// # let mut file = &input[..];
-    ///
-    /// # fn code<T: std::io::Read>(mut file: T) -> std::io::Result<()> {
-    /// let data: Box<Data> = unsafe { file.read_host_value_boxed()? };
-    /// assert_eq!(data.v1, 32);
-    /// # Ok(())
-    /// # }
-    /// # code(&input[..]).unwrap();
-    /// ```
-    ///
-    /// # Safety
-    ///
-    /// This should only used for types with a defined storage representation, usually
-    /// `#[repr(C)]`, otherwise the results may be inconsistent.
-    unsafe fn read_host_value_boxed<T>(&mut self) -> io::Result<Box<T>>;
-
-    /// Try to read the exact number of bytes required to fill buf.
-    ///
-    /// This function reads as many bytes as necessary to completely
-    /// fill the specified buffer buf. If this function encounters an
-    /// "end of file" before getting any data, it returns Ok(false).
-    /// If there is some data, but not enough, it return an error of
-    /// the kind ErrorKind::UnexpectedEof. The contents of buf are
-    /// unspecified in this case.
-    fn read_exact_or_eof(&mut self, buf: &mut [u8]) -> io::Result<bool>;
-
-    /// Read until EOF
-    fn skip_to_end(&mut self) -> io::Result<usize>;
-}
-
-impl<R: io::Read> ReadExt for R {
-    fn read_exact_allocated(&mut self, size: usize) -> io::Result<Vec<u8>> {
-        let mut out = unsafe { vec::uninitialized(size) };
-        self.read_exact(&mut out)?;
-        Ok(out)
-    }
-
-    fn append_to_vec(&mut self, out: &mut Vec<u8>, size: usize) -> io::Result<usize> {
-        let pos = out.len();
-        unsafe {
-            out.grow_uninitialized(size);
-        }
-        let got = self.read(&mut out[pos..])?;
-        unsafe {
-            out.set_len(pos + got);
-        }
-        Ok(got)
-    }
-
-    fn append_exact_to_vec(&mut self, out: &mut Vec<u8>, size: usize) -> io::Result<()> {
-        let pos = out.len();
-        unsafe {
-            out.grow_uninitialized(size);
-        }
-        self.read_exact(&mut out[pos..])?;
-        Ok(())
-    }
-
-    unsafe fn read_host_value<T: Endian>(&mut self) -> io::Result<T> {
-        let mut value = std::mem::MaybeUninit::<T>::uninit();
-        self.read_exact(std::slice::from_raw_parts_mut(
-            value.as_mut_ptr() as *mut u8,
-            mem::size_of::<T>(),
-        ))?;
-        Ok(value.assume_init())
-    }
-
-    unsafe fn read_le_value<T: Endian>(&mut self) -> io::Result<T> {
-        Ok(self.read_host_value::<T>()?.from_le())
-    }
-
-    unsafe fn read_be_value<T: Endian>(&mut self) -> io::Result<T> {
-        Ok(self.read_host_value::<T>()?.from_be())
-    }
-
-    unsafe fn read_host_value_boxed<T>(&mut self) -> io::Result<Box<T>> {
-        // FIXME: Change this once #![feature(new_uninit)] lands for Box<T>!
-
-        let ptr = std::alloc::alloc(std::alloc::Layout::new::<T>()) as *mut T;
-        self.read_exact(std::slice::from_raw_parts_mut(
-            ptr as *mut u8,
-            mem::size_of::<T>(),
-        ))?;
-        Ok(Box::from_raw(ptr))
-    }
-
-    fn read_exact_or_eof(&mut self, mut buf: &mut [u8]) -> io::Result<bool> {
-        let mut read_bytes = 0;
-        loop {
-            match self.read(&mut buf) {
-                Ok(0) => {
-                    if read_bytes == 0 {
-                        return Ok(false);
-                    }
-                    return Err(io::Error::new(
-                        io::ErrorKind::UnexpectedEof,
-                        "failed to fill whole buffer",
-                    ));
-                }
-                Ok(n) => {
-                    let tmp = buf;
-                    buf = &mut tmp[n..];
-                    read_bytes += n;
-                    if buf.is_empty() {
-                        return Ok(true);
-                    }
-                }
-                Err(ref e) if e.kind() == std::io::ErrorKind::Interrupted => {}
-                Err(e) => return Err(e),
-            }
-        }
-    }
-
-    fn skip_to_end(&mut self) -> io::Result<usize> {
-        let mut skipped_bytes = 0;
-        let mut buf = unsafe { vec::uninitialized(32 * 1024) };
-        loop {
-            match self.read(&mut buf) {
-                Ok(0) => return Ok(skipped_bytes),
-                Ok(n) => skipped_bytes += n,
-                Err(ref e) if e.kind() == std::io::ErrorKind::Interrupted => {}
-                Err(e) => return Err(e),
-            }
-        }
-    }
-}
diff --git a/proxmox/src/tools/io/write.rs b/proxmox/src/tools/io/write.rs
deleted file mode 100644 (file)
index 3ed3562..0000000
+++ /dev/null
@@ -1,183 +0,0 @@
-//! Helpers for `Write`.
-
-use std::io;
-
-use endian_trait::Endian;
-
-/// Adds some additional related functionality for types implementing [`Write`](std::io::Write).
-///
-/// Particularly for writing values of a specific endianess (types implementing [`Endian`]).
-///
-/// Examples:
-/// ```no_run
-/// # use endian_trait::Endian;
-/// # use proxmox::tools::io::WriteExt;
-///
-/// #[derive(Endian)]
-/// #[repr(C)]
-/// struct Header {
-///     version: u16,
-///     data_size: u16,
-/// }
-///
-/// # fn code(mut file: std::fs::File) -> std::io::Result<()> {
-/// let header = Header {
-///     version: 1,
-///     data_size: 16,
-/// };
-/// // We have given `Header` a proper binary representation via `#[repr]`, so this is safe:
-/// unsafe {
-///     file.write_le_value(header)?;
-/// }
-/// # Ok(())
-/// # }
-/// ```
-///
-/// [`Endian`]: https://docs.rs/endian_trait/0.6/endian_trait/trait.Endian.html
-pub trait WriteExt {
-    /// Write a value with host endianess.
-    ///
-    /// This is limited to types implementing the [`Endian`] trait under the assumption that this
-    /// is only done for types which are supposed to be read/writable directly.
-    ///
-    /// There's no way to directly depend on a type having a specific `#[repr(...)]`, therefore
-    /// this is considered unsafe.
-    ///
-    /// The underlying write call is `.write_all()`, so there are no partial writes.
-    ///
-    /// ```no_run
-    /// # use endian_trait::Endian;
-    /// use proxmox::tools::io::WriteExt;
-    ///
-    /// #[derive(Endian)]
-    /// #[repr(C, packed)]
-    /// struct Data {
-    ///     value: u16,
-    ///     count: u32,
-    /// }
-    ///
-    /// # fn code() -> std::io::Result<()> {
-    /// let mut file = std::fs::File::create("my-raw.dat")?;
-    /// // We know `Data` has a safe binary representation (#[repr(C, packed)]), so we can
-    /// // safely use our helper:
-    /// unsafe {
-    ///     file.write_host_value(Data {
-    ///         value: 1,
-    ///         count: 2,
-    ///     })?;
-    /// }
-    /// # Ok(())
-    /// # }
-    /// ```
-    ///
-    /// # Safety
-    ///
-    /// This should only used for types with a defined storage representation, usually
-    /// `#[repr(C)]`, otherwise the results may be inconsistent.
-    ///
-    /// [`Endian`]: https://docs.rs/endian_trait/0.6/endian_trait/trait.Endian.html
-    unsafe fn write_host_value<T: Endian>(&mut self, value: T) -> io::Result<()>;
-
-    /// Write a little endian value.
-    ///
-    /// The input type is required to implement the [`Endian`] trait, and we make the assumption
-    /// that this is only done for types which are supposed to be read/writable directly.
-    ///
-    /// There's no way to directly depend on a type having a specific `#[repr(...)]`, therefore
-    /// this is considered unsafe.
-    ///
-    /// The underlying write call is `.write_all()`, so there are no partial writes.
-    ///
-    /// ```no_run
-    /// # use endian_trait::Endian;
-    /// use proxmox::tools::io::WriteExt;
-    ///
-    /// #[derive(Endian)]
-    /// #[repr(C, packed)]
-    /// struct Data {
-    ///     value: u16,
-    ///     count: u32,
-    /// }
-    ///
-    /// # fn code() -> std::io::Result<()> {
-    /// let mut file = std::fs::File::create("my-raw.dat")?;
-    /// // We know `Data` has a safe binary representation (#[repr(C, packed)]), so we can
-    /// // safely use our helper:
-    /// unsafe {
-    ///     file.write_le_value(Data {
-    ///         value: 1,
-    ///         count: 2,
-    ///     })?;
-    /// }
-    /// # Ok(())
-    /// # }
-    /// ```
-    ///
-    /// # Safety
-    ///
-    /// This should only used for types with a defined storage representation, usually
-    /// `#[repr(C)]`, otherwise the results may be inconsistent.
-    ///
-    /// [`Endian`]: https://docs.rs/endian_trait/0.6/endian_trait/trait.Endian.html
-    unsafe fn write_le_value<T: Endian>(&mut self, value: T) -> io::Result<()>;
-
-    /// Read a big endian value.
-    ///
-    /// The input type is required to implement the [`Endian`] trait, and we make the assumption
-    /// that this is only done for types which are supposed to be read/writable directly.
-    ///
-    /// There's no way to directly depend on a type having a specific `#[repr(...)]`, therefore
-    /// this is considered unsafe.
-    ///
-    /// The underlying write call is `.write_all()`, so there are no partial writes.
-    ///
-    /// ```no_run
-    /// # use endian_trait::Endian;
-    /// use proxmox::tools::io::WriteExt;
-    ///
-    /// #[derive(Endian)]
-    /// #[repr(C, packed)]
-    /// struct Data {
-    ///     value: u16,
-    ///     count: u32,
-    /// }
-    ///
-    /// # fn code() -> std::io::Result<()> {
-    /// let mut file = std::fs::File::create("my-raw.dat")?;
-    /// // We know `Data` has a safe binary representation (#[repr(C, packed)]), so we can
-    /// // safely use our helper:
-    /// unsafe {
-    ///     file.write_be_value(Data {
-    ///         value: 1,
-    ///         count: 2,
-    ///     })?;
-    /// }
-    /// # Ok(())
-    /// # }
-    /// ```
-    ///
-    /// # Safety
-    ///
-    /// This should only used for types with a defined storage representation, usually
-    /// `#[repr(C)]`, otherwise the results may be inconsistent.
-    ///
-    /// [`Endian`]: https://docs.rs/endian_trait/0.6/endian_trait/trait.Endian.html
-    unsafe fn write_be_value<T: Endian>(&mut self, value: T) -> io::Result<()>;
-}
-
-impl<W: io::Write> WriteExt for W {
-    unsafe fn write_host_value<T: Endian>(&mut self, value: T) -> io::Result<()> {
-        self.write_all(std::slice::from_raw_parts(
-            &value as *const T as *const u8,
-            std::mem::size_of::<T>(),
-        ))
-    }
-
-    unsafe fn write_le_value<T: Endian>(&mut self, value: T) -> io::Result<()> {
-        self.write_host_value::<T>(value.to_le())
-    }
-
-    unsafe fn write_be_value<T: Endian>(&mut self, value: T) -> io::Result<()> {
-        self.write_host_value::<T>(value.to_be())
-    }
-}
index f4f78dcbe900906459dee2ee311bf2887c3ab2d8..1c426666d1567441c78544e27e06a098aa04b13a 100644 (file)
@@ -6,16 +6,13 @@ use anyhow::*;
 use lazy_static::lazy_static;
 
 pub mod as_any;
-pub mod byte_buffer;
 pub mod common_regex;
 pub mod email;
 pub mod fd;
 pub mod fs;
-pub mod io;
 pub mod mmap;
 pub mod parse;
 pub mod serde;
-pub mod vec;
 pub mod systemd;
 
 #[doc(inline)]
diff --git a/proxmox/src/tools/vec/byte_vec.rs b/proxmox/src/tools/vec/byte_vec.rs
deleted file mode 100644 (file)
index 0b9c504..0000000
+++ /dev/null
@@ -1,111 +0,0 @@
-//! This module provides additional operations for `Vec<u8>`.
-//!
-//! Example:
-//! ```
-//! # use std::io::Read;
-//! use proxmox::tools::vec::{self, ByteVecExt};
-//!
-//! fn append_1024_to_vec<T: Read>(mut input: T, buffer: &mut Vec<u8>) -> std::io::Result<()> {
-//!     input.read_exact(unsafe { buffer.grow_uninitialized(1024) })
-//! }
-//! ```
-
-/// Some additional byte vector operations useful for I/O code.
-/// Example:
-/// ```
-/// # use std::io::Read;
-/// # use proxmox::tools::io::{self, ReadExt};
-/// use proxmox::tools::vec::{self, ByteVecExt};
-///
-/// # fn code(mut file: std::fs::File, mut data: Vec<u8>) -> std::io::Result<()> {
-/// file.read_exact(unsafe {
-///     data.grow_uninitialized(1024)
-/// })?;
-/// # Ok(())
-/// # }
-/// ```
-///
-/// Note that this module also provides a safe alternative for the case where
-/// `grow_uninitialized()` is directly followed by a `read_exact()` call via the [`ReadExt`]
-/// trait:
-/// ```ignore
-/// file.append_to_vec(&mut data, 1024)?;
-/// ```
-///
-/// [`ReadExt`]: crate::tools::io::ReadExt
-pub trait ByteVecExt {
-    /// Grow a vector without initializing its elements. The difference to simply using `reserve`
-    /// is that it also updates the actual length, making the newly allocated data part of the
-    /// slice.
-    ///
-    /// This is a shortcut for:
-    /// ```ignore
-    /// vec.reserve(more);
-    /// let total = vec.len() + more;
-    /// unsafe {
-    ///     vec.set_len(total);
-    /// }
-    /// ```
-    ///
-    /// This returns a mutable slice to the newly allocated space, so it can be used inline:
-    /// ```
-    /// # use std::io::Read;
-    /// # use proxmox::tools::vec::ByteVecExt;
-    /// # fn test(mut file: std::fs::File, buffer: &mut Vec<u8>) -> std::io::Result<()> {
-    /// file.read_exact(unsafe { buffer.grow_uninitialized(1024) })?;
-    /// # Ok(())
-    /// # }
-    /// ```
-    ///
-    /// Although for the above case it is recommended to use the even shorter version from the
-    /// [`ReadExt`] trait:
-    /// ```ignore
-    /// // use crate::tools::vec::ByteVecExt;
-    /// file.append_to_vec(&mut buffer, 1024)?;
-    /// ```
-    ///
-    /// # Safety
-    ///
-    /// When increasing the size, the new contents are uninitialized and have nothing to do with
-    /// the previously contained content. Since we cannot track this state through the type system,
-    /// this method is marked as an unsafe API for good measure.
-    ///
-    /// [`ReadExt`]: crate::tools::io::ReadExt
-    unsafe fn grow_uninitialized(&mut self, more: usize) -> &mut [u8];
-
-    /// Resize a vector to a specific size without initializing its data. This is a shortcut for:
-    /// ```ignore
-    /// if new_size <= vec.len() {
-    ///     vec.truncate(new_size);
-    /// } else {
-    ///     unsafe {
-    ///         vec.grow_uninitialized(new_size - vec.len());
-    ///     }
-    /// }
-    /// ```
-    ///
-    /// # Safety
-    ///
-    /// When increasing the size, the new contents are uninitialized and have nothing to do with
-    /// the previously contained content. Since we cannot track this state through the type system,
-    /// this method is marked as an unsafe API for good measure.
-    unsafe fn resize_uninitialized(&mut self, total: usize);
-}
-
-impl ByteVecExt for Vec<u8> {
-    unsafe fn grow_uninitialized(&mut self, more: usize) -> &mut [u8] {
-        let old_len = self.len();
-        self.reserve(more);
-        let total = old_len + more;
-        self.set_len(total);
-        &mut self[old_len..]
-    }
-
-    unsafe fn resize_uninitialized(&mut self, new_size: usize) {
-        if new_size <= self.len() {
-            self.truncate(new_size);
-        } else {
-            self.grow_uninitialized(new_size - self.len());
-        }
-    }
-}
diff --git a/proxmox/src/tools/vec/mod.rs b/proxmox/src/tools/vec/mod.rs
deleted file mode 100644 (file)
index 8ea343c..0000000
+++ /dev/null
@@ -1,130 +0,0 @@
-//! Byte vector helpers.
-//!
-//! We have a lot of I/O code such as:
-//! ```ignore
-//! let mut buffer = vec![0u8; header_size];
-//! file.read_exact(&mut buffer)?;
-//! ```
-//! (We even have this case with a 4M buffer!)
-//!
-//! This needlessly initializes the buffer to zero (which not only wastes time (an insane amount of
-//! time on debug builds, actually) but also prevents tools such as valgrind from pointing out
-//! access to actually uninitialized data, which may hide bugs...)
-//!
-//! This module provides some helpers for this kind of code. Many of these are supposed to stay on
-//! a lower level, with I/O helpers for types implementing [`Read`](std::io::Read) being available
-//! in the [`tools::io`](crate::tools::io) module.
-//!
-//! Examples:
-//! ```no_run
-//! use proxmox::tools::vec::{self, ByteVecExt};
-//!
-//! # let size = 64usize;
-//! # let more = 64usize;
-//! let mut buffer = vec::undefined(size); // A zero-initialized buffer with valgrind support
-//!
-//! let mut buffer = unsafe { vec::uninitialized(size) }; // an actually uninitialized buffer
-//! vec::clear(&mut buffer); // zero out an &mut [u8]
-//!
-//! vec::clear(unsafe {
-//!     buffer.grow_uninitialized(more) // grow the buffer with uninitialized bytes
-//! });
-//! ```
-
-mod byte_vec;
-pub use byte_vec::*;
-
-/// Create an uninitialized byte vector of a specific size.
-///
-/// This is just a shortcut for:
-/// ```no_run
-/// # let len = 64usize;
-/// let mut v = Vec::<u8>::with_capacity(len);
-/// unsafe {
-///     v.set_len(len);
-/// }
-/// ```
-///
-/// # Safety
-///
-/// It's generally not unsafe to use this method, but the contents are uninitialized, and since
-/// this does not return a `MaybeUninit` type to track the initialization state, this is simply
-/// marked as unsafe for good measure.
-#[inline]
-pub unsafe fn uninitialized(len: usize) -> Vec<u8> {
-    let mut out = Vec::with_capacity(len);
-    out.set_len(len);
-    out
-}
-
-/// Shortcut to zero out a slice of bytes.
-#[inline]
-pub fn clear(data: &mut [u8]) {
-    unsafe {
-        std::ptr::write_bytes(data.as_mut_ptr(), 0, data.len());
-    }
-}
-
-/// Create a newly allocated, zero initialized byte vector.
-#[inline]
-pub fn zeroed(len: usize) -> Vec<u8> {
-    unsafe {
-        let mut out = uninitialized(len);
-        clear(&mut out);
-        out
-    }
-}
-
-/// Create a newly allocated byte vector of a specific size with "undefined" content.
-///
-/// The data will be zero initialized, but, if the `valgrind` feature is activated, it will be
-/// marked as uninitialized for debugging.
-#[inline]
-pub fn undefined(len: usize) -> Vec<u8> {
-    undefined_impl(len)
-}
-
-#[cfg(not(feature = "valgrind"))]
-fn undefined_impl(len: usize) -> Vec<u8> {
-    zeroed(len)
-}
-
-#[cfg(feature = "valgrind")]
-fn undefined_impl(len: usize) -> Vec<u8> {
-    let out = zeroed(len);
-    vg::make_slice_undefined(&out[..]);
-    out
-}
-
-#[cfg(feature = "valgrind")]
-mod vg {
-    type ValgrindValue = valgrind_request::Value;
-
-    /// Mark a memory region as undefined when using valgrind, causing it to treat read access to
-    /// it as error.
-    #[inline]
-    pub(crate) fn make_mem_undefined(addr: *const u8, len: usize) -> ValgrindValue {
-        const MAKE_MEM_UNDEFINED: ValgrindValue =
-            (((b'M' as ValgrindValue) << 24) | ((b'C' as ValgrindValue) << 16)) + 1;
-        unsafe {
-            valgrind_request::do_client_request(
-                0,
-                &[
-                    MAKE_MEM_UNDEFINED,
-                    addr as usize as ValgrindValue,
-                    len as ValgrindValue,
-                    0,
-                    0,
-                    0,
-                ],
-            )
-        }
-    }
-
-    /// Mark a slice of bytes as undefined when using valgrind, causing it to treat read access to
-    /// it as error.
-    #[inline]
-    pub(crate) fn make_slice_undefined(data: &[u8]) -> ValgrindValue {
-        make_mem_undefined(data.as_ptr(), data.len())
-    }
-}