From 1827bd19c9c65552b0cce8e091442b302db5ee59 Mon Sep 17 00:00:00 2001 From: Nilstrieb <48135649+Nilstrieb@users.noreply.github.com> Date: Sat, 30 Sep 2023 22:52:11 +0200 Subject: [PATCH] more tests --- example-user/src/main.rs | 4 +- libuwuc/src/env.rs | 36 ++++------- libuwuc/src/fmt/mod.rs | 12 +++- libuwuc/src/fmt/parse.rs | 18 ++++++ libuwuc/src/fmt/printf.rs | 12 ++-- libuwuc/src/lib.rs | 1 + libuwuc/src/mem.rs | 125 ++++++++++++++++++++++++++++++++++++++ libuwuc/src/start.rs | 2 +- libuwuc/src/utils/mod.rs | 78 ++++++++++++------------ rawc/src/stdio.rs | 2 +- rawc/src/stdlib.rs | 9 +++ tests/c/getenv.c | 13 ++++ 12 files changed, 240 insertions(+), 72 deletions(-) create mode 100644 libuwuc/src/fmt/parse.rs create mode 100644 tests/c/getenv.c diff --git a/example-user/src/main.rs b/example-user/src/main.rs index 80fade8..a1209d6 100644 --- a/example-user/src/main.rs +++ b/example-user/src/main.rs @@ -4,14 +4,14 @@ use core::ffi::c_char; -use libuwuc::{println, utils::SharedThinCstr}; +use libuwuc::println; extern crate rawc; #[no_mangle] extern "C" fn main(_argc: i32, _argv: *const *const c_char) -> i32 { println!("Hello, world!"); - let pwd = libuwuc::env::getenv(SharedThinCstr::from_array(b"PWD\0")); + let pwd = libuwuc::env::getenv(libuwuc::cstr!("PWD")); println!("PWD={pwd:?}"); 0 } diff --git a/libuwuc/src/env.rs b/libuwuc/src/env.rs index bfece0f..4d792fa 100644 --- a/libuwuc/src/env.rs +++ b/libuwuc/src/env.rs @@ -11,7 +11,7 @@ mod global { static ENVP: SyncUnsafeCell = SyncUnsafeCell(UnsafeCell::new(EnvP(None))); - pub(super) unsafe fn init(envp: *mut Option) { + pub(super) unsafe fn init(envp: *mut Option>) { assert!((*ENVP.0.get()).0.is_none()); *ENVP.0.get() = EnvP(Some(NonNull::new(envp).unwrap())); } @@ -23,21 +23,21 @@ mod global { } } -pub(crate) unsafe fn init(envp: *mut Option) { +pub(crate) unsafe fn init(envp: *mut Option>) { global::init(envp); } #[derive(Clone, Copy)] -struct EnvP(Option>>); +struct EnvP(Option>>>); unsafe impl Sync for EnvP {} impl Iterator for EnvP { - type Item = SharedThinCstr; + type Item = SharedThinCstr<'static>; fn next(&mut self) -> Option { unsafe { - let value: Option = self.0.unwrap().as_ptr().read(); + let value: Option> = self.0.unwrap().as_ptr().read(); value.map(|value| { self.0 = Some(NonNull::new_unchecked(self.0.unwrap().as_ptr().add(1))); @@ -57,11 +57,11 @@ pub(crate) fn debug_env() { println!("end vars"); } -pub fn getenv(name: SharedThinCstr) -> Option { +pub fn getenv(name: SharedThinCstr<'_>) -> Option> { getenv_inner(global::get(), name) } -fn getenv_inner(mut envp: EnvP, name: SharedThinCstr) -> Option { +fn getenv_inner(mut envp: EnvP, name: SharedThinCstr<'_>) -> Option> { let mut eq_idx = 0; envp.find(|env| { // Find ENV @@ -109,13 +109,9 @@ mod tests { .map(|s| CString::new(s.to_string()).unwrap()) .collect::>(); - let mut envs: Vec> = cstrs + let mut envs: Vec>> = cstrs .iter() - .map(|cstr| unsafe { - Some(SharedThinCstr::from_ptr( - NonNull::new(cstr.as_ptr() as _).unwrap(), - )) - }) + .map(|cstr| unsafe { Some(SharedThinCstr::from_raw(cstr.as_ptr() as _)) }) .collect(); envs.push(None); @@ -126,34 +122,28 @@ mod tests { #[test] fn getenv_exact_first() { with_envp(&["UWU=a"], |envp| { - assert_eq!( - super::getenv_inner(envp, cstr("UWU\0")).unwrap(), - cstr("a\0") - ); + assert_eq!(super::getenv_inner(envp, cstr!("UWU")).unwrap(), cstr!("a")); }) } #[test] fn getenv_previous_mismatches() { with_envp(&["UW=a", "UWUU=b", "UWU=c"], |envp| { - assert_eq!( - super::getenv_inner(envp, cstr("UWU\0")).unwrap(), - cstr("c\0") - ); + assert_eq!(super::getenv_inner(envp, cstr!("UWU")).unwrap(), cstr!("c")); }) } #[test] fn getenv_name_long() { with_envp(&["U=w"], |envp| { - assert_eq!(super::getenv_inner(envp, cstr("LONG_NAME\0")), None); + assert_eq!(super::getenv_inner(envp, cstr!("LONG_NAME")), None); }) } #[test] fn getenv_same_length() { with_envp(&["OWO=a", "UWU=b"], |envp| { - assert_eq!(super::getenv_inner(envp, cstr("UWU\0")), Some(cstr("b\0"))); + assert_eq!(super::getenv_inner(envp, cstr!("UWU")), Some(cstr!("b"))); }) } } diff --git a/libuwuc/src/fmt/mod.rs b/libuwuc/src/fmt/mod.rs index 933562f..9c26847 100644 --- a/libuwuc/src/fmt/mod.rs +++ b/libuwuc/src/fmt/mod.rs @@ -1 +1,11 @@ -pub mod printf; \ No newline at end of file +use core::ffi::c_int; + +pub mod parse; +pub mod printf; + +pub fn is_space(c: c_int) -> c_int { + // todo: is this correct? + char::from_u32(c as _) + .map(char::is_whitespace) + .unwrap_or_default() as _ +} diff --git a/libuwuc/src/fmt/parse.rs b/libuwuc/src/fmt/parse.rs new file mode 100644 index 0000000..5e44722 --- /dev/null +++ b/libuwuc/src/fmt/parse.rs @@ -0,0 +1,18 @@ +use core::ffi::{c_int, c_long}; + +use crate::utils::SharedThinCstr; + +pub unsafe fn parse_long<'a>( + str: SharedThinCstr<'a>, + endptr: *mut SharedThinCstr<'a>, + base: c_int, +) -> c_long { + let mut cur = str; + while cur + .first() + .map(|c| super::is_space(c as _) == 1) + .unwrap_or(false) + {} + + 0 +} diff --git a/libuwuc/src/fmt/printf.rs b/libuwuc/src/fmt/printf.rs index 8b9264a..e1eec98 100644 --- a/libuwuc/src/fmt/printf.rs +++ b/libuwuc/src/fmt/printf.rs @@ -4,8 +4,8 @@ use crate::{io::IoWrite, utils::SharedThinCstr}; pub unsafe fn printf_generic( mut sink: impl IoWrite, - format: SharedThinCstr, - args: VaList<'_, '_>, + format: SharedThinCstr<'_>, + _args: VaList<'_, '_>, ) -> Result<(), i32> { let mut chars = format.into_iter(); @@ -31,7 +31,7 @@ mod tests { use super::printf_generic; - unsafe extern "C" fn test_printf(expected: &str, fmt: SharedThinCstr, mut args: ...) { + unsafe extern "C" fn test_printf(expected: &str, fmt: SharedThinCstr<'_>, mut args: ...) { let mut sink = Vec::new(); printf_generic(&mut sink, fmt, args.as_va_list()).unwrap(); @@ -41,12 +41,14 @@ mod tests { } #[test] + #[cfg_attr(miri, ignore = "variadic")] fn empty_format() { - unsafe { test_printf("\0", cstr("\0")) } + unsafe { test_printf("\0", cstr!("")) } } #[test] + #[cfg_attr(miri, ignore = "variadic")] fn constant_string() { - unsafe { test_printf("hello, world\0", cstr("hello, world\0")) } + unsafe { test_printf("hello, world\0", cstr!("hello, world")) } } } diff --git a/libuwuc/src/lib.rs b/libuwuc/src/lib.rs index f42f278..6bec230 100644 --- a/libuwuc/src/lib.rs +++ b/libuwuc/src/lib.rs @@ -1,6 +1,7 @@ #![no_std] #![feature(c_variadic)] #![warn(unreachable_pub)] +#![warn(rust_2018_idioms)] #![allow(clippy::missing_safety_doc)] #[cfg(test)] diff --git a/libuwuc/src/mem.rs b/libuwuc/src/mem.rs index d6e0849..2fb80bb 100644 --- a/libuwuc/src/mem.rs +++ b/libuwuc/src/mem.rs @@ -40,3 +40,128 @@ pub unsafe fn strlen(mut s: *const u8) -> usize { } len } + +#[cfg(test)] +mod tests { + #[test] + fn memcpy_null() { + unsafe { super::memcpy(std::ptr::null_mut(), std::ptr::null_mut(), 0) }; + } + + #[test] + fn memcpy() { + let src = [1, 2, 3]; + let mut dest = [0; 3]; + unsafe { super::memcpy(dest.as_mut_ptr(), src.as_ptr(), 3) }; + assert_eq!(dest, src); + } + + #[test] + fn memset_null() { + unsafe { super::memset(std::ptr::null_mut(), 0, 0) }; + } + + #[test] + fn memset() { + let mut dest = [1; 10]; + unsafe { super::memset(dest.as_mut_ptr(), 0, 9) }; + assert_eq!(&dest[..9], &[0; 9]); + assert_eq!(dest[9], 1); + } + + #[test] + fn memcmp_null() { + let result = unsafe { super::memcmp(std::ptr::null(), std::ptr::null(), 0) }; + assert_eq!(result, 0); + } + + #[test] + fn memcmp_eq_one() { + let a = [1]; + let b = [1]; + let result = unsafe { super::memcmp(a.as_ptr(), b.as_ptr(), 1)}; + assert_eq!(result, 0); + } + + #[test] + fn memcmp_eq_two() { + let a = [1, 2]; + let b = [1, 2]; + let result = unsafe { super::memcmp(a.as_ptr(), b.as_ptr(), 2) }; + assert_eq!(result, 0); + } + + #[test] + fn memcmp_eq_many() { + let a = [1, 2, 5, 3, 5, 67, 7]; + let b = [1, 2, 5, 3, 5, 67, 7]; + let result = unsafe { super::memcmp(a.as_ptr(), b.as_ptr(), 7) }; + assert_eq!(result, 0); + } + + #[test] + fn memcmp_lt_one() { + let a = [0]; + let b = [1]; + let result = unsafe { super::memcmp(a.as_ptr(), b.as_ptr(), 1)}; + assert_eq!(result, -1); + } + + #[test] + fn memcmp_lt_two() { + let a = [1, 1]; + let b = [1, 2]; + let result = unsafe { super::memcmp(a.as_ptr(), b.as_ptr(), 2) }; + assert_eq!(result, -1); + } + + #[test] + fn memcmp_lt_many() { + let a = [1, 2, 5, 3, 4, 67, 7]; + let b = [1, 2, 5, 3, 5, 67, 7]; + let result = unsafe { super::memcmp(a.as_ptr(), b.as_ptr(), 7) }; + assert_eq!(result, -1); + } + + #[test] + fn memcmp_gt_one() { + let a = [255]; + let b = [1]; + let result = unsafe { super::memcmp(a.as_ptr(), b.as_ptr(), 1)}; + assert_eq!(result, 1); + } + + #[test] + fn memcmp_gt_two() { + let a = [1, 4]; + let b = [1, 2]; + let result = unsafe { super::memcmp(a.as_ptr(), b.as_ptr(), 2) }; + assert_eq!(result, 1); + } + + #[test] + fn memcmp_gt_many() { + let a = [1, 2, 6, 3, 4, 67, 7]; + let b = [1, 2, 5, 3, 5, 67, 7]; + let result = unsafe { super::memcmp(a.as_ptr(), b.as_ptr(), 7) }; + assert_eq!(result, 1); + } + + #[test] + fn strlen_empty() { + let str = b"\0"; + assert_eq!(unsafe { super::strlen(str.as_ptr()) }, 0); + } + + #[test] + fn strlen_one() { + let str = b"A\0"; + assert_eq!(unsafe { super::strlen(str.as_ptr()) }, 1); + } + + #[test] + fn strlen_many() { + let str = b"meow meow meow meow\0"; + assert_eq!(unsafe { super::strlen(str.as_ptr()) }, 19); + } +} diff --git a/libuwuc/src/start.rs b/libuwuc/src/start.rs index 7099d4e..1d0a408 100644 --- a/libuwuc/src/start.rs +++ b/libuwuc/src/start.rs @@ -5,7 +5,7 @@ use crate::utils::SharedThinCstr; /// The entrypoint of the program. /// This is called by a bit of assembly handling architecture-specific _start. pub(crate) unsafe extern "C" fn start(argc: u64, argv: *const *const c_char, rsp: u64) -> ! { - let envp = (8 + 8 * argc + rsp + 8) as *mut Option; + let envp = (8 + 8 * argc + rsp + 8) as *mut Option>; crate::env::init(envp); diff --git a/libuwuc/src/utils/mod.rs b/libuwuc/src/utils/mod.rs index f09adb7..51ac58e 100644 --- a/libuwuc/src/utils/mod.rs +++ b/libuwuc/src/utils/mod.rs @@ -1,9 +1,4 @@ -use core::{ - cell::UnsafeCell, - ffi::{c_char, CStr}, - fmt::Debug, - ptr::NonNull, -}; +use core::{cell::UnsafeCell, ffi::CStr, fmt::Debug, marker::PhantomData, ptr::NonNull}; #[repr(transparent)] #[derive(Default)] @@ -13,44 +8,55 @@ unsafe impl Sync for SyncUnsafeCell {} #[derive(Clone, Copy)] #[repr(transparent)] -pub struct SharedThinCstr(pub NonNull); +pub struct SharedThinCstr<'a>(NonNull, PhantomData<&'a CStr>); -impl SharedThinCstr { - pub unsafe fn from_ptr(ptr: NonNull) -> Self { - Self(ptr) +#[macro_export] +macro_rules! cstr { + ($value:literal) => {{ + let s = concat!($value, "\0"); + #[allow(unused_unsafe)] + unsafe { $crate::utils::SharedThinCstr::from_raw(s.as_ptr().cast()) } + }}; +} + +pub use cstr; + +impl<'a> SharedThinCstr<'a> { + pub unsafe fn from_raw(ptr: *const u8) -> Self { + Self(NonNull::new_unchecked(ptr as *mut u8), PhantomData) } - pub unsafe fn from_raw(ptr: *const c_char) -> Self { - Self(NonNull::new_unchecked(ptr as _)) - } - - pub fn from_array(arr: &[u8; N]) -> Self { - assert!(arr[N - 1] == 0); - unsafe { Self(NonNull::new_unchecked(arr as *const u8 as *mut c_char)) } - } - - pub fn as_ptr(self) -> NonNull { + pub fn as_ptr(self) -> NonNull { self.0 } + pub fn as_raw(self) -> *const u8 { + self.0.as_ptr() + } + pub unsafe fn add(self, amount: usize) -> Self { - Self(NonNull::new_unchecked(self.0.as_ptr().add(amount))) + Self::from_raw(self.0.as_ptr().add(amount)) + } + + pub fn first(self) -> Option { + let c = unsafe { self.0.as_ptr().read() }; + (c != 0).then_some(c as _) } } -impl IntoIterator for SharedThinCstr { +impl<'a> IntoIterator for SharedThinCstr<'a> { type Item = u8; - type IntoIter = CStrIter; + type IntoIter = CStrIter<'a>; fn into_iter(self) -> Self::IntoIter { CStrIter(self) } } -pub struct CStrIter(SharedThinCstr); +pub struct CStrIter<'a>(SharedThinCstr<'a>); -impl Iterator for CStrIter { +impl<'a> Iterator for CStrIter<'a> { type Item = u8; fn next(&mut self) -> Option { @@ -66,10 +72,10 @@ impl Iterator for CStrIter { } } -unsafe impl Send for SharedThinCstr {} -unsafe impl Sync for SharedThinCstr {} +unsafe impl<'a> Send for SharedThinCstr<'a> {} +unsafe impl<'a> Sync for SharedThinCstr<'a> {} -impl Debug for SharedThinCstr { +impl<'a> Debug for SharedThinCstr<'a> { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { let str = <&CStr>::from(*self).to_str(); match str { @@ -79,25 +85,19 @@ impl Debug for SharedThinCstr { } } -impl From for &CStr { - fn from(value: SharedThinCstr) -> Self { - unsafe { CStr::from_ptr(value.0.as_ptr()) } +impl<'a> From> for &'a CStr { + fn from(value: SharedThinCstr<'a>) -> Self { + unsafe { CStr::from_ptr(value.0.as_ptr().cast()) } } } -impl PartialEq for SharedThinCstr { +impl<'a> PartialEq for SharedThinCstr<'a> { fn eq(&self, other: &Self) -> bool { self.into_iter().eq(*other) } } -impl Eq for SharedThinCstr {} - - -pub fn cstr(s: &str) -> SharedThinCstr { - assert_eq!(s.as_bytes()[s.len() - 1], 0); - unsafe { SharedThinCstr::from_ptr(NonNull::new(s.as_ptr() as _).unwrap()) } -} +impl<'a> Eq for SharedThinCstr<'a> {} #[repr(transparent)] pub(crate) struct SyncPtr(pub(crate) *mut T); diff --git a/rawc/src/stdio.rs b/rawc/src/stdio.rs index 4c4cf25..4cd0128 100644 --- a/rawc/src/stdio.rs +++ b/rawc/src/stdio.rs @@ -13,7 +13,7 @@ pub unsafe extern "C" fn puts(s: *const c_char) -> i32 { // PRINTF: #[no_mangle] -pub unsafe extern "C" fn __printf_chk(_flag: c_int, format: *const c_char, mut args: ...) -> c_int { +pub unsafe extern "C" fn __printf_chk(_flag: c_int, format: *const u8, mut args: ...) -> c_int { let mut sink = WriteCounter(stdout, 0); let result = libuwuc::fmt::printf::printf_generic( diff --git a/rawc/src/stdlib.rs b/rawc/src/stdlib.rs index 7b4cb12..2a72434 100644 --- a/rawc/src/stdlib.rs +++ b/rawc/src/stdlib.rs @@ -1,3 +1,5 @@ +use libuwuc::utils::SharedThinCstr; + #[no_mangle] pub unsafe extern "C" fn malloc(size: usize) -> *mut u8 { libuwuc::alloc::malloc_zeroed(size, 16) @@ -12,3 +14,10 @@ pub unsafe extern "C" fn free(ptr: *mut u8) { pub unsafe extern "C" fn exit(code: i32) -> ! { libuwuc::start::exit(code as i64 as _) } + +#[no_mangle] +pub unsafe extern "C" fn getenv(name: *const u8) -> *const u8 { + libuwuc::env::getenv(SharedThinCstr::from_raw(name)) + .map(SharedThinCstr::as_raw) + .unwrap_or(core::ptr::null()) +} diff --git a/tests/c/getenv.c b/tests/c/getenv.c new file mode 100644 index 0000000..cfaf1f1 --- /dev/null +++ b/tests/c/getenv.c @@ -0,0 +1,13 @@ +#include + +int main(int argc, char *argv[]) { + char *env = getenv("PATH"); + if (!env) { + return 1; + } + + char *env2 = getenv("__some absolutely NONSENSE that no one would ever define please.."); + if (env2) { + return 1; + } +}