mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-29 14:54:47 +00:00
Add RocStr and convert HelloWord example to use it
This commit is contained in:
parent
d37ed89653
commit
77b26c3193
6 changed files with 283 additions and 10 deletions
23
examples/hello-world/platform/Cargo.lock
generated
Normal file
23
examples/hello-world/platform/Cargo.lock
generated
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
[[package]]
|
||||||
|
name = "host"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"roc_std 0.1.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libc"
|
||||||
|
version = "0.2.79"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "roc_std"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"libc 0.2.79 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
|
[metadata]
|
||||||
|
"checksum libc 0.2.79 (registry+https://github.com/rust-lang/crates.io-index)" = "2448f6066e80e3bfc792e9c98bf705b4b0fc6e8ef5b43e5889aff0eaa9c58743"
|
13
examples/hello-world/platform/Cargo.toml
Normal file
13
examples/hello-world/platform/Cargo.toml
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
[package]
|
||||||
|
name = "host"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["Richard Feldman <oss@rtfeldman.com>"]
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
crate-type = ["staticlib"]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
roc_std = { path = "../../../roc_std" }
|
||||||
|
|
||||||
|
[workspace]
|
|
@ -1,4 +1,12 @@
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
# We have a C host, so this is only one step.
|
# compile c_host.o and rust_host.o
|
||||||
clang -c host.c -o host.o
|
clang -c host.c -o c_host.o
|
||||||
|
rustc host.rs -o rust_host.o
|
||||||
|
|
||||||
|
# link them together into host.o
|
||||||
|
ld -r c_host.o rust_host.o -o host.o
|
||||||
|
|
||||||
|
# clean up
|
||||||
|
rm -f c_host.o
|
||||||
|
rm -f rust_host.o
|
||||||
|
|
|
@ -1,13 +1,7 @@
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
// TODO: use an actual RocStr here instead of char* - which is not the correct
|
extern int rust_main();
|
||||||
// type here, and which only works out to be equivalent in this particular
|
|
||||||
// example by pure coincidence. This should be easy to segfault by returning
|
|
||||||
// (for example) small or empty strings from Hello.roc. We should fix this!
|
|
||||||
extern char* main_1();
|
|
||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
printf("Roc says: %s\n", main_1());
|
return rust_main();
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
18
examples/hello-world/platform/src/lib.rs
Normal file
18
examples/hello-world/platform/src/lib.rs
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
use roc_std::RocStr;
|
||||||
|
use std::str;
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
#[link_name = "main_1"]
|
||||||
|
fn main() -> RocStr;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub fn rust_main() -> isize {
|
||||||
|
println!(
|
||||||
|
"Roc says: {}",
|
||||||
|
str::from_utf8(unsafe { main().as_slice() }).unwrap()
|
||||||
|
);
|
||||||
|
|
||||||
|
// Exit code
|
||||||
|
0
|
||||||
|
}
|
|
@ -214,3 +214,220 @@ impl<T> Drop for RocList<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct RocStr {
|
||||||
|
elements: *mut u8,
|
||||||
|
length: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RocStr {
|
||||||
|
pub fn len(&self) -> usize {
|
||||||
|
if self.is_small_str() {
|
||||||
|
let bytes = self.length.to_ne_bytes();
|
||||||
|
let last_byte = bytes[bytes.len() - 1];
|
||||||
|
(last_byte ^ 0b1000_0000) as usize
|
||||||
|
} else {
|
||||||
|
self.length
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_empty(&self) -> bool {
|
||||||
|
self.len() == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_small_str(&self) -> bool {
|
||||||
|
(self.length as isize) < 0
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn empty() -> Self {
|
||||||
|
RocStr {
|
||||||
|
// The first bit of length is 1 to specify small str.
|
||||||
|
length: isize::MIN as usize,
|
||||||
|
elements: core::ptr::null_mut(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get(&self, index: usize) -> Option<&u8> {
|
||||||
|
if index < self.len() {
|
||||||
|
Some(unsafe {
|
||||||
|
let raw = if self.is_small_str() {
|
||||||
|
self.get_small_str_ptr().add(index)
|
||||||
|
} else {
|
||||||
|
self.elements.add(index)
|
||||||
|
};
|
||||||
|
|
||||||
|
&*raw
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn storage(&self) -> Option<Storage> {
|
||||||
|
use core::cmp::Ordering::*;
|
||||||
|
|
||||||
|
if self.is_small_str() || self.length == 0 {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
let value = *self.get_storage_ptr();
|
||||||
|
|
||||||
|
// NOTE doesn't work with elements of 16 or more bytes
|
||||||
|
match isize::cmp(&(value as isize), &0) {
|
||||||
|
Equal => Some(Storage::ReadOnly),
|
||||||
|
Less => Some(Storage::Refcounted(value)),
|
||||||
|
Greater => Some(Storage::Capacity(value)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_storage_ptr(&self) -> *const usize {
|
||||||
|
let ptr = self.elements as *const usize;
|
||||||
|
|
||||||
|
unsafe { ptr.offset(-1) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_storage_ptr_mut(&mut self) -> *mut usize {
|
||||||
|
self.get_storage_ptr() as *mut usize
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_element_ptr(elements: *const u8) -> *const usize {
|
||||||
|
let elem_alignment = core::mem::align_of::<u8>();
|
||||||
|
let ptr = elements as *const usize;
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
if elem_alignment <= core::mem::align_of::<usize>() {
|
||||||
|
ptr.offset(1)
|
||||||
|
} else {
|
||||||
|
// If elements have an alignment bigger than usize (e.g. an i128),
|
||||||
|
// we will have necessarily allocated two usize slots worth of
|
||||||
|
// space for the storage value (with the first usize slot being
|
||||||
|
// padding for alignment's sake), and we need to skip past both.
|
||||||
|
ptr.offset(2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_small_str_ptr(&self) -> *const u8 {
|
||||||
|
(self as *const RocStr).cast()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_slice_with_capacity(slice: &[u8], capacity: usize) -> RocStr {
|
||||||
|
assert!(slice.len() <= capacity);
|
||||||
|
if capacity < core::mem::size_of::<usize>() {
|
||||||
|
let rocstr = RocStr::empty();
|
||||||
|
let target_ptr = rocstr.get_small_str_ptr() as *mut u8;
|
||||||
|
let source_ptr = slice.as_ptr() as *const u8;
|
||||||
|
for index in 0..(slice.len() as isize) {
|
||||||
|
unsafe {
|
||||||
|
*target_ptr.offset(index) = *source_ptr.offset(index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let ptr = slice.as_ptr();
|
||||||
|
let element_bytes = capacity;
|
||||||
|
|
||||||
|
let num_bytes = core::mem::size_of::<usize>() + element_bytes;
|
||||||
|
|
||||||
|
let elements = unsafe {
|
||||||
|
let raw_ptr = libc::malloc(num_bytes);
|
||||||
|
|
||||||
|
// write the capacity
|
||||||
|
let capacity_ptr = raw_ptr as *mut usize;
|
||||||
|
*capacity_ptr = capacity;
|
||||||
|
|
||||||
|
let raw_ptr = Self::get_element_ptr(raw_ptr as *mut u8);
|
||||||
|
|
||||||
|
{
|
||||||
|
// NOTE: using a memcpy here causes weird issues
|
||||||
|
let target_ptr = raw_ptr as *mut u8;
|
||||||
|
let source_ptr = ptr as *const u8;
|
||||||
|
let length = slice.len() as isize;
|
||||||
|
for index in 0..length {
|
||||||
|
*target_ptr.offset(index) = *source_ptr.offset(index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
raw_ptr as *mut u8
|
||||||
|
};
|
||||||
|
|
||||||
|
RocStr {
|
||||||
|
length: slice.len(),
|
||||||
|
elements,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_slice(slice: &[u8]) -> RocStr {
|
||||||
|
Self::from_slice_with_capacity(slice, slice.len())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn as_slice(&self) -> &[u8] {
|
||||||
|
if self.is_small_str() {
|
||||||
|
unsafe { core::slice::from_raw_parts(self.get_small_str_ptr(), self.len()) }
|
||||||
|
} else {
|
||||||
|
unsafe { core::slice::from_raw_parts(self.elements, self.length) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for RocStr {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
if self.is_small_str() {
|
||||||
|
// RocStr { is_small_str: true, elements: [ 1,2,3,4] }
|
||||||
|
f.debug_struct("RocStr")
|
||||||
|
.field("is_small_str", &true)
|
||||||
|
.field("elements", &self.as_slice())
|
||||||
|
.finish()
|
||||||
|
} else {
|
||||||
|
// RocStr { is_small_str: false, storage: Refcounted(3), elements: [ 1,2,3,4] }
|
||||||
|
f.debug_struct("RocStr")
|
||||||
|
.field("is_small_str", &false)
|
||||||
|
.field("storage", &self.storage())
|
||||||
|
.field("elements", &self.as_slice())
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialEq for RocStr {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
if self.length != other.length {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for i in 0..(self.length as isize) {
|
||||||
|
unsafe {
|
||||||
|
if *self.elements.offset(i) != *other.elements.offset(i) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Eq for RocStr {}
|
||||||
|
|
||||||
|
impl Drop for RocStr {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
if !self.is_small_str() {
|
||||||
|
use Storage::*;
|
||||||
|
match self.storage() {
|
||||||
|
None | Some(ReadOnly) => {}
|
||||||
|
Some(Capacity(_)) | Some(Refcounted(REFCOUNT_1)) => unsafe {
|
||||||
|
libc::free(self.get_storage_ptr() as *mut libc::c_void);
|
||||||
|
},
|
||||||
|
Some(Refcounted(rc)) => {
|
||||||
|
let sptr = self.get_storage_ptr_mut();
|
||||||
|
unsafe {
|
||||||
|
*sptr = rc - 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue