From f0b3d5166ef1255fac880b146875ef46d2599a13 Mon Sep 17 00:00:00 2001 From: Matthias Schiffer Date: Sat, 4 Apr 2020 13:22:18 +0200 Subject: Reorganize --- .gitignore | 4 +- Cargo.lock | 7 ++ Cargo.toml | 2 +- safe_libc/Cargo.lock | 14 +++ safe_libc/Cargo.toml | 10 ++ safe_libc/src/boxed.rs | 146 ++++++++++++++++++++++++++ safe_libc/src/lib.rs | 18 ++++ safe_libc/src/stdio.rs | 47 +++++++++ safe_libc/src/string.rs | 151 +++++++++++++++++++++++++++ safe_libc/src/util.rs | 18 ++++ src/c/mod.rs | 3 - src/c/posix.rs | 7 -- src/c/stdio.rs | 42 -------- src/c/string.rs | 266 ------------------------------------------------ src/main.rs | 17 ++-- 15 files changed, 422 insertions(+), 330 deletions(-) create mode 100644 safe_libc/Cargo.lock create mode 100644 safe_libc/Cargo.toml create mode 100644 safe_libc/src/boxed.rs create mode 100644 safe_libc/src/lib.rs create mode 100644 safe_libc/src/stdio.rs create mode 100644 safe_libc/src/string.rs create mode 100644 safe_libc/src/util.rs delete mode 100644 src/c/mod.rs delete mode 100644 src/c/posix.rs delete mode 100644 src/c/stdio.rs delete mode 100644 src/c/string.rs diff --git a/.gitignore b/.gitignore index f0e3bca..11d7baa 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,2 @@ -/target -**/*.rs.bk \ No newline at end of file +target +*.rs.bk diff --git a/Cargo.lock b/Cargo.lock index 7495526..45e6ba1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -9,6 +9,13 @@ checksum = "dea0c0405123bba743ee3f91f49b1c7cfb684eef0da0a50110f758ccf24cdff0" [[package]] name = "neco" version = "0.1.0" +dependencies = [ + "safe_libc", +] + +[[package]] +name = "safe_libc" +version = "0.1.0" dependencies = [ "libc", ] diff --git a/Cargo.toml b/Cargo.toml index 4f8be4f..b7770a0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,7 +5,7 @@ authors = ["Matthias Schiffer "] edition = "2018" [dependencies] -libc = { version = "0.2.68", default-features = false } +safe_libc = { path = "safe_libc" } [profile.dev] panic = "abort" diff --git a/safe_libc/Cargo.lock b/safe_libc/Cargo.lock new file mode 100644 index 0000000..e659af2 --- /dev/null +++ b/safe_libc/Cargo.lock @@ -0,0 +1,14 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "libc" +version = "0.2.68" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dea0c0405123bba743ee3f91f49b1c7cfb684eef0da0a50110f758ccf24cdff0" + +[[package]] +name = "safe_libc" +version = "0.1.0" +dependencies = [ + "libc", +] diff --git a/safe_libc/Cargo.toml b/safe_libc/Cargo.toml new file mode 100644 index 0000000..f776d09 --- /dev/null +++ b/safe_libc/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "safe_libc" +version = "0.1.0" +authors = ["Matthias Schiffer "] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +libc = { version = "0.2.68", default-features = false } diff --git a/safe_libc/src/boxed.rs b/safe_libc/src/boxed.rs new file mode 100644 index 0000000..72c961c --- /dev/null +++ b/safe_libc/src/boxed.rs @@ -0,0 +1,146 @@ +use crate::util; + +use core::{mem, ptr}; +use core::ops::{Deref, DerefMut}; + +fn alloc(len: usize) -> *mut T { + if util::zst::(len) { + return ptr::null_mut(); + } + let size = len.checked_mul(mem::size_of::()).expect("allocation overflow"); + let align = mem::align_of::(); + util::must_succeed( + unsafe { + libc::memalign(align as libc::size_t, size as libc::size_t) as *mut T + } + ) +} + +#[inline] +fn dangling() -> *mut T { + mem::align_of::() as *mut T +} + +#[inline] +fn slice_len(p: *const [T]) -> usize { + unsafe { mem::transmute::<*const [T], [usize; 2]>(p)[1] } +} + +pub struct CBox(*mut T); + +impl CBox { + #[inline] + pub unsafe fn from_raw_unchecked(p: *mut T) -> CBox { + CBox(p) + } + + #[inline] + pub fn into_raw(self) -> *mut T { + let p = self.0; + mem::forget(self); + p + } + + #[inline] + pub fn as_ptr(&self) -> *const T { + self.0 + } + + #[inline] + pub fn as_mut_ptr(&mut self) -> *mut T { + self.0 + } +} + +impl CBox { + #[inline] + pub fn new(value: T) -> CBox { + let p = alloc(1); + unsafe { + ptr::write(p, value); + CBox::from_raw_unchecked(p) + } + } + + #[inline] + pub unsafe fn from_raw(p: *mut T) -> CBox { + util::check_ptr(p, 1); + CBox(p) + } + + #[inline] + pub unsafe fn slice_from_raw_parts_unchecked(p: *mut T, len: usize) -> CBox<[T]> { + CBox(ptr::slice_from_raw_parts_mut(p, len)) + } + + #[inline] + pub unsafe fn slice_from_raw_parts(p: *mut T, len: usize) -> CBox<[T]> { + util::check_ptr(p, len); + CBox::slice_from_raw_parts_unchecked(p, len) + } + + #[inline] + fn safe_ptr(&self) -> *mut T { + if util::zst::(1) { + return dangling(); + } + + debug_assert!(!self.0.is_null(), "NULL ptr"); + self.0 + } +} + +impl CBox<[T]> { + #[inline] + fn safe_ptr(&self) -> *mut [T] { + if self.0.is_null() { + let len = slice_len(self.0); + debug_assert!(util::zst::(len), "NULL ptr"); + return ptr::slice_from_raw_parts_mut(dangling(), len); + } + + self.0 + } +} + +impl Drop for CBox { + #[inline] + fn drop(&mut self) { + unsafe { + ptr::drop_in_place(self.0); + libc::free(self.0 as *mut libc::c_void); + } + } +} + +impl Deref for CBox { + type Target = T; + + #[inline] + fn deref(&self) -> &T { + unsafe { &*self.safe_ptr() } + } +} + +impl DerefMut for CBox { + #[inline] + fn deref_mut(&mut self) -> &mut T { + unsafe { &mut *self.safe_ptr() } + } +} + +impl Deref for CBox<[T]> { + type Target = [T]; + + #[inline] + fn deref(&self) -> &[T] { + unsafe { &*self.safe_ptr() } + } +} + +impl DerefMut for CBox<[T]> { + #[inline] + fn deref_mut(&mut self) -> &mut [T] { + unsafe { &mut *self.safe_ptr() } + } +} diff --git a/safe_libc/src/lib.rs b/safe_libc/src/lib.rs new file mode 100644 index 0000000..0ab0e89 --- /dev/null +++ b/safe_libc/src/lib.rs @@ -0,0 +1,18 @@ +#![no_std] + +pub use libc::*; + +mod util; + +pub mod boxed; +pub mod stdio; +pub mod string; + + +extern "C" { + pub fn strndup(cs: *const libc::c_char, n: libc::size_t) -> *mut libc::c_char; + + // pub static stdin: *mut libc::FILE; + pub static stdout: *mut libc::FILE; + pub static stderr: *mut libc::FILE; +} diff --git a/safe_libc/src/stdio.rs b/safe_libc/src/stdio.rs new file mode 100644 index 0000000..5f33f74 --- /dev/null +++ b/safe_libc/src/stdio.rs @@ -0,0 +1,47 @@ +use crate as libc; +use crate::string; + +use core::fmt; + +pub struct OStream { + file: *mut libc::FILE +} + +#[inline] +pub unsafe fn stdout() -> OStream { + OStream { file: libc::stdout } +} + +#[inline] +pub unsafe fn stderr() -> OStream { + OStream { file: libc::stderr } +} + +impl OStream { + #[inline] + pub fn write(&mut self, b: &[u8]) { + unsafe { + libc::fwrite( + b.as_ptr() as *const libc::c_void, + 1, + b.len(), + self.file, + ); + } + } + + #[inline] + pub fn puts(&mut self, s: &string::CStr) { + unsafe { + libc::fputs(s.as_ptr(), self.file); + } + } +} + +impl fmt::Write for OStream { + #[inline] + fn write_str(&mut self, s: &str) -> fmt::Result { + self.write(s.as_bytes()); + Ok(()) + } +} diff --git a/safe_libc/src/string.rs b/safe_libc/src/string.rs new file mode 100644 index 0000000..c85f788 --- /dev/null +++ b/safe_libc/src/string.rs @@ -0,0 +1,151 @@ +use crate as libc; +use crate::util; +use crate::boxed::CBox; + +use core::slice; +use core::ops::{Deref, DerefMut}; + +//pub struct FromBytesWithNulError {} + +pub struct CStr { inner: libc::c_char } + +impl CStr { + #[inline] + pub unsafe fn from_ptr_unchecked<'a>(p: *const libc::c_char) -> &'a CStr { + &*(p as *const CStr) + } + + #[inline] + pub unsafe fn from_ptr<'a>(p: *const libc::c_char) -> &'a CStr { + util::check_ptr(p, 1); + CStr::from_ptr_unchecked(p) + } + + #[inline] + pub unsafe fn from_mut_ptr_unchecked<'a>(p: *mut libc::c_char) -> &'a mut CStr { + &mut *(p as *mut CStr) + } + + #[inline] + pub unsafe fn from_mut_ptr<'a>(p: *mut libc::c_char) -> &'a mut CStr { + util::check_ptr(p, 1); + CStr::from_mut_ptr_unchecked(p) + } + + #[inline] + pub unsafe fn from_bytes_with_nul_unchecked(bytes: &[u8]) -> &CStr { + CStr::from_ptr_unchecked(bytes.as_ptr() as *const libc::c_char) + } + + // TODO + //pub fn from_bytes_with_nul(bytes: &[u8]) -> Result<&CStr, FromBytesWithNulError> { + //} + + #[inline] + pub fn len(&self) -> usize { + unsafe { libc::strlen(self.as_ptr()) as usize } + } + + #[inline] + pub const fn as_ptr(&self) -> *const libc::c_char { + &self.inner + } + + #[inline] + pub fn as_mut_ptr(&mut self) -> *mut libc::c_char { + &mut self.inner + } + + #[inline] + pub fn as_bytes(&self) -> &[u8] { + unsafe { slice::from_raw_parts( + self.as_ptr() as *const u8, + self.len(), + ) } + } + + #[inline] + pub fn as_mut_bytes(&mut self) -> &mut [u8] { + unsafe { slice::from_raw_parts_mut( + self.as_mut_ptr() as *mut u8, + self.len(), + ) } + } + + #[inline] + pub fn to_owned(self: &CStr) -> CString { + CString::from(self) + } +} + +#[macro_export] +macro_rules! cstr { + ($s:expr) => ( + unsafe { $crate::string::CStr::from_bytes_with_nul_unchecked(concat!($s, "\0").as_bytes()) } + ) +} + +pub struct CString { inner: CBox } + +impl CString { + #[inline] + pub unsafe fn from_raw_unchecked(p: *mut libc::c_char) -> CString { + CString { inner: CBox::from_raw_unchecked(p) } + } + + #[inline] + pub unsafe fn from_raw(p: *mut libc::c_char) -> CString { + util::check_ptr(p, 1); + CString::from_raw_unchecked(p) + } + + #[inline] + pub fn into_raw(self) -> *mut libc::c_char { + self.inner.into_raw() + } +} + +impl Deref for CString { + type Target = CStr; + + #[inline] + fn deref(&self) -> &CStr { + unsafe { CStr::from_ptr(&*self.inner) } + } +} + +impl DerefMut for CString { + #[inline] + fn deref_mut(&mut self) -> &mut CStr { + unsafe { CStr::from_mut_ptr(&mut *self.inner) } + } +} + +impl From<&[u8]> for CString { + fn from(s: &[u8]) -> CString { + unsafe { + CString::from_raw_unchecked( + util::must_succeed(libc::strndup( + s.as_ptr() as *const libc::c_char, + s.len() as libc::size_t, + )) + ) + } + } +} + +impl From<&str> for CString { + #[inline] + fn from(s: &str) -> CString { + CString::from(s.as_bytes()) + } +} + +impl From<&CStr> for CString { + #[inline] + fn from(s: &CStr) -> CString { + unsafe { + CString::from_raw_unchecked(util::must_succeed(libc::strdup(s.as_ptr()))) + } + } +} diff --git a/safe_libc/src/util.rs b/safe_libc/src/util.rs new file mode 100644 index 0000000..b8a4ed5 --- /dev/null +++ b/safe_libc/src/util.rs @@ -0,0 +1,18 @@ +use core::mem; + +#[inline] +pub fn zst(len: usize) -> bool { + mem::size_of::() == 0 || len == 0 +} + +#[inline] +pub fn check_ptr(p: *const T, len: usize) { + debug_assert!((p as usize) % mem::align_of::() == 0, "unaligned ptr"); + assert!(zst::(len) || !p.is_null(), "NULL ptr"); +} + +#[inline] +pub fn must_succeed(p: *mut T) -> *mut T { + assert!(!p.is_null(), "allocation failure"); + p +} diff --git a/src/c/mod.rs b/src/c/mod.rs deleted file mode 100644 index 3694561..0000000 --- a/src/c/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -pub mod posix; -pub mod stdio; -pub mod string; diff --git a/src/c/posix.rs b/src/c/posix.rs deleted file mode 100644 index 6b63ea7..0000000 --- a/src/c/posix.rs +++ /dev/null @@ -1,7 +0,0 @@ -extern "C" { - pub fn strndup(cs: *const libc::c_char, n: libc::size_t) -> *mut libc::c_char; - - // pub static stdin: *mut libc::FILE; - pub static stdout: *mut libc::FILE; - pub static stderr: *mut libc::FILE; -} diff --git a/src/c/stdio.rs b/src/c/stdio.rs deleted file mode 100644 index c878f19..0000000 --- a/src/c/stdio.rs +++ /dev/null @@ -1,42 +0,0 @@ -use core::fmt; - -use super::posix; -use super::string; - -pub struct OStream { - file: *mut libc::FILE -} - -pub unsafe fn stdout() -> OStream { - OStream { file: posix::stdout } -} - -pub unsafe fn stderr() -> OStream { - OStream { file: posix::stderr } -} - -impl OStream { - pub fn write(&mut self, b: &[u8]) { - unsafe { - libc::fwrite( - b.as_ptr() as *const libc::c_void, - 1, - b.len(), - self.file, - ); - } - } - - pub fn puts(&mut self, s: &string::CStr) { - unsafe { - libc::fputs(s.as_ptr(), self.file); - } - } -} - -impl fmt::Write for OStream { - fn write_str(&mut self, s: &str) -> fmt::Result { - self.write(s.as_bytes()); - Ok(()) - } -} diff --git a/src/c/string.rs b/src/c/string.rs deleted file mode 100644 index 3d991dc..0000000 --- a/src/c/string.rs +++ /dev/null @@ -1,266 +0,0 @@ -use super::posix; -use core::{mem, ptr, slice}; -use core::ops::{Deref, DerefMut}; - -fn must_succeed(p: *mut T) -> *mut T { - assert!(!p.is_null(), "allocation failure"); - p -} - -fn zst(count: usize) -> bool { - mem::size_of::() == 0 || count == 0 -} - -fn alloc(count: usize) -> *mut T { - if zst::(count) { - return ptr::null_mut(); - } - let size = count * mem::size_of::(); - let align = mem::align_of::(); - must_succeed( - unsafe { - libc::memalign(align as libc::size_t, size as libc::size_t) as *mut T - } - ) -} - -fn dangling() -> *mut T { - mem::align_of::() as *mut T -} - -fn check_ptr(p: *const T, len: usize) { - debug_assert!((p as usize) % mem::align_of::() == 0, "unaligned ptr"); - assert!(zst::(len) || !p.is_null(), "NULL ptr"); -} - -fn slice_len(p: *const [T]) -> usize { - unsafe { mem::transmute::<*const [T], [usize; 2]>(p)[1] } -} - -pub struct CBox(*mut T); - -impl CBox { - pub unsafe fn from_raw_unchecked(p: *mut T) -> CBox { - CBox(p) - } - - pub fn into_raw(self) -> *mut T { - let p = self.0; - mem::forget(self); - p - } - - pub fn as_ptr(&self) -> *const T { - self.0 - } - - pub fn as_mut_ptr(&mut self) -> *mut T { - self.0 - } -} - -impl CBox { - pub fn new(value: T) -> CBox { - let p = alloc(1); - unsafe { - ptr::write(p, value); - CBox::from_raw_unchecked(p) - } - } - - pub unsafe fn from_raw(p: *mut T) -> CBox { - check_ptr(p, 1); - CBox(p) - } - - pub unsafe fn slice_from_raw_parts_unchecked(p: *mut T, len: usize) -> CBox<[T]> { - CBox(ptr::slice_from_raw_parts_mut(p, len)) - } - - pub unsafe fn slice_from_raw_parts(p: *mut T, len: usize) -> CBox<[T]> { - check_ptr(p, len); - CBox::slice_from_raw_parts_unchecked(p, len) - } - - fn safe_ptr(&self) -> *mut T { - if self.0.is_null() { - debug_assert!(zst::(1), "NULL ptr"); - return dangling(); - } - - self.0 - } -} - -impl CBox<[T]> { - fn safe_ptr(&self) -> *mut [T] { - if self.0.is_null() { - let len = slice_len(self.0); - debug_assert!(zst::(len), "NULL ptr"); - return ptr::slice_from_raw_parts_mut(dangling(), len); - } - - self.0 - } -} - -impl Drop for CBox { - fn drop(&mut self) { - unsafe { - ptr::drop_in_place(self.0); - libc::free(self.0 as *mut libc::c_void); - } - } -} - -impl Deref for CBox { - type Target = T; - - fn deref(&self) -> &T { - unsafe { &*self.safe_ptr() } - } -} - -impl DerefMut for CBox { - fn deref_mut(&mut self) -> &mut T { - unsafe { &mut *self.safe_ptr() } - } -} - -impl Deref for CBox<[T]> { - type Target = [T]; - - fn deref(&self) -> &[T] { - unsafe { &*self.safe_ptr() } - } -} - -impl DerefMut for CBox<[T]> { - fn deref_mut(&mut self) -> &mut [T] { - unsafe { &mut *self.safe_ptr() } - } -} - -//pub struct FromBytesWithNulError {} - -pub struct CStr { inner: libc::c_char } - -impl CStr { - pub unsafe fn from_ptr_unchecked<'a>(p: *const libc::c_char) -> &'a CStr { - &*(p as *const CStr) - } - - pub unsafe fn from_ptr<'a>(p: *const libc::c_char) -> &'a CStr { - check_ptr(p, 1); - CStr::from_ptr_unchecked(p) - } - - pub unsafe fn from_mut_ptr_unchecked<'a>(p: *mut libc::c_char) -> &'a mut CStr { - &mut *(p as *mut CStr) - } - - pub unsafe fn from_mut_ptr<'a>(p: *mut libc::c_char) -> &'a mut CStr { - check_ptr(p, 1); - CStr::from_mut_ptr_unchecked(p) - } - - pub unsafe fn from_bytes_with_nul_unchecked(bytes: &[u8]) -> &CStr { - CStr::from_ptr_unchecked(bytes.as_ptr() as *const libc::c_char) - } - - // TODO - //pub fn from_bytes_with_nul(bytes: &[u8]) -> Result<&CStr, FromBytesWithNulError> { - //} - - pub fn len(&self) -> usize { - unsafe { libc::strlen(self.as_ptr()) as usize } - } - - pub const fn as_ptr(&self) -> *const libc::c_char { - &self.inner - } - - pub fn as_mut_ptr(&mut self) -> *mut libc::c_char { - &mut self.inner - } - - pub fn as_bytes(&self) -> &[u8] { - unsafe { slice::from_raw_parts( - self.as_ptr() as *const u8, - self.len(), - ) } - } - - pub fn as_mut_bytes(&mut self) -> &mut [u8] { - unsafe { slice::from_raw_parts_mut( - self.as_mut_ptr() as *mut u8, - self.len(), - ) } - } - - pub fn to_owned(self: &CStr) -> CString { - CString::from(self) - } -} - -#[macro_export] -macro_rules! cstr { - ($s:expr) => ( - unsafe { $crate::c::string::CStr::from_bytes_with_nul_unchecked(concat!($s, "\0").as_bytes()) } - ) -} - -pub struct CString { inner: CBox } - -impl CString { - pub unsafe fn from_raw_unchecked(p: *mut libc::c_char) -> CString { - CString { inner: CBox::from_raw_unchecked(p) } - } - - pub unsafe fn from_raw(p: *mut libc::c_char) -> CString { - CString { inner: CBox::from_raw(p) } - } - - pub fn into_raw(self) -> *mut libc::c_char { - self.inner.into_raw() - } -} - -impl Deref for CString { - type Target = CStr; - - fn deref(&self) -> &CStr { - unsafe { CStr::from_ptr(&*self.inner) } - } -} - -impl DerefMut for CString { - fn deref_mut(&mut self) -> &mut CStr { - unsafe { CStr::from_mut_ptr(&mut *self.inner) } - } -} - -impl From<&[u8]> for CString { - fn from(s: &[u8]) -> CString { - unsafe { - CString::from_raw_unchecked(must_succeed(posix::strndup( - s.as_ptr() as *const libc::c_char, - s.len() as libc::size_t, - ))) - } - } -} - -impl From<&str> for CString { - fn from(s: &str) -> CString { - CString::from(s.as_bytes()) - } -} - -impl From<&CStr> for CString { - fn from(s: &CStr) -> CString { - unsafe { - CString::from_raw_unchecked(must_succeed(libc::strdup(s.as_ptr()))) - } - } -} diff --git a/src/main.rs b/src/main.rs index 5f86a61..67d8baa 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,30 +1,29 @@ #![no_main] #![no_std] -extern crate libc; - -mod c; +use safe_libc as libc; +use libc::cstr; use core::fmt::Write; #[no_mangle] pub extern "C" fn main(_nargs: libc::c_int, _args: *const *const libc::c_char) -> libc::c_int { - let mut stdout = unsafe { c::stdio::stdout() }; - let x = c::string::CString::from("foo"); + let mut stdout = unsafe { libc::stdio::stdout() }; + let x = libc::string::CString::from("foo"); let l = x.len(); let y = x.into_raw(); let z = unsafe { - c::string::CBox::slice_from_raw_parts(y, l) + libc::boxed::CBox::slice_from_raw_parts(y, l) }; //let y = unsafe { c::string::CBox::from_raw(x) }; - //let foo = cstr!("Foo! %p\n"); - //c::stdio::stdout().puts(foo); + let foo = cstr!("Foo!\n"); + stdout.puts(foo); let _ = writeln!(stdout, "Foo: {} {} {}", z[0], z[1], z[2]); 0 } #[panic_handler] fn panic(info: &core::panic::PanicInfo) -> ! { - let _ = writeln!(unsafe { c::stdio::stderr() }, "Panic: {}", info); + let _ = writeln!(unsafe { libc::stdio::stderr() }, "Panic: {}", info); unsafe { libc::abort() } } -- cgit v1.2.3