use std::{alloc::*, hash::Hash, marker::PhantomData, ptr::*};
pub struct Flake<T: ?Sized> {
_marker: PhantomData<T>,
inner_ptr: *const T,
}
impl<T: Sized> Flake<T> {
pub fn steal(value: T) -> Self {
unsafe {
let layout = Layout::new::<T>();
let raw = alloc(layout);
if raw.is_null() {
dealloc(raw, layout);
handle_alloc_error(layout);
}
write(raw as *mut T, value);
Self::from_raw(raw as *const T)
}
}
}
impl<T: ?Sized> Flake<T> {
#[inline(always)]
pub unsafe fn from_raw(ptr: *const T) -> Self {
Self { _marker: PhantomData, inner_ptr: ptr }
}
#[inline(always)]
pub unsafe fn from_raw_parts(ptr: *const u8, metadata: <T as Pointee>::Metadata) -> Self {
Self { _marker: PhantomData, inner_ptr: from_raw_parts::<T>(ptr, metadata) }
}
pub fn new(value: &T) -> Self {
unsafe {
let layout = Layout::for_value(value);
let raw = alloc(layout);
if raw.is_null() {
dealloc(raw, layout);
handle_alloc_error(layout);
}
copy_nonoverlapping(value as *const T as *const u8, raw, size_of_val(value));
Self::from_raw_parts(raw, metadata(value))
}
}
#[inline(always)]
pub unsafe fn drop_inner(&mut self) {
drop_in_place(self.inner_ptr.cast_mut());
}
#[inline(always)]
pub fn as_ptr(&self) -> *const T {
self.inner_ptr
}
#[inline(always)]
pub fn as_mut_ptr(&self) -> *mut T {
self.inner_ptr.cast_mut()
}
}
impl<T: ?Sized> Drop for Flake<T> {
fn drop(&mut self) {
unsafe {
dealloc(self.inner_ptr as *mut u8, Layout::for_value(&*self.inner_ptr));
}
}
}
impl<T: ?Sized> AsRef<T> for Flake<T> {
#[inline]
fn as_ref(&self) -> &T {
unsafe { &*self.inner_ptr }
}
}
impl<T: ?Sized> std::ops::Deref for Flake<T> {
type Target = T;
#[inline]
fn deref(&self) -> &Self::Target {
unsafe { &*self.inner_ptr }
}
}
impl<T: ?Sized> Eq for Flake<T> { }
impl<T: ?Sized> PartialEq for Flake<T> {
#[inline(always)]
fn eq(&self, other: &Self) -> bool {
addr_eq(self.inner_ptr, other.inner_ptr)
}
}
impl<T: ?Sized + Ord> Ord for Flake<T> {
#[inline(always)]
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
unsafe { (&*self.inner_ptr).cmp(&*other) }
}
}
impl<T: ?Sized + PartialOrd> PartialOrd for Flake<T> {
#[inline(always)]
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
unsafe { (&*self.inner_ptr).partial_cmp(&*other) }
}
}
impl<T: ?Sized + Hash> Hash for Flake<T> {
#[inline(always)]
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
unsafe { (&*self.inner_ptr).hash(state) }
}
}
unsafe impl<T: ?Sized> Send for Flake<T> {}
unsafe impl<T: ?Sized> Sync for Flake<T> {}
#[cfg(test)]
mod tests {
use super::Flake;
#[test]
fn flake_as_bytes() {
let input: &str = "hello";
let flake = Flake::new(input);
assert_eq!(&*flake, "hello");
println!("{:?} {:?}", flake.len(), b"hello");
}
#[test]
fn flake_from_str() {
let input: &str = "hello";
let flake = Flake::new(input);
assert_eq!(&*flake, "hello");
assert_eq!(flake.as_ref(), "hello");
}
#[test]
fn flake_from_slice() {
let slice: &[u8] = &[1, 2, 3, 4];
let flake = Flake::new(slice);
assert_eq!(&*flake, &[1, 2, 3, 4]);
}
#[test]
fn flake_eq_cmp_ord() {
let a = Flake::new("abc");
let b = Flake::new("abc");
let c = Flake::new("xyz");
assert!(a != b); assert_eq!(*a, *b); assert!(a < c);
}
#[test]
fn flake_mutate_bytes() {
let slice: &[u8] = &[1, 2, 3];
let flake = Flake::new(slice);
unsafe {
let ptr = flake.as_mut_ptr();
(*ptr)[1] = 9;
}
assert_eq!(&*flake, &[1, 9, 3]);
}
#[test]
fn flake_steal_string() {
let string = String::from("yo");
let flake = Flake::steal(string);
assert_eq!(&*flake, "yo");
}
#[test]
fn flake_from_raw_manual() {
use std::{alloc::*, ptr::copy_nonoverlapping, ptr::from_raw_parts_mut};
let slice: &[u8] = &[10, 20, 30];
let layout = Layout::for_value(slice);
unsafe {
let raw = alloc(layout);
assert!(!raw.is_null());
copy_nonoverlapping(slice.as_ptr(), raw, slice.len());
let ptr = from_raw_parts_mut::<[u8]>(raw as *mut (), slice.len());
let flake = Flake::from_raw(ptr);
assert_eq!(&*flake, &[10, 20, 30]);
}
}
}