mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-27 13:59:08 +00:00
moved all crates into seperate folder + related path fixes
This commit is contained in:
parent
12ef03bb86
commit
eee85fa45d
1063 changed files with 92 additions and 93 deletions
536
crates/roc_std/src/lib.rs
Normal file
536
crates/roc_std/src/lib.rs
Normal file
|
@ -0,0 +1,536 @@
|
|||
#![crate_type = "lib"]
|
||||
#![cfg_attr(feature = "no_std", no_std)]
|
||||
|
||||
use core::cmp::Ordering;
|
||||
use core::ffi::c_void;
|
||||
use core::fmt::{self, Debug};
|
||||
use core::hash::{Hash, Hasher};
|
||||
use core::mem::{ManuallyDrop, MaybeUninit};
|
||||
use core::ops::Drop;
|
||||
use core::str;
|
||||
|
||||
use arrayvec::ArrayString;
|
||||
|
||||
mod roc_box;
|
||||
mod roc_list;
|
||||
mod roc_str;
|
||||
mod storage;
|
||||
|
||||
pub use roc_box::RocBox;
|
||||
pub use roc_list::RocList;
|
||||
pub use roc_str::{InteriorNulError, RocStr};
|
||||
pub use storage::Storage;
|
||||
|
||||
// A list of C functions that are being imported
|
||||
#[cfg(feature = "platform")]
|
||||
extern "C" {
|
||||
pub fn roc_alloc(size: usize, alignment: u32) -> *mut c_void;
|
||||
pub fn roc_realloc(
|
||||
ptr: *mut c_void,
|
||||
new_size: usize,
|
||||
old_size: usize,
|
||||
alignment: u32,
|
||||
) -> *mut c_void;
|
||||
pub fn roc_dealloc(ptr: *mut c_void, alignment: u32);
|
||||
pub fn roc_panic(c_ptr: *mut c_void, tag_id: u32);
|
||||
pub fn roc_memcpy(dst: *mut c_void, src: *mut c_void, n: usize) -> *mut c_void;
|
||||
pub fn roc_memset(dst: *mut c_void, c: i32, n: usize) -> *mut c_void;
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
/// This is only marked unsafe to typecheck without warnings in the rest of the code here.
|
||||
#[cfg(not(feature = "platform"))]
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn roc_alloc(_size: usize, _alignment: u32) -> *mut c_void {
|
||||
unimplemented!("It is not valid to call roc alloc from within the compiler. Please use the \"platform\" feature if this is a platform.")
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
/// This is only marked unsafe to typecheck without warnings in the rest of the code here.
|
||||
#[cfg(not(feature = "platform"))]
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn roc_realloc(
|
||||
_ptr: *mut c_void,
|
||||
_new_size: usize,
|
||||
_old_size: usize,
|
||||
_alignment: u32,
|
||||
) -> *mut c_void {
|
||||
unimplemented!("It is not valid to call roc realloc from within the compiler. Please use the \"platform\" feature if this is a platform.")
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
/// This is only marked unsafe to typecheck without warnings in the rest of the code here.
|
||||
#[cfg(not(feature = "platform"))]
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn roc_dealloc(_ptr: *mut c_void, _alignment: u32) {
|
||||
unimplemented!("It is not valid to call roc dealloc from within the compiler. Please use the \"platform\" feature if this is a platform.")
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "platform"))]
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn roc_panic(c_ptr: *mut c_void, tag_id: u32) {
|
||||
unimplemented!("It is not valid to call roc panic from within the compiler. Please use the \"platform\" feature if this is a platform.")
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
/// This is only marked unsafe to typecheck without warnings in the rest of the code here.
|
||||
#[cfg(not(feature = "platform"))]
|
||||
#[no_mangle]
|
||||
pub fn roc_memcpy(_dst: *mut c_void, _src: *mut c_void, _n: usize) -> *mut c_void {
|
||||
unimplemented!("It is not valid to call roc memcpy from within the compiler. Please use the \"platform\" feature if this is a platform.")
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
/// This is only marked unsafe to typecheck without warnings in the rest of the code here.
|
||||
#[cfg(not(feature = "platform"))]
|
||||
#[no_mangle]
|
||||
pub fn roc_memset(_dst: *mut c_void, _c: i32, _n: usize) -> *mut c_void {
|
||||
unimplemented!("It is not valid to call roc memset from within the compiler. Please use the \"platform\" feature if this is a platform.")
|
||||
}
|
||||
|
||||
pub fn roc_alloc_refcounted<T>() -> *mut T {
|
||||
let size = core::mem::size_of::<T>();
|
||||
let align = core::mem::align_of::<T>();
|
||||
|
||||
roc_alloc_refcounted_help(size, align) as *mut T
|
||||
}
|
||||
|
||||
fn roc_alloc_refcounted_help(mut size: usize, mut align: usize) -> *mut u8 {
|
||||
let prefix = if align > 8 { 16 } else { 8 };
|
||||
size += prefix;
|
||||
align = align.max(core::mem::size_of::<crate::Storage>());
|
||||
|
||||
unsafe {
|
||||
let allocation_ptr = roc_alloc(size, align as _) as *mut u8;
|
||||
let data_ptr = allocation_ptr.add(prefix);
|
||||
let storage_ptr = (data_ptr as *mut crate::Storage).sub(1);
|
||||
|
||||
*storage_ptr = Storage::new_reference_counted();
|
||||
|
||||
data_ptr
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(u8)]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub enum RocOrder {
|
||||
Eq = 0,
|
||||
Gt = 1,
|
||||
Lt = 2,
|
||||
}
|
||||
|
||||
/// Like a Rust `Result`, but following Roc's ABI instead of Rust's.
|
||||
/// (Using Rust's `Result` instead of this will not work properly with Roc code!)
|
||||
///
|
||||
/// This can be converted to/from a Rust `Result` using `.into()`
|
||||
#[repr(C)]
|
||||
pub struct RocResult<T, E> {
|
||||
payload: RocResultPayload<T, E>,
|
||||
tag: RocResultTag,
|
||||
}
|
||||
|
||||
impl<T, E> Debug for RocResult<T, E>
|
||||
where
|
||||
T: Debug,
|
||||
E: Debug,
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self.as_result_of_refs() {
|
||||
Ok(payload) => {
|
||||
f.write_str("RocOk(")?;
|
||||
payload.fmt(f)?;
|
||||
f.write_str(")")
|
||||
}
|
||||
Err(payload) => {
|
||||
f.write_str("RocErr(")?;
|
||||
payload.fmt(f)?;
|
||||
f.write_str(")")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, E> PartialEq for RocResult<T, E>
|
||||
where
|
||||
T: PartialEq,
|
||||
E: PartialEq,
|
||||
{
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.as_result_of_refs() == other.as_result_of_refs()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, E> Clone for RocResult<T, E>
|
||||
where
|
||||
T: Clone,
|
||||
E: Clone,
|
||||
{
|
||||
fn clone(&self) -> Self {
|
||||
match self.as_result_of_refs() {
|
||||
Ok(payload) => RocResult::ok(ManuallyDrop::into_inner(payload.clone())),
|
||||
Err(payload) => RocResult::err(ManuallyDrop::into_inner(payload.clone())),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, E> RocResult<T, E> {
|
||||
pub fn ok(payload: T) -> Self {
|
||||
Self {
|
||||
tag: RocResultTag::RocOk,
|
||||
payload: RocResultPayload {
|
||||
ok: ManuallyDrop::new(payload),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn err(payload: E) -> Self {
|
||||
Self {
|
||||
tag: RocResultTag::RocErr,
|
||||
payload: RocResultPayload {
|
||||
err: ManuallyDrop::new(payload),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_ok(&self) -> bool {
|
||||
matches!(self.tag, RocResultTag::RocOk)
|
||||
}
|
||||
|
||||
pub fn is_err(&self) -> bool {
|
||||
matches!(self.tag, RocResultTag::RocErr)
|
||||
}
|
||||
|
||||
fn into_payload(mut self) -> RocResultPayload<T, E> {
|
||||
let mut value = MaybeUninit::uninit();
|
||||
let ref_mut_value = unsafe { &mut *value.as_mut_ptr() };
|
||||
|
||||
// move the value into our MaybeUninit memory
|
||||
core::mem::swap(&mut self.payload, ref_mut_value);
|
||||
|
||||
// don't run the destructor on self; the `payload` has been moved out
|
||||
// and replaced by uninitialized memory
|
||||
core::mem::forget(self);
|
||||
|
||||
unsafe { value.assume_init() }
|
||||
}
|
||||
|
||||
fn as_result_of_refs(&self) -> Result<&ManuallyDrop<T>, &ManuallyDrop<E>> {
|
||||
use RocResultTag::*;
|
||||
|
||||
unsafe {
|
||||
match self.tag {
|
||||
RocOk => Ok(&self.payload.ok),
|
||||
RocErr => Err(&self.payload.err),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, E> From<RocResult<T, E>> for Result<T, E> {
|
||||
fn from(roc_result: RocResult<T, E>) -> Self {
|
||||
use RocResultTag::*;
|
||||
|
||||
let tag = roc_result.tag;
|
||||
let payload = roc_result.into_payload();
|
||||
|
||||
unsafe {
|
||||
match tag {
|
||||
RocOk => Ok(ManuallyDrop::into_inner(payload.ok)),
|
||||
RocErr => Err(ManuallyDrop::into_inner(payload.err)),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, E> From<Result<T, E>> for RocResult<T, E> {
|
||||
fn from(result: Result<T, E>) -> Self {
|
||||
match result {
|
||||
Ok(payload) => RocResult::ok(payload),
|
||||
Err(payload) => RocResult::err(payload),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(u8)]
|
||||
#[derive(Clone, Copy)]
|
||||
enum RocResultTag {
|
||||
RocErr = 0,
|
||||
RocOk = 1,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
union RocResultPayload<T, E> {
|
||||
ok: ManuallyDrop<T>,
|
||||
err: ManuallyDrop<E>,
|
||||
}
|
||||
|
||||
impl<T, E> Drop for RocResult<T, E> {
|
||||
fn drop(&mut self) {
|
||||
use RocResultTag::*;
|
||||
|
||||
match self.tag {
|
||||
RocOk => unsafe { ManuallyDrop::drop(&mut self.payload.ok) },
|
||||
RocErr => unsafe { ManuallyDrop::drop(&mut self.payload.err) },
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
|
||||
#[repr(C)]
|
||||
pub struct RocDec([u8; 16]);
|
||||
|
||||
impl RocDec {
|
||||
pub const MIN: Self = Self(i128::MIN.to_ne_bytes());
|
||||
pub const MAX: Self = Self(i128::MAX.to_ne_bytes());
|
||||
|
||||
const DECIMAL_PLACES: usize = 18;
|
||||
const ONE_POINT_ZERO: i128 = 10i128.pow(Self::DECIMAL_PLACES as u32);
|
||||
const MAX_DIGITS: usize = 39;
|
||||
const MAX_STR_LENGTH: usize = Self::MAX_DIGITS + 2; // + 2 here to account for the sign & decimal dot
|
||||
|
||||
pub fn new(num: i128) -> Self {
|
||||
Self(num.to_ne_bytes())
|
||||
}
|
||||
|
||||
pub fn as_bits(&self) -> (i64, u64) {
|
||||
let lower_bits = self.as_i128() as u64;
|
||||
let upper_bits = (self.as_i128() >> 64) as i64;
|
||||
(upper_bits, lower_bits)
|
||||
}
|
||||
|
||||
#[allow(clippy::should_implement_trait)]
|
||||
pub fn from_str(value: &str) -> Option<Self> {
|
||||
// Split the string into the parts before and after the "."
|
||||
let mut parts = value.split('.');
|
||||
|
||||
let before_point = match parts.next() {
|
||||
Some(answer) => answer,
|
||||
None => {
|
||||
return None;
|
||||
}
|
||||
};
|
||||
|
||||
let opt_after_point = match parts.next() {
|
||||
Some(answer) if answer.len() <= Self::DECIMAL_PLACES => Some(answer),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
// There should have only been one "." in the string!
|
||||
if parts.next().is_some() {
|
||||
return None;
|
||||
}
|
||||
|
||||
// Calculate the low digits - the ones after the decimal point.
|
||||
let lo = match opt_after_point {
|
||||
Some(after_point) => {
|
||||
match after_point.parse::<i128>() {
|
||||
Ok(answer) => {
|
||||
// Translate e.g. the 1 from 0.1 into 10000000000000000000
|
||||
// by "restoring" the elided trailing zeroes to the number!
|
||||
let trailing_zeroes = Self::DECIMAL_PLACES - after_point.len();
|
||||
let lo = answer * 10i128.pow(trailing_zeroes as u32);
|
||||
|
||||
if !before_point.starts_with('-') {
|
||||
lo
|
||||
} else {
|
||||
-lo
|
||||
}
|
||||
}
|
||||
Err(_) => {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
}
|
||||
None => 0,
|
||||
};
|
||||
|
||||
// Calculate the high digits - the ones before the decimal point.
|
||||
match before_point.parse::<i128>() {
|
||||
Ok(answer) => match answer.checked_mul(Self::ONE_POINT_ZERO) {
|
||||
Some(hi) => hi.checked_add(lo).map(|num| Self(num.to_ne_bytes())),
|
||||
None => None,
|
||||
},
|
||||
Err(_) => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_str_to_i128_unsafe(val: &str) -> i128 {
|
||||
Self::from_str(val).unwrap().as_i128()
|
||||
}
|
||||
|
||||
/// This is private because RocDec being an i128 is an implementation detail
|
||||
#[inline(always)]
|
||||
fn as_i128(&self) -> i128 {
|
||||
i128::from_ne_bytes(self.0)
|
||||
}
|
||||
|
||||
pub fn from_ne_bytes(bytes: [u8; 16]) -> Self {
|
||||
Self(bytes)
|
||||
}
|
||||
|
||||
pub fn to_ne_bytes(&self) -> [u8; 16] {
|
||||
self.0
|
||||
}
|
||||
|
||||
fn to_str_helper(self, string: &mut ArrayString<{ Self::MAX_STR_LENGTH }>) -> &str {
|
||||
use std::fmt::Write;
|
||||
|
||||
if self.as_i128() == 0 {
|
||||
return "0";
|
||||
}
|
||||
|
||||
// The :019 in the following write! is computed as Self::DECIMAL_PLACES + 1. If you change
|
||||
// Self::DECIMAL_PLACES, this assert should remind you to change that format string as well.
|
||||
static_assertions::const_assert!(RocDec::DECIMAL_PLACES + 1 == 19);
|
||||
|
||||
// By using the :019 format, we're guaranteeing that numbers less than 1, say 0.01234
|
||||
// get their leading zeros placed in bytes for us. i.e. `string = b"0012340000000000000"`
|
||||
write!(string, "{:019}", self.as_i128()).unwrap();
|
||||
|
||||
let is_negative = self.as_i128() < 0;
|
||||
let decimal_location = string.len() - Self::DECIMAL_PLACES + (is_negative as usize);
|
||||
|
||||
// skip trailing zeros
|
||||
let last_nonzero_byte = string.trim_end_matches('0').len();
|
||||
|
||||
if last_nonzero_byte < decimal_location {
|
||||
// This means that we've removed trailing zeros and are left with an integer. Our
|
||||
// convention is to print these without a decimal point or trailing zeros, so we're done.
|
||||
string.truncate(decimal_location);
|
||||
return string.as_str();
|
||||
}
|
||||
|
||||
// otherwise, we're dealing with a fraction, and need to insert the decimal dot
|
||||
|
||||
// truncate all extra zeros off
|
||||
string.truncate(last_nonzero_byte);
|
||||
|
||||
// push a dummy character so we have space for the decimal dot
|
||||
string.push('$');
|
||||
|
||||
// Safety: at any time, the string only contains ascii characters, so it is always valid utf8
|
||||
let bytes = unsafe { string.as_bytes_mut() };
|
||||
|
||||
// shift the fractional part by one
|
||||
bytes.copy_within(decimal_location..last_nonzero_byte, decimal_location + 1);
|
||||
|
||||
// and put in the decimal dot in the right place
|
||||
bytes[decimal_location] = b'.';
|
||||
|
||||
string.as_str()
|
||||
}
|
||||
|
||||
pub fn to_str(&self) -> RocStr {
|
||||
RocStr::from(self.to_str_helper(&mut ArrayString::new()))
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for RocDec {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.write_str(self.to_str_helper(&mut ArrayString::new()))
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C, align(16))]
|
||||
#[derive(Clone, Copy, Eq, Default)]
|
||||
pub struct I128([u8; 16]);
|
||||
|
||||
impl From<i128> for I128 {
|
||||
fn from(other: i128) -> Self {
|
||||
Self(other.to_ne_bytes())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<I128> for i128 {
|
||||
fn from(other: I128) -> Self {
|
||||
unsafe { core::mem::transmute::<I128, i128>(other) }
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for I128 {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
i128::from(*self).fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for I128 {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
Debug::fmt(&i128::from(*self), f)
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for I128 {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
i128::from(*self).eq(&i128::from(*other))
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd for I128 {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||
i128::from(*self).partial_cmp(&i128::from(*other))
|
||||
}
|
||||
}
|
||||
|
||||
impl Ord for I128 {
|
||||
fn cmp(&self, other: &Self) -> Ordering {
|
||||
i128::from(*self).cmp(&i128::from(*other))
|
||||
}
|
||||
}
|
||||
|
||||
impl Hash for I128 {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
i128::from(*self).hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C, align(16))]
|
||||
#[derive(Clone, Copy, Eq, Default)]
|
||||
pub struct U128([u8; 16]);
|
||||
|
||||
impl From<u128> for U128 {
|
||||
fn from(other: u128) -> Self {
|
||||
Self(other.to_ne_bytes())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<U128> for u128 {
|
||||
fn from(other: U128) -> Self {
|
||||
unsafe { core::mem::transmute::<U128, u128>(other) }
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for U128 {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
u128::from(*self).fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for U128 {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
Debug::fmt(&u128::from(*self), f)
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for U128 {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
u128::from(*self).eq(&u128::from(*other))
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd for U128 {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||
u128::from(*self).partial_cmp(&u128::from(*other))
|
||||
}
|
||||
}
|
||||
|
||||
impl Ord for U128 {
|
||||
fn cmp(&self, other: &Self) -> Ordering {
|
||||
u128::from(*self).cmp(&u128::from(*other))
|
||||
}
|
||||
}
|
||||
|
||||
impl Hash for U128 {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
u128::from(*self).hash(state);
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue