diff --git a/Cargo.toml b/Cargo.toml index 9d2d114..6895294 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [workspace] resolver = "3" -members = ["crates/bytes-struct","crates/io","crates/os-std", "crates/shared", "user/*"] +members = ["crates/bytes-struct","crates/io","crates/std", "crates/shared", "user/*"] [package] name = "kernel-rust" diff --git a/crates/os-std/src/io.rs b/crates/os-std/src/io.rs deleted file mode 100644 index 079c8bb..0000000 --- a/crates/os-std/src/io.rs +++ /dev/null @@ -1 +0,0 @@ -pub use io::SeekFrom; diff --git a/crates/os-std/src/lib.rs b/crates/os-std/src/lib.rs deleted file mode 100644 index 8797a23..0000000 --- a/crates/os-std/src/lib.rs +++ /dev/null @@ -1,62 +0,0 @@ -#![no_std] - -extern crate alloc; - -pub mod io; -pub mod prelude; - -pub use shared::fs; -pub use shared::syscall; - -#[macro_export] -macro_rules! custom_std_setup { - () => { - use $crate::prelude::*; - - extern crate alloc; - - struct GlobalAllocator; - - #[global_allocator] - static GLOBAL_ALLOCATOR: GlobalAllocator = GlobalAllocator; - - unsafe impl core::alloc::GlobalAlloc for GlobalAllocator { - unsafe fn alloc(&self, layout: core::alloc::Layout) -> *mut u8 { - $crate::syscall::alloc(layout) - } - - unsafe fn dealloc(&self, ptr: *mut u8, layout: core::alloc::Layout) { - $crate::syscall::dealloc(ptr, layout) - } - } - - #[panic_handler] - fn panic(_panic_info: &core::panic::PanicInfo) -> ! { - // TODO print - loop {} - } - - #[unsafe(no_mangle)] - pub extern "C" fn _start() { - main() - } - }; -} - -#[macro_export] -macro_rules! print { - ($($args:expr),*) => { - $crate::syscall::write_string_temp(&format!($($args),*)) - }; -} -#[macro_export] -macro_rules! println { - () => { - $crate::print!(""); - // $crate::print!("\n\r"); - }; - ($($args:expr),*) => { - $crate::print!($($args),*); - // $crate::println!(); - }; -} diff --git a/crates/os-std/src/prelude.rs b/crates/os-std/src/prelude.rs deleted file mode 100644 index 48ffcc0..0000000 --- a/crates/os-std/src/prelude.rs +++ /dev/null @@ -1,5 +0,0 @@ -pub use crate::print; -pub use crate::println; -pub use alloc::format; -pub use alloc::string::String; -pub use alloc::vec; diff --git a/crates/shared/src/fs.rs b/crates/shared/src/fs.rs index b8f3fcc..45b6831 100644 --- a/crates/shared/src/fs.rs +++ b/crates/shared/src/fs.rs @@ -1,3 +1,7 @@ +use io::{IoBase, Read, Write}; + +use crate::syscall; + #[derive(Debug)] pub struct File { fd: u64, @@ -6,7 +10,7 @@ pub struct File { impl File { /// # Safety /// The file descriptor must be valid - pub unsafe fn new(fd: u64) -> Self { + pub unsafe fn from_raw_fd(fd: u64) -> Self { Self { fd } } @@ -14,3 +18,23 @@ impl File { self.fd } } + +impl IoBase for File { + type Error = (); +} + +impl Read for File { + fn read(&mut self, buf: &mut [u8]) -> Result { + Ok(syscall::read(self.as_fd(), buf) as usize) + } +} + +impl Write for File { + fn write(&mut self, buf: &[u8]) -> Result { + Ok(syscall::write(self.as_fd(), buf) as usize) + } + + fn flush(&mut self) -> Result<(), Self::Error> { + todo!() + } +} diff --git a/crates/shared/src/syscall.rs b/crates/shared/src/syscall.rs index cdbdf46..9d02301 100644 --- a/crates/shared/src/syscall.rs +++ b/crates/shared/src/syscall.rs @@ -10,9 +10,12 @@ pub enum SysCall { Read = 0, Write = 1, Open = 2, + Close = 3, Seek = 8, Alloc = 40, Dealloc = 41, + Spawn = 58, + ExecVE = 59, Exit = 60, NanoSleep = 101, WriteIntTemp = 998, @@ -26,9 +29,12 @@ impl From for SysCall { 0 => SysCall::Read, 1 => SysCall::Write, 2 => SysCall::Open, + 3 => SysCall::Close, 8 => SysCall::Seek, 40 => SysCall::Alloc, 41 => SysCall::Dealloc, + 58 => SysCall::Spawn, + 59 => SysCall::ExecVE, 60 => SysCall::Exit, 101 => SysCall::NanoSleep, 998 => SysCall::WriteIntTemp, @@ -143,30 +149,53 @@ pub fn open>(path: P) -> File { let ptr = path_str.as_ptr(); let size = path_str.len(); let (fd, ..) = syscall!(SysCall::Open, ptr as u64, size as u64); - File::new(fd) + File::from_raw_fd(fd) } } -pub fn write(file: &mut File, buf: &[u8]) { +pub fn close(file_descriptor: u64) { + unsafe { + syscall!(SysCall::Close, file_descriptor); + } +} +pub fn write(file_descriptor: u64, buf: &[u8]) -> u64 { unsafe { let ptr = buf.as_ptr(); let size = buf.len(); - syscall!(SysCall::Write, file.as_fd(), ptr as u64, size as u64); + let (len, ..) = syscall!(SysCall::Write, file_descriptor, ptr as u64, size as u64); + len } } -pub fn read(file: &mut File, buf: &mut [u8]) { +pub fn read(file_descriptor: u64, buf: &mut [u8]) -> u64 { unsafe { let ptr = buf.as_ptr(); let size = buf.len(); - syscall!(SysCall::Read, file.as_fd(), ptr as u64, size as u64); + let (len, ..) = syscall!(SysCall::Read, file_descriptor, ptr as u64, size as u64); + len } } -pub fn seek(file: &mut File, seek: SeekFrom) { +pub fn seek(file_descriptor: u64, seek: SeekFrom) { unsafe { let (discriminant, value) = match seek { SeekFrom::Start(v) => (0, v), SeekFrom::End(v) => (1, v as u64), SeekFrom::Current(v) => (2, v as u64), }; - syscall!(SysCall::Seek, file.as_fd(), discriminant, value); + syscall!(SysCall::Seek, file_descriptor, discriminant, value); + } +} +pub fn spawn>(path: P) { + unsafe { + let path_str = path.as_ref().as_str(); + let ptr = path_str.as_ptr(); + let size = path_str.len(); + syscall!(SysCall::Spawn, ptr as u64, size as u64); + } +} +pub fn execve>(path: P) { + unsafe { + let path_str = path.as_ref().as_str(); + let ptr = path_str.as_ptr(); + let size = path_str.len(); + syscall!(SysCall::ExecVE, ptr as u64, size as u64); } } diff --git a/crates/os-std/Cargo.toml b/crates/std/Cargo.toml similarity index 92% rename from crates/os-std/Cargo.toml rename to crates/std/Cargo.toml index d2575ec..9d12f40 100644 --- a/crates/os-std/Cargo.toml +++ b/crates/std/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "os-std" +name = "std" version = "0.1.0" edition = "2024" diff --git a/crates/std/src/ffi/c_str.rs b/crates/std/src/ffi/c_str.rs new file mode 100644 index 0000000..a7d1de4 --- /dev/null +++ b/crates/std/src/ffi/c_str.rs @@ -0,0 +1,7 @@ +//! [`CStr`], [`CString`], and related types. +pub use alloc::ffi::c_str::FromVecWithNulError; +pub use alloc::ffi::c_str::IntoStringError; +pub use alloc::ffi::c_str::{CString, NulError}; +pub use core::ffi::c_str::CStr; +pub use core::ffi::c_str::FromBytesUntilNulError; +pub use core::ffi::c_str::FromBytesWithNulError; diff --git a/crates/std/src/ffi/mod.rs b/crates/std/src/ffi/mod.rs new file mode 100644 index 0000000..7f24b7b --- /dev/null +++ b/crates/std/src/ffi/mod.rs @@ -0,0 +1,184 @@ +//! Utilities related to FFI bindings. +//! +//! This module provides utilities to handle data across non-Rust +//! interfaces, like other programming languages and the underlying +//! operating system. It is mainly of use for FFI (Foreign Function +//! Interface) bindings and code that needs to exchange C-like strings +//! with other languages. +//! +//! # Overview +//! +//! Rust represents owned strings with the [`String`] type, and +//! borrowed slices of strings with the [`str`] primitive. Both are +//! always in UTF-8 encoding, and may contain nul bytes in the middle, +//! i.e., if you look at the bytes that make up the string, there may +//! be a `\0` among them. Both `String` and `str` store their length +//! explicitly; there are no nul terminators at the end of strings +//! like in C. +//! +//! C strings are different from Rust strings: +//! +//! * **Encodings** - Rust strings are UTF-8, but C strings may use +//! other encodings. If you are using a string from C, you should +//! check its encoding explicitly, rather than just assuming that it +//! is UTF-8 like you can do in Rust. +//! +//! * **Character size** - C strings may use `char` or `wchar_t`-sized +//! characters; please **note** that C's `char` is different from Rust's. +//! The C standard leaves the actual sizes of those types open to +//! interpretation, but defines different APIs for strings made up of +//! each character type. Rust strings are always UTF-8, so different +//! Unicode characters will be encoded in a variable number of bytes +//! each. The Rust type [`char`] represents a '[Unicode scalar +//! value]', which is similar to, but not the same as, a '[Unicode +//! code point]'. +//! +//! * **Nul terminators and implicit string lengths** - Often, C +//! strings are nul-terminated, i.e., they have a `\0` character at the +//! end. The length of a string buffer is not stored, but has to be +//! calculated; to compute the length of a string, C code must +//! manually call a function like `strlen()` for `char`-based strings, +//! or `wcslen()` for `wchar_t`-based ones. Those functions return +//! the number of characters in the string excluding the nul +//! terminator, so the buffer length is really `len+1` characters. +//! Rust strings don't have a nul terminator; their length is always +//! stored and does not need to be calculated. While in Rust +//! accessing a string's length is an *O*(1) operation (because the +//! length is stored); in C it is an *O*(*n*) operation because the +//! length needs to be computed by scanning the string for the nul +//! terminator. +//! +//! * **Internal nul characters** - When C strings have a nul +//! terminator character, this usually means that they cannot have nul +//! characters in the middle — a nul character would essentially +//! truncate the string. Rust strings *can* have nul characters in +//! the middle, because nul does not have to mark the end of the +//! string in Rust. +//! +//! # Representations of non-Rust strings +//! +//! [`CString`] and [`CStr`] are useful when you need to transfer +//! UTF-8 strings to and from languages with a C ABI, like Python. +//! +//! * **From Rust to C:** [`CString`] represents an owned, C-friendly +//! string: it is nul-terminated, and has no internal nul characters. +//! Rust code can create a [`CString`] out of a normal string (provided +//! that the string doesn't have nul characters in the middle), and +//! then use a variety of methods to obtain a raw \*mut [u8] that can +//! then be passed as an argument to functions which use the C +//! conventions for strings. +//! +//! * **From C to Rust:** [`CStr`] represents a borrowed C string; it +//! is what you would use to wrap a raw \*const [u8] that you got from +//! a C function. A [`CStr`] is guaranteed to be a nul-terminated array +//! of bytes. Once you have a [`CStr`], you can convert it to a Rust +//! &[str] if it's valid UTF-8, or lossily convert it by adding +//! replacement characters. +//! +//! [`OsString`] and [`OsStr`] are useful when you need to transfer +//! strings to and from the operating system itself, or when capturing +//! the output of external commands. Conversions between [`OsString`], +//! [`OsStr`] and Rust strings work similarly to those for [`CString`] +//! and [`CStr`]. +//! +//! * [`OsString`] losslessly represents an owned platform string. However, this +//! representation is not necessarily in a form native to the platform. +//! In the Rust standard library, various APIs that transfer strings to/from the operating +//! system use [`OsString`] instead of plain strings. For example, +//! [`env::var_os()`] is used to query environment variables; it +//! returns an [Option]<[OsString]>. If the environment variable +//! exists you will get a [Some]\(os_string), which you can +//! *then* try to convert to a Rust string. This yields a [`Result`], so that +//! your code can detect errors in case the environment variable did +//! not in fact contain valid Unicode data. +//! +//! * [`OsStr`] losslessly represents a borrowed reference to a platform string. +//! However, this representation is not necessarily in a form native to the platform. +//! It can be converted into a UTF-8 Rust string slice in a similar way to +//! [`OsString`]. +//! +//! # Conversions +//! +//! ## On Unix +//! +//! On Unix, [`OsStr`] implements the +//! std::os::unix::ffi::[OsStrExt][unix.OsStrExt] trait, which +//! augments it with two methods, [`from_bytes`] and [`as_bytes`]. +//! These do inexpensive conversions from and to byte slices. +//! +//! Additionally, on Unix [`OsString`] implements the +//! std::os::unix::ffi::[OsStringExt][unix.OsStringExt] trait, +//! which provides [`from_vec`] and [`into_vec`] methods that consume +//! their arguments, and take or produce vectors of [`u8`]. +//! +//! ## On Windows +//! +//! An [`OsStr`] can be losslessly converted to a native Windows string. And +//! a native Windows string can be losslessly converted to an [`OsString`]. +//! +//! On Windows, [`OsStr`] implements the +//! std::os::windows::ffi::[OsStrExt][windows.OsStrExt] trait, +//! which provides an [`encode_wide`] method. This provides an +//! iterator that can be [`collect`]ed into a vector of [`u16`]. After a nul +//! characters is appended, this is the same as a native Windows string. +//! +//! Additionally, on Windows [`OsString`] implements the +//! std::os::windows:ffi::[OsStringExt][windows.OsStringExt] +//! trait, which provides a [`from_wide`] method to convert a native Windows +//! string (without the terminating nul character) to an [`OsString`]. +//! +//! ## Other platforms +//! +//! Many other platforms provide their own extension traits in a +//! `std::os::*::ffi` module. +//! +//! ## On all platforms +//! +//! On all platforms, [`OsStr`] consists of a sequence of bytes that is encoded as a superset of +//! UTF-8; see [`OsString`] for more details on its encoding on different platforms. +//! +//! For limited, inexpensive conversions from and to bytes, see [`OsStr::as_encoded_bytes`] and +//! [`OsStr::from_encoded_bytes_unchecked`]. +//! +//! For basic string processing, see [`OsStr::slice_encoded_bytes`]. +//! +//! [Unicode scalar value]: https://www.unicode.org/glossary/#unicode_scalar_value +//! [Unicode code point]: https://www.unicode.org/glossary/#code_point +//! [`env::set_var()`]: crate::env::set_var "env::set_var" +//! [`env::var_os()`]: crate::env::var_os "env::var_os" +//! [unix.OsStringExt]: crate::os::unix::ffi::OsStringExt "os::unix::ffi::OsStringExt" +//! [`from_vec`]: crate::os::unix::ffi::OsStringExt::from_vec "os::unix::ffi::OsStringExt::from_vec" +//! [`into_vec`]: crate::os::unix::ffi::OsStringExt::into_vec "os::unix::ffi::OsStringExt::into_vec" +//! [unix.OsStrExt]: crate::os::unix::ffi::OsStrExt "os::unix::ffi::OsStrExt" +//! [`from_bytes`]: crate::os::unix::ffi::OsStrExt::from_bytes "os::unix::ffi::OsStrExt::from_bytes" +//! [`as_bytes`]: crate::os::unix::ffi::OsStrExt::as_bytes "os::unix::ffi::OsStrExt::as_bytes" +//! [`OsStrExt`]: crate::os::unix::ffi::OsStrExt "os::unix::ffi::OsStrExt" +//! [windows.OsStrExt]: crate::os::windows::ffi::OsStrExt "os::windows::ffi::OsStrExt" +//! [`encode_wide`]: crate::os::windows::ffi::OsStrExt::encode_wide "os::windows::ffi::OsStrExt::encode_wide" +//! [`collect`]: crate::iter::Iterator::collect "iter::Iterator::collect" +//! [windows.OsStringExt]: crate::os::windows::ffi::OsStringExt "os::windows::ffi::OsStringExt" +//! [`from_wide`]: crate::os::windows::ffi::OsStringExt::from_wide "os::windows::ffi::OsStringExt::from_wide" +pub mod c_str; +pub use core::ffi::c_void; +pub use core::ffi::{VaArgSafe, VaList}; +pub use core::ffi::{ + c_char, c_double, c_float, c_int, c_long, c_longlong, c_schar, c_short, c_uchar, c_uint, + c_ulong, c_ulonglong, c_ushort, +}; +pub use core::ffi::{c_ptrdiff_t, c_size_t, c_ssize_t}; + +#[doc(inline)] +pub use self::c_str::FromBytesUntilNulError; +#[doc(inline)] +pub use self::c_str::FromBytesWithNulError; +#[doc(inline)] +pub use self::c_str::FromVecWithNulError; +#[doc(inline)] +pub use self::c_str::IntoStringError; +#[doc(inline)] +pub use self::c_str::NulError; +#[doc(inline)] +pub use self::c_str::{CStr, CString}; +// #[doc(inline)] +// pub use self::os_str::{OsStr, OsString}; +pub mod os_str; diff --git a/crates/std/src/ffi/os_str.rs b/crates/std/src/ffi/os_str.rs new file mode 100644 index 0000000..2862f4f --- /dev/null +++ b/crates/std/src/ffi/os_str.rs @@ -0,0 +1,1661 @@ +// //! The [`OsStr`] and [`OsString`] types and associated utilities. + +// #[cfg(test)] +// mod tests; + +// use core::clone::CloneToUninit; + +// use crate::borrow::{Borrow, Cow}; +// use crate::collections::TryReserveError; +// use crate::hash::{Hash, Hasher}; +// use crate::ops::{self, Range}; +// use crate::rc::Rc; +// use crate::str::FromStr; +// use crate::sync::Arc; +// use crate::sys::os_str::{Buf, Slice}; +// use crate::sys::{AsInner, FromInner, IntoInner}; +// use crate::{cmp, fmt, slice}; + +// /// A type that can represent owned, mutable platform-native strings, but is +// /// cheaply inter-convertible with Rust strings. +// /// +// /// The need for this type arises from the fact that: +// /// +// /// * On Unix systems, strings are often arbitrary sequences of non-zero +// /// bytes, in many cases interpreted as UTF-8. +// /// +// /// * On Windows, strings are often arbitrary sequences of non-zero 16-bit +// /// values, interpreted as UTF-16 when it is valid to do so. +// /// +// /// * In Rust, strings are always valid UTF-8, which may contain zeros. +// /// +// /// `OsString` and [`OsStr`] bridge this gap by simultaneously representing Rust +// /// and platform-native string values, and in particular allowing a Rust string +// /// to be converted into an "OS" string with no cost if possible. A consequence +// /// of this is that `OsString` instances are *not* `NUL` terminated; in order +// /// to pass to e.g., Unix system call, you should create a [`CStr`]. +// /// +// /// `OsString` is to &[OsStr] as [`String`] is to &[str]: the former +// /// in each pair are owned strings; the latter are borrowed +// /// references. +// /// +// /// Note, `OsString` and [`OsStr`] internally do not necessarily hold strings in +// /// the form native to the platform; While on Unix, strings are stored as a +// /// sequence of 8-bit values, on Windows, where strings are 16-bit value based +// /// as just discussed, strings are also actually stored as a sequence of 8-bit +// /// values, encoded in a less-strict variant of UTF-8. This is useful to +// /// understand when handling capacity and length values. +// /// +// /// # Capacity of `OsString` +// /// +// /// Capacity uses units of UTF-8 bytes for OS strings which were created from valid unicode, and +// /// uses units of bytes in an unspecified encoding for other contents. On a given target, all +// /// `OsString` and `OsStr` values use the same units for capacity, so the following will work: +// /// ``` +// /// use std::ffi::{OsStr, OsString}; +// /// +// /// fn concat_os_strings(a: &OsStr, b: &OsStr) -> OsString { +// /// let mut ret = OsString::with_capacity(a.len() + b.len()); // This will allocate +// /// ret.push(a); // This will not allocate further +// /// ret.push(b); // This will not allocate further +// /// ret +// /// } +// /// ``` +// /// +// /// # Creating an `OsString` +// /// +// /// **From a Rust string**: `OsString` implements +// /// [From]<[String]>, so you can use my_string.[into]\() to +// /// create an `OsString` from a normal Rust string. +// /// +// /// **From slices:** Just like you can start with an empty Rust +// /// [`String`] and then [`String::push_str`] some &[str] +// /// sub-string slices into it, you can create an empty `OsString` with +// /// the [`OsString::new`] method and then push string slices into it with the +// /// [`OsString::push`] method. +// /// +// /// # Extracting a borrowed reference to the whole OS string +// /// +// /// You can use the [`OsString::as_os_str`] method to get an &[OsStr] from +// /// an `OsString`; this is effectively a borrowed reference to the +// /// whole string. +// /// +// /// # Conversions +// /// +// /// See the [module's toplevel documentation about conversions][conversions] for a discussion on +// /// the traits which `OsString` implements for [conversions] from/to native representations. +// /// +// /// [`CStr`]: crate::ffi::CStr +// /// [conversions]: super#conversions +// /// [into]: Into::into +// #[cfg_attr(not(test), rustc_diagnostic_item = "OsString")] +// pub struct OsString { +// inner: Buf, +// } + +// /// Allows extension traits within `std`. +// impl crate::sealed::Sealed for OsString {} + +// /// Borrowed reference to an OS string (see [`OsString`]). +// /// +// /// This type represents a borrowed reference to a string in the operating system's preferred +// /// representation. +// /// +// /// `&OsStr` is to [`OsString`] as &[str] is to [`String`]: the +// /// former in each pair are borrowed references; the latter are owned strings. +// /// +// /// See the [module's toplevel documentation about conversions][conversions] for a discussion on +// /// the traits which `OsStr` implements for [conversions] from/to native representations. +// /// +// /// [conversions]: super#conversions +// #[cfg_attr(not(test), rustc_diagnostic_item = "OsStr")] +// // `OsStr::from_inner` and `impl CloneToUninit for OsStr` current implementation relies +// // on `OsStr` being layout-compatible with `Slice`. +// // However, `OsStr` layout is considered an implementation detail and must not be relied upon. +// #[repr(transparent)] +// pub struct OsStr { +// inner: Slice, +// } + +// /// Allows extension traits within `std`. +// impl crate::sealed::Sealed for OsStr {} + +// impl OsString { +// /// Constructs a new empty `OsString`. +// /// +// /// # Examples +// /// +// /// ``` +// /// use std::ffi::OsString; +// /// +// /// let os_string = OsString::new(); +// /// ``` +// #[must_use] +// #[inline] +// pub const fn new() -> OsString { +// OsString { inner: Buf::from_string(String::new()) } +// } + +// /// Converts bytes to an `OsString` without checking that the bytes contains +// /// valid [`OsStr`]-encoded data. +// /// +// /// The byte encoding is an unspecified, platform-specific, self-synchronizing superset of UTF-8. +// /// By being a self-synchronizing superset of UTF-8, this encoding is also a superset of 7-bit +// /// ASCII. +// /// +// /// See the [module's toplevel documentation about conversions][conversions] for safe, +// /// cross-platform [conversions] from/to native representations. +// /// +// /// # Safety +// /// +// /// As the encoding is unspecified, callers must pass in bytes that originated as a mixture of +// /// validated UTF-8 and bytes from [`OsStr::as_encoded_bytes`] from within the same Rust version +// /// built for the same target platform. For example, reconstructing an `OsString` from bytes sent +// /// over the network or stored in a file will likely violate these safety rules. +// /// +// /// Due to the encoding being self-synchronizing, the bytes from [`OsStr::as_encoded_bytes`] can be +// /// split either immediately before or immediately after any valid non-empty UTF-8 substring. +// /// +// /// # Example +// /// +// /// ``` +// /// use std::ffi::OsStr; +// /// +// /// let os_str = OsStr::new("Mary had a little lamb"); +// /// let bytes = os_str.as_encoded_bytes(); +// /// let words = bytes.split(|b| *b == b' '); +// /// let words: Vec<&OsStr> = words.map(|word| { +// /// // SAFETY: +// /// // - Each `word` only contains content that originated from `OsStr::as_encoded_bytes` +// /// // - Only split with ASCII whitespace which is a non-empty UTF-8 substring +// /// unsafe { OsStr::from_encoded_bytes_unchecked(word) } +// /// }).collect(); +// /// ``` +// /// +// /// [conversions]: super#conversions +// #[inline] +// pub unsafe fn from_encoded_bytes_unchecked(bytes: Vec) -> Self { +// OsString { inner: unsafe { Buf::from_encoded_bytes_unchecked(bytes) } } +// } + +// /// Converts to an [`OsStr`] slice. +// /// +// /// # Examples +// /// +// /// ``` +// /// use std::ffi::{OsString, OsStr}; +// /// +// /// let os_string = OsString::from("foo"); +// /// let os_str = OsStr::new("foo"); +// /// assert_eq!(os_string.as_os_str(), os_str); +// /// ``` +// #[cfg_attr(not(test), rustc_diagnostic_item = "os_string_as_os_str")] +// #[must_use] +// #[inline] +// pub fn as_os_str(&self) -> &OsStr { +// self +// } + +// /// Converts the `OsString` into a byte vector. To convert the byte vector back into an +// /// `OsString`, use the [`OsString::from_encoded_bytes_unchecked`] function. +// /// +// /// The byte encoding is an unspecified, platform-specific, self-synchronizing superset of UTF-8. +// /// By being a self-synchronizing superset of UTF-8, this encoding is also a superset of 7-bit +// /// ASCII. +// /// +// /// Note: As the encoding is unspecified, any sub-slice of bytes that is not valid UTF-8 should +// /// be treated as opaque and only comparable within the same Rust version built for the same +// /// target platform. For example, sending the bytes over the network or storing it in a file +// /// will likely result in incompatible data. See [`OsString`] for more encoding details +// /// and [`std::ffi`] for platform-specific, specified conversions. +// /// +// /// [`std::ffi`]: crate::ffi +// #[inline] +// pub fn into_encoded_bytes(self) -> Vec { +// self.inner.into_encoded_bytes() +// } + +// /// Converts the `OsString` into a [`String`] if it contains valid Unicode data. +// /// +// /// On failure, ownership of the original `OsString` is returned. +// /// +// /// # Examples +// /// +// /// ``` +// /// use std::ffi::OsString; +// /// +// /// let os_string = OsString::from("foo"); +// /// let string = os_string.into_string(); +// /// assert_eq!(string, Ok(String::from("foo"))); +// /// ``` +// #[inline] +// pub fn into_string(self) -> Result { +// self.inner.into_string().map_err(|buf| OsString { inner: buf }) +// } + +// /// Extends the string with the given &[OsStr] slice. +// /// +// /// # Examples +// /// +// /// ``` +// /// use std::ffi::OsString; +// /// +// /// let mut os_string = OsString::from("foo"); +// /// os_string.push("bar"); +// /// assert_eq!(&os_string, "foobar"); +// /// ``` +// #[inline] +// pub fn push>(&mut self, s: T) { +// trait SpecPushTo { +// fn spec_push_to(&self, buf: &mut OsString); +// } + +// impl> SpecPushTo for T { +// #[inline] +// default fn spec_push_to(&self, buf: &mut OsString) { +// buf.inner.push_slice(&self.as_ref().inner); +// } +// } + +// // Use a more efficient implementation when the string is UTF-8. +// macro spec_str($T:ty) { +// impl SpecPushTo for $T { +// #[inline] +// fn spec_push_to(&self, buf: &mut OsString) { +// buf.inner.push_str(self); +// } +// } +// } +// spec_str!(str); +// spec_str!(String); + +// s.spec_push_to(self) +// } + +// /// Creates a new `OsString` with at least the given capacity. +// /// +// /// The string will be able to hold at least `capacity` length units of other +// /// OS strings without reallocating. This method is allowed to allocate for +// /// more units than `capacity`. If `capacity` is 0, the string will not +// /// allocate. +// /// +// /// See the main `OsString` documentation information about encoding and capacity units. +// /// +// /// # Examples +// /// +// /// ``` +// /// use std::ffi::OsString; +// /// +// /// let mut os_string = OsString::with_capacity(10); +// /// let capacity = os_string.capacity(); +// /// +// /// // This push is done without reallocating +// /// os_string.push("foo"); +// /// +// /// assert_eq!(capacity, os_string.capacity()); +// /// ``` +// #[must_use] +// #[inline] +// pub fn with_capacity(capacity: usize) -> OsString { +// OsString { inner: Buf::with_capacity(capacity) } +// } + +// /// Truncates the `OsString` to zero length. +// /// +// /// # Examples +// /// +// /// ``` +// /// use std::ffi::OsString; +// /// +// /// let mut os_string = OsString::from("foo"); +// /// assert_eq!(&os_string, "foo"); +// /// +// /// os_string.clear(); +// /// assert_eq!(&os_string, ""); +// /// ``` +// #[inline] +// pub fn clear(&mut self) { +// self.inner.clear() +// } + +// /// Returns the capacity this `OsString` can hold without reallocating. +// /// +// /// See the main `OsString` documentation information about encoding and capacity units. +// /// +// /// # Examples +// /// +// /// ``` +// /// use std::ffi::OsString; +// /// +// /// let os_string = OsString::with_capacity(10); +// /// assert!(os_string.capacity() >= 10); +// /// ``` +// #[must_use] +// #[inline] +// pub fn capacity(&self) -> usize { +// self.inner.capacity() +// } + +// /// Reserves capacity for at least `additional` more capacity to be inserted +// /// in the given `OsString`. Does nothing if the capacity is +// /// already sufficient. +// /// +// /// The collection may reserve more space to speculatively avoid frequent reallocations. +// /// +// /// See the main `OsString` documentation information about encoding and capacity units. +// /// +// /// # Examples +// /// +// /// ``` +// /// use std::ffi::OsString; +// /// +// /// let mut s = OsString::new(); +// /// s.reserve(10); +// /// assert!(s.capacity() >= 10); +// /// ``` +// #[inline] +// pub fn reserve(&mut self, additional: usize) { +// self.inner.reserve(additional) +// } + +// /// Tries to reserve capacity for at least `additional` more length units +// /// in the given `OsString`. The string may reserve more space to speculatively avoid +// /// frequent reallocations. After calling `try_reserve`, capacity will be +// /// greater than or equal to `self.len() + additional` if it returns `Ok(())`. +// /// Does nothing if capacity is already sufficient. This method preserves +// /// the contents even if an error occurs. +// /// +// /// See the main `OsString` documentation information about encoding and capacity units. +// /// +// /// # Errors +// /// +// /// If the capacity overflows, or the allocator reports a failure, then an error +// /// is returned. +// /// +// /// # Examples +// /// +// /// ``` +// /// use std::ffi::{OsStr, OsString}; +// /// use std::collections::TryReserveError; +// /// +// /// fn process_data(data: &str) -> Result { +// /// let mut s = OsString::new(); +// /// +// /// // Pre-reserve the memory, exiting if we can't +// /// s.try_reserve(OsStr::new(data).len())?; +// /// +// /// // Now we know this can't OOM in the middle of our complex work +// /// s.push(data); +// /// +// /// Ok(s) +// /// } +// /// # process_data("123").expect("why is the test harness OOMing on 3 bytes?"); +// /// ``` +// #[inline] +// pub fn try_reserve(&mut self, additional: usize) -> Result<(), TryReserveError> { +// self.inner.try_reserve(additional) +// } + +// /// Reserves the minimum capacity for at least `additional` more capacity to +// /// be inserted in the given `OsString`. Does nothing if the capacity is +// /// already sufficient. +// /// +// /// Note that the allocator may give the collection more space than it +// /// requests. Therefore, capacity can not be relied upon to be precisely +// /// minimal. Prefer [`reserve`] if future insertions are expected. +// /// +// /// [`reserve`]: OsString::reserve +// /// +// /// See the main `OsString` documentation information about encoding and capacity units. +// /// +// /// # Examples +// /// +// /// ``` +// /// use std::ffi::OsString; +// /// +// /// let mut s = OsString::new(); +// /// s.reserve_exact(10); +// /// assert!(s.capacity() >= 10); +// /// ``` +// #[inline] +// pub fn reserve_exact(&mut self, additional: usize) { +// self.inner.reserve_exact(additional) +// } + +// /// Tries to reserve the minimum capacity for at least `additional` +// /// more length units in the given `OsString`. After calling +// /// `try_reserve_exact`, capacity will be greater than or equal to +// /// `self.len() + additional` if it returns `Ok(())`. +// /// Does nothing if the capacity is already sufficient. +// /// +// /// Note that the allocator may give the `OsString` more space than it +// /// requests. Therefore, capacity can not be relied upon to be precisely +// /// minimal. Prefer [`try_reserve`] if future insertions are expected. +// /// +// /// [`try_reserve`]: OsString::try_reserve +// /// +// /// See the main `OsString` documentation information about encoding and capacity units. +// /// +// /// # Errors +// /// +// /// If the capacity overflows, or the allocator reports a failure, then an error +// /// is returned. +// /// +// /// # Examples +// /// +// /// ``` +// /// use std::ffi::{OsStr, OsString}; +// /// use std::collections::TryReserveError; +// /// +// /// fn process_data(data: &str) -> Result { +// /// let mut s = OsString::new(); +// /// +// /// // Pre-reserve the memory, exiting if we can't +// /// s.try_reserve_exact(OsStr::new(data).len())?; +// /// +// /// // Now we know this can't OOM in the middle of our complex work +// /// s.push(data); +// /// +// /// Ok(s) +// /// } +// /// # process_data("123").expect("why is the test harness OOMing on 3 bytes?"); +// /// ``` +// #[inline] +// pub fn try_reserve_exact(&mut self, additional: usize) -> Result<(), TryReserveError> { +// self.inner.try_reserve_exact(additional) +// } + +// /// Shrinks the capacity of the `OsString` to match its length. +// /// +// /// See the main `OsString` documentation information about encoding and capacity units. +// /// +// /// # Examples +// /// +// /// ``` +// /// use std::ffi::OsString; +// /// +// /// let mut s = OsString::from("foo"); +// /// +// /// s.reserve(100); +// /// assert!(s.capacity() >= 100); +// /// +// /// s.shrink_to_fit(); +// /// assert_eq!(3, s.capacity()); +// /// ``` +// #[inline] +// pub fn shrink_to_fit(&mut self) { +// self.inner.shrink_to_fit() +// } + +// /// Shrinks the capacity of the `OsString` with a lower bound. +// /// +// /// The capacity will remain at least as large as both the length +// /// and the supplied value. +// /// +// /// If the current capacity is less than the lower limit, this is a no-op. +// /// +// /// See the main `OsString` documentation information about encoding and capacity units. +// /// +// /// # Examples +// /// +// /// ``` +// /// use std::ffi::OsString; +// /// +// /// let mut s = OsString::from("foo"); +// /// +// /// s.reserve(100); +// /// assert!(s.capacity() >= 100); +// /// +// /// s.shrink_to(10); +// /// assert!(s.capacity() >= 10); +// /// s.shrink_to(0); +// /// assert!(s.capacity() >= 3); +// /// ``` +// #[inline] +// pub fn shrink_to(&mut self, min_capacity: usize) { +// self.inner.shrink_to(min_capacity) +// } + +// /// Converts this `OsString` into a boxed [`OsStr`]. +// /// +// /// # Examples +// /// +// /// ``` +// /// use std::ffi::{OsString, OsStr}; +// /// +// /// let s = OsString::from("hello"); +// /// +// /// let b: Box = s.into_boxed_os_str(); +// /// ``` +// #[must_use = "`self` will be dropped if the result is not used"] +// pub fn into_boxed_os_str(self) -> Box { +// let rw = Box::into_raw(self.inner.into_box()) as *mut OsStr; +// unsafe { Box::from_raw(rw) } +// } + +// /// Consumes and leaks the `OsString`, returning a mutable reference to the contents, +// /// `&'a mut OsStr`. +// /// +// /// The caller has free choice over the returned lifetime, including 'static. +// /// Indeed, this function is ideally used for data that lives for the remainder of +// /// the program’s life, as dropping the returned reference will cause a memory leak. +// /// +// /// It does not reallocate or shrink the `OsString`, so the leaked allocation may include +// /// unused capacity that is not part of the returned slice. If you want to discard excess +// /// capacity, call [`into_boxed_os_str`], and then [`Box::leak`] instead. +// /// However, keep in mind that trimming the capacity may result in a reallocation and copy. +// /// +// /// [`into_boxed_os_str`]: Self::into_boxed_os_str +// #[inline] +// pub fn leak<'a>(self) -> &'a mut OsStr { +// OsStr::from_inner_mut(self.inner.leak()) +// } + +// /// Truncate the `OsString` to the specified length. +// /// +// /// # Panics +// /// Panics if `len` does not lie on a valid `OsStr` boundary +// /// (as described in [`OsStr::slice_encoded_bytes`]). +// #[inline] +// pub fn truncate(&mut self, len: usize) { +// self.as_os_str().inner.check_public_boundary(len); +// // SAFETY: The length was just checked to be at a valid boundary. +// unsafe { self.inner.truncate_unchecked(len) }; +// } + +// /// Provides plumbing to `Vec::extend_from_slice` without giving full +// /// mutable access to the `Vec`. +// /// +// /// # Safety +// /// +// /// The slice must be valid for the platform encoding (as described in +// /// [`OsStr::from_encoded_bytes_unchecked`]). +// /// +// /// This bypasses the encoding-dependent surrogate joining, so either +// /// `self` must not end with a leading surrogate half, or `other` must not +// /// start with a trailing surrogate half. +// #[inline] +// pub(crate) unsafe fn extend_from_slice_unchecked(&mut self, other: &[u8]) { +// // SAFETY: Guaranteed by caller. +// unsafe { self.inner.extend_from_slice_unchecked(other) }; +// } +// } +// impl From for OsString { +// /// Converts a [`String`] into an [`OsString`]. +// /// +// /// This conversion does not allocate or copy memory. +// #[inline] +// fn from(s: String) -> OsString { +// OsString { inner: Buf::from_string(s) } +// } +// } +// impl> From<&T> for OsString { +// /// Copies any value implementing [AsRef]<[OsStr]> +// /// into a newly allocated [`OsString`]. +// fn from(s: &T) -> OsString { +// trait SpecToOsString { +// fn spec_to_os_string(&self) -> OsString; +// } + +// impl> SpecToOsString for T { +// #[inline] +// default fn spec_to_os_string(&self) -> OsString { +// self.as_ref().to_os_string() +// } +// } + +// // Preserve the known-UTF-8 property for strings. +// macro spec_str($T:ty) { +// impl SpecToOsString for $T { +// #[inline] +// fn spec_to_os_string(&self) -> OsString { +// OsString::from(String::from(self)) +// } +// } +// } +// spec_str!(str); +// spec_str!(String); + +// s.spec_to_os_string() +// } +// } +// impl ops::Index for OsString { +// type Output = OsStr; + +// #[inline] +// fn index(&self, _index: ops::RangeFull) -> &OsStr { +// OsStr::from_inner(self.inner.as_slice()) +// } +// } +// impl ops::IndexMut for OsString { +// #[inline] +// fn index_mut(&mut self, _index: ops::RangeFull) -> &mut OsStr { +// OsStr::from_inner_mut(self.inner.as_mut_slice()) +// } +// } +// impl ops::Deref for OsString { +// type Target = OsStr; + +// #[inline] +// fn deref(&self) -> &OsStr { +// &self[..] +// } +// } +// impl ops::DerefMut for OsString { +// #[inline] +// fn deref_mut(&mut self) -> &mut OsStr { +// &mut self[..] +// } +// } +// impl Default for OsString { +// /// Constructs an empty `OsString`. +// #[inline] +// fn default() -> OsString { +// OsString::new() +// } +// } +// impl Clone for OsString { +// #[inline] +// fn clone(&self) -> Self { +// OsString { inner: self.inner.clone() } +// } + +// /// Clones the contents of `source` into `self`. +// /// +// /// This method is preferred over simply assigning `source.clone()` to `self`, +// /// as it avoids reallocation if possible. +// #[inline] +// fn clone_from(&mut self, source: &Self) { +// self.inner.clone_from(&source.inner) +// } +// } +// impl fmt::Debug for OsString { +// fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { +// fmt::Debug::fmt(&**self, formatter) +// } +// } +// impl PartialEq for OsString { +// #[inline] +// fn eq(&self, other: &OsString) -> bool { +// &**self == &**other +// } +// } +// impl PartialEq for OsString { +// #[inline] +// fn eq(&self, other: &str) -> bool { +// &**self == other +// } +// } +// impl PartialEq for str { +// #[inline] +// fn eq(&self, other: &OsString) -> bool { +// &**other == self +// } +// } +// impl PartialEq<&str> for OsString { +// #[inline] +// fn eq(&self, other: &&str) -> bool { +// **self == **other +// } +// } +// impl<'a> PartialEq for &'a str { +// #[inline] +// fn eq(&self, other: &OsString) -> bool { +// **other == **self +// } +// } +// impl Eq for OsString {} +// impl PartialOrd for OsString { +// #[inline] +// fn partial_cmp(&self, other: &OsString) -> Option { +// (&**self).partial_cmp(&**other) +// } +// #[inline] +// fn lt(&self, other: &OsString) -> bool { +// &**self < &**other +// } +// #[inline] +// fn le(&self, other: &OsString) -> bool { +// &**self <= &**other +// } +// #[inline] +// fn gt(&self, other: &OsString) -> bool { +// &**self > &**other +// } +// #[inline] +// fn ge(&self, other: &OsString) -> bool { +// &**self >= &**other +// } +// } +// impl PartialOrd for OsString { +// #[inline] +// fn partial_cmp(&self, other: &str) -> Option { +// (&**self).partial_cmp(other) +// } +// } +// impl Ord for OsString { +// #[inline] +// fn cmp(&self, other: &OsString) -> cmp::Ordering { +// (&**self).cmp(&**other) +// } +// } +// impl Hash for OsString { +// #[inline] +// fn hash(&self, state: &mut H) { +// (&**self).hash(state) +// } +// } +// impl fmt::Write for OsString { +// fn write_str(&mut self, s: &str) -> fmt::Result { +// self.push(s); +// Ok(()) +// } +// } + +// impl OsStr { +// /// Coerces into an `OsStr` slice. +// /// +// /// # Examples +// /// +// /// ``` +// /// use std::ffi::OsStr; +// /// +// /// let os_str = OsStr::new("foo"); +// /// ``` +// #[inline] +// pub const fn new + ?Sized>(s: &S) -> &OsStr { +// s.as_ref() +// } + +// /// Converts a slice of bytes to an OS string slice without checking that the string contains +// /// valid `OsStr`-encoded data. +// /// +// /// The byte encoding is an unspecified, platform-specific, self-synchronizing superset of UTF-8. +// /// By being a self-synchronizing superset of UTF-8, this encoding is also a superset of 7-bit +// /// ASCII. +// /// +// /// See the [module's toplevel documentation about conversions][conversions] for safe, +// /// cross-platform [conversions] from/to native representations. +// /// +// /// # Safety +// /// +// /// As the encoding is unspecified, callers must pass in bytes that originated as a mixture of +// /// validated UTF-8 and bytes from [`OsStr::as_encoded_bytes`] from within the same Rust version +// /// built for the same target platform. For example, reconstructing an `OsStr` from bytes sent +// /// over the network or stored in a file will likely violate these safety rules. +// /// +// /// Due to the encoding being self-synchronizing, the bytes from [`OsStr::as_encoded_bytes`] can be +// /// split either immediately before or immediately after any valid non-empty UTF-8 substring. +// /// +// /// # Example +// /// +// /// ``` +// /// use std::ffi::OsStr; +// /// +// /// let os_str = OsStr::new("Mary had a little lamb"); +// /// let bytes = os_str.as_encoded_bytes(); +// /// let words = bytes.split(|b| *b == b' '); +// /// let words: Vec<&OsStr> = words.map(|word| { +// /// // SAFETY: +// /// // - Each `word` only contains content that originated from `OsStr::as_encoded_bytes` +// /// // - Only split with ASCII whitespace which is a non-empty UTF-8 substring +// /// unsafe { OsStr::from_encoded_bytes_unchecked(word) } +// /// }).collect(); +// /// ``` +// /// +// /// [conversions]: super#conversions +// #[inline] +// pub unsafe fn from_encoded_bytes_unchecked(bytes: &[u8]) -> &Self { +// Self::from_inner(unsafe { Slice::from_encoded_bytes_unchecked(bytes) }) +// } + +// #[inline] +// const fn from_inner(inner: &Slice) -> &OsStr { +// // SAFETY: OsStr is just a wrapper of Slice, +// // therefore converting &Slice to &OsStr is safe. +// unsafe { &*(inner as *const Slice as *const OsStr) } +// } + +// #[inline] +// const fn from_inner_mut(inner: &mut Slice) -> &mut OsStr { +// // SAFETY: OsStr is just a wrapper of Slice, +// // therefore converting &mut Slice to &mut OsStr is safe. +// // Any method that mutates OsStr must be careful not to +// // break platform-specific encoding, in particular Wtf8 on Windows. +// unsafe { &mut *(inner as *mut Slice as *mut OsStr) } +// } + +// /// Yields a &[str] slice if the `OsStr` is valid Unicode. +// /// +// /// This conversion may entail doing a check for UTF-8 validity. +// /// +// /// # Examples +// /// +// /// ``` +// /// use std::ffi::OsStr; +// /// +// /// let os_str = OsStr::new("foo"); +// /// assert_eq!(os_str.to_str(), Some("foo")); +// /// ``` +// #[must_use = "this returns the result of the operation, \ +// without modifying the original"] +// #[inline] +// pub fn to_str(&self) -> Option<&str> { +// self.inner.to_str().ok() +// } + +// /// Converts an `OsStr` to a [Cow]<[str]>. +// /// +// /// Any non-UTF-8 sequences are replaced with +// /// [`U+FFFD REPLACEMENT CHARACTER`][U+FFFD]. +// /// +// /// [U+FFFD]: crate::char::REPLACEMENT_CHARACTER +// /// +// /// # Examples +// /// +// /// Calling `to_string_lossy` on an `OsStr` with invalid unicode: +// /// +// /// ``` +// /// // Note, due to differences in how Unix and Windows represent strings, +// /// // we are forced to complicate this example, setting up example `OsStr`s +// /// // with different source data and via different platform extensions. +// /// // Understand that in reality you could end up with such example invalid +// /// // sequences simply through collecting user command line arguments, for +// /// // example. +// /// +// /// #[cfg(unix)] { +// /// use std::ffi::OsStr; +// /// use std::os::unix::ffi::OsStrExt; +// /// +// /// // Here, the values 0x66 and 0x6f correspond to 'f' and 'o' +// /// // respectively. The value 0x80 is a lone continuation byte, invalid +// /// // in a UTF-8 sequence. +// /// let source = [0x66, 0x6f, 0x80, 0x6f]; +// /// let os_str = OsStr::from_bytes(&source[..]); +// /// +// /// assert_eq!(os_str.to_string_lossy(), "fo�o"); +// /// } +// /// #[cfg(windows)] { +// /// use std::ffi::OsString; +// /// use std::os::windows::prelude::*; +// /// +// /// // Here the values 0x0066 and 0x006f correspond to 'f' and 'o' +// /// // respectively. The value 0xD800 is a lone surrogate half, invalid +// /// // in a UTF-16 sequence. +// /// let source = [0x0066, 0x006f, 0xD800, 0x006f]; +// /// let os_string = OsString::from_wide(&source[..]); +// /// let os_str = os_string.as_os_str(); +// /// +// /// assert_eq!(os_str.to_string_lossy(), "fo�o"); +// /// } +// /// ``` +// #[must_use = "this returns the result of the operation, \ +// without modifying the original"] +// #[inline] +// pub fn to_string_lossy(&self) -> Cow<'_, str> { +// self.inner.to_string_lossy() +// } + +// /// Copies the slice into an owned [`OsString`]. +// /// +// /// # Examples +// /// +// /// ``` +// /// use std::ffi::{OsStr, OsString}; +// /// +// /// let os_str = OsStr::new("foo"); +// /// let os_string = os_str.to_os_string(); +// /// assert_eq!(os_string, OsString::from("foo")); +// /// ``` +// #[must_use = "this returns the result of the operation, \ +// without modifying the original"] +// #[inline] +// #[cfg_attr(not(test), rustc_diagnostic_item = "os_str_to_os_string")] +// pub fn to_os_string(&self) -> OsString { +// OsString { inner: self.inner.to_owned() } +// } + +// /// Checks whether the `OsStr` is empty. +// /// +// /// # Examples +// /// +// /// ``` +// /// use std::ffi::OsStr; +// /// +// /// let os_str = OsStr::new(""); +// /// assert!(os_str.is_empty()); +// /// +// /// let os_str = OsStr::new("foo"); +// /// assert!(!os_str.is_empty()); +// /// ``` +// #[must_use] +// #[inline] +// pub fn is_empty(&self) -> bool { +// self.inner.inner.is_empty() +// } + +// /// Returns the length of this `OsStr`. +// /// +// /// Note that this does **not** return the number of bytes in the string in +// /// OS string form. +// /// +// /// The length returned is that of the underlying storage used by `OsStr`. +// /// As discussed in the [`OsString`] introduction, [`OsString`] and `OsStr` +// /// store strings in a form best suited for cheap inter-conversion between +// /// native-platform and Rust string forms, which may differ significantly +// /// from both of them, including in storage size and encoding. +// /// +// /// This number is simply useful for passing to other methods, like +// /// [`OsString::with_capacity`] to avoid reallocations. +// /// +// /// See the main `OsString` documentation information about encoding and capacity units. +// /// +// /// # Examples +// /// +// /// ``` +// /// use std::ffi::OsStr; +// /// +// /// let os_str = OsStr::new(""); +// /// assert_eq!(os_str.len(), 0); +// /// +// /// let os_str = OsStr::new("foo"); +// /// assert_eq!(os_str.len(), 3); +// /// ``` +// #[must_use] +// #[inline] +// pub fn len(&self) -> usize { +// self.inner.inner.len() +// } + +// /// Converts a [Box]<[OsStr]> into an [`OsString`] without copying or allocating. +// #[must_use = "`self` will be dropped if the result is not used"] +// pub fn into_os_string(self: Box) -> OsString { +// let boxed = unsafe { Box::from_raw(Box::into_raw(self) as *mut Slice) }; +// OsString { inner: Buf::from_box(boxed) } +// } + +// /// Converts an OS string slice to a byte slice. To convert the byte slice back into an OS +// /// string slice, use the [`OsStr::from_encoded_bytes_unchecked`] function. +// /// +// /// The byte encoding is an unspecified, platform-specific, self-synchronizing superset of UTF-8. +// /// By being a self-synchronizing superset of UTF-8, this encoding is also a superset of 7-bit +// /// ASCII. +// /// +// /// Note: As the encoding is unspecified, any sub-slice of bytes that is not valid UTF-8 should +// /// be treated as opaque and only comparable within the same Rust version built for the same +// /// target platform. For example, sending the slice over the network or storing it in a file +// /// will likely result in incompatible byte slices. See [`OsString`] for more encoding details +// /// and [`std::ffi`] for platform-specific, specified conversions. +// /// +// /// [`std::ffi`]: crate::ffi +// #[inline] +// pub fn as_encoded_bytes(&self) -> &[u8] { +// self.inner.as_encoded_bytes() +// } + +// /// Takes a substring based on a range that corresponds to the return value of +// /// [`OsStr::as_encoded_bytes`]. +// /// +// /// The range's start and end must lie on valid `OsStr` boundaries. +// /// A valid `OsStr` boundary is one of: +// /// - The start of the string +// /// - The end of the string +// /// - Immediately before a valid non-empty UTF-8 substring +// /// - Immediately after a valid non-empty UTF-8 substring +// /// +// /// # Panics +// /// +// /// Panics if `range` does not lie on valid `OsStr` boundaries or if it +// /// exceeds the end of the string. +// /// +// /// # Example +// /// +// /// ``` +// /// #![feature(os_str_slice)] +// /// +// /// use std::ffi::OsStr; +// /// +// /// let os_str = OsStr::new("foo=bar"); +// /// let bytes = os_str.as_encoded_bytes(); +// /// if let Some(index) = bytes.iter().position(|b| *b == b'=') { +// /// let key = os_str.slice_encoded_bytes(..index); +// /// let value = os_str.slice_encoded_bytes(index + 1..); +// /// assert_eq!(key, "foo"); +// /// assert_eq!(value, "bar"); +// /// } +// /// ``` +// pub fn slice_encoded_bytes>(&self, range: R) -> &Self { +// let encoded_bytes = self.as_encoded_bytes(); +// let Range { start, end } = slice::range(range, ..encoded_bytes.len()); + +// // `check_public_boundary` should panic if the index does not lie on an +// // `OsStr` boundary as described above. It's possible to do this in an +// // encoding-agnostic way, but details of the internal encoding might +// // permit a more efficient implementation. +// self.inner.check_public_boundary(start); +// self.inner.check_public_boundary(end); + +// // SAFETY: `slice::range` ensures that `start` and `end` are valid +// let slice = unsafe { encoded_bytes.get_unchecked(start..end) }; + +// // SAFETY: `slice` comes from `self` and we validated the boundaries +// unsafe { Self::from_encoded_bytes_unchecked(slice) } +// } + +// /// Converts this string to its ASCII lower case equivalent in-place. +// /// +// /// ASCII letters 'A' to 'Z' are mapped to 'a' to 'z', +// /// but non-ASCII letters are unchanged. +// /// +// /// To return a new lowercased value without modifying the existing one, use +// /// [`OsStr::to_ascii_lowercase`]. +// /// +// /// # Examples +// /// +// /// ``` +// /// use std::ffi::OsString; +// /// +// /// let mut s = OsString::from("GRÜßE, JÜRGEN ❤"); +// /// +// /// s.make_ascii_lowercase(); +// /// +// /// assert_eq!("grÜße, jÜrgen ❤", s); +// /// ``` +// #[inline] +// pub fn make_ascii_lowercase(&mut self) { +// self.inner.make_ascii_lowercase() +// } + +// /// Converts this string to its ASCII upper case equivalent in-place. +// /// +// /// ASCII letters 'a' to 'z' are mapped to 'A' to 'Z', +// /// but non-ASCII letters are unchanged. +// /// +// /// To return a new uppercased value without modifying the existing one, use +// /// [`OsStr::to_ascii_uppercase`]. +// /// +// /// # Examples +// /// +// /// ``` +// /// use std::ffi::OsString; +// /// +// /// let mut s = OsString::from("Grüße, Jürgen ❤"); +// /// +// /// s.make_ascii_uppercase(); +// /// +// /// assert_eq!("GRüßE, JüRGEN ❤", s); +// /// ``` +// #[inline] +// pub fn make_ascii_uppercase(&mut self) { +// self.inner.make_ascii_uppercase() +// } + +// /// Returns a copy of this string where each character is mapped to its +// /// ASCII lower case equivalent. +// /// +// /// ASCII letters 'A' to 'Z' are mapped to 'a' to 'z', +// /// but non-ASCII letters are unchanged. +// /// +// /// To lowercase the value in-place, use [`OsStr::make_ascii_lowercase`]. +// /// +// /// # Examples +// /// +// /// ``` +// /// use std::ffi::OsString; +// /// let s = OsString::from("Grüße, Jürgen ❤"); +// /// +// /// assert_eq!("grüße, jürgen ❤", s.to_ascii_lowercase()); +// /// ``` +// #[must_use = "to lowercase the value in-place, use `make_ascii_lowercase`"] +// pub fn to_ascii_lowercase(&self) -> OsString { +// OsString::from_inner(self.inner.to_ascii_lowercase()) +// } + +// /// Returns a copy of this string where each character is mapped to its +// /// ASCII upper case equivalent. +// /// +// /// ASCII letters 'a' to 'z' are mapped to 'A' to 'Z', +// /// but non-ASCII letters are unchanged. +// /// +// /// To uppercase the value in-place, use [`OsStr::make_ascii_uppercase`]. +// /// +// /// # Examples +// /// +// /// ``` +// /// use std::ffi::OsString; +// /// let s = OsString::from("Grüße, Jürgen ❤"); +// /// +// /// assert_eq!("GRüßE, JüRGEN ❤", s.to_ascii_uppercase()); +// /// ``` +// #[must_use = "to uppercase the value in-place, use `make_ascii_uppercase`"] +// pub fn to_ascii_uppercase(&self) -> OsString { +// OsString::from_inner(self.inner.to_ascii_uppercase()) +// } + +// /// Checks if all characters in this string are within the ASCII range. +// /// +// /// An empty string returns `true`. +// /// +// /// # Examples +// /// +// /// ``` +// /// use std::ffi::OsString; +// /// +// /// let ascii = OsString::from("hello!\n"); +// /// let non_ascii = OsString::from("Grüße, Jürgen ❤"); +// /// +// /// assert!(ascii.is_ascii()); +// /// assert!(!non_ascii.is_ascii()); +// /// ``` +// #[must_use] +// #[inline] +// pub fn is_ascii(&self) -> bool { +// self.inner.is_ascii() +// } + +// /// Checks that two strings are an ASCII case-insensitive match. +// /// +// /// Same as `to_ascii_lowercase(a) == to_ascii_lowercase(b)`, +// /// but without allocating and copying temporaries. +// /// +// /// # Examples +// /// +// /// ``` +// /// use std::ffi::OsString; +// /// +// /// assert!(OsString::from("Ferris").eq_ignore_ascii_case("FERRIS")); +// /// assert!(OsString::from("Ferrös").eq_ignore_ascii_case("FERRöS")); +// /// assert!(!OsString::from("Ferrös").eq_ignore_ascii_case("FERRÖS")); +// /// ``` +// pub fn eq_ignore_ascii_case>(&self, other: S) -> bool { +// self.inner.eq_ignore_ascii_case(&other.as_ref().inner) +// } + +// /// Returns an object that implements [`Display`] for safely printing an +// /// [`OsStr`] that may contain non-Unicode data. This may perform lossy +// /// conversion, depending on the platform. If you would like an +// /// implementation which escapes the [`OsStr`] please use [`Debug`] +// /// instead. +// /// +// /// [`Display`]: fmt::Display +// /// [`Debug`]: fmt::Debug +// /// +// /// # Examples +// /// +// /// ``` +// /// use std::ffi::OsStr; +// /// +// /// let s = OsStr::new("Hello, world!"); +// /// println!("{}", s.display()); +// /// ``` +// #[must_use = "this does not display the `OsStr`; \ +// it returns an object that can be displayed"] +// #[inline] +// pub fn display(&self) -> Display<'_> { +// Display { os_str: self } +// } + +// /// Returns the same string as a string slice `&OsStr`. +// /// +// /// This method is redundant when used directly on `&OsStr`, but +// /// it helps dereferencing other string-like types to string slices, +// /// for example references to `Box` or `Arc`. +// #[inline] +// pub const fn as_os_str(&self) -> &OsStr { +// self +// } +// } +// impl From<&OsStr> for Box { +// /// Copies the string into a newly allocated [Box]<[OsStr]>. +// #[inline] +// fn from(s: &OsStr) -> Box { +// Box::clone_from_ref(s) +// } +// } +// impl From<&mut OsStr> for Box { +// /// Copies the string into a newly allocated [Box]<[OsStr]>. +// #[inline] +// fn from(s: &mut OsStr) -> Box { +// Self::from(&*s) +// } +// } +// impl From> for Box { +// /// Converts a `Cow<'a, OsStr>` into a [Box]<[OsStr]>, +// /// by copying the contents if they are borrowed. +// #[inline] +// fn from(cow: Cow<'_, OsStr>) -> Box { +// match cow { +// Cow::Borrowed(s) => Box::from(s), +// Cow::Owned(s) => Box::from(s), +// } +// } +// } +// impl From> for OsString { +// /// Converts a [Box]<[OsStr]> into an [`OsString`] without copying or +// /// allocating. +// #[inline] +// fn from(boxed: Box) -> OsString { +// boxed.into_os_string() +// } +// } +// impl From for Box { +// /// Converts an [`OsString`] into a [Box]<[OsStr]> without copying or allocating. +// #[inline] +// fn from(s: OsString) -> Box { +// s.into_boxed_os_str() +// } +// } +// impl Clone for Box { +// #[inline] +// fn clone(&self) -> Self { +// self.to_os_string().into_boxed_os_str() +// } +// } +// unsafe impl CloneToUninit for OsStr { +// #[inline] +// #[cfg_attr(debug_assertions, track_caller)] +// unsafe fn clone_to_uninit(&self, dst: *mut u8) { +// // SAFETY: we're just a transparent wrapper around a platform-specific Slice +// unsafe { self.inner.clone_to_uninit(dst) } +// } +// } +// impl From for Arc { +// /// Converts an [`OsString`] into an [Arc]<[OsStr]> by moving the [`OsString`] +// /// data into a new [`Arc`] buffer. +// #[inline] +// fn from(s: OsString) -> Arc { +// let arc = s.inner.into_arc(); +// unsafe { Arc::from_raw(Arc::into_raw(arc) as *const OsStr) } +// } +// } +// impl From<&OsStr> for Arc { +// /// Copies the string into a newly allocated [Arc]<[OsStr]>. +// #[inline] +// fn from(s: &OsStr) -> Arc { +// let arc = s.inner.into_arc(); +// unsafe { Arc::from_raw(Arc::into_raw(arc) as *const OsStr) } +// } +// } +// impl From<&mut OsStr> for Arc { +// /// Copies the string into a newly allocated [Arc]<[OsStr]>. +// #[inline] +// fn from(s: &mut OsStr) -> Arc { +// Arc::from(&*s) +// } +// } +// impl From for Rc { +// /// Converts an [`OsString`] into an [Rc]<[OsStr]> by moving the [`OsString`] +// /// data into a new [`Rc`] buffer. +// #[inline] +// fn from(s: OsString) -> Rc { +// let rc = s.inner.into_rc(); +// unsafe { Rc::from_raw(Rc::into_raw(rc) as *const OsStr) } +// } +// } +// impl From<&OsStr> for Rc { +// /// Copies the string into a newly allocated [Rc]<[OsStr]>. +// #[inline] +// fn from(s: &OsStr) -> Rc { +// let rc = s.inner.into_rc(); +// unsafe { Rc::from_raw(Rc::into_raw(rc) as *const OsStr) } +// } +// } +// impl From<&mut OsStr> for Rc { +// /// Copies the string into a newly allocated [Rc]<[OsStr]>. +// #[inline] +// fn from(s: &mut OsStr) -> Rc { +// Rc::from(&*s) +// } +// } +// impl<'a> From for Cow<'a, OsStr> { +// /// Moves the string into a [`Cow::Owned`]. +// #[inline] +// fn from(s: OsString) -> Cow<'a, OsStr> { +// Cow::Owned(s) +// } +// } +// impl<'a> From<&'a OsStr> for Cow<'a, OsStr> { +// /// Converts the string reference into a [`Cow::Borrowed`]. +// #[inline] +// fn from(s: &'a OsStr) -> Cow<'a, OsStr> { +// Cow::Borrowed(s) +// } +// } +// impl<'a> From<&'a OsString> for Cow<'a, OsStr> { +// /// Converts the string reference into a [`Cow::Borrowed`]. +// #[inline] +// fn from(s: &'a OsString) -> Cow<'a, OsStr> { +// Cow::Borrowed(s.as_os_str()) +// } +// } +// impl<'a> From> for OsString { +// /// Converts a `Cow<'a, OsStr>` into an [`OsString`], +// /// by copying the contents if they are borrowed. +// #[inline] +// fn from(s: Cow<'a, OsStr>) -> Self { +// s.into_owned() +// } +// } +// impl<'a> TryFrom<&'a OsStr> for &'a str { +// type Error = crate::str::Utf8Error; + +// /// Tries to convert an `&OsStr` to a `&str`. +// /// +// /// ``` +// /// use std::ffi::OsStr; +// /// +// /// let os_str = OsStr::new("foo"); +// /// let as_str = <&str>::try_from(os_str).unwrap(); +// /// assert_eq!(as_str, "foo"); +// /// ``` +// fn try_from(value: &'a OsStr) -> Result { +// value.inner.to_str() +// } +// } +// impl Default for Box { +// #[inline] +// fn default() -> Box { +// let rw = Box::into_raw(Slice::empty_box()) as *mut OsStr; +// unsafe { Box::from_raw(rw) } +// } +// } +// impl Default for &OsStr { +// /// Creates an empty `OsStr`. +// #[inline] +// fn default() -> Self { +// OsStr::new("") +// } +// } +// impl PartialEq for OsStr { +// #[inline] +// fn eq(&self, other: &OsStr) -> bool { +// self.as_encoded_bytes().eq(other.as_encoded_bytes()) +// } +// } +// impl PartialEq for OsStr { +// #[inline] +// fn eq(&self, other: &str) -> bool { +// *self == *OsStr::new(other) +// } +// } +// impl PartialEq for str { +// #[inline] +// fn eq(&self, other: &OsStr) -> bool { +// *other == *OsStr::new(self) +// } +// } +// impl Eq for OsStr {} +// impl PartialOrd for OsStr { +// #[inline] +// fn partial_cmp(&self, other: &OsStr) -> Option { +// self.as_encoded_bytes().partial_cmp(other.as_encoded_bytes()) +// } +// #[inline] +// fn lt(&self, other: &OsStr) -> bool { +// self.as_encoded_bytes().lt(other.as_encoded_bytes()) +// } +// #[inline] +// fn le(&self, other: &OsStr) -> bool { +// self.as_encoded_bytes().le(other.as_encoded_bytes()) +// } +// #[inline] +// fn gt(&self, other: &OsStr) -> bool { +// self.as_encoded_bytes().gt(other.as_encoded_bytes()) +// } +// #[inline] +// fn ge(&self, other: &OsStr) -> bool { +// self.as_encoded_bytes().ge(other.as_encoded_bytes()) +// } +// } +// impl PartialOrd for OsStr { +// #[inline] +// fn partial_cmp(&self, other: &str) -> Option { +// self.partial_cmp(OsStr::new(other)) +// } +// } + +// // FIXME (#19470): cannot provide PartialOrd for str until we +// // have more flexible coherence rules. +// impl Ord for OsStr { +// #[inline] +// fn cmp(&self, other: &OsStr) -> cmp::Ordering { +// self.as_encoded_bytes().cmp(other.as_encoded_bytes()) +// } +// } + +// macro_rules! impl_cmp { +// ($lhs:ty, $rhs: ty) => { +// impl PartialEq<$rhs> for $lhs { +// #[inline] +// fn eq(&self, other: &$rhs) -> bool { +// ::eq(self, other) +// } +// } +// impl PartialEq<$lhs> for $rhs { +// #[inline] +// fn eq(&self, other: &$lhs) -> bool { +// ::eq(self, other) +// } +// } +// impl PartialOrd<$rhs> for $lhs { +// #[inline] +// fn partial_cmp(&self, other: &$rhs) -> Option { +// ::partial_cmp(self, other) +// } +// } +// impl PartialOrd<$lhs> for $rhs { +// #[inline] +// fn partial_cmp(&self, other: &$lhs) -> Option { +// ::partial_cmp(self, other) +// } +// } +// }; +// } + +// impl_cmp!(OsString, OsStr); +// impl_cmp!(OsString, &OsStr); +// impl_cmp!(Cow<'_, OsStr>, OsStr); +// impl_cmp!(Cow<'_, OsStr>, &OsStr); +// impl_cmp!(Cow<'_, OsStr>, OsString); +// impl Hash for OsStr { +// #[inline] +// fn hash(&self, state: &mut H) { +// self.as_encoded_bytes().hash(state) +// } +// } +// impl fmt::Debug for OsStr { +// fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { +// fmt::Debug::fmt(&self.inner, formatter) +// } +// } + +// /// Helper struct for safely printing an [`OsStr`] with [`format!`] and `{}`. +// /// +// /// An [`OsStr`] might contain non-Unicode data. This `struct` implements the +// /// [`Display`] trait in a way that mitigates that. It is created by the +// /// [`display`](OsStr::display) method on [`OsStr`]. This may perform lossy +// /// conversion, depending on the platform. If you would like an implementation +// /// which escapes the [`OsStr`] please use [`Debug`] instead. +// /// +// /// # Examples +// /// +// /// ``` +// /// use std::ffi::OsStr; +// /// +// /// let s = OsStr::new("Hello, world!"); +// /// println!("{}", s.display()); +// /// ``` +// /// +// /// [`Display`]: fmt::Display +// /// [`format!`]: crate::format +// pub struct Display<'a> { +// os_str: &'a OsStr, +// } +// impl fmt::Debug for Display<'_> { +// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { +// fmt::Debug::fmt(&self.os_str, f) +// } +// } +// impl fmt::Display for Display<'_> { +// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { +// fmt::Display::fmt(&self.os_str.inner, f) +// } +// } +// impl> alloc::slice::Join<&OsStr> for [S] { +// type Output = OsString; + +// fn join(slice: &Self, sep: &OsStr) -> OsString { +// let Some((first, suffix)) = slice.split_first() else { +// return OsString::new(); +// }; +// let first_owned = first.borrow().to_owned(); +// suffix.iter().fold(first_owned, |mut a, b| { +// a.push(sep); +// a.push(b.borrow()); +// a +// }) +// } +// } +// impl Borrow for OsString { +// #[inline] +// fn borrow(&self) -> &OsStr { +// &self[..] +// } +// } +// impl ToOwned for OsStr { +// type Owned = OsString; +// #[inline] +// fn to_owned(&self) -> OsString { +// self.to_os_string() +// } +// #[inline] +// fn clone_into(&self, target: &mut OsString) { +// self.inner.clone_into(&mut target.inner) +// } +// } +// impl const AsRef for OsStr { +// #[inline] +// fn as_ref(&self) -> &OsStr { +// self +// } +// } +// impl AsRef for OsString { +// #[inline] +// fn as_ref(&self) -> &OsStr { +// self +// } +// } +// impl AsRef for str { +// #[inline] +// fn as_ref(&self) -> &OsStr { +// OsStr::from_inner(Slice::from_str(self)) +// } +// } +// impl AsRef for String { +// #[inline] +// fn as_ref(&self) -> &OsStr { +// (&**self).as_ref() +// } +// } + +// impl FromInner for OsString { +// #[inline] +// fn from_inner(buf: Buf) -> OsString { +// OsString { inner: buf } +// } +// } + +// impl IntoInner for OsString { +// #[inline] +// fn into_inner(self) -> Buf { +// self.inner +// } +// } + +// impl AsInner for OsStr { +// #[inline] +// fn as_inner(&self) -> &Slice { +// &self.inner +// } +// } +// impl FromStr for OsString { +// type Err = core::convert::Infallible; + +// #[inline] +// fn from_str(s: &str) -> Result { +// Ok(OsString::from(s)) +// } +// } +// impl Extend for OsString { +// #[inline] +// fn extend>(&mut self, iter: T) { +// for s in iter { +// self.push(&s); +// } +// } +// } +// impl<'a> Extend<&'a OsStr> for OsString { +// #[inline] +// fn extend>(&mut self, iter: T) { +// for s in iter { +// self.push(s); +// } +// } +// } +// impl<'a> Extend> for OsString { +// #[inline] +// fn extend>>(&mut self, iter: T) { +// for s in iter { +// self.push(&s); +// } +// } +// } +// impl FromIterator for OsString { +// #[inline] +// fn from_iter>(iter: I) -> Self { +// let mut iterator = iter.into_iter(); + +// // Because we're iterating over `OsString`s, we can avoid at least +// // one allocation by getting the first string from the iterator +// // and appending to it all the subsequent strings. +// match iterator.next() { +// None => OsString::new(), +// Some(mut buf) => { +// buf.extend(iterator); +// buf +// } +// } +// } +// } +// impl<'a> FromIterator<&'a OsStr> for OsString { +// #[inline] +// fn from_iter>(iter: I) -> Self { +// let mut buf = Self::new(); +// for s in iter { +// buf.push(s); +// } +// buf +// } +// } +// impl<'a> FromIterator> for OsString { +// #[inline] +// fn from_iter>>(iter: I) -> Self { +// let mut iterator = iter.into_iter(); + +// // Because we're iterating over `OsString`s, we can avoid at least +// // one allocation by getting the first owned string from the iterator +// // and appending to it all the subsequent strings. +// match iterator.next() { +// None => OsString::new(), +// Some(Cow::Owned(mut buf)) => { +// buf.extend(iterator); +// buf +// } +// Some(Cow::Borrowed(buf)) => { +// let mut buf = OsString::from(buf); +// buf.extend(iterator); +// buf +// } +// } +// } +// } diff --git a/crates/std/src/io.rs b/crates/std/src/io.rs new file mode 100644 index 0000000..709f22d --- /dev/null +++ b/crates/std/src/io.rs @@ -0,0 +1,22 @@ +use crate::fs::File; +use io::IoBase; +pub use io::Read; +pub use io::Seek; +pub use io::SeekFrom; +pub use io::Write; + +pub struct Stdin; + +impl IoBase for Stdin { + type Error = (); +} + +impl Read for Stdin { + fn read(&mut self, buf: &mut [u8]) -> Result { + unsafe { File::from_raw_fd(0).read(buf) } + } +} + +pub fn stdin() -> Stdin { + Stdin +} diff --git a/crates/std/src/lib.rs b/crates/std/src/lib.rs new file mode 100644 index 0000000..871b601 --- /dev/null +++ b/crates/std/src/lib.rs @@ -0,0 +1,242 @@ +#![no_std] +#![allow(internal_features)] +// +// Language features: +// tidy-alphabetical-start +#![feature(alloc_error_handler)] +#![feature(allocator_internals)] +#![feature(allow_internal_unsafe)] +#![feature(allow_internal_unstable)] +#![feature(asm_experimental_arch)] +#![feature(autodiff)] +#![feature(cfg_sanitizer_cfi)] +#![feature(cfg_target_thread_local)] +#![feature(cfi_encoding)] +#![feature(const_default)] +#![feature(const_trait_impl)] +#![feature(core_float_math)] +#![feature(decl_macro)] +#![feature(deprecated_suggestion)] +#![feature(doc_cfg)] +#![feature(doc_masked)] +#![feature(doc_notable_trait)] +#![feature(dropck_eyepatch)] +#![feature(f16)] +#![feature(f128)] +#![feature(ffi_const)] +#![feature(formatting_options)] +#![feature(funnel_shifts)] +#![feature(if_let_guard)] +#![feature(intra_doc_pointers)] +#![feature(iter_advance_by)] +#![feature(iter_next_chunk)] +#![feature(lang_items)] +#![feature(link_cfg)] +#![feature(linkage)] +#![feature(macro_metavar_expr_concat)] +#![feature(maybe_uninit_fill)] +#![feature(min_specialization)] +#![feature(must_not_suspend)] +#![feature(needs_panic_runtime)] +#![feature(negative_impls)] +#![feature(never_type)] +#![feature(optimize_attribute)] +#![feature(prelude_import)] +#![feature(rustc_attrs)] +#![feature(rustdoc_internals)] +// #![feature(staged_api)] +#![feature(stmt_expr_attributes)] +#![feature(strict_provenance_lints)] +#![feature(thread_local)] +#![feature(try_blocks)] +#![feature(try_trait_v2)] +#![feature(type_alias_impl_trait)] +// tidy-alphabetical-end +// +// Library features (core): +// tidy-alphabetical-start +#![feature(bstr)] +#![feature(bstr_internals)] +#![feature(cast_maybe_uninit)] +#![feature(cfg_select)] +#![feature(char_internals)] +#![feature(clone_to_uninit)] +#![feature(const_convert)] +#![feature(core_intrinsics)] +#![feature(core_io_borrowed_buf)] +#![feature(drop_guard)] +#![feature(duration_constants)] +#![feature(error_generic_member_access)] +#![feature(error_iter)] +#![feature(exact_size_is_empty)] +#![feature(exclusive_wrapper)] +#![feature(extend_one)] +#![feature(float_algebraic)] +// #![feature(float_gamma)] +#![feature(float_minimum_maximum)] +#![feature(fmt_internals)] +#![feature(fn_ptr_trait)] +#![feature(generic_atomic)] +#![feature(hasher_prefixfree_extras)] +#![feature(hashmap_internals)] +#![feature(hint_must_use)] +#![feature(int_from_ascii)] +#![feature(ip)] +#![feature(maybe_uninit_array_assume_init)] +#![feature(panic_can_unwind)] +#![feature(panic_internals)] +#![feature(pin_coerce_unsized_trait)] +#![feature(pointer_is_aligned_to)] +#![feature(portable_simd)] +#![feature(ptr_as_uninit)] +#![feature(ptr_mask)] +#![feature(random)] +#![feature(slice_internals)] +#![feature(slice_ptr_get)] +#![feature(slice_range)] +#![feature(slice_split_once)] +#![feature(std_internals)] +#![feature(str_internals)] +#![feature(sync_unsafe_cell)] +#![feature(temporary_niche_types)] +#![feature(ub_checks)] +#![feature(used_with_arg)] +// tidy-alphabetical-end +// +// Library features (alloc): +// tidy-alphabetical-start +#![feature(alloc_layout_extra)] +#![feature(allocator_api)] +#![feature(clone_from_ref)] +#![feature(get_mut_unchecked)] +#![feature(map_try_insert)] +#![feature(slice_concat_trait)] +#![feature(thin_box)] +#![feature(try_reserve_kind)] +#![feature(try_with_capacity)] +#![feature(unique_rc_arc)] +#![feature(wtf8_internals)] +// tidy-alphabetical-end +// +// Library features (unwind): +// tidy-alphabetical-start +// #![feature(panic_unwind)] +// tidy-alphabetical-end +// +// Library features (std_detect): +// tidy-alphabetical-start +// #![feature(stdarch_internal)] +// tidy-alphabetical-end +// +// Only for re-exporting: +// tidy-alphabetical-start +#![feature(assert_matches)] +#![feature(async_iterator)] +#![feature(c_variadic)] +#![feature(cfg_accessible)] +#![feature(cfg_eval)] +#![feature(concat_bytes)] +#![feature(const_format_args)] +#![feature(custom_test_frameworks)] +#![feature(edition_panic)] +#![feature(format_args_nl)] +#![feature(log_syntax)] +#![feature(test)] +#![feature(trace_macros)] +// tidy-alphabetical-end +// +// Only used in tests/benchmarks: +// +// Only for const-ness: +// tidy-alphabetical-start +// #![feature(io_const_error)] +// tidy-alphabetical-end +// +#![feature(c_size_t, unsafe_binders)] +#![allow(clippy::doc_lazy_continuation, clippy::legacy_numeric_constants)] +#![allow(stable_features, incomplete_features)] + +extern crate alloc; + +pub use core::any; +pub use core::array; +pub use core::async_iter; +pub use core::cell; +pub use core::char; +pub use core::clone; +pub use core::cmp; +pub use core::convert; +pub use core::default; +pub use core::future; +pub use core::hint; +#[allow(deprecated, deprecated_in_future)] +pub use core::i8; +#[allow(deprecated, deprecated_in_future)] +pub use core::i16; +#[allow(deprecated, deprecated_in_future)] +pub use core::i32; +#[allow(deprecated, deprecated_in_future)] +pub use core::i64; +#[allow(deprecated, deprecated_in_future)] +pub use core::i128; +pub use core::intrinsics; +#[allow(deprecated, deprecated_in_future)] +pub use core::isize; +pub use core::iter; +pub use core::marker; +pub use core::mem; +pub use core::ops; +pub use core::option; +pub use core::pin; +pub use core::ptr; +pub use core::range; +pub use core::result; +#[allow(deprecated, deprecated_in_future)] +pub use core::u8; +#[allow(deprecated, deprecated_in_future)] +pub use core::u16; +#[allow(deprecated, deprecated_in_future)] +pub use core::u32; +#[allow(deprecated, deprecated_in_future)] +pub use core::u64; +#[allow(deprecated, deprecated_in_future)] +pub use core::u128; +pub use core::unsafe_binder; +#[allow(deprecated, deprecated_in_future)] +pub use core::usize; + +pub use alloc::borrow; +pub use alloc::boxed; +pub use alloc::fmt; +pub use alloc::format; +pub use alloc::rc; +pub use alloc::slice; +pub use alloc::str; +pub use alloc::string; +pub use alloc::vec; + +pub mod ffi; +pub mod io; +pub mod prelude; +pub mod process; + +pub use shared::fs; +pub use shared::syscall; + +#[macro_export] +macro_rules! print { + ($($args:expr),*) => { + $crate::syscall::write_string_temp(&format!($($args),*)) + }; +} +#[macro_export] +macro_rules! println { + () => { + $crate::print!(""); + // $crate::print!("\n\r"); + }; + ($($args:expr),*) => { + $crate::print!($($args),*); + // $crate::println!(); + }; +} diff --git a/crates/std/src/prelude.rs b/crates/std/src/prelude.rs new file mode 100644 index 0000000..895a601 --- /dev/null +++ b/crates/std/src/prelude.rs @@ -0,0 +1,54 @@ +pub mod rust_2024 { + pub use crate::print; + pub use crate::println; + pub use alloc::borrow::ToOwned; + pub use alloc::format; + pub use alloc::string::String; + pub use alloc::vec; + + extern crate alloc; + + struct GlobalAllocator; + + #[core::prelude::v1::global_allocator] + static GLOBAL_ALLOCATOR: GlobalAllocator = GlobalAllocator; + + unsafe impl core::alloc::GlobalAlloc for GlobalAllocator { + unsafe fn alloc(&self, layout: core::alloc::Layout) -> *mut u8 { + crate::syscall::alloc(layout) + } + + unsafe fn dealloc(&self, ptr: *mut u8, layout: core::alloc::Layout) { + crate::syscall::dealloc(ptr, layout) + } + } + + #[panic_handler] + fn panic(_panic_info: &core::panic::PanicInfo) -> ! { + // TODO print + loop {} + } + + /// # Safety + /// `argc` and `argv` are passed by the kernel + #[unsafe(no_mangle)] + pub unsafe extern "C" fn _start(argc: isize, argv: *const *const u8) -> isize { + unsafe extern "Rust" { + fn main(argc: isize, argv: *const *const u8) -> isize; + } + + unsafe { main(argc, argv) } + } + + #[lang = "start"] + pub fn lang_start( + main: fn() -> T, + argc: isize, + argv: *const *const u8, + _sigpipe: u8, + ) -> isize { + println!("{}", argc); + println!("{:?}", argv); + main().report().to_isize() + } +} diff --git a/crates/std/src/process.rs b/crates/std/src/process.rs new file mode 100644 index 0000000..11e4ff4 --- /dev/null +++ b/crates/std/src/process.rs @@ -0,0 +1,29 @@ +pub struct ExitCode(isize); + +impl ExitCode { + pub const SUCCESS: ExitCode = ExitCode(0); + + pub fn to_isize(self) -> isize { + self.0 + } +} + +#[lang = "termination"] +pub trait Termination { + /// Is called to get the representation of the value as status code. + /// This status code is returned to the operating system. + fn report(self) -> ExitCode; +} + +impl Termination for () { + #[inline] + fn report(self) -> ExitCode { + ExitCode::SUCCESS + } +} +impl Termination for isize { + #[inline] + fn report(self) -> ExitCode { + ExitCode(self) + } +} diff --git a/ilm.ld b/ilm.ld index 4ada6b1..5bf4e02 100644 --- a/ilm.ld +++ b/ilm.ld @@ -5,7 +5,7 @@ OUTPUT_ARCH(riscv) ENTRY(_start) MEMORY { - RAM (wxa) : ORIGIN = 0x80000000, LENGTH = 128M + RAM (wxa) : ORIGIN = 0x80000000, LENGTH = 512M } SECTIONS { diff --git a/justfile b/justfile index 8b85fee..d8c779b 100644 --- a/justfile +++ b/justfile @@ -2,6 +2,7 @@ release := "" qemu_flags := "" cargo_flags := "" + if release != "" { "--release" } else { "" } bin_path := if release != "" { "target/riscv64/release" } else { "target/riscv64/debug" } +rust_src := `rustc --print sysroot` / "lib/rustlib/src/rust/library/std/src" default: run @@ -12,6 +13,18 @@ mount_filesystem: sync_filesystem: sync +cp_std path: + @echo "Copying {{ path }}" + @mkdir {{ "crates/std/src" / parent_directory(path) }} -p + @cp {{ rust_src / path }} {{ "crates/std/src" / path }} + @perl -i -0777 -pe 's/^\s*#!?\[(un)?stable\(.*?\)\s*\]\n//gsm' {{ "crates/std/src" / path }} + @perl -i -0777 -pe 's/^\s*#!?\[rustc_.*?\]\n//gsm' {{ "crates/std/src" / path }} + +update_std: + @just cp_std "ffi/c_str.rs" + @just cp_std "ffi/mod.rs" + @just cp_std "ffi/os_str.rs" + build_user_prog prog: RUSTFLAGS="-C relocation-model=pic -C link-arg=-Tuser.ld -C link-arg=-pie" cargo b {{ cargo_flags }} --package {{ prog }} riscv64-elf-strip {{ bin_path / prog }} @@ -34,8 +47,8 @@ qemu := f"qemu-system-riscv64 \ -device bochs-display \ -device virtio-keyboard-pci \ -device virtio-mouse-pci \ - -device loader,file=disk.img,addr=0x90000000 \ - -bios none -m 512M {{qemu_flags}}" + -device loader,file=disk.img,addr=0xA0000000 \ + -bios none -m 1024M {{qemu_flags}}" # -trace \"virtio*\" # -d guest_errors,unimp,int" diff --git a/src/drivers.rs b/src/drivers.rs new file mode 100644 index 0000000..73cdf76 --- /dev/null +++ b/src/drivers.rs @@ -0,0 +1,2 @@ +pub mod keyboard; +pub mod mouse; diff --git a/src/drivers/keyboard.rs b/src/drivers/keyboard.rs new file mode 100644 index 0000000..051b9fb --- /dev/null +++ b/src/drivers/keyboard.rs @@ -0,0 +1,67 @@ +use crate::{ + keymap::{KeyType, ModifierType, map_keycode}, + tty::TTY0, + virtio::{ + Virtqueue, + input::{EventCodeValue, VirtioInputEvent, VirtioPciDriver}, + }, + virtual_fs::{FILE_SYSTEM, VirtualFileSystem}, +}; + +#[derive(Debug, Clone, Copy, Default)] +pub struct KeyboardState { + // ctrl_modifier: bool, + pub shift_modifier: bool, + pub alt_gr_modifier: bool, +} + +impl KeyboardState { + pub const fn new() -> Self { + Self { + // ctrl_modifier: false, + shift_modifier: false, + alt_gr_modifier: false, + } + } +} + +static mut KBD_QUEUE: Virtqueue = unsafe { core::mem::zeroed() }; +pub static mut KBD_DRIVER: VirtioPciDriver = unsafe { + VirtioPciDriver::new( + |state, event| { + let mut kbd_buffer = FILE_SYSTEM.open("/dev/input/keyboard".as_ref()).unwrap(); + kbd_buffer + .write(core::mem::transmute::< + &VirtioInputEvent, + &[u8; size_of::()], + >(event)) + .unwrap(); + + if event.is_key() { + let event = event.as_key_event(); + #[allow(clippy::single_match)] + match map_keycode(event.code, state) { + KeyType::Ascii(c) if event.value == EventCodeValue::Pressed => { + let mut buf = [0; 4]; + let to_send = c.encode_utf8(&mut buf); + for c in to_send.as_bytes() { + TTY0.buffer.borrow_mut().push(*c); + } + } + KeyType::Modifier(ModifierType::Shift) => { + state.shift_modifier = !state.shift_modifier; + } + KeyType::Modifier(ModifierType::AltGr) => { + state.alt_gr_modifier = !state.alt_gr_modifier; + } + _ => {} + } + // println!("event: {:#?}", event); + } else { + // println!("key pressed, {:#?}", event); + } + }, + KeyboardState::new(), + &mut KBD_QUEUE, + ) +}; diff --git a/src/drivers/mouse.rs b/src/drivers/mouse.rs new file mode 100644 index 0000000..c531d8e --- /dev/null +++ b/src/drivers/mouse.rs @@ -0,0 +1,38 @@ +use crate::{ + cursor::{clear_cursor, draw_cursor}, + vga::Vga, + virtio::{self, Virtqueue, input::VirtioPciDriver}, +}; + +pub static mut MOUSE_POSITION: (u16, u16) = (0, 0); + +static mut MOUSE_QUEUE: Virtqueue = unsafe { core::mem::zeroed() }; +pub static mut MOUSE_DRIVER: VirtioPciDriver<()> = unsafe { + VirtioPciDriver::new( + |_, event| { + if event.is_relative() { + let event = event.as_relative_event(); + + clear_cursor(&mut Vga, MOUSE_POSITION.0, MOUSE_POSITION.1); + + match event.code { + virtio::input::EventCodeRelative::X => { + MOUSE_POSITION.0 = (MOUSE_POSITION.0 as i32 + event.value) as u16 + } + virtio::input::EventCodeRelative::Y => { + MOUSE_POSITION.1 = (MOUSE_POSITION.1 as i32 + event.value) as u16 + } + _ => {} + } + + draw_cursor(&mut Vga, MOUSE_POSITION.0, MOUSE_POSITION.1); + + // println!("mouse moved relatively, {:#?}", event); + } else { + // println!("mouse moved, {:#?}", event); + } + }, + (), + &mut MOUSE_QUEUE, + ) +}; diff --git a/src/fs.rs b/src/fs.rs index 58c7afb..af509f2 100644 --- a/src/fs.rs +++ b/src/fs.rs @@ -12,7 +12,7 @@ use io::{IoBase, Read, Seek, Write}; use crate::virtual_fs::{VirtualFileSystem, VirtualNode}; -const DISK_ADDR: *const u8 = 0x9000_0000 as *const _; +const DISK_ADDR: *const u8 = 0xA000_0000 as *const _; #[derive(Debug)] /// Simple disk backend that reads from a fixed memory region. diff --git a/src/interrupt.rs b/src/interrupt.rs index e1e08b4..cef3b13 100644 --- a/src/interrupt.rs +++ b/src/interrupt.rs @@ -9,9 +9,9 @@ use log::info; use shared::syscall::SysCall; use crate::{ - KBD_DRIVER, MOUSE_DRIVER, boot::sbi::{ExtensionID, TimerFunctionID}, clear_csr, + drivers::{keyboard::KBD_DRIVER, mouse::MOUSE_DRIVER}, process::{ExecutionContext, exit_process, sleep}, read_csr, riscv::{disable_interrupt, dump_cpu}, @@ -160,6 +160,15 @@ unsafe extern "C" fn supervisor_trap_handler( unsafe { (*interrupt_state).a[0] = fd as u64 }; } + SysCall::Close => { + let fd = a1; + + let mut scheduler = SCHEDULER.lock(); + let current_process = scheduler.get_current_process(); + let mut vnode = current_process.fd_table[fd as usize].take().unwrap(); + + vnode.close(); + } SysCall::Write => { let fd = a1; let buf = @@ -168,7 +177,8 @@ unsafe extern "C" fn supervisor_trap_handler( let mut scheduler = SCHEDULER.lock(); let current_process = scheduler.get_current_process(); let vnode = current_process.fd_table[fd as usize].as_mut().unwrap(); - vnode.write(buf).unwrap(); + let res = vnode.write(buf).unwrap(); + unsafe { (*interrupt_state).a[0] = res as u64 }; } SysCall::Read => { let fd = a1; @@ -182,6 +192,8 @@ unsafe extern "C" fn supervisor_trap_handler( if res == 0 && !buf.is_empty() { loop_syscall(interrupt_state); scheduler.schedule(&mut interrupt_state); + } else { + unsafe { (*interrupt_state).a[0] = res as u64 }; } } SysCall::Seek => { @@ -197,6 +209,17 @@ unsafe extern "C" fn supervisor_trap_handler( let vnode = current_process.fd_table[fd as usize].as_mut().unwrap(); vnode.seek(seek).unwrap(); } + SysCall::Spawn => { + let path = unsafe { str::from_raw_parts(a1 as *const u8, a2 as usize) }; + let mut scheduler = SCHEDULER.lock(); + scheduler.create_process_from_file(path, 0, core::ptr::null()); + } + SysCall::ExecVE => { + // let path = unsafe { str::from_raw_parts(a1 as *const u8, a2 as usize) }; + // let mut scheduler = SCHEDULER.lock(); + // scheduler.create_process_from_file(path, &[]); + unimplemented!("ExecVE is not implemented") + } SysCall::Alloc => { let layout = Layout::from_size_align(a1 as usize, a2 as usize).unwrap(); // Allocate memory and put the pointer in a0 diff --git a/src/keymap.rs b/src/keymap.rs index 8b4af0e..91d5d0d 100644 --- a/src/keymap.rs +++ b/src/keymap.rs @@ -1,4 +1,4 @@ -use crate::KeyboardState; +use crate::drivers::keyboard::KeyboardState; #[derive(Debug, Clone, Copy)] #[derive_const(PartialEq, Eq)] diff --git a/src/main.rs b/src/main.rs index 5b63d6b..b932234 100644 --- a/src/main.rs +++ b/src/main.rs @@ -12,7 +12,8 @@ arbitrary_self_types_pointers, derive_const, const_cmp, - const_trait_impl + const_trait_impl, + trait_alias )] use core::sync::atomic::AtomicBool; @@ -22,20 +23,15 @@ use embedded_alloc::LlffHeap as Heap; use log::info; use crate::{ - cursor::{clear_cursor, draw_cursor}, + drivers::{keyboard::KBD_DRIVER, mouse::MOUSE_DRIVER}, io::init_log, - keymap::{KeyType, ModifierType, map_keycode}, - pci::{PciDeviceIterator, scan_virtio_devices}, + pci::scan_virtio_devices, riscv::enable_supervisor_interrupt, scheduler::{SCHEDULER, idle}, - tty::TTY0, user::{proc2, test}, vga::Vga, - virtio::{ - Virtqueue, - input::{EventCodeValue, VirtioInputEvent, VirtioPciDriver, init_plic_pci}, - }, - virtual_fs::{FILE_SYSTEM, VirtualFileSystem, init_file_system}, + virtio::input::init_plic_pci, + virtual_fs::init_file_system, }; extern crate alloc; @@ -44,6 +40,7 @@ mod critical_section; mod cursor; mod data_structures; mod draw; +mod drivers; mod fs; mod interrupt; mod io; @@ -75,97 +72,6 @@ const _: () = assert!(core::mem::size_of::() == core::mem::size_of:: #[cfg(not(target_endian = "little"))] compile_error! {"This kernel implementation assume endianness is little-endian. Some memory access like PCI could not work in big-endian."} -#[derive(Debug, Clone, Copy, Default)] -pub struct KeyboardState { - // ctrl_modifier: bool, - pub shift_modifier: bool, - pub alt_gr_modifier: bool, -} - -impl KeyboardState { - pub const fn new() -> Self { - Self { - // ctrl_modifier: false, - shift_modifier: false, - alt_gr_modifier: false, - } - } -} - -static mut KBD_QUEUE: Virtqueue = unsafe { core::mem::zeroed() }; -pub static mut KBD_DRIVER: VirtioPciDriver = unsafe { - VirtioPciDriver::new( - |state, event| { - let mut kbd_buffer = FILE_SYSTEM.open("/dev/input/keyboard".as_ref()).unwrap(); - kbd_buffer - .write(core::mem::transmute::< - &VirtioInputEvent, - &[u8; size_of::()], - >(event)) - .unwrap(); - - if event.is_key() { - let event = event.as_key_event(); - #[allow(clippy::single_match)] - match map_keycode(event.code, state) { - KeyType::Ascii(c) if event.value == EventCodeValue::Pressed => { - let mut buf = [0; 4]; - let to_send = c.encode_utf8(&mut buf); - for c in to_send.as_bytes() { - TTY0.buffer.borrow_mut().push(*c); - } - } - KeyType::Modifier(ModifierType::Shift) => { - state.shift_modifier = !state.shift_modifier; - } - KeyType::Modifier(ModifierType::AltGr) => { - state.alt_gr_modifier = !state.alt_gr_modifier; - } - _ => {} - } - println!("event: {:#?}", event); - } else { - // println!("key pressed, {:#?}", event); - } - }, - KeyboardState::new(), - &mut KBD_QUEUE, - ) -}; - -pub static mut MOUSE_POSITION: (u16, u16) = (0, 0); - -static mut MOUSE_QUEUE: Virtqueue = unsafe { core::mem::zeroed() }; -pub static mut MOUSE_DRIVER: VirtioPciDriver<()> = unsafe { - VirtioPciDriver::new( - |_, event| { - if event.is_relative() { - let event = event.as_relative_event(); - - clear_cursor(&mut Vga, MOUSE_POSITION.0, MOUSE_POSITION.1); - - match event.code { - virtio::input::EventCodeRelative::X => { - MOUSE_POSITION.0 = (MOUSE_POSITION.0 as i32 + event.value) as u16 - } - virtio::input::EventCodeRelative::Y => { - MOUSE_POSITION.1 = (MOUSE_POSITION.1 as i32 + event.value) as u16 - } - _ => {} - } - - draw_cursor(&mut Vga, MOUSE_POSITION.0, MOUSE_POSITION.1); - - // println!("mouse moved relatively, {:#?}", event); - } else { - // println!("mouse moved, {:#?}", event); - } - }, - (), - &mut MOUSE_QUEUE, - ) -}; - #[unsafe(no_mangle)] pub extern "C" fn supervisor_mode_entry() { unsafe { @@ -180,19 +86,20 @@ pub extern "C" fn supervisor_mode_entry() { info!("Hello World !"); // unsafe { Vga.draw_string(10, 10, "Hello World !", Color::WHITE, Color::BLACK) }; - SCHEDULER.lock().create_process(Box::new(test), "proc1"); - SCHEDULER.lock().create_process(Box::new(proc2), "proc2"); + // let binding = Box::leak(Box::new(["coucou".as_bytes()])); + SCHEDULER + .lock() + .create_process(Box::new(test), "proc1", 0, core::ptr::null()); + SCHEDULER + .lock() + .create_process(Box::new(proc2), "proc2", 0, core::ptr::null()); SCHEDULER .lock() - .create_process_from_file("/usr/bin/test_pic"); + .create_process_from_file("/usr/bin/test_pic", 0, core::ptr::null()); enable_supervisor_interrupt(); - for pci in PciDeviceIterator::new() { - println!("{:x?}", pci.vendor_and_device_id()) - } - unsafe { let (pci_keyboard, pci_mouse) = scan_virtio_devices(); let (pci_keyboard, pci_mouse) = (pci_keyboard.unwrap(), pci_mouse.unwrap()); @@ -214,5 +121,5 @@ pub extern "C" fn supervisor_mode_entry() { init_plic_pci(pci_keyboard.irq); init_plic_pci(pci_mouse.irq); } - idle(); + idle(0, core::ptr::null()); } diff --git a/src/process.rs b/src/process.rs index 485a68f..6d6b4e0 100644 --- a/src/process.rs +++ b/src/process.rs @@ -19,7 +19,6 @@ use crate::{ riscv::SStatus, scheduler::{ACTIVE_PID, SCHEDULER, Scheduler}, time::elapsed_time_since_startup, - tty::TTY0, virtual_fs::{FILE_SYSTEM, VirtualFileSystem, VirtualNode}, }; @@ -66,6 +65,9 @@ pub struct ExecutionContext { pub mstatus: u64, } +pub trait ProcessEntryTrait = Fn(isize, *const *const u8); +pub type ProcessEntry = dyn ProcessEntryTrait; + /// Represents a process in the system. /// /// Each process has its own execution context, stack, @@ -78,13 +80,13 @@ pub struct Process { /// Current state of the process. pub state: ProcessState, /// Optional entry point for the process code. - pub entry: Option>, + pub entry: Option>, /// Wake time for sleeping processes. pub wake_time: Duration, /// Saved execution context. pub ctx: ExecutionContext, /// Process stack. - pub stack: [u64; STACK_SIZE], + pub stack: Box<[u64; STACK_SIZE]>, /// File descriptor table. pub fd_table: Vec>>, } @@ -109,7 +111,7 @@ impl Default for Process { mepc: core::ptr::null(), mstatus: 0, }, - stack: [0; _], + stack: unsafe { Box::new_zeroed().assume_init() }, entry: None, fd_table: Vec::new(), } @@ -150,7 +152,12 @@ impl Scheduler { /// Attempts to open `path`, load its contents into memory and create a new /// kernel process that will execute the loaded binary. Returns the PID of the /// created process, or -1 on failure. - pub fn create_process_from_file>(&mut self, path: T) -> i64 { + pub fn create_process_from_file>( + &mut self, + path: T, + argc: isize, + argv: *const *const u8, + ) -> i64 { let path = path.as_ref(); let name = path.as_str(); @@ -231,25 +238,30 @@ impl Scheduler { // Entry point let entry_va = gelf.entry; let entry_addr = unsafe { base.add((entry_va - min_vaddr) as usize) } as *const u8; - let entry_fn = - unsafe { core::mem::transmute::<*const u8, extern "C" fn()>(entry_addr) }; - let wrapper = Box::new(move || { - entry_fn(); - }); + let entry_fn = unsafe { + core::mem::transmute::<*const u8, extern "C" fn(isize, *const *const u8)>( + entry_addr, + ) + }; + let wrapper = move |argc, argv| { + entry_fn(argc, argv); + }; println!("Program loaded at : {:x?}", entry_addr); - return self.create_process(wrapper, name); + return self.create_process(wrapper, name, argc, argv); } } // Fallback: treat the file as a raw binary blob and execute in-place let entry_point = unsafe { - core::mem::transmute::<*const u8, extern "C" fn()>(Vec::leak(content).as_ptr()) + core::mem::transmute::<*const u8, extern "C" fn(isize, *const *const u8)>( + Vec::leak(content).as_ptr(), + ) + }; + let wrapper = move |argc, argv| { + entry_point(argc, argv); }; - let wrapper = Box::new(move || { - entry_point(); - }); - self.create_process(wrapper, name) + self.create_process(wrapper, name, argc, argv) } /// Creates a new process with the specified code and name. @@ -271,39 +283,48 @@ impl Scheduler { /// /// The provided `code` function will be executed when the process is first /// scheduled. Returns the new PID, or -1 if the process table is full. - pub fn create_process, F: Fn() + 'static + Send>( + pub fn create_process, F: ProcessEntryTrait + 'static + Send>( &mut self, - code: Box, + code: F, name: T, + argc: isize, + argv: *const *const u8, ) -> i64 { // SAFETY: Initializing process in the global table. // Access is safe because we verified bounds and found a Dead slot. unsafe { - self.process_table - .insert(self.next_pid, Box::new(Process::default())); + self.process_table.insert(self.next_pid, Process::default()); let process = self.process_table.get_mut(&self.next_pid).unwrap(); // Configure process metadata process.pid = self.next_pid as i64; process.name = name.into(); process.state = ProcessState::Activable; - process.entry = Some(code); + process.entry = Some(Box::new(code)); process.fd_table = Vec::new(); - FILE_SYSTEM.mount( - format!("/proc/{}/0", process.pid).into(), - Box::new(TTY0.clone()), - ); + // FILE_SYSTEM.mount( + // format!("/proc/{}/0", process.pid).into(), + // Box::new(TTY0.clone()), + // ); // FD 0 - process.fd_table.push(Some( - FILE_SYSTEM - .open(format!("/proc/{}/0", process.pid).as_ref()) - .unwrap(), - )); + process + .fd_table + .push(Some(FILE_SYSTEM.open("/dev/null".as_ref()).unwrap())); + // FD 1 + process + .fd_table + .push(Some(FILE_SYSTEM.open("/dev/null".as_ref()).unwrap())); + // FD 2 + process + .fd_table + .push(Some(FILE_SYSTEM.open("/dev/null".as_ref()).unwrap())); // Configure execution context // a0 contains the pointer to the function to execute process.ctx.a[0] = &raw const *process.entry.as_ref().unwrap_unchecked() as u64; + process.ctx.a[1] = argc as u64; + process.ctx.a[2] = argv as u64; // mepc points to process_launcher which will call the function process.ctx.mepc = process_launcher as *const _; @@ -338,10 +359,15 @@ impl Scheduler { /// This function is installed into the process `mepc` so that when the new /// process is scheduled it will run this launcher which calls the user /// function and ensures the process exits cleanly. -extern "C" fn process_launcher(code: *const Box) { +#[allow(improper_ctypes_definitions)] +extern "C" fn process_launcher( + code: *const Box, + argc: isize, + argv: *const *const u8, +) { // SAFETY: The code pointer was initialized in create_process // and points to a valid function. - unsafe { (*code)() }; + unsafe { (*code)(argc, argv) }; // If user code didn't exit explicitly, call exit() to clean up the process exit(); diff --git a/src/scheduler.rs b/src/scheduler.rs index 9c9c6cc..7dd58b3 100644 --- a/src/scheduler.rs +++ b/src/scheduler.rs @@ -17,7 +17,7 @@ use crate::{ #[derive(Debug)] pub struct Scheduler { pub next_pid: u64, - pub process_table: BTreeMap>, + pub process_table: BTreeMap, } pub static ACTIVE_PID: AtomicU64 = AtomicU64::new(0); @@ -30,7 +30,7 @@ pub static SCHEDULER: Mutex> = Mutex::new(LazyCell::new(|| S /// Idle loop executed when there is no runnable process. /// /// Uses the `wfi` instruction to yield the CPU while waiting for interrupts. -pub fn idle() { +pub fn idle(_argc: isize, _argv: *const *const u8) { loop { // write_string_temp("idle"); // info!("idle"); @@ -47,7 +47,7 @@ impl Scheduler { /// it as the active process. pub fn init(&mut self) { info!("scheduler init"); - self.create_process(Box::new(idle), "idle"); + self.create_process(Box::new(idle), "idle", 0, core::ptr::null()); self.process_table.get_mut(&0).unwrap().state = ProcessState::Active; } @@ -69,9 +69,7 @@ impl Scheduler { } } - let mut current_process_iter = self - .process_table - .range_mut((Bound::Excluded(prev_pid), Bound::Unbounded)); + let mut current_process_iter = self.process_table.range_mut(prev_pid + 1..); ACTIVE_PID.store( loop { diff --git a/src/user.rs b/src/user.rs index 0e0e3f3..8b003b7 100644 --- a/src/user.rs +++ b/src/user.rs @@ -6,14 +6,15 @@ use core::time::Duration; use shared::syscall::{sleep, write_string_temp}; -pub fn test() { +pub fn test(_argc: isize, _argv: *const *const u8) { loop { + // write_string_temp(str::from_utf8(_args[0]).unwrap()); write_string_temp("test"); sleep(Duration::new(2, 0)); } } -pub fn proc2() { +pub fn proc2(_argc: isize, _argv: *const *const u8) { loop { write_string_temp("proc2"); sleep(Duration::new(3, 0)); diff --git a/src/virtio/input.rs b/src/virtio/input.rs index ef9c519..c2a83e2 100644 --- a/src/virtio/input.rs +++ b/src/virtio/input.rs @@ -22,6 +22,7 @@ pub struct VirtioPciDriver { diff --git a/src/virtual_fs.rs b/src/virtual_fs.rs index b026e8f..2ff5bd0 100644 --- a/src/virtual_fs.rs +++ b/src/virtual_fs.rs @@ -9,6 +9,7 @@ use hashbrown::HashMap; use io::{IoBase, Read, Seek, Write}; pub mod keyboard; +pub mod null; pub mod stdin; pub mod virtual_stdin; @@ -16,10 +17,12 @@ use crate::{ fs::Disk, tty::TTY0, vga::Vga, - virtual_fs::{keyboard::KeyboardBuffer, virtual_stdin::VirtualStdin}, + virtual_fs::{keyboard::KeyboardBuffer, null::Null, virtual_stdin::VirtualStdin}, }; -pub trait VirtualNode: IoBase + Read + Write + Seek + Debug {} +pub trait VirtualNode: IoBase + Read + Write + Seek + Debug { + fn close(&mut self) {} +} pub trait VirtualFileSystem: Debug { fn open(&mut self, path: &Path) -> Result, ()>; @@ -62,6 +65,7 @@ pub unsafe fn init_file_system() { unsafe { FILE_SYSTEM.mount("/dev/fb0".into(), Box::new(VGAFileSystem)); FILE_SYSTEM.mount("/dev/tty0".into(), Box::new(TTY0.clone())); + FILE_SYSTEM.mount("/dev/null".into(), Box::new(Null)); FILE_SYSTEM.mount( "/dev/input/keyboard".into(), Box::new(KeyboardBuffer::new()), diff --git a/src/virtual_fs/null.rs b/src/virtual_fs/null.rs new file mode 100644 index 0000000..d5667d5 --- /dev/null +++ b/src/virtual_fs/null.rs @@ -0,0 +1,51 @@ +use alloc::boxed::Box; +use io::{IoBase, Read, Seek, Write}; + +use crate::virtual_fs::{VirtualFileSystem, VirtualNode}; + +#[derive(Debug)] +pub struct Null; + +#[derive(Debug)] +pub struct NullNode; + +impl VirtualFileSystem for Null { + fn open( + &mut self, + path: &bffs::path::Path, + ) -> Result, ()> { + if !path.is_empty() { + Err(()) + } else { + Ok(Box::new(NullNode)) + } + } +} + +impl IoBase for NullNode { + type Error = (); +} + +impl Read for NullNode { + fn read(&mut self, _buf: &mut [u8]) -> Result { + Ok(0) + } +} + +impl Seek for NullNode { + fn seek(&mut self, _pos: io::SeekFrom) -> Result { + todo!() + } +} + +impl Write for NullNode { + fn write(&mut self, _buf: &[u8]) -> Result { + todo!() + } + + fn flush(&mut self) -> Result<(), Self::Error> { + todo!() + } +} + +impl VirtualNode for NullNode {} diff --git a/user/shell/Cargo.toml b/user/shell/Cargo.toml new file mode 100644 index 0000000..a973d7f --- /dev/null +++ b/user/shell/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "shell" +version = "0.1.0" +edition = "2024" + +[dependencies] +std = { path = "../../crates/std" } diff --git a/user/shell/src/main.rs b/user/shell/src/main.rs new file mode 100644 index 0000000..c531d7a --- /dev/null +++ b/user/shell/src/main.rs @@ -0,0 +1,6 @@ +fn main() -> isize { + println!( + "Hello from PIC program loaded dynamically with custom std and a better justfile, and syscalls ! " + ); + 0 +} diff --git a/user/test_pic/Cargo.toml b/user/test_pic/Cargo.toml index c94477f..6a9b6ac 100644 --- a/user/test_pic/Cargo.toml +++ b/user/test_pic/Cargo.toml @@ -4,4 +4,4 @@ version = "0.1.0" edition = "2024" [dependencies] -os-std = { path = "../../crates/os-std" } +std = { path = "../../crates/std" } diff --git a/user/test_pic/src/main.rs b/user/test_pic/src/main.rs index 984f10e..a1c2f43 100644 --- a/user/test_pic/src/main.rs +++ b/user/test_pic/src/main.rs @@ -1,10 +1,9 @@ -#![no_std] -#![no_main] +#![allow(unused)] -// use core::time::Duration; - -use os_std::syscall; -os_std::custom_std_setup! {} +use std::{ + io::{Read, Write, stdin}, + syscall, +}; fn main() { // let mut input = String::new(); @@ -12,19 +11,13 @@ fn main() { // syscall::seek(&mut file, SeekFrom::End(-3)); // syscall::write(&mut file, &[255; 6400 * 50]); // syscall::sleep(Duration::from_secs_f64(2.0)); - let mut stdin = syscall::open("/dev/tty0"); - let mut file = syscall::open("/dev/tty0"); + syscall::close(0); + let mut tty = syscall::open("/dev/tty0"); + syscall::spawn("/usr/bin/shell"); loop { let mut test = [0; 2]; - syscall::read(&mut stdin, &mut test); - let len = *test.iter().find(|x| **x == 0).unwrap_or(&1) + 1; - syscall::write( - &mut file, - str::from_utf8(&test[..len as usize]).unwrap().as_bytes(), - ); + let len = stdin().read(&mut test).unwrap(); + tty.write_all(str::from_utf8(&test[..len as usize]).unwrap().as_bytes()) + .unwrap(); } - // println!( - // "Hello from PIC program loaded dynamically with custom std and a better justfile, and syscalls ! {:?}", - // str::from_utf8(&test) - // ); }