use super::*;
use core::mem;
use std::alloc::{alloc, alloc_zeroed, dealloc, Layout};

#[repr(C)]
pub struct OwnedByteSliceCellPtr(*mut u8, usize);

impl OwnedByteSliceCellPtr {
    /// # Safety
    /// The given pointer must point to a valid value of type `[u8]` with the specified length, be
    /// valid for reads and writes and be aligned to an 8-byte boundary.
    #[inline]
    pub unsafe fn new(ptr: *mut u8, len: usize) -> Self {
        OwnedByteSliceCellPtr(ptr, len)
    }

    #[inline]
    pub fn new_zeroed(len: usize) -> Self {
        let layout = Layout::from_size_align((len + 7) & !7, 8).unwrap();
        unsafe { OwnedByteSliceCellPtr(alloc_zeroed(layout), len) }
    }

    #[inline]
    pub fn len(&self) -> usize {
        self.1
    }

    #[inline]
    pub fn is_empty(&self) -> bool {
        self.len() == 0
    }

    #[inline]
    pub fn as_ptr(&self) -> *const u8 {
        self.0
    }

    pub fn as_mut_ptr(&self) -> *mut u8 {
        self.0
    }

    #[inline]
    pub fn as_slice_ptr(&self) -> *mut [u8] {
        core::ptr::slice_from_raw_parts_mut(self.0, self.1)
    }

    /// # Safety
    /// The lifetime of the returned value must not intersect with those of other unique references
    /// to the slice.
    #[inline]
    pub unsafe fn as_byte_slice(&self) -> ByteSlice {
        ByteSlice::new(&*self.as_slice_ptr())
    }

    /// # Safety
    /// The lifetime of the returned value must not intersect with those of other references to the
    /// slice.
    #[inline]
    pub unsafe fn as_byte_mut_slice(&self) -> ByteMutSlice {
        ByteMutSlice::new(&mut *self.as_slice_ptr())
    }
}

impl Clone for OwnedByteSliceCellPtr {
    fn clone(&self) -> Self {
        let layout = Layout::from_size_align((self.1 + 7) & !7, 8).unwrap();
        unsafe {
            let buffer = alloc(layout);
            core::ptr::copy_nonoverlapping(self.0, buffer, self.1);
            OwnedByteSliceCellPtr(buffer, self.1)
        }
    }
}

impl Drop for OwnedByteSliceCellPtr {
    #[inline]
    fn drop(&mut self) {
        let layout = Layout::from_size_align((self.1 + 7) & !7, 8).unwrap();
        unsafe { dealloc(self.0, layout) }
    }
}

impl_byteorder!(OwnedByteSliceCellPtr);
