mirror of
https://github.com/NotAShelf/microfetch.git
synced 2026-04-12 12:57:41 +00:00
treewide: going no_std
Signed-off-by: NotAShelf <raf@notashelf.dev> Change-Id: Ia1c001eb099ea8cae9bdf76642b873376a6a6964
This commit is contained in:
parent
1b0e3070bb
commit
472dbfc7e7
14 changed files with 856 additions and 143 deletions
|
|
@ -1,3 +1,3 @@
|
|||
# https://github.com/rui314/mold?tab=readme-ov-file#how-to-use
|
||||
[target.'cfg(target_os = "linux")']
|
||||
rustflags = [ "-C", "link-arg=-fuse-ld=mold" ]
|
||||
rustflags = [ "-C", "link-arg=-fuse-ld=mold", "-C", "link-arg=-lc", "-C", "link-arg=-lgcc_s" ]
|
||||
|
|
|
|||
6
Cargo.lock
generated
6
Cargo.lock
generated
|
|
@ -582,9 +582,15 @@ name = "microfetch"
|
|||
version = "0.4.13"
|
||||
dependencies = [
|
||||
"hotpath",
|
||||
"microfetch-alloc",
|
||||
"microfetch-asm",
|
||||
"microfetch-lib",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "microfetch-alloc"
|
||||
version = "0.4.13"
|
||||
|
||||
[[package]]
|
||||
name = "microfetch-asm"
|
||||
version = "0.4.13"
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ rust-version = "1.92.0"
|
|||
version = "0.4.13"
|
||||
|
||||
[workspace.dependencies]
|
||||
microfetch-alloc = { path = "./crates/alloc" }
|
||||
microfetch-asm = { path = "./crates/asm" }
|
||||
microfetch-lib = { path = "./crates/lib" }
|
||||
|
||||
|
|
@ -18,6 +19,7 @@ criterion-cycles-per-byte = "0.8.0"
|
|||
|
||||
[profile.dev]
|
||||
opt-level = 1
|
||||
panic = "abort"
|
||||
|
||||
[profile.release]
|
||||
codegen-units = 1
|
||||
|
|
|
|||
12
crates/alloc/Cargo.toml
Normal file
12
crates/alloc/Cargo.toml
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
[package]
|
||||
name = "microfetch-alloc"
|
||||
description = "Simple, std-free bump allocator for Microfetch"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
authors.workspace = true
|
||||
rust-version.workspace = true
|
||||
license.workspace = true
|
||||
publish = false
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
106
crates/alloc/src/lib.rs
Normal file
106
crates/alloc/src/lib.rs
Normal file
|
|
@ -0,0 +1,106 @@
|
|||
//! Simple bump allocator for `no_std` environments. Uses a statically allocated
|
||||
//! 32KB buffer and provides O(1) allocation with no deallocation support
|
||||
//! (memory is never freed).
|
||||
#![no_std]
|
||||
use core::{
|
||||
alloc::{GlobalAlloc, Layout},
|
||||
cell::UnsafeCell,
|
||||
ptr::null_mut,
|
||||
};
|
||||
|
||||
/// Default heap size is 32KB, should be plenty for Microfetch. Technically it
|
||||
/// can be invoked with more (or less) depending on our needs but I am quite
|
||||
/// sure 32KB is more than enough.
|
||||
pub const DEFAULT_HEAP_SIZE: usize = 32 * 1024;
|
||||
|
||||
/// A simple bump allocator that never frees memory.
|
||||
///
|
||||
/// This allocator maintains a static buffer and a bump pointer. Allocations are
|
||||
/// fast (just bump the pointer), but memory is never reclaimed. While you might
|
||||
/// be inclined to point out that this is ugly, it's suitable for a short-lived
|
||||
/// program with bounded memory usage.
|
||||
pub struct BumpAllocator<const N: usize = DEFAULT_HEAP_SIZE> {
|
||||
heap: UnsafeCell<[u8; N]>,
|
||||
next: UnsafeCell<usize>,
|
||||
}
|
||||
|
||||
// SAFETY: BumpAllocator is thread-safe because it uses UnsafeCell
|
||||
// and the allocator is only used in single-threaded contexts (i.e., no_std).
|
||||
unsafe impl<const N: usize> Sync for BumpAllocator<N> {}
|
||||
|
||||
impl<const N: usize> BumpAllocator<N> {
|
||||
/// Creates a new bump allocator with the specified heap size.
|
||||
#[must_use]
|
||||
pub const fn new() -> Self {
|
||||
Self {
|
||||
heap: UnsafeCell::new([0; N]),
|
||||
next: UnsafeCell::new(0),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the number of bytes currently allocated.
|
||||
#[must_use]
|
||||
pub fn used(&self) -> usize {
|
||||
// SAFETY: We're just reading the value, and this is only called
|
||||
// in single-threaded contexts.
|
||||
unsafe { *self.next.get() }
|
||||
}
|
||||
|
||||
/// Returns the total heap size.
|
||||
#[must_use]
|
||||
pub const fn capacity(&self) -> usize {
|
||||
N
|
||||
}
|
||||
|
||||
/// Returns the number of bytes remaining.
|
||||
#[must_use]
|
||||
pub fn remaining(&self) -> usize {
|
||||
N - self.used()
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> Default for BumpAllocator<N> {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<const N: usize> GlobalAlloc for BumpAllocator<N> {
|
||||
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
|
||||
unsafe {
|
||||
let next = self.next.get();
|
||||
let heap = self.heap.get();
|
||||
|
||||
// Align the current position
|
||||
let align = layout.align();
|
||||
let start = (*next + align - 1) & !(align - 1);
|
||||
let end = start + layout.size();
|
||||
|
||||
if end > N {
|
||||
// Out of memory
|
||||
null_mut()
|
||||
} else {
|
||||
*next = end;
|
||||
(*heap).as_mut_ptr().add(start)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn dealloc(&self, _ptr: *mut u8, _layout: Layout) {
|
||||
// Bump allocator doesn't support deallocation
|
||||
// Memory is reclaimed when the program exits
|
||||
}
|
||||
}
|
||||
|
||||
/// Static bump allocator instance with 32KB heap.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// Use this with `#[global_allocator]` in your binary:
|
||||
///
|
||||
///
|
||||
/// ```rust,ignore
|
||||
/// #[global_allocator]
|
||||
/// static ALLOCATOR: BumpAllocator = BumpAllocator::new();
|
||||
/// ```
|
||||
pub type BumpAlloc = BumpAllocator<DEFAULT_HEAP_SIZE>;
|
||||
|
|
@ -507,6 +507,7 @@ pub fn read_file_fast(path: &str, buffer: &mut [u8]) -> Result<usize, i32> {
|
|||
let _ = sys_close(fd);
|
||||
|
||||
if bytes_read < 0 {
|
||||
#[allow(clippy::cast_possible_truncation)]
|
||||
return Err(bytes_read as i32);
|
||||
}
|
||||
|
||||
|
|
@ -598,3 +599,41 @@ pub unsafe fn sys_sysinfo(info: *mut SysInfo) -> i64 {
|
|||
ret
|
||||
}
|
||||
}
|
||||
|
||||
/// Direct syscall to exit the process
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// This syscall never returns. The process will terminate immediately.
|
||||
#[inline]
|
||||
pub unsafe fn sys_exit(code: i32) -> ! {
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
unsafe {
|
||||
core::arch::asm!(
|
||||
"syscall",
|
||||
in("rax") 60i64, // SYS_exit
|
||||
in("rdi") code,
|
||||
options(noreturn, nostack)
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
unsafe {
|
||||
core::arch::asm!(
|
||||
"svc #0",
|
||||
in("x8") 93i64, // SYS_exit
|
||||
in("x0") code,
|
||||
options(noreturn, nostack)
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "riscv64")]
|
||||
unsafe {
|
||||
core::arch::asm!(
|
||||
"ecall",
|
||||
in("a7") 93i64, // SYS_exit
|
||||
in("a0") code,
|
||||
options(noreturn, nostack)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
use std::sync::LazyLock;
|
||||
use alloc::string::String;
|
||||
|
||||
/// Color codes for terminal output
|
||||
pub struct Colors {
|
||||
pub reset: &'static str,
|
||||
pub blue: &'static str,
|
||||
|
|
@ -11,7 +12,8 @@ pub struct Colors {
|
|||
}
|
||||
|
||||
impl Colors {
|
||||
const fn new(is_no_color: bool) -> Self {
|
||||
#[must_use]
|
||||
pub const fn new(is_no_color: bool) -> Self {
|
||||
if is_no_color {
|
||||
Self {
|
||||
reset: "",
|
||||
|
|
@ -36,46 +38,68 @@ impl Colors {
|
|||
}
|
||||
}
|
||||
|
||||
pub static COLORS: LazyLock<Colors> = LazyLock::new(|| {
|
||||
use core::sync::atomic::{AtomicBool, Ordering};
|
||||
|
||||
// Check if NO_COLOR is set (only once, lazily)
|
||||
// Only presence matters; value is irrelevant per the NO_COLOR spec
|
||||
let is_no_color = std::env::var_os("NO_COLOR").is_some();
|
||||
Colors::new(is_no_color)
|
||||
});
|
||||
static NO_COLOR_CHECKED: AtomicBool = AtomicBool::new(false);
|
||||
static NO_COLOR_SET: AtomicBool = AtomicBool::new(false);
|
||||
|
||||
/// Checks if `NO_COLOR` environment variable is set.
|
||||
pub(crate) fn is_no_color() -> bool {
|
||||
// Fast path: already checked
|
||||
if NO_COLOR_CHECKED.load(Ordering::Acquire) {
|
||||
return NO_COLOR_SET.load(Ordering::Relaxed);
|
||||
}
|
||||
|
||||
// Slow path: check environment
|
||||
let is_set = crate::env_exists("NO_COLOR");
|
||||
NO_COLOR_SET.store(is_set, Ordering::Relaxed);
|
||||
NO_COLOR_CHECKED.store(true, Ordering::Release);
|
||||
is_set
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
#[cfg_attr(feature = "hotpath", hotpath::measure)]
|
||||
pub fn print_dots() -> String {
|
||||
// Pre-calculate capacity: 6 color codes + " " (glyph + 2 spaces) per color
|
||||
const GLYPH: &str = "";
|
||||
let capacity = COLORS.blue.len()
|
||||
+ COLORS.cyan.len()
|
||||
+ COLORS.green.len()
|
||||
+ COLORS.yellow.len()
|
||||
+ COLORS.red.len()
|
||||
+ COLORS.magenta.len()
|
||||
+ COLORS.reset.len()
|
||||
|
||||
let colors = if is_no_color() {
|
||||
Colors::new(true)
|
||||
} else {
|
||||
Colors::new(false)
|
||||
};
|
||||
|
||||
// Pre-calculate capacity: 6 color codes + " " (glyph + 2 spaces) per color
|
||||
let capacity = colors.blue.len()
|
||||
+ colors.cyan.len()
|
||||
+ colors.green.len()
|
||||
+ colors.yellow.len()
|
||||
+ colors.red.len()
|
||||
+ colors.magenta.len()
|
||||
+ colors.reset.len()
|
||||
+ (GLYPH.len() + 2) * 6;
|
||||
|
||||
let mut result = String::with_capacity(capacity);
|
||||
result.push_str(COLORS.blue);
|
||||
result.push_str(colors.blue);
|
||||
result.push_str(GLYPH);
|
||||
result.push_str(" ");
|
||||
result.push_str(COLORS.cyan);
|
||||
result.push_str(colors.cyan);
|
||||
result.push_str(GLYPH);
|
||||
result.push_str(" ");
|
||||
result.push_str(COLORS.green);
|
||||
result.push_str(colors.green);
|
||||
result.push_str(GLYPH);
|
||||
result.push_str(" ");
|
||||
result.push_str(COLORS.yellow);
|
||||
result.push_str(colors.yellow);
|
||||
result.push_str(GLYPH);
|
||||
result.push_str(" ");
|
||||
result.push_str(COLORS.red);
|
||||
result.push_str(colors.red);
|
||||
result.push_str(GLYPH);
|
||||
result.push_str(" ");
|
||||
result.push_str(COLORS.magenta);
|
||||
result.push_str(colors.magenta);
|
||||
result.push_str(GLYPH);
|
||||
result.push_str(" ");
|
||||
result.push_str(COLORS.reset);
|
||||
result.push_str(colors.reset);
|
||||
|
||||
result
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,18 +1,15 @@
|
|||
use std::{ffi::OsStr, fmt::Write};
|
||||
use alloc::string::String;
|
||||
|
||||
use crate::getenv_str;
|
||||
|
||||
#[must_use]
|
||||
#[cfg_attr(feature = "hotpath", hotpath::measure)]
|
||||
pub fn get_desktop_info() -> String {
|
||||
let desktop_os = std::env::var_os("XDG_CURRENT_DESKTOP");
|
||||
let session_os = std::env::var_os("XDG_SESSION_TYPE");
|
||||
let desktop_raw = getenv_str("XDG_CURRENT_DESKTOP").unwrap_or("Unknown");
|
||||
let session_raw = getenv_str("XDG_SESSION_TYPE").unwrap_or("");
|
||||
|
||||
let desktop_raw = desktop_os
|
||||
.as_deref()
|
||||
.and_then(OsStr::to_str)
|
||||
.unwrap_or("Unknown");
|
||||
let desktop_str = desktop_raw.strip_prefix("none+").unwrap_or(desktop_raw);
|
||||
|
||||
let session_raw = session_os.as_deref().and_then(OsStr::to_str).unwrap_or("");
|
||||
let backend_str = if session_raw.is_empty() {
|
||||
"Unknown"
|
||||
} else {
|
||||
|
|
@ -27,9 +24,15 @@ pub fn get_desktop_info() -> String {
|
|||
result.push_str(" (");
|
||||
|
||||
// Capitalize first character of backend
|
||||
if let Some(first_char) = backend_str.chars().next() {
|
||||
let _ = write!(result, "{}", first_char.to_ascii_uppercase());
|
||||
result.push_str(&backend_str[first_char.len_utf8()..]);
|
||||
if let Some(first_byte) = backend_str.as_bytes().first() {
|
||||
// Convert first byte to uppercase if it's ASCII lowercase
|
||||
let upper = if first_byte.is_ascii_lowercase() {
|
||||
(first_byte - b'a' + b'A') as char
|
||||
} else {
|
||||
*first_byte as char
|
||||
};
|
||||
result.push(upper);
|
||||
result.push_str(&backend_str[1..]);
|
||||
}
|
||||
|
||||
result.push(')');
|
||||
|
|
|
|||
|
|
@ -1,13 +1,17 @@
|
|||
#![no_std]
|
||||
extern crate alloc;
|
||||
|
||||
pub mod colors;
|
||||
pub mod desktop;
|
||||
pub mod release;
|
||||
pub mod system;
|
||||
pub mod uptime;
|
||||
|
||||
use std::{
|
||||
use alloc::string::String;
|
||||
use core::{
|
||||
ffi::CStr,
|
||||
io::{self, Cursor, Write},
|
||||
mem::MaybeUninit,
|
||||
sync::atomic::{AtomicPtr, Ordering},
|
||||
};
|
||||
|
||||
pub use microfetch_asm as syscall;
|
||||
|
|
@ -25,6 +29,200 @@ pub use microfetch_asm::{
|
|||
sys_write,
|
||||
};
|
||||
|
||||
/// A simple error type for microfetch operations.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum Error {
|
||||
/// An OS error occurred, containing the errno value.
|
||||
OsError(i32),
|
||||
/// Invalid data or encoding error.
|
||||
InvalidData,
|
||||
/// Not found.
|
||||
NotFound,
|
||||
/// Write operation failed or partial write.
|
||||
WriteError,
|
||||
}
|
||||
|
||||
impl Error {
|
||||
/// Creates an error from the last OS error (reads errno).
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn last_os_error() -> Self {
|
||||
// This is a simplified version - in a real implementation,
|
||||
// we'd need to get the actual errno from the syscall return
|
||||
Self::OsError(0)
|
||||
}
|
||||
|
||||
/// Creates an error from a raw OS error code (negative errno from syscall).
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub const fn from_raw_os_error(errno: i32) -> Self {
|
||||
Self::OsError(-errno)
|
||||
}
|
||||
}
|
||||
|
||||
impl core::fmt::Display for Error {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
match self {
|
||||
Self::OsError(errno) => write!(f, "OS error: {errno}"),
|
||||
Self::InvalidData => write!(f, "Invalid data"),
|
||||
Self::NotFound => write!(f, "Not found"),
|
||||
Self::WriteError => write!(f, "Write error"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Simple OnceLock implementation for no_std
|
||||
pub struct OnceLock<T> {
|
||||
ptr: AtomicPtr<T>,
|
||||
}
|
||||
|
||||
impl<T> Default for OnceLock<T> {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> OnceLock<T> {
|
||||
#[must_use]
|
||||
pub const fn new() -> Self {
|
||||
Self {
|
||||
ptr: AtomicPtr::new(core::ptr::null_mut()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_or_init<F>(&self, f: F) -> &T
|
||||
where
|
||||
F: FnOnce() -> T,
|
||||
{
|
||||
// Load the current pointer
|
||||
let mut ptr = self.ptr.load(Ordering::Acquire);
|
||||
|
||||
if ptr.is_null() {
|
||||
// Need to initialize
|
||||
let value = f();
|
||||
let boxed = alloc::boxed::Box::new(value);
|
||||
let new_ptr = alloc::boxed::Box::into_raw(boxed);
|
||||
|
||||
// Try to set the pointer
|
||||
match self.ptr.compare_exchange(
|
||||
core::ptr::null_mut(),
|
||||
new_ptr,
|
||||
Ordering::Release,
|
||||
Ordering::Acquire,
|
||||
) {
|
||||
Ok(_) => {
|
||||
// We successfully set it
|
||||
ptr = new_ptr;
|
||||
},
|
||||
Err(existing) => {
|
||||
// Someone else set it first, free our allocation
|
||||
// SAFETY: We just allocated this and no one else has seen it
|
||||
unsafe {
|
||||
let _ = alloc::boxed::Box::from_raw(new_ptr);
|
||||
}
|
||||
ptr = existing;
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// SAFETY: We know ptr is non-null and points to a valid T
|
||||
unsafe { &*ptr }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Drop for OnceLock<T> {
|
||||
fn drop(&mut self) {
|
||||
let ptr = self.ptr.load(Ordering::Acquire);
|
||||
if !ptr.is_null() {
|
||||
// SAFETY: We know this was allocated via Box::into_raw
|
||||
unsafe {
|
||||
let _ = alloc::boxed::Box::from_raw(ptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Access to the environ pointer (provided by libc startup code)
|
||||
unsafe extern "C" {
|
||||
static environ: *const *const u8;
|
||||
}
|
||||
|
||||
/// Gets an environment variable by name (without using std).
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// This function reads from the environ global which is initialized
|
||||
/// by the C runtime before `main()` is called.
|
||||
#[must_use]
|
||||
pub fn getenv(name: &str) -> Option<&'static [u8]> {
|
||||
// SAFETY: environ is set up by the C runtime before main() runs
|
||||
// and remains valid for the lifetime of the program
|
||||
let envp = unsafe { environ };
|
||||
if envp.is_null() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let name_bytes = name.as_bytes();
|
||||
|
||||
// Walk through environment variables
|
||||
let mut i = 0;
|
||||
loop {
|
||||
// SAFETY: environ is null-terminated array of pointers
|
||||
let entry = unsafe { *envp.add(i) };
|
||||
if entry.is_null() {
|
||||
break;
|
||||
}
|
||||
|
||||
// Check if this entry starts with our variable name followed by '='
|
||||
let mut matches = true;
|
||||
for (j, &b) in name_bytes.iter().enumerate() {
|
||||
// SAFETY: entry is a valid C string
|
||||
let entry_byte = unsafe { *entry.add(j) };
|
||||
if entry_byte != b {
|
||||
matches = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if matches {
|
||||
// Check for '=' after the name
|
||||
// SAFETY: entry is a valid C string
|
||||
let eq_byte = unsafe { *entry.add(name_bytes.len()) };
|
||||
if eq_byte == b'=' {
|
||||
// Found it! Calculate the value length
|
||||
let value_start = unsafe { entry.add(name_bytes.len() + 1) };
|
||||
let mut len = 0;
|
||||
loop {
|
||||
// SAFETY: entry is a valid C string
|
||||
let b = unsafe { *value_start.add(len) };
|
||||
if b == 0 {
|
||||
break;
|
||||
}
|
||||
len += 1;
|
||||
}
|
||||
// SAFETY: We calculated the exact length
|
||||
return Some(unsafe { core::slice::from_raw_parts(value_start, len) });
|
||||
}
|
||||
}
|
||||
|
||||
i += 1;
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
/// Gets an environment variable as a UTF-8 string.
|
||||
#[must_use]
|
||||
pub fn getenv_str(name: &str) -> Option<&'static str> {
|
||||
getenv(name).and_then(|bytes| core::str::from_utf8(bytes).ok())
|
||||
}
|
||||
|
||||
/// Checks if an environment variable exists (regardless of its value).
|
||||
#[must_use]
|
||||
pub fn env_exists(name: &str) -> bool {
|
||||
getenv(name).is_some()
|
||||
}
|
||||
|
||||
/// Wrapper for `utsname` with safe accessor methods
|
||||
pub struct UtsName(UtsNameBuf);
|
||||
|
||||
|
|
@ -34,10 +232,10 @@ impl UtsName {
|
|||
/// # Errors
|
||||
///
|
||||
/// Returns an error if the `uname` syscall fails
|
||||
pub fn uname() -> Result<Self, std::io::Error> {
|
||||
pub fn uname() -> Result<Self, Error> {
|
||||
let mut uts = MaybeUninit::uninit();
|
||||
if unsafe { sys_uname(uts.as_mut_ptr()) } != 0 {
|
||||
return Err(std::io::Error::last_os_error());
|
||||
return Err(Error::last_os_error());
|
||||
}
|
||||
Ok(Self(unsafe { uts.assume_init() }))
|
||||
}
|
||||
|
|
@ -79,9 +277,7 @@ struct Fields {
|
|||
}
|
||||
|
||||
#[cfg_attr(feature = "hotpath", hotpath::measure)]
|
||||
fn print_system_info(
|
||||
fields: &Fields,
|
||||
) -> Result<(), Box<dyn std::error::Error>> {
|
||||
fn print_system_info(fields: &Fields) -> Result<(), Error> {
|
||||
let Fields {
|
||||
user_info,
|
||||
os_name,
|
||||
|
|
@ -94,55 +290,229 @@ fn print_system_info(
|
|||
colors,
|
||||
} = fields;
|
||||
|
||||
let cyan = colors::COLORS.cyan;
|
||||
let blue = colors::COLORS.blue;
|
||||
let reset = colors::COLORS.reset;
|
||||
let no_color = colors::is_no_color();
|
||||
let colors_obj = colors::Colors::new(no_color);
|
||||
let cyan = colors_obj.cyan;
|
||||
let blue = colors_obj.blue;
|
||||
let reset = colors_obj.reset;
|
||||
|
||||
// Build output string
|
||||
let mut buf = [0u8; 2048];
|
||||
let mut cursor = Cursor::new(&mut buf[..]);
|
||||
let mut pos = 0usize;
|
||||
|
||||
write!(
|
||||
cursor,
|
||||
"
|
||||
{blue} ▟█▖ {cyan}▝█▙ ▗█▛ {user_info} ~{reset}
|
||||
{blue} ▗▄▄▟██▄▄▄▄▄{cyan}▝█▙█▛ {blue}▖ {cyan} {blue}System{reset} {os_name}
|
||||
{blue} ▀▀▀▀▀▀▀▀▀▀▀▘{cyan}▝██ {blue}▟█▖ {cyan} {blue}Kernel{reset} {kernel_version}
|
||||
{cyan} ▟█▛ {cyan}▝█▘{blue}▟█▛ {cyan} {blue}Shell{reset} {shell}
|
||||
{cyan}▟█████▛ {blue}▟█████▛ {cyan} {blue}Uptime{reset} {uptime}
|
||||
{cyan} ▟█▛{blue}▗█▖ {blue}▟█▛ {cyan} {blue}Desktop{reset} {desktop}
|
||||
{cyan} ▝█▛ {blue}██▖{cyan}▗▄▄▄▄▄▄▄▄▄▄▄ {cyan} {blue}Memory{reset} {memory_usage}
|
||||
{cyan} ▝ {blue}▟█▜█▖{cyan}▀▀▀▀▀██▛▀▀▘ {cyan} {blue}Storage (/){reset} {storage}
|
||||
{blue} ▟█▘ ▜█▖ {cyan}▝█▛ {cyan} {blue}Colors{reset} {colors}\n\n"
|
||||
)?;
|
||||
// Helper to write to buffer
|
||||
let mut write_str = |s: &str| {
|
||||
let bytes = s.as_bytes();
|
||||
let remaining = buf.len() - pos;
|
||||
let to_write = bytes.len().min(remaining);
|
||||
buf[pos..pos + to_write].copy_from_slice(&bytes[..to_write]);
|
||||
pos += to_write;
|
||||
};
|
||||
|
||||
write_str("\n ");
|
||||
write_str(blue);
|
||||
write_str(" ▟█▖ ");
|
||||
write_str(cyan);
|
||||
write_str("▝█▙ ▗█▛ ");
|
||||
write_str(user_info);
|
||||
write_str(" ~");
|
||||
write_str(reset);
|
||||
write_str("\n");
|
||||
|
||||
write_str(" ");
|
||||
write_str(blue);
|
||||
write_str(" ▗▄▄▟██▄▄▄▄▄");
|
||||
write_str(cyan);
|
||||
write_str("▝█▙█▛ ");
|
||||
write_str(blue);
|
||||
write_str("▖ ");
|
||||
write_str(cyan);
|
||||
write_str(" ");
|
||||
write_str(blue);
|
||||
write_str("System");
|
||||
write_str(reset);
|
||||
write_str(" ");
|
||||
write_str(os_name);
|
||||
write_str("\n");
|
||||
|
||||
write_str(" ");
|
||||
write_str(blue);
|
||||
write_str(" ▀▀▀▀▀▀▀▀▀▀▀▘");
|
||||
write_str(cyan);
|
||||
write_str("▝██ ");
|
||||
write_str(blue);
|
||||
write_str("▟█▖ ");
|
||||
write_str(cyan);
|
||||
write_str(" ");
|
||||
write_str(blue);
|
||||
write_str("Kernel");
|
||||
write_str(reset);
|
||||
write_str(" ");
|
||||
write_str(kernel_version);
|
||||
write_str("\n");
|
||||
|
||||
write_str(" ");
|
||||
write_str(cyan);
|
||||
write_str(" ▟█▛ ");
|
||||
write_str(cyan);
|
||||
write_str("▝█▘");
|
||||
write_str(blue);
|
||||
write_str("▟█▛ ");
|
||||
write_str(cyan);
|
||||
write_str(" ");
|
||||
write_str(blue);
|
||||
write_str("Shell");
|
||||
write_str(reset);
|
||||
write_str(" ");
|
||||
write_str(shell);
|
||||
write_str("\n");
|
||||
|
||||
write_str(" ");
|
||||
write_str(cyan);
|
||||
write_str("▟█████▛ ");
|
||||
write_str(blue);
|
||||
write_str("▟█████▛ ");
|
||||
write_str(cyan);
|
||||
write_str(" ");
|
||||
write_str(blue);
|
||||
write_str("Uptime");
|
||||
write_str(reset);
|
||||
write_str(" ");
|
||||
write_str(uptime);
|
||||
write_str("\n");
|
||||
|
||||
write_str(" ");
|
||||
write_str(cyan);
|
||||
write_str(" ▟█▛");
|
||||
write_str(blue);
|
||||
write_str("▗█▖ ");
|
||||
write_str(blue);
|
||||
write_str("▟█▛ ");
|
||||
write_str(cyan);
|
||||
write_str(" ");
|
||||
write_str(blue);
|
||||
write_str("Desktop");
|
||||
write_str(reset);
|
||||
write_str(" ");
|
||||
write_str(desktop);
|
||||
write_str("\n");
|
||||
|
||||
write_str(" ");
|
||||
write_str(cyan);
|
||||
write_str(" ▝█▛ ");
|
||||
write_str(blue);
|
||||
write_str("██▖");
|
||||
write_str(cyan);
|
||||
write_str("▗▄▄▄▄▄▄▄▄▄▄▄ ");
|
||||
write_str(cyan);
|
||||
write_str(" ");
|
||||
write_str(blue);
|
||||
write_str("Memory");
|
||||
write_str(reset);
|
||||
write_str(" ");
|
||||
write_str(memory_usage);
|
||||
write_str("\n");
|
||||
|
||||
write_str(" ");
|
||||
write_str(cyan);
|
||||
write_str(" ▝ ");
|
||||
write_str(blue);
|
||||
write_str("▟█▜█▖");
|
||||
write_str(cyan);
|
||||
write_str("▀▀▀▀▀██▛▀▀▘ ");
|
||||
write_str(cyan);
|
||||
write_str(" ");
|
||||
write_str(blue);
|
||||
write_str("Storage (/)");
|
||||
write_str(reset);
|
||||
write_str(" ");
|
||||
write_str(storage);
|
||||
write_str("\n");
|
||||
|
||||
write_str(" ");
|
||||
write_str(blue);
|
||||
write_str(" ▟█▘ ▜█▖ ");
|
||||
write_str(cyan);
|
||||
write_str("▝█▛ ");
|
||||
write_str(cyan);
|
||||
write_str(" ");
|
||||
write_str(blue);
|
||||
write_str("Colors");
|
||||
write_str(reset);
|
||||
write_str(" ");
|
||||
write_str(colors);
|
||||
write_str("\n\n");
|
||||
|
||||
let len =
|
||||
usize::try_from(cursor.position()).expect("cursor position fits usize");
|
||||
// Direct syscall to avoid stdout buffering allocation
|
||||
let written = unsafe { sys_write(1, buf.as_ptr(), len) };
|
||||
let written = unsafe { sys_write(1, buf.as_ptr(), pos) };
|
||||
if written < 0 {
|
||||
return Err(io::Error::last_os_error().into());
|
||||
#[allow(clippy::cast_possible_truncation)]
|
||||
return Err(Error::OsError(written as i32));
|
||||
}
|
||||
#[allow(clippy::cast_sign_loss)] // non-negative verified by the guard above
|
||||
if written as usize != len {
|
||||
return Err(
|
||||
io::Error::new(io::ErrorKind::WriteZero, "partial write to stdout")
|
||||
.into(),
|
||||
);
|
||||
#[allow(clippy::cast_sign_loss)]
|
||||
if written as usize != pos {
|
||||
return Err(Error::WriteError);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Print version information using direct syscall.
|
||||
fn print_version() {
|
||||
const VERSION: &str = concat!("Microfetch ", env!("CARGO_PKG_VERSION"), "\n");
|
||||
unsafe {
|
||||
let _ = sys_write(1, VERSION.as_ptr(), VERSION.len());
|
||||
}
|
||||
}
|
||||
|
||||
/// Check if --version was passed via argc/argv.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// This function must be called with valid argc and argv from the program entry
|
||||
/// point.
|
||||
unsafe fn check_version_flag(argc: i32, argv: *const *const u8) -> bool {
|
||||
if argc < 2 {
|
||||
return false;
|
||||
}
|
||||
// SAFETY: argv is a valid array of argc pointers
|
||||
let arg1 = unsafe { *argv.add(1) };
|
||||
if arg1.is_null() {
|
||||
return false;
|
||||
}
|
||||
// Check if arg1 is "--version"
|
||||
let version_flag = b"--version\0";
|
||||
for (i, &b) in version_flag.iter().enumerate() {
|
||||
// SAFETY: arg1 is a valid C string
|
||||
let arg_byte = unsafe { *arg1.add(i) };
|
||||
if arg_byte != b {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
/// Main entry point for microfetch - can be called by the binary crate
|
||||
/// or by other consumers of the library
|
||||
/// or by other consumers of the library.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `argc` - Argument count from main
|
||||
/// * `argv` - Argument vector from main
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns an error if any system call fails
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// argv must be a valid null-terminated array of C strings.
|
||||
#[cfg_attr(feature = "hotpath", hotpath::main)]
|
||||
pub fn run() -> Result<(), Box<dyn std::error::Error>> {
|
||||
if Some("--version") == std::env::args().nth(1).as_deref() {
|
||||
println!("Microfetch {}", env!("CARGO_PKG_VERSION"));
|
||||
} else {
|
||||
pub unsafe fn run(argc: i32, argv: *const *const u8) -> Result<(), Error> {
|
||||
if unsafe { check_version_flag(argc, argv) } {
|
||||
print_version();
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let utsname = UtsName::uname()?;
|
||||
let fields = Fields {
|
||||
user_info: system::get_username_and_hostname(&utsname),
|
||||
|
|
@ -156,7 +526,6 @@ pub fn run() -> Result<(), Box<dyn std::error::Error>> {
|
|||
colors: colors::print_dots(),
|
||||
};
|
||||
print_system_info(&fields)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use std::{fmt::Write as _, io};
|
||||
use alloc::string::String;
|
||||
|
||||
use crate::{UtsName, syscall::read_file_fast};
|
||||
use crate::{Error, UtsName, syscall::read_file_fast};
|
||||
|
||||
#[must_use]
|
||||
#[cfg_attr(feature = "hotpath", hotpath::measure)]
|
||||
|
|
@ -13,7 +13,14 @@ pub fn get_system_info(utsname: &UtsName) -> String {
|
|||
let capacity = sysname.len() + 1 + release.len() + 2 + machine.len() + 1;
|
||||
let mut result = String::with_capacity(capacity);
|
||||
|
||||
write!(result, "{sysname} {release} ({machine})").unwrap();
|
||||
// Manual string construction instead of write! macro
|
||||
result.push_str(sysname);
|
||||
result.push(' ');
|
||||
result.push_str(release);
|
||||
result.push_str(" (");
|
||||
result.push_str(machine);
|
||||
result.push(')');
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
|
|
@ -23,7 +30,7 @@ pub fn get_system_info(utsname: &UtsName) -> String {
|
|||
///
|
||||
/// Returns an error if `/etc/os-release` cannot be read.
|
||||
#[cfg_attr(feature = "hotpath", hotpath::measure)]
|
||||
pub fn get_os_pretty_name() -> Result<String, io::Error> {
|
||||
pub fn get_os_pretty_name() -> Result<String, Error> {
|
||||
// Fast byte-level scanning for PRETTY_NAME=
|
||||
const PREFIX: &[u8] = b"PRETTY_NAME=";
|
||||
|
||||
|
|
@ -31,7 +38,7 @@ pub fn get_os_pretty_name() -> Result<String, io::Error> {
|
|||
|
||||
// Use fast syscall-based file reading
|
||||
let bytes_read = read_file_fast("/etc/os-release", &mut buffer)
|
||||
.map_err(|e| io::Error::from_raw_os_error(-e))?;
|
||||
.map_err(Error::from_raw_os_error)?;
|
||||
let content = &buffer[..bytes_read];
|
||||
|
||||
let mut offset = 0;
|
||||
|
|
@ -66,5 +73,5 @@ pub fn get_os_pretty_name() -> Result<String, io::Error> {
|
|||
offset += line_end + 1;
|
||||
}
|
||||
|
||||
Ok("Unknown".to_owned())
|
||||
Ok(String::from("Unknown"))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,37 +1,39 @@
|
|||
use std::{ffi::OsStr, fmt::Write as _, io, mem::MaybeUninit};
|
||||
use alloc::string::String;
|
||||
use core::mem::MaybeUninit;
|
||||
|
||||
use crate::{
|
||||
Error,
|
||||
UtsName,
|
||||
colors::COLORS,
|
||||
colors::Colors,
|
||||
syscall::{StatfsBuf, read_file_fast, sys_statfs},
|
||||
};
|
||||
|
||||
#[must_use]
|
||||
#[cfg_attr(feature = "hotpath", hotpath::measure)]
|
||||
pub fn get_username_and_hostname(utsname: &UtsName) -> String {
|
||||
let username_os = std::env::var_os("USER");
|
||||
let username = username_os
|
||||
.as_deref()
|
||||
.and_then(OsStr::to_str)
|
||||
.unwrap_or("unknown_user");
|
||||
let username = crate::getenv_str("USER").unwrap_or("unknown_user");
|
||||
let hostname = utsname.nodename().to_str().unwrap_or("unknown_host");
|
||||
|
||||
let capacity = COLORS.yellow.len()
|
||||
// Get colors (checking NO_COLOR only once)
|
||||
let no_color = crate::colors::is_no_color();
|
||||
let colors = Colors::new(no_color);
|
||||
|
||||
let capacity = colors.yellow.len()
|
||||
+ username.len()
|
||||
+ COLORS.red.len()
|
||||
+ colors.red.len()
|
||||
+ 1
|
||||
+ COLORS.green.len()
|
||||
+ colors.green.len()
|
||||
+ hostname.len()
|
||||
+ COLORS.reset.len();
|
||||
+ colors.reset.len();
|
||||
let mut result = String::with_capacity(capacity);
|
||||
|
||||
result.push_str(COLORS.yellow);
|
||||
result.push_str(colors.yellow);
|
||||
result.push_str(username);
|
||||
result.push_str(COLORS.red);
|
||||
result.push_str(colors.red);
|
||||
result.push('@');
|
||||
result.push_str(COLORS.green);
|
||||
result.push_str(colors.green);
|
||||
result.push_str(hostname);
|
||||
result.push_str(COLORS.reset);
|
||||
result.push_str(colors.reset);
|
||||
|
||||
result
|
||||
}
|
||||
|
|
@ -39,13 +41,12 @@ pub fn get_username_and_hostname(utsname: &UtsName) -> String {
|
|||
#[must_use]
|
||||
#[cfg_attr(feature = "hotpath", hotpath::measure)]
|
||||
pub fn get_shell() -> String {
|
||||
let shell_os = std::env::var_os("SHELL");
|
||||
let shell = shell_os.as_deref().and_then(OsStr::to_str).unwrap_or("");
|
||||
let shell = crate::getenv_str("SHELL").unwrap_or("");
|
||||
let start = shell.rfind('/').map_or(0, |i| i + 1);
|
||||
if shell.is_empty() {
|
||||
"unknown_shell".into()
|
||||
String::from("unknown_shell")
|
||||
} else {
|
||||
shell[start..].into()
|
||||
String::from(&shell[start..])
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -56,12 +57,12 @@ pub fn get_shell() -> String {
|
|||
/// Returns an error if the filesystem information cannot be retrieved.
|
||||
#[cfg_attr(feature = "hotpath", hotpath::measure)]
|
||||
#[allow(clippy::cast_precision_loss)]
|
||||
pub fn get_root_disk_usage() -> Result<String, io::Error> {
|
||||
pub fn get_root_disk_usage() -> Result<String, Error> {
|
||||
let mut vfs = MaybeUninit::<StatfsBuf>::uninit();
|
||||
let path = b"/\0";
|
||||
|
||||
if unsafe { sys_statfs(path.as_ptr(), vfs.as_mut_ptr()) } != 0 {
|
||||
return Err(io::Error::last_os_error());
|
||||
return Err(Error::last_os_error());
|
||||
}
|
||||
|
||||
let vfs = unsafe { vfs.assume_init() };
|
||||
|
|
@ -77,18 +78,96 @@ pub fn get_root_disk_usage() -> Result<String, io::Error> {
|
|||
let used_size = used_size as f64 / (1024.0 * 1024.0 * 1024.0);
|
||||
let usage = (used_size / total_size) * 100.0;
|
||||
|
||||
let no_color = crate::colors::is_no_color();
|
||||
let colors = Colors::new(no_color);
|
||||
|
||||
let mut result = String::with_capacity(64);
|
||||
write!(
|
||||
result,
|
||||
"{used_size:.2} GiB / {total_size:.2} GiB ({cyan}{usage:.0}%{reset})",
|
||||
cyan = COLORS.cyan,
|
||||
reset = COLORS.reset,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// Manual float formatting
|
||||
write_float(&mut result, used_size, 2);
|
||||
result.push_str(" GiB / ");
|
||||
write_float(&mut result, total_size, 2);
|
||||
result.push_str(" GiB (");
|
||||
result.push_str(colors.cyan);
|
||||
write_float(&mut result, usage, 0);
|
||||
result.push('%');
|
||||
result.push_str(colors.reset);
|
||||
result.push(')');
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
/// Write a float to string with specified decimal places
|
||||
#[allow(
|
||||
clippy::cast_sign_loss,
|
||||
clippy::cast_possible_truncation,
|
||||
clippy::cast_precision_loss
|
||||
)]
|
||||
fn write_float(s: &mut String, val: f64, decimals: u32) {
|
||||
// Handle integer part
|
||||
let int_part = val as u64;
|
||||
write_u64(s, int_part);
|
||||
|
||||
if decimals > 0 {
|
||||
s.push('.');
|
||||
|
||||
// Calculate fractional part
|
||||
let mut frac = val - int_part as f64;
|
||||
for _ in 0..decimals {
|
||||
frac *= 10.0;
|
||||
let digit = frac as u8;
|
||||
s.push((b'0' + digit) as char);
|
||||
frac -= f64::from(digit);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Round an f64 to nearest integer (`f64::round` is not in core)
|
||||
#[allow(
|
||||
clippy::cast_precision_loss,
|
||||
clippy::cast_possible_truncation,
|
||||
clippy::cast_sign_loss
|
||||
)]
|
||||
fn round_f64(x: f64) -> f64 {
|
||||
if x >= 0.0 {
|
||||
let int_part = x as u64 as f64;
|
||||
let frac = x - int_part;
|
||||
if frac >= 0.5 {
|
||||
int_part + 1.0
|
||||
} else {
|
||||
int_part
|
||||
}
|
||||
} else {
|
||||
let int_part = (-x) as u64 as f64;
|
||||
let frac = -x - int_part;
|
||||
if frac >= 0.5 {
|
||||
-(int_part + 1.0)
|
||||
} else {
|
||||
-int_part
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Write a u64 to string
|
||||
fn write_u64(s: &mut String, mut n: u64) {
|
||||
if n == 0 {
|
||||
s.push('0');
|
||||
return;
|
||||
}
|
||||
|
||||
let mut buf = [0u8; 20];
|
||||
let mut i = 20;
|
||||
|
||||
while n > 0 {
|
||||
i -= 1;
|
||||
buf[i] = b'0' + (n % 10) as u8;
|
||||
n /= 10;
|
||||
}
|
||||
|
||||
// SAFETY: buf contains only ASCII digits
|
||||
s.push_str(unsafe { core::str::from_utf8_unchecked(&buf[i..]) });
|
||||
}
|
||||
|
||||
/// Fast integer parsing without stdlib overhead
|
||||
#[inline]
|
||||
fn parse_u64_fast(s: &[u8]) -> u64 {
|
||||
|
|
@ -109,16 +188,16 @@ fn parse_u64_fast(s: &[u8]) -> u64 {
|
|||
///
|
||||
/// Returns an error if `/proc/meminfo` cannot be read.
|
||||
#[cfg_attr(feature = "hotpath", hotpath::measure)]
|
||||
pub fn get_memory_usage() -> Result<String, io::Error> {
|
||||
pub fn get_memory_usage() -> Result<String, Error> {
|
||||
#[cfg_attr(feature = "hotpath", hotpath::measure)]
|
||||
fn parse_memory_info() -> Result<(f64, f64), io::Error> {
|
||||
fn parse_memory_info() -> Result<(f64, f64), Error> {
|
||||
let mut total_memory_kb = 0u64;
|
||||
let mut available_memory_kb = 0u64;
|
||||
let mut buffer = [0u8; 1024];
|
||||
|
||||
// Use fast syscall-based file reading
|
||||
let bytes_read = read_file_fast("/proc/meminfo", &mut buffer)
|
||||
.map_err(|e| io::Error::from_raw_os_error(-e))?;
|
||||
.map_err(Error::from_raw_os_error)?;
|
||||
let meminfo = &buffer[..bytes_read];
|
||||
|
||||
// Fast scanning for MemTotal and MemAvailable
|
||||
|
|
@ -168,17 +247,22 @@ pub fn get_memory_usage() -> Result<String, io::Error> {
|
|||
|
||||
let (used_memory, total_memory) = parse_memory_info()?;
|
||||
#[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
|
||||
let percentage_used = (used_memory / total_memory * 100.0).round() as u64;
|
||||
let percentage_used = round_f64(used_memory / total_memory * 100.0) as u64;
|
||||
|
||||
let no_color = crate::colors::is_no_color();
|
||||
let colors = Colors::new(no_color);
|
||||
|
||||
let mut result = String::with_capacity(64);
|
||||
write!(
|
||||
result,
|
||||
"{used_memory:.2} GiB / {total_memory:.2} GiB \
|
||||
({cyan}{percentage_used}%{reset})",
|
||||
cyan = COLORS.cyan,
|
||||
reset = COLORS.reset,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
write_float(&mut result, used_memory, 2);
|
||||
result.push_str(" GiB / ");
|
||||
write_float(&mut result, total_memory, 2);
|
||||
result.push_str(" GiB (");
|
||||
result.push_str(colors.cyan);
|
||||
write_u64(&mut result, percentage_used);
|
||||
result.push('%');
|
||||
result.push_str(colors.reset);
|
||||
result.push(')');
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
use std::{io, mem::MaybeUninit};
|
||||
use alloc::string::String;
|
||||
use core::mem::MaybeUninit;
|
||||
|
||||
use crate::syscall::sys_sysinfo;
|
||||
use crate::{Error, syscall::sys_sysinfo};
|
||||
|
||||
/// Faster integer to string conversion without the formatting overhead.
|
||||
#[inline]
|
||||
|
|
@ -16,7 +17,8 @@ fn itoa(mut n: u64, buf: &mut [u8]) -> &str {
|
|||
n /= 10;
|
||||
}
|
||||
|
||||
unsafe { std::str::from_utf8_unchecked(&buf[i..]) }
|
||||
// SAFETY: We only wrote ASCII digits
|
||||
unsafe { core::str::from_utf8_unchecked(&buf[i..]) }
|
||||
}
|
||||
|
||||
/// Gets the current system uptime.
|
||||
|
|
@ -25,11 +27,11 @@ fn itoa(mut n: u64, buf: &mut [u8]) -> &str {
|
|||
///
|
||||
/// Returns an error if the system uptime cannot be retrieved.
|
||||
#[cfg_attr(feature = "hotpath", hotpath::measure)]
|
||||
pub fn get_current() -> Result<String, io::Error> {
|
||||
pub fn get_current() -> Result<String, Error> {
|
||||
let uptime_seconds = {
|
||||
let mut info = MaybeUninit::uninit();
|
||||
if unsafe { sys_sysinfo(info.as_mut_ptr()) } != 0 {
|
||||
return Err(io::Error::last_os_error());
|
||||
return Err(Error::last_os_error());
|
||||
}
|
||||
#[allow(clippy::cast_sign_loss)]
|
||||
unsafe {
|
||||
|
|
|
|||
|
|
@ -12,7 +12,9 @@ publish = false
|
|||
|
||||
[dependencies]
|
||||
hotpath = { optional = true, version = "0.14.0" }
|
||||
microfetch-alloc.workspace = true
|
||||
microfetch-lib.workspace = true
|
||||
microfetch-asm.workspace = true
|
||||
|
||||
[features]
|
||||
hotpath = [ "dep:hotpath" ]
|
||||
|
|
|
|||
|
|
@ -1,3 +1,60 @@
|
|||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
microfetch_lib::run()
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
use microfetch_alloc::BumpAlloc;
|
||||
use microfetch_asm::sys_write;
|
||||
#[cfg(not(test))]
|
||||
use {core::panic::PanicInfo, microfetch_asm::sys_exit};
|
||||
|
||||
#[global_allocator]
|
||||
static ALLOCATOR: BumpAlloc = BumpAlloc::new();
|
||||
|
||||
/// Receives argc and argv directly. The C runtime will call this after
|
||||
/// initializing the environment. Cool right?
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// argv must be a valid pointer to an array of argc C strings.
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn main(argc: i32, argv: *const *const u8) -> i32 {
|
||||
// SAFETY: argc and argv are provided by the C runtime and are valid
|
||||
unsafe {
|
||||
match microfetch_lib::run(argc, argv) {
|
||||
Ok(()) => 0,
|
||||
Err(e) => {
|
||||
// Print error message to stderr (fd 2)
|
||||
let msg = alloc::format!("Error: {e}\n");
|
||||
let _ = sys_write(2, msg.as_ptr(), msg.len());
|
||||
1
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(test))]
|
||||
#[panic_handler]
|
||||
fn panic(_info: &PanicInfo) -> ! {
|
||||
// Write "panic" to stderr and exit
|
||||
const PANIC_MSG: &[u8] = b"panic\n";
|
||||
unsafe {
|
||||
let _ = sys_write(2, PANIC_MSG.as_ptr(), PANIC_MSG.len());
|
||||
sys_exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: Stubs for Rust exception handling symbols needed when using alloc with
|
||||
// panic=abort These are normally provided by the unwinding runtime, but we're
|
||||
// using panic=abort. I don't actually think this is the correct approach, but I
|
||||
// cannot think of anything better.
|
||||
|
||||
#[cfg(not(test))]
|
||||
#[unsafe(no_mangle)]
|
||||
const extern "C" fn rust_eh_personality() {}
|
||||
|
||||
#[cfg(not(test))]
|
||||
#[unsafe(no_mangle)]
|
||||
extern "C" fn _Unwind_Resume() -> ! {
|
||||
unsafe { sys_exit(1) }
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue