pebble_skip/
lib.rs

1//! Documentation for this crate is work in progress.
2//!
3//! For now, please also refer to the C API documentation at <https://developer.rebble.io/developer.pebble.com/docs/c/index.html> for more information.
4
5#![no_std]
6#![feature(coerce_unsized)]
7#![feature(layout_for_ptr)]
8#![feature(maybe_uninit_extra)]
9#![feature(maybe_uninit_ref)]
10#![feature(maybe_uninit_slice)]
11#![feature(min_specialization)]
12#![feature(never_type)]
13#![feature(unsize)]
14#![doc(html_root_url = "https://docs.rs/pebble/0.0.1")]
15#![warn(clippy::pedantic)]
16#![allow(clippy::match_bool)]
17#![allow(clippy::module_name_repetitions)] // Matching the SDK documentation.
18
19use core::{
20	future::Future,
21	intrinsics::drop_in_place,
22	marker::{PhantomData, Unsize},
23	mem::{size_of_val_raw, ManuallyDrop, MaybeUninit},
24	ops::{CoerceUnsized, Deref, DerefMut},
25	pin::Pin,
26	str,
27	task::{Context, Poll},
28};
29use pebble_sys::standard_c::memory::free;
30use standard_c::{
31	memory::{calloc, malloc, malloc_buffer_uninit, memcpy_uninit},
32	CStr, Heap,
33};
34
35pub mod foundation;
36pub mod graphics;
37pub mod standard_c;
38pub mod user_interface;
39
40trait SpecialDrop {
41	fn special_drop(&mut self);
42}
43
44/// Just a standard Box, more or less. The main difference is that its constructor is fallible instead of panicking.
45///
46/// It probably has fewer features than Rust's version, but it should be possible to add or emulate those.
47pub struct Box<'a, T: ?Sized>(&'a mut T);
48
49impl<'a, T> Box<'a, T> {
50	/// Moves `value` onto the Pebble heap.
51	///
52	/// # Errors
53	///
54	/// Iff the heap allocation fails.
55	pub fn new(value: T) -> Result<Self, T> {
56		match malloc::<T>() {
57			Ok(uninit) => Ok(Self(uninit.write(value))),
58			Err(()) => Err(value),
59		}
60	}
61
62	/// Moves `r#box`'s value off the Pebble heap.
63	#[must_use]
64	pub fn into_inner(r#box: Self) -> T {
65		let value;
66		unsafe {
67			let mem = Box::leak(r#box) as *mut T;
68			value = mem.read();
69			free(&mut *(mem as *mut _));
70		}
71		value
72	}
73}
74
75impl<'a> Box<'a, [MaybeUninit<u8>]> {
76	/// # Errors
77	///
78	/// Iff not enough heap memory could be allocated.
79	pub fn new_buffer_uninit(len: usize) -> Result<Self, ()> {
80		let mem = malloc_buffer_uninit(len)?;
81		Ok(Self(mem))
82	}
83
84	#[must_use]
85	pub fn assume_init(r#box: Self) -> Box<'a, [u8]> {
86		unsafe { Box::from_raw(MaybeUninit::slice_assume_init_mut(Box::leak(r#box))) }
87	}
88}
89
90impl<'a, T: ?Sized> Drop for Box<'a, T> {
91	fn drop(&mut self) {
92		unsafe {
93			//SAFETY: ptr is always a valid pointer here that originally belonged to a sized type.
94			let ptr = self.0 as *mut T;
95			drop_in_place(ptr);
96			match size_of_val_raw(ptr) {
97				0 => (),
98				_ => free(&mut *(ptr as *mut _)),
99			};
100		}
101	}
102}
103
104impl<'a, T: ?Sized> Box<'a, T> {
105	#[must_use = "If the Box instance is not reassembled later, a memory leak occurs."]
106	pub fn leak(r#box: Self) -> &'a mut T
107	where
108		T: 'a,
109	{
110		unsafe { &mut *(ManuallyDrop::new(r#box).deref_mut().0 as *mut T) }
111	}
112
113	/// Reassembles a [`Box`] instance from a leaked reference.
114	///
115	/// # Safety
116	///
117	/// Iff the reference was previously leaked from a matching [`Box`] instance.
118	pub unsafe fn from_raw(raw: &'a mut T) -> Self {
119		Self(raw)
120	}
121
122	/// Reinterprets a [`Box`] of an type `T` into its original sized type `Box<U>`.
123	///
124	/// # Safety
125	///
126	/// Iff this instance was created from a value memory-compatible to `U`.
127	#[must_use]
128	pub unsafe fn downcast_unchecked<U: Unsize<T>>(r#box: Self) -> Box<'a, U> {
129		Box::from_raw(&mut *(Box::leak(r#box) as *mut _ as *mut U))
130	}
131}
132
133impl<'a, T: ?Sized> Deref for Box<'a, T> {
134	type Target = T;
135
136	fn deref(&self) -> &Self::Target {
137		self.0
138	}
139}
140
141impl<'a, T: ?Sized> DerefMut for Box<'a, T> {
142	fn deref_mut(&mut self) -> &mut Self::Target {
143		self.0
144	}
145}
146
147impl<'a, T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<Box<'a, U>> for Box<'a, T> {}
148
149impl<'a, T: ?Sized> Unpin for Box<'a, T> {}
150
151impl<'a, F: ?Sized + Future + Unpin> Future for Box<'a, F> {
152	type Output = F::Output;
153
154	fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
155		F::poll(Pin::new(&mut *self), cx)
156	}
157}
158
159impl Box<'static, CStr<Heap>> {
160	/// Clones a Rust [`prim@str`] onto the Pebble heap, appending `'\0'` in the process.
161	///
162	/// # Errors
163	///
164	/// Iff the heap allocation fails.
165	pub fn clone_to_c_str(value: &str) -> Result<Self, ()> {
166		let mem = calloc::<u8>(value.len() + 1)?;
167		unsafe {
168			memcpy_uninit(&mut mem[..value.len()], value.as_bytes());
169			mem[value.len()].write(0);
170			let slice = MaybeUninit::slice_assume_init_mut(mem);
171			let str = str::from_utf8_unchecked_mut(slice);
172			let c_str = CStr::from_zero_terminated_unchecked_mut(str);
173			Ok(Self::from_raw(c_str))
174		}
175	}
176}
177
178/// This is *sort of* like a Cell, but for constant handles. It should still allow surface-level aliasing.
179///
180/// Note that this is a reference wrapper and does not drop its target!
181struct Handle<'a, T: 'a + ?Sized>(*mut T, PhantomData<&'a mut T>);
182
183impl<'a, T: 'a + ?Sized> Handle<'a, T> {
184	pub fn new(exclusive_handle: &'a mut T) -> Self {
185		Self(exclusive_handle as *mut T, PhantomData)
186	}
187
188	pub fn unwrap(self) -> &'a mut T {
189		unsafe { &mut *self.0 }
190	}
191
192	#[allow(clippy::mut_from_ref)]
193	pub unsafe fn as_mut_unchecked(&self) -> &mut T {
194		&mut *self.0
195	}
196
197	pub unsafe fn duplicate(&self) -> Self {
198		Self(self.0, self.1)
199	}
200}
201
202impl<'a, T: ?Sized> Deref for Handle<'a, T> {
203	type Target = T;
204
205	fn deref(&self) -> &Self::Target {
206		unsafe { &*self.0 }
207	}
208}
209
210impl<'a, T: ?Sized> DerefMut for Handle<'a, T> {
211	fn deref_mut(&mut self) -> &mut Self::Target {
212		unsafe { &mut *self.0 }
213	}
214}