//! Bytes allocated by LevelDB.
use ::std::slice;

/// Bytes allocated by LevelDB
///
/// It's basically the same thing as `Box<[u8]>` except that it uses
/// leveldb_free() as a destructor.
pub struct Bytes {
    // We use static reference instead of pointer to inform the compiler that
    // it can't be null. (Because `NonZero` is unstable now.)
    bytes: &'static mut u8,
    size: usize,
    // Tells the compiler that we own u8
    _marker: ::std::marker::PhantomData<u8>,
}

impl Bytes {
    /// Creates instance of `Bytes` from LevelDB-allocated data.
    ///
    /// Returns `None` if `ptr` is `null`.
    ///
    /// # Safety
    ///
    /// The caller must ensure that:
    /// - `ptr` points to a valid memory block of at least `size` bytes
    /// - The memory block was allocated by LevelDB
    /// - The memory block remains valid for the lifetime of the returned `Bytes`
    /// - If `ptr` is not null, it must be properly aligned for u8
    pub unsafe fn from_raw(ptr: *mut u8, size: usize) -> Option<Self> {
        if ptr.is_null() {
            None
        } else {
            Some(Bytes {
                bytes: unsafe { &mut *ptr },
                size,
                _marker: Default::default(),
            })
        }
    }

    /// Creates instance of `Bytes` from LevelDB-allocated data without null checking.
    ///
    /// # Safety
    ///
    /// The caller must ensure that:
    /// - `ptr` is not null and points to a valid memory block of at least `size` bytes
    /// - The memory block was allocated by LevelDB
    /// - The memory block remains valid for the lifetime of the returned `Bytes`
    /// - `ptr` is properly aligned for u8
    pub unsafe fn from_raw_unchecked(ptr: *mut u8, size: usize) -> Self {
        Bytes {
            bytes: unsafe { &mut *ptr },
            size,
            _marker: Default::default(),
        }
    }
}

impl Drop for Bytes {
    fn drop(&mut self) {
        unsafe {
            use libc::c_void;

            crate::binding::leveldb_free(self.bytes as *mut u8 as *mut c_void);
        }
    }
}

impl ::std::ops::Deref for Bytes {
    type Target = [u8];

    fn deref(&self) -> &Self::Target {
        unsafe { slice::from_raw_parts(self.bytes, self.size) }
    }
}

impl AsRef<[u8]> for Bytes {
    fn as_ref(&self) -> &[u8] {
        self
    }
}
