pebble_skip/standard_c/
mod.rs

1use pebble_sys::standard_c::memory::c_str;
2
3use crate::Box;
4use core::{
5	convert::{TryFrom, TryInto},
6	marker::PhantomData,
7	ops::{Deref, DerefMut},
8	slice, str,
9};
10
11pub mod memory;
12
13#[allow(non_camel_case_types)]
14pub type void = pebble_sys::standard_c::memory::void;
15
16/// A zero-terminated UTF-8 string slice.  
17/// Note: When dereferencing this type to [`prim@str`], the trailing `'\0'` is **not** included.
18///
19/// # Why this is reimplemented (aside from not being in [`core`]):
20///
21/// Comment on [`std::ffi::CStr`]:
22/// ```text
23/// // Anyway, `CStr` representation and layout are considered implementation detail, are
24/// // not documented and must not be relied upon.
25/// ```
26///
27/// [`std::ffi::CStr`]: https://doc.rust-lang.org/stable/std/ffi/struct.CStr.html
28#[repr(transparent)]
29pub struct CStr<T: Storage>(PhantomData<T>, str);
30
31pub struct Heap(!);
32pub struct Stack(!);
33pub struct Static(!);
34
35mod private {
36	use super::{Heap, Stack, Static};
37
38	pub trait Sealed {}
39	impl Sealed for Heap {}
40	impl Sealed for Stack {}
41	impl Sealed for Static {}
42}
43
44pub trait Storage: private::Sealed {}
45impl Storage for Heap {}
46impl Storage for Stack {}
47impl Storage for Static {}
48
49pub trait NotStack: Storage {}
50impl NotStack for Heap {}
51impl NotStack for Static {}
52
53trait AsCStr {
54	type Storage: Storage;
55	fn as_c_str(&self) -> Result<&CStr<Self::Storage>, ()>;
56}
57
58impl<'a> TryFrom<Box<'a, str>> for Box<'a, CStr<Heap>> {
59	type Error = ();
60
61	fn try_from(value: Box<str>) -> Result<Self, Self::Error> {
62		match value.ends_with('\0') {
63			true => {
64				Ok(unsafe { Box::from_raw(&mut *(Box::leak(value) as *mut _ as *mut CStr<Heap>)) })
65			}
66			false => Err(()),
67		}
68	}
69}
70
71impl TryFrom<&'static str> for &'static CStr<Static> {
72	type Error = ();
73
74	fn try_from(value: &'static str) -> Result<Self, Self::Error> {
75		match value.ends_with('\0') {
76			true => Ok(unsafe { &*(value as *const _ as *const CStr<Static>) }),
77			false => Err(()),
78		}
79	}
80}
81
82impl TryFrom<&'static mut str> for &'static mut CStr<Static> {
83	type Error = ();
84
85	fn try_from(value: &'static mut str) -> Result<Self, Self::Error> {
86		match value.ends_with('\0') {
87			true => Ok(unsafe { &mut *(value as *mut _ as *mut CStr<Static>) }),
88			false => Err(()),
89		}
90	}
91}
92
93impl<'a> TryFrom<&'a str> for &'a CStr<Stack> {
94	type Error = ();
95
96	fn try_from(value: &'a str) -> Result<Self, Self::Error> {
97		match value.ends_with('\0') {
98			true => Ok(unsafe { &*(value as *const _ as *const CStr<Stack>) }),
99			false => Err(()),
100		}
101	}
102}
103
104impl<'a> TryFrom<&'a mut str> for &'a mut CStr<Stack> {
105	type Error = ();
106
107	fn try_from(value: &'a mut str) -> Result<Self, Self::Error> {
108		match value.ends_with('\0') {
109			true => Ok(unsafe { &mut *(value as *mut _ as *mut CStr<Stack>) }),
110			false => Err(()),
111		}
112	}
113}
114
115impl<'a> From<Box<'a, CStr<Heap>>> for Box<'a, str> {
116	fn from(value: Box<'a, CStr<Heap>>) -> Self {
117		unsafe { Box::from_raw(&mut *(Box::leak(value) as *mut _ as *mut str)) }
118	}
119}
120
121impl<T: Storage> Deref for CStr<T> {
122	type Target = str;
123
124	fn deref(&self) -> &Self::Target {
125		let s = unsafe { &*(self as *const _ as *const str) };
126		&s[..s.len() - 1]
127	}
128}
129
130impl<T: Storage> DerefMut for CStr<T> {
131	fn deref_mut(&mut self) -> &mut Self::Target {
132		let s = unsafe { &mut *(self as *mut _ as *mut str) };
133		let len = s.len();
134		&mut s[..len - 1]
135	}
136}
137
138impl<T: Storage> CStr<T> {
139	#[must_use]
140	pub fn as_c_str(&self) -> &c_str {
141		unsafe { &*(self as *const _ as *const c_str) }
142	}
143
144	#[must_use]
145	pub fn as_c_str_mut(&mut self) -> &mut c_str {
146		unsafe { &mut *(self as *mut _ as *mut c_str) }
147	}
148}
149
150impl<T: Storage> CStr<T> {
151	/// # Safety
152	///
153	/// As the name says, only if `slice` is zero-terminated and has [`Storage`] `T`.
154	#[must_use]
155	pub unsafe fn from_zero_terminated_unchecked(str: &str) -> &Self {
156		&*(str as *const _ as *const CStr<T>)
157	}
158
159	/// # Safety
160	///
161	/// As the name says, only if `slice` is zero-terminated and has [`Storage`] `T`.
162	#[must_use]
163	pub unsafe fn from_zero_terminated_unchecked_mut(str: &mut str) -> &mut Self {
164		&mut *(str as *mut _ as *mut CStr<T>)
165	}
166
167	/// # Safety
168	///
169	/// Only safe if `c_str` and `len` represent a valid zero-terminated UTF-8 string with [`Storage`] `T`.
170	#[must_use]
171	pub unsafe fn from_raw_parts_mut(c_str: &mut c_str, len: usize) -> &mut Self {
172		let slice = slice::from_raw_parts_mut(c_str as *mut _ as *mut u8, len);
173		let str = str::from_utf8_unchecked_mut(slice);
174		&mut *(str as *mut _ as *mut CStr<T>)
175	}
176}
177
178impl CStr<Stack> {
179	/// # Errors
180	///
181	/// If `str` doesn't end with `'\0'`.
182	pub fn try_from_stack(str: &str) -> Result<&Self, ()> {
183		str.try_into()
184	}
185}
186
187impl CStr<Static> {
188	/// # Errors
189	///
190	/// If `str` doesn't end with `'\0'`.
191	pub fn try_from_static(str: &'static str) -> Result<&'static Self, ()> {
192		str.try_into()
193	}
194
195	/// # Safety
196	///
197	/// Safe iff str is a zero-terminated str with static storage.
198	#[must_use]
199	pub unsafe fn from_static_zero_terminated_unchecked(str: &'static str) -> &'static Self {
200		CStr::<Static>::from_zero_terminated_unchecked(str)
201	}
202}