use crate::fmt;
use crate::mem::take;
use crate::ops::{Deref, DerefMut};

cfg_select! {
    any(
        target_family = "unix",
        target_os = "hermit",
        target_os = "solid_asp3",
        target_os = "trusty",
        target_os = "wasi"
    ) => {
        #[path = "io_slice/repr_iovec.rs"]
        mod repr;
    }
    target_os = "windows" => {
        #[path = "io_slice/repr_windows.rs"]
        mod repr;
    }
    target_os = "uefi" => {
        #[path = "io_slice/repr_uefi.rs"]
        mod repr;
    }
    _ => {
        #[path = "io_slice/repr_generic.rs"]
        mod repr;
    }
}

/// A buffer type used with `Read::read_vectored`.
///
/// It is semantically a wrapper around a `&mut [u8]`, but is guaranteed to be
/// ABI compatible with the `iovec` type on Unix platforms and `WSABUF` on
/// Windows.
#[stable(feature = "iovec", since = "1.36.0")]
#[repr(transparent)]
pub struct IoSliceMut<'a>(repr::IoSliceMut<'a>);

#[stable(feature = "iovec_send_sync", since = "1.44.0")]
unsafe impl<'a> Send for IoSliceMut<'a> {}

#[stable(feature = "iovec_send_sync", since = "1.44.0")]
unsafe impl<'a> Sync for IoSliceMut<'a> {}

#[stable(feature = "iovec", since = "1.36.0")]
impl<'a> fmt::Debug for IoSliceMut<'a> {
    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
        fmt::Debug::fmt(self.0.as_slice(), fmt)
    }
}

impl<'a> IoSliceMut<'a> {
    /// Creates a new `IoSliceMut` wrapping a byte slice.
    ///
    /// # Panics
    ///
    /// Panics on Windows if the slice is larger than 4GB.
    #[stable(feature = "iovec", since = "1.36.0")]
    #[inline]
    pub fn new(buf: &'a mut [u8]) -> IoSliceMut<'a> {
        IoSliceMut(repr::IoSliceMut::new(buf))
    }

    /// Advance the internal cursor of the slice.
    ///
    /// Also see [`IoSliceMut::advance_slices`] to advance the cursors of
    /// multiple buffers.
    ///
    /// # Panics
    ///
    /// Panics when trying to advance beyond the end of the slice.
    ///
    /// # Examples
    ///
    /// ```
    /// use std::io::IoSliceMut;
    /// use std::ops::Deref;
    ///
    /// let mut data = [1; 8];
    /// let mut buf = IoSliceMut::new(&mut data);
    ///
    /// // Mark 3 bytes as read.
    /// buf.advance(3);
    /// assert_eq!(buf.deref(), [1; 5].as_ref());
    /// ```
    #[stable(feature = "io_slice_advance", since = "1.81.0")]
    #[inline]
    pub fn advance(&mut self, n: usize) {
        self.0.advance(n)
    }

    /// Advance a slice of slices.
    ///
    /// Shrinks the slice to remove any `IoSliceMut`s that are fully advanced over.
    /// If the cursor ends up in the middle of an `IoSliceMut`, it is modified
    /// to start at that cursor.
    ///
    /// For example, if we have a slice of two 8-byte `IoSliceMut`s, and we advance by 10 bytes,
    /// the result will only include the second `IoSliceMut`, advanced by 2 bytes.
    ///
    /// # Panics
    ///
    /// Panics when trying to advance beyond the end of the slices.
    ///
    /// # Examples
    ///
    /// ```
    /// use std::io::IoSliceMut;
    /// use std::ops::Deref;
    ///
    /// let mut buf1 = [1; 8];
    /// let mut buf2 = [2; 16];
    /// let mut buf3 = [3; 8];
    /// let mut bufs = &mut [
    ///     IoSliceMut::new(&mut buf1),
    ///     IoSliceMut::new(&mut buf2),
    ///     IoSliceMut::new(&mut buf3),
    /// ][..];
    ///
    /// // Mark 10 bytes as read.
    /// IoSliceMut::advance_slices(&mut bufs, 10);
    /// assert_eq!(bufs[0].deref(), [2; 14].as_ref());
    /// assert_eq!(bufs[1].deref(), [3; 8].as_ref());
    /// ```
    #[stable(feature = "io_slice_advance", since = "1.81.0")]
    #[inline]
    pub fn advance_slices(bufs: &mut &mut [IoSliceMut<'a>], n: usize) {
        // Number of buffers to remove.
        let mut remove = 0;
        // Remaining length before reaching n.
        let mut left = n;
        for buf in bufs.iter() {
            if let Some(remainder) = left.checked_sub(buf.len()) {
                left = remainder;
                remove += 1;
            } else {
                break;
            }
        }

        *bufs = &mut take(bufs)[remove..];
        if bufs.is_empty() {
            assert!(left == 0, "advancing io slices beyond their length");
        } else {
            bufs[0].advance(left);
        }
    }

    /// Get the underlying bytes as a mutable slice with the original lifetime.
    ///
    /// # Examples
    ///
    /// ```
    /// #![feature(io_slice_as_bytes)]
    /// use std::io::IoSliceMut;
    ///
    /// let mut data = *b"abcdef";
    /// let io_slice = IoSliceMut::new(&mut data);
    /// io_slice.into_slice()[0] = b'A';
    ///
    /// assert_eq!(&data, b"Abcdef");
    /// ```
    #[unstable(feature = "io_slice_as_bytes", issue = "132818")]
    pub const fn into_slice(self) -> &'a mut [u8] {
        self.0.into_slice()
    }
}

#[stable(feature = "iovec", since = "1.36.0")]
impl<'a> Deref for IoSliceMut<'a> {
    type Target = [u8];

    #[inline]
    fn deref(&self) -> &[u8] {
        self.0.as_slice()
    }
}

#[stable(feature = "iovec", since = "1.36.0")]
impl<'a> DerefMut for IoSliceMut<'a> {
    #[inline]
    fn deref_mut(&mut self) -> &mut [u8] {
        self.0.as_mut_slice()
    }
}

/// A buffer type used with `Write::write_vectored`.
///
/// It is semantically a wrapper around a `&[u8]`, but is guaranteed to be
/// ABI compatible with the `iovec` type on Unix platforms and `WSABUF` on
/// Windows.
#[stable(feature = "iovec", since = "1.36.0")]
#[derive(Copy, Clone)]
#[repr(transparent)]
pub struct IoSlice<'a>(repr::IoSlice<'a>);

#[stable(feature = "iovec_send_sync", since = "1.44.0")]
unsafe impl<'a> Send for IoSlice<'a> {}

#[stable(feature = "iovec_send_sync", since = "1.44.0")]
unsafe impl<'a> Sync for IoSlice<'a> {}

#[stable(feature = "iovec", since = "1.36.0")]
impl<'a> fmt::Debug for IoSlice<'a> {
    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
        fmt::Debug::fmt(self.0.as_slice(), fmt)
    }
}

impl<'a> IoSlice<'a> {
    /// Creates a new `IoSlice` wrapping a byte slice.
    ///
    /// # Panics
    ///
    /// Panics on Windows if the slice is larger than 4GB.
    #[stable(feature = "iovec", since = "1.36.0")]
    #[must_use]
    #[inline]
    pub fn new(buf: &'a [u8]) -> IoSlice<'a> {
        IoSlice(repr::IoSlice::new(buf))
    }

    /// Advance the internal cursor of the slice.
    ///
    /// Also see [`IoSlice::advance_slices`] to advance the cursors of multiple
    /// buffers.
    ///
    /// # Panics
    ///
    /// Panics when trying to advance beyond the end of the slice.
    ///
    /// # Examples
    ///
    /// ```
    /// use std::io::IoSlice;
    /// use std::ops::Deref;
    ///
    /// let data = [1; 8];
    /// let mut buf = IoSlice::new(&data);
    ///
    /// // Mark 3 bytes as read.
    /// buf.advance(3);
    /// assert_eq!(buf.deref(), [1; 5].as_ref());
    /// ```
    #[stable(feature = "io_slice_advance", since = "1.81.0")]
    #[inline]
    pub fn advance(&mut self, n: usize) {
        self.0.advance(n)
    }

    /// Advance a slice of slices.
    ///
    /// Shrinks the slice to remove any `IoSlice`s that are fully advanced over.
    /// If the cursor ends up in the middle of an `IoSlice`, it is modified
    /// to start at that cursor.
    ///
    /// For example, if we have a slice of two 8-byte `IoSlice`s, and we advance by 10 bytes,
    /// the result will only include the second `IoSlice`, advanced by 2 bytes.
    ///
    /// # Panics
    ///
    /// Panics when trying to advance beyond the end of the slices.
    ///
    /// # Examples
    ///
    /// ```
    /// use std::io::IoSlice;
    /// use std::ops::Deref;
    ///
    /// let buf1 = [1; 8];
    /// let buf2 = [2; 16];
    /// let buf3 = [3; 8];
    /// let mut bufs = &mut [
    ///     IoSlice::new(&buf1),
    ///     IoSlice::new(&buf2),
    ///     IoSlice::new(&buf3),
    /// ][..];
    ///
    /// // Mark 10 bytes as written.
    /// IoSlice::advance_slices(&mut bufs, 10);
    /// assert_eq!(bufs[0].deref(), [2; 14].as_ref());
    /// assert_eq!(bufs[1].deref(), [3; 8].as_ref());
    #[stable(feature = "io_slice_advance", since = "1.81.0")]
    #[inline]
    pub fn advance_slices(bufs: &mut &mut [IoSlice<'a>], n: usize) {
        // Number of buffers to remove.
        let mut remove = 0;
        // Remaining length before reaching n. This prevents overflow
        // that could happen if the length of slices in `bufs` were instead
        // accumulated. Those slice may be aliased and, if they are large
        // enough, their added length may overflow a `usize`.
        let mut left = n;
        for buf in bufs.iter() {
            if let Some(remainder) = left.checked_sub(buf.len()) {
                left = remainder;
                remove += 1;
            } else {
                break;
            }
        }

        *bufs = &mut take(bufs)[remove..];
        if bufs.is_empty() {
            assert!(left == 0, "advancing io slices beyond their length");
        } else {
            bufs[0].advance(left);
        }
    }

    /// Get the underlying bytes as a slice with the original lifetime.
    ///
    /// This doesn't borrow from `self`, so is less restrictive than calling
    /// `.deref()`, which does.
    ///
    /// # Examples
    ///
    /// ```
    /// #![feature(io_slice_as_bytes)]
    /// use std::io::IoSlice;
    ///
    /// let data = b"abcdef";
    ///
    /// let mut io_slice = IoSlice::new(data);
    /// let tail = &io_slice.as_slice()[3..];
    ///
    /// // This works because `tail` doesn't borrow `io_slice`
    /// io_slice = IoSlice::new(tail);
    ///
    /// assert_eq!(io_slice.as_slice(), b"def");
    /// ```
    #[unstable(feature = "io_slice_as_bytes", issue = "132818")]
    pub const fn as_slice(self) -> &'a [u8] {
        self.0.as_slice()
    }
}

#[stable(feature = "iovec", since = "1.36.0")]
impl<'a> Deref for IoSlice<'a> {
    type Target = [u8];

    #[inline]
    fn deref(&self) -> &[u8] {
        self.0.as_slice()
    }
}
