more tests

This commit is contained in:
nora 2023-09-30 22:52:11 +02:00
parent 043c960708
commit 1827bd19c9
12 changed files with 240 additions and 72 deletions

View file

@ -4,14 +4,14 @@
use core::ffi::c_char; use core::ffi::c_char;
use libuwuc::{println, utils::SharedThinCstr}; use libuwuc::println;
extern crate rawc; extern crate rawc;
#[no_mangle] #[no_mangle]
extern "C" fn main(_argc: i32, _argv: *const *const c_char) -> i32 { extern "C" fn main(_argc: i32, _argv: *const *const c_char) -> i32 {
println!("Hello, world!"); 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:?}"); println!("PWD={pwd:?}");
0 0
} }

View file

@ -11,7 +11,7 @@ mod global {
static ENVP: SyncUnsafeCell<EnvP> = SyncUnsafeCell(UnsafeCell::new(EnvP(None))); static ENVP: SyncUnsafeCell<EnvP> = SyncUnsafeCell(UnsafeCell::new(EnvP(None)));
pub(super) unsafe fn init(envp: *mut Option<SharedThinCstr>) { pub(super) unsafe fn init(envp: *mut Option<SharedThinCstr<'static>>) {
assert!((*ENVP.0.get()).0.is_none()); assert!((*ENVP.0.get()).0.is_none());
*ENVP.0.get() = EnvP(Some(NonNull::new(envp).unwrap())); *ENVP.0.get() = EnvP(Some(NonNull::new(envp).unwrap()));
} }
@ -23,21 +23,21 @@ mod global {
} }
} }
pub(crate) unsafe fn init(envp: *mut Option<SharedThinCstr>) { pub(crate) unsafe fn init(envp: *mut Option<SharedThinCstr<'static>>) {
global::init(envp); global::init(envp);
} }
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
struct EnvP(Option<NonNull<Option<SharedThinCstr>>>); struct EnvP(Option<NonNull<Option<SharedThinCstr<'static>>>>);
unsafe impl Sync for EnvP {} unsafe impl Sync for EnvP {}
impl Iterator for EnvP { impl Iterator for EnvP {
type Item = SharedThinCstr; type Item = SharedThinCstr<'static>;
fn next(&mut self) -> Option<Self::Item> { fn next(&mut self) -> Option<Self::Item> {
unsafe { unsafe {
let value: Option<SharedThinCstr> = self.0.unwrap().as_ptr().read(); let value: Option<SharedThinCstr<'static>> = self.0.unwrap().as_ptr().read();
value.map(|value| { value.map(|value| {
self.0 = Some(NonNull::new_unchecked(self.0.unwrap().as_ptr().add(1))); 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"); println!("end vars");
} }
pub fn getenv(name: SharedThinCstr) -> Option<SharedThinCstr> { pub fn getenv(name: SharedThinCstr<'_>) -> Option<SharedThinCstr<'static>> {
getenv_inner(global::get(), name) getenv_inner(global::get(), name)
} }
fn getenv_inner(mut envp: EnvP, name: SharedThinCstr) -> Option<SharedThinCstr> { fn getenv_inner(mut envp: EnvP, name: SharedThinCstr<'_>) -> Option<SharedThinCstr<'static>> {
let mut eq_idx = 0; let mut eq_idx = 0;
envp.find(|env| { envp.find(|env| {
// Find ENV // Find ENV
@ -109,13 +109,9 @@ mod tests {
.map(|s| CString::new(s.to_string()).unwrap()) .map(|s| CString::new(s.to_string()).unwrap())
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let mut envs: Vec<Option<SharedThinCstr>> = cstrs let mut envs: Vec<Option<SharedThinCstr<'static>>> = cstrs
.iter() .iter()
.map(|cstr| unsafe { .map(|cstr| unsafe { Some(SharedThinCstr::from_raw(cstr.as_ptr() as _)) })
Some(SharedThinCstr::from_ptr(
NonNull::new(cstr.as_ptr() as _).unwrap(),
))
})
.collect(); .collect();
envs.push(None); envs.push(None);
@ -126,34 +122,28 @@ mod tests {
#[test] #[test]
fn getenv_exact_first() { fn getenv_exact_first() {
with_envp(&["UWU=a"], |envp| { with_envp(&["UWU=a"], |envp| {
assert_eq!( assert_eq!(super::getenv_inner(envp, cstr!("UWU")).unwrap(), cstr!("a"));
super::getenv_inner(envp, cstr("UWU\0")).unwrap(),
cstr("a\0")
);
}) })
} }
#[test] #[test]
fn getenv_previous_mismatches() { fn getenv_previous_mismatches() {
with_envp(&["UW=a", "UWUU=b", "UWU=c"], |envp| { with_envp(&["UW=a", "UWUU=b", "UWU=c"], |envp| {
assert_eq!( assert_eq!(super::getenv_inner(envp, cstr!("UWU")).unwrap(), cstr!("c"));
super::getenv_inner(envp, cstr("UWU\0")).unwrap(),
cstr("c\0")
);
}) })
} }
#[test] #[test]
fn getenv_name_long() { fn getenv_name_long() {
with_envp(&["U=w"], |envp| { 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] #[test]
fn getenv_same_length() { fn getenv_same_length() {
with_envp(&["OWO=a", "UWU=b"], |envp| { 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")));
}) })
} }
} }

View file

@ -1 +1,11 @@
pub mod printf; 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 _
}

18
libuwuc/src/fmt/parse.rs Normal file
View file

@ -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
}

View file

@ -4,8 +4,8 @@ use crate::{io::IoWrite, utils::SharedThinCstr};
pub unsafe fn printf_generic( pub unsafe fn printf_generic(
mut sink: impl IoWrite, mut sink: impl IoWrite,
format: SharedThinCstr, format: SharedThinCstr<'_>,
args: VaList<'_, '_>, _args: VaList<'_, '_>,
) -> Result<(), i32> { ) -> Result<(), i32> {
let mut chars = format.into_iter(); let mut chars = format.into_iter();
@ -31,7 +31,7 @@ mod tests {
use super::printf_generic; 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(); let mut sink = Vec::new();
printf_generic(&mut sink, fmt, args.as_va_list()).unwrap(); printf_generic(&mut sink, fmt, args.as_va_list()).unwrap();
@ -41,12 +41,14 @@ mod tests {
} }
#[test] #[test]
#[cfg_attr(miri, ignore = "variadic")]
fn empty_format() { fn empty_format() {
unsafe { test_printf("\0", cstr("\0")) } unsafe { test_printf("\0", cstr!("")) }
} }
#[test] #[test]
#[cfg_attr(miri, ignore = "variadic")]
fn constant_string() { fn constant_string() {
unsafe { test_printf("hello, world\0", cstr("hello, world\0")) } unsafe { test_printf("hello, world\0", cstr!("hello, world")) }
} }
} }

View file

@ -1,6 +1,7 @@
#![no_std] #![no_std]
#![feature(c_variadic)] #![feature(c_variadic)]
#![warn(unreachable_pub)] #![warn(unreachable_pub)]
#![warn(rust_2018_idioms)]
#![allow(clippy::missing_safety_doc)] #![allow(clippy::missing_safety_doc)]
#[cfg(test)] #[cfg(test)]

View file

@ -40,3 +40,128 @@ pub unsafe fn strlen(mut s: *const u8) -> usize {
} }
len 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);
}
}

View file

@ -5,7 +5,7 @@ use crate::utils::SharedThinCstr;
/// The entrypoint of the program. /// The entrypoint of the program.
/// This is called by a bit of assembly handling architecture-specific _start. /// 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) -> ! { 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<SharedThinCstr>; let envp = (8 + 8 * argc + rsp + 8) as *mut Option<SharedThinCstr<'static>>;
crate::env::init(envp); crate::env::init(envp);

View file

@ -1,9 +1,4 @@
use core::{ use core::{cell::UnsafeCell, ffi::CStr, fmt::Debug, marker::PhantomData, ptr::NonNull};
cell::UnsafeCell,
ffi::{c_char, CStr},
fmt::Debug,
ptr::NonNull,
};
#[repr(transparent)] #[repr(transparent)]
#[derive(Default)] #[derive(Default)]
@ -13,44 +8,55 @@ unsafe impl<T: Sync> Sync for SyncUnsafeCell<T> {}
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
#[repr(transparent)] #[repr(transparent)]
pub struct SharedThinCstr(pub NonNull<c_char>); pub struct SharedThinCstr<'a>(NonNull<u8>, PhantomData<&'a CStr>);
impl SharedThinCstr { #[macro_export]
pub unsafe fn from_ptr(ptr: NonNull<c_char>) -> Self { macro_rules! cstr {
Self(ptr) ($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 { pub fn as_ptr(self) -> NonNull<u8> {
Self(NonNull::new_unchecked(ptr as _))
}
pub fn from_array<const N: usize>(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<c_char> {
self.0 self.0
} }
pub fn as_raw(self) -> *const u8 {
self.0.as_ptr()
}
pub unsafe fn add(self, amount: usize) -> Self { 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<u8> {
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 Item = u8;
type IntoIter = CStrIter; type IntoIter = CStrIter<'a>;
fn into_iter(self) -> Self::IntoIter { fn into_iter(self) -> Self::IntoIter {
CStrIter(self) 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; type Item = u8;
fn next(&mut self) -> Option<Self::Item> { fn next(&mut self) -> Option<Self::Item> {
@ -66,10 +72,10 @@ impl Iterator for CStrIter {
} }
} }
unsafe impl Send for SharedThinCstr {} unsafe impl<'a> Send for SharedThinCstr<'a> {}
unsafe impl Sync for SharedThinCstr {} 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 { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
let str = <&CStr>::from(*self).to_str(); let str = <&CStr>::from(*self).to_str();
match str { match str {
@ -79,25 +85,19 @@ impl Debug for SharedThinCstr {
} }
} }
impl From<SharedThinCstr> for &CStr { impl<'a> From<SharedThinCstr<'a>> for &'a CStr {
fn from(value: SharedThinCstr) -> Self { fn from(value: SharedThinCstr<'a>) -> Self {
unsafe { CStr::from_ptr(value.0.as_ptr()) } 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 { fn eq(&self, other: &Self) -> bool {
self.into_iter().eq(*other) self.into_iter().eq(*other)
} }
} }
impl Eq for SharedThinCstr {} impl<'a> Eq for SharedThinCstr<'a> {}
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()) }
}
#[repr(transparent)] #[repr(transparent)]
pub(crate) struct SyncPtr<T>(pub(crate) *mut T); pub(crate) struct SyncPtr<T>(pub(crate) *mut T);

View file

@ -13,7 +13,7 @@ pub unsafe extern "C" fn puts(s: *const c_char) -> i32 {
// PRINTF: // PRINTF:
#[no_mangle] #[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 mut sink = WriteCounter(stdout, 0);
let result = libuwuc::fmt::printf::printf_generic( let result = libuwuc::fmt::printf::printf_generic(

View file

@ -1,3 +1,5 @@
use libuwuc::utils::SharedThinCstr;
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn malloc(size: usize) -> *mut u8 { pub unsafe extern "C" fn malloc(size: usize) -> *mut u8 {
libuwuc::alloc::malloc_zeroed(size, 16) 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) -> ! { pub unsafe extern "C" fn exit(code: i32) -> ! {
libuwuc::start::exit(code as i64 as _) 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())
}

13
tests/c/getenv.c Normal file
View file

@ -0,0 +1,13 @@
#include <stdlib.h>
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;
}
}