mirror of
https://github.com/roc-lang/roc.git
synced 2025-10-03 00:24:34 +00:00
make RocStr clone
This commit is contained in:
parent
981a84ccee
commit
0abc1cce15
2 changed files with 66 additions and 27 deletions
|
@ -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>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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() {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue