commit 8fadf72227e60ae9d6c82fcb2cd38f11854c493c Author: Nilstrieb <48135649+Nilstrieb@users.noreply.github.com> Date: Sun Oct 23 12:36:02 2022 +0200 uwu diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..4e1a577 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "haha" +version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..9ea908b --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "haha" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/src/cfg_match.rs b/src/cfg_match.rs new file mode 100644 index 0000000..e69de29 diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..ea0baad --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,6 @@ +#![feature(ptr_metadata)] + +mod cfg_match; +mod unsized_clone; + +pub use unsized_clone::*; diff --git a/src/unsized_clone.rs b/src/unsized_clone.rs new file mode 100644 index 0000000..2a84234 --- /dev/null +++ b/src/unsized_clone.rs @@ -0,0 +1,159 @@ +// TODO: This should probably be fallible instead of panic +// TODO: Needs more safety docs around alignment + +use std::{marker::PhantomData, ptr::Pointee}; + +/// a replacement for Clone (ignoring the old methods) +pub trait NewClone { + fn clone_unsized

(&self, place: ClonePlace) -> InitClonePlace; +} + +/// a replacement for Copy +pub trait NewCopy: NewClone {} + +/// A trait which denotes a pointer to a place +pub trait Pointer { + /// Create a pointer from a raw pointer + /// # Safety + /// The pointer needs to be valid to create a `Self`. This method can't really be called + /// generically, but `ClonePlace` provides a safe interface over it. + unsafe fn from_raw(ptr: *mut T) -> Self; +} + +impl Pointer for Box { + unsafe fn from_raw(ptr: *mut T) -> Self { + Self::from_raw(ptr) + } +} + +impl Pointer for &mut T { + unsafe fn from_raw(ptr: *mut T) -> Self { + &mut *ptr + } +} + +// more impls... + +/// Denotes a place which something can be cloned into. +pub struct ClonePlace { + ptr: *mut u8, + max_size: usize, + _boo: PhantomData<(P, *const T)>, +} + +/// Denotes a place where something has been cloned into successfully. +pub struct InitClonePlace { + ptr: *mut u8, + metadata: ::Metadata, + _boo: PhantomData

, +} + +impl ClonePlace { + /// Get the raw pointer of the place to write things yourself + pub fn as_ptr(&self) -> *const u8 { + self.ptr + } + + /// Get the maximum allocation size of the place + pub fn max_size(&self) -> usize { + self.max_size + } + + /// Create a new ClonePlace from a pointer and maximum size + /// # Safety + /// `ptr` has to be valid for writes of size `max_size` + unsafe fn from_raw(ptr: *mut u8, max_size: usize) -> Self { + Self { + ptr, + max_size, + _boo: PhantomData, + } + } + + /// Unsafely assert that the place has been initialized for as many bytes as covered + /// by the metadata. This is done by using `as_ptr` and writing to it before + /// # Safety + /// `self.ptr` must be valid for reads of at least as much bytes as denoted by the `metadata` + unsafe fn assert_init_with_meta( + self, + metadata: ::Metadata, + ) -> InitClonePlace { + InitClonePlace { + ptr: self.ptr, + metadata, + _boo: PhantomData, + } + } +} + +impl ClonePlace { + /// Safe convenience function for implementing Clone via Copy + pub fn copy_trivially(self, data: &T) -> InitClonePlace { + let size = std::mem::size_of_val(data); + assert!(self.max_size() >= size); + // SAFETY: `data` is valid for reads of `sizeof(data)` + // `self.ptr` must be writable for at least as many bytes as `self.max_size`, which we just asserted + // We have initialized `self.ptr` by `sizeof(data)` bytes, meaning it's fine to assert it as init + unsafe { + std::ptr::copy_nonoverlapping(data as *const T as *const u8, self.ptr, size); + ClonePlace::assert_init_with_meta(self, std::ptr::metadata(data)) + } + } +} + +impl, T: ?Sized> InitClonePlace { + /// Turn the initialized place into the safe pointer type + pub fn into_init_value(self) -> P { + // SAFETY: Our pointer must point to valid initialized data + // The way it has been created initially asserts that it's valid for the pointer type or something like that i guess + unsafe { P::from_raw(std::ptr::from_raw_parts_mut(self.ptr.cast(), self.metadata)) } + } +} + +// convenience function +impl ClonePlace, T> { + /// Creates a new boxed ClonePlace and allocates as many bytes as required for `value` + pub fn boxed(value: &T) -> Self { + // SAFETY: We checked the pointer for null meaning it's valid for `laoyut.size()` bytes + // That's the safety requirement for creating a box basically so we're fine + unsafe { + let layout = std::alloc::Layout::for_value(value); + let allocated = std::alloc::alloc(layout); + if allocated.is_null() { + std::alloc::handle_alloc_error(layout); + } + Self::from_raw(allocated, layout.size()) + } + } +} + +impl NewClone for str { + fn clone_unsized

(&self, place: ClonePlace) -> InitClonePlace { + place.copy_trivially(self) + } +} + +impl NewCopy for str {} + +#[test] +fn boxit() { + let str = "aaaa"; + let place = ClonePlace::boxed(str); + let init_place = str.clone_unsized(place); + let the_box = init_place.into_init_value(); + assert_eq!(&*the_box, "aaaa"); +} + +#[test] +fn on_the_stack() { + let mut storage = [std::mem::MaybeUninit::::uninit(); 10]; + let str = "aaaa"; + + // SAFETY: `storage` is valid for writes of 10 bytes. + let place: ClonePlace<&mut str, _> = + unsafe { ClonePlace::from_raw(storage.as_mut_ptr().cast::(), 10) }; + + let init_place = str.clone_unsized(place); + let the_box = init_place.into_init_value(); + assert_eq!(&*the_box, "aaaa"); +}