make RocStr clone

This commit is contained in:
Folkert 2021-01-03 03:04:28 +01:00
parent 981a84ccee
commit 0abc1cce15
2 changed files with 66 additions and 27 deletions

View file

@ -416,21 +416,18 @@ mod gen_list {
} }
#[test] #[test]
#[ignore]
fn list_keep_if_str_is_hello() { fn list_keep_if_str_is_hello() {
// keepIf causes a segfault with this function
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
r#" r#"
strIsHello : Str -> Bool List.keepIf ["x", "y", "x"] (\x -> x == "x")
strIsHello = \str ->
str == "Hello"
List.keepIf ["Hello", "Hello", "Goodbye"] strIsHello
"# "#
), ),
RocList::from_slice(&["Hello", "Hello"]), RocList::from_slice(&[
RocList<&'static str> RocStr::from_slice("x".as_bytes()),
RocStr::from_slice("x".as_bytes())
]),
RocList<RocStr>
); );
} }

View file

@ -99,26 +99,26 @@ impl<T> RocList<T> {
self.get_storage_ptr() as *mut usize self.get_storage_ptr() as *mut usize
} }
fn get_element_ptr<Q>(elements: *const Q) -> *const usize { fn get_element_ptr(elements: *const T) -> *const T {
let elem_alignment = core::mem::align_of::<T>(); let elem_alignment = core::mem::align_of::<T>();
let ptr = elements as *const usize; let ptr = elements as *const usize;
unsafe { unsafe {
if elem_alignment <= core::mem::align_of::<usize>() { if elem_alignment <= core::mem::align_of::<usize>() {
ptr.offset(1) ptr.offset(1) as *const T
} else { } else {
// If elements have an alignment bigger than usize (e.g. an i128), // If elements have an alignment bigger than usize (e.g. an i128),
// we will have necessarily allocated two usize slots worth of // we will have necessarily allocated two usize slots worth of
// space for the storage value (with the first usize slot being // space for the storage value (with the first usize slot being
// padding for alignment's sake), and we need to skip past both. // padding for alignment's sake), and we need to skip past both.
ptr.offset(2) ptr.offset(2) as *const T
} }
} }
} }
pub fn from_slice_with_capacity(slice: &[T], capacity: usize) -> RocList<T> pub fn from_slice_with_capacity(slice: &[T], capacity: usize) -> RocList<T>
where where
T: Copy, T: Clone,
{ {
assert!(slice.len() <= capacity); assert!(slice.len() <= capacity);
@ -138,25 +138,37 @@ impl<T> RocList<T> {
let num_bytes = core::mem::size_of::<usize>() + padding + element_bytes; let num_bytes = core::mem::size_of::<usize>() + padding + element_bytes;
let elements = unsafe { let elements = unsafe {
let raw_ptr = libc::malloc(num_bytes); let raw_ptr = libc::malloc(num_bytes) as *mut u8;
// pointer to the first element
let raw_ptr = Self::get_element_ptr(raw_ptr as *mut T) as *mut T;
// write the capacity // write the capacity
let capacity_ptr = raw_ptr as *mut usize; let capacity_ptr = raw_ptr as *mut usize;
*capacity_ptr = capacity; *(capacity_ptr.offset(-1)) = capacity;
let raw_ptr = Self::get_element_ptr(raw_ptr as *mut T);
{ {
// NOTE: using a memcpy here causes weird issues // NOTE: using a memcpy here causes weird issues
let target_ptr = raw_ptr as *mut T; let target_ptr = raw_ptr as *mut T;
let source_ptr = ptr as *const T; let source_ptr = ptr as *const T;
let length = slice.len() as isize; for index in 0..slice.len() {
for index in 0..length { let source = &*source_ptr.add(index);
*target_ptr.offset(index) = *source_ptr.offset(index); let target = &mut *target_ptr.add(index);
// NOTE for a weird reason, it's important that we clone onto the stack
// and explicitly forget the swapped-in value
// cloning directly from source to target causes some garbage memory (cast to a
// RocStr) to end up in the drop implementation of RocStr and cause havoc by
// freeing NULL
let mut temporary = source.clone();
core::mem::swap(target, &mut temporary);
core::mem::forget(temporary);
} }
} }
raw_ptr as *mut T raw_ptr
}; };
RocList { RocList {
@ -167,7 +179,7 @@ impl<T> RocList<T> {
pub fn from_slice(slice: &[T]) -> RocList<T> pub fn from_slice(slice: &[T]) -> RocList<T>
where where
T: Copy, T: Clone,
{ {
Self::from_slice_with_capacity(slice, slice.len()) Self::from_slice_with_capacity(slice, slice.len())
} }
@ -328,21 +340,27 @@ impl RocStr {
(self as *mut RocStr).cast() (self as *mut RocStr).cast()
} }
pub fn from_slice_with_capacity(slice: &[u8], capacity: usize) -> RocStr { fn from_slice_with_capacity_str(slice: &[u8], capacity: usize) -> RocStr {
assert!(slice.len() <= capacity); assert!(
slice.len() <= capacity,
"RocStr::from_slice_with_capacity_str length bigger than capacity {} {}",
slice.len(),
capacity
);
if capacity < core::mem::size_of::<RocStr>() { if capacity < core::mem::size_of::<RocStr>() {
let mut rocstr = RocStr::empty(); let mut rocstr = RocStr::empty();
let target_ptr = rocstr.get_small_str_ptr_mut(); let target_ptr = rocstr.get_small_str_ptr_mut();
let source_ptr = slice.as_ptr() as *const u8; let source_ptr = slice.as_ptr() as *const u8;
for index in 0..(slice.len() as isize) { for index in 0..slice.len() {
unsafe { unsafe {
*target_ptr.offset(index) = *source_ptr.offset(index); *target_ptr.add(index) = *source_ptr.add(index);
} }
} }
// Write length and small string bit to last byte of length. // Write length and small string bit to last byte of length.
let mut bytes = rocstr.length.to_ne_bytes(); let mut bytes = rocstr.length.to_ne_bytes();
bytes[bytes.len() - 1] = capacity as u8 ^ 0b1000_0000; bytes[bytes.len() - 1] = capacity as u8 ^ 0b1000_0000;
rocstr.length = usize::from_ne_bytes(bytes); rocstr.length = usize::from_ne_bytes(bytes);
rocstr rocstr
} else { } else {
let ptr = slice.as_ptr(); let ptr = slice.as_ptr();
@ -380,7 +398,7 @@ impl RocStr {
} }
pub fn from_slice(slice: &[u8]) -> RocStr { pub fn from_slice(slice: &[u8]) -> RocStr {
Self::from_slice_with_capacity(slice, slice.len()) Self::from_slice_with_capacity_str(slice, slice.len())
} }
pub fn as_slice(&self) -> &[u8] { pub fn as_slice(&self) -> &[u8] {
@ -423,6 +441,30 @@ impl PartialEq for RocStr {
impl Eq for RocStr {} impl Eq for RocStr {}
impl Clone for RocStr {
fn clone(&self) -> Self {
if self.is_small_str() {
Self {
elements: self.elements,
length: self.length,
}
} else {
let elements = unsafe {
let raw = libc::malloc(self.length);
libc::memcpy(raw, self.elements as *mut libc::c_void, self.length);
raw as *mut u8
};
Self {
elements,
length: self.length,
}
}
}
}
impl Drop for RocStr { impl Drop for RocStr {
fn drop(&mut self) { fn drop(&mut self) {
if !self.is_small_str() { if !self.is_small_str() {