use core::{cell::UnsafeCell, ffi::CStr, mem::MaybeUninit, ptr::NonNull};
#[cfg(not(any(feature = "std", feature = "rustc-dep-of-std")))]
extern crate alloc;
#[cfg(feature = "std")]
use std as alloc;
use alloc::boxed::Box;
use alloc::vec::Vec;
use safa_abi::ffi::option::OptZero;
use safa_abi::ffi::slice::Slice;
use alloc::ffi::CString;
use crate::sync::cell::LazyCell;
use crate::sync::locks::Mutex;
struct EnvVars {
env: Vec<(Box<[u8]>, Box<CStr>)>,
size_hint: usize,
}
impl EnvVars {
pub const fn new() -> Self {
Self {
env: Vec::new(),
size_hint: 0,
}
}
pub fn get(&self, key: &[u8]) -> Option<&[u8]> {
for (k, v) in &self.env {
if &**k == key {
return Some(v.to_bytes());
}
}
None
}
#[inline(always)]
pub unsafe fn push(&mut self, key: &[u8], value: &[u8]) {
let cstr = CString::new(value)
.unwrap_or_else(|_| CStr::from_bytes_until_nul(value).unwrap().into());
self.env
.push((key.to_vec().into_boxed_slice(), cstr.into_boxed_c_str()));
self.size_hint += key.len() + value.len() + 2;
}
#[inline(always)]
pub fn set(&mut self, key: &[u8], value: &[u8]) {
for (k, v) in &mut self.env {
if &**k == key {
let old_len = v.count_bytes();
let new_value = CString::new(value)
.unwrap_or_else(|_| CStr::from_bytes_until_nul(value).unwrap().into());
*v = new_value.into_boxed_c_str();
self.size_hint -= old_len + 1;
self.size_hint += value.len() + 1;
return;
}
}
unsafe {
self.push(key, value);
}
}
#[inline(always)]
pub fn remove(&mut self, key: &[u8]) {
for (i, (k, v)) in self.env.iter().enumerate() {
if &**k == key {
self.size_hint -= key.len() + 1 + v.count_bytes();
self.env.swap_remove(i);
return;
}
}
}
unsafe fn insert_raw(&mut self, raw: &[&[u8]]) {
self.env.reserve(raw.len());
for slice in raw {
let mut spilt = slice.splitn(2, |c| *c == b'=');
let Some(key) = spilt.next() else {
continue;
};
let value = spilt.next();
let value = value.unwrap_or_default();
self.push(key, value);
}
}
pub fn clear(&mut self) {
self.env.clear();
self.size_hint = 0;
}
fn duplicate(&self) -> (Box<[u8]>, Vec<Slice<u8>>) {
let mut buf: Vec<u8> = Vec::with_capacity(self.size_hint);
unsafe { buf.set_len(buf.capacity()) };
let mut buf = buf.into_boxed_slice();
let mut offset = 0;
let mut slices = Vec::with_capacity(self.env.len());
for (key, value) in &self.env {
let ptr = unsafe { buf.as_mut_ptr().add(offset) };
slices.push(unsafe { Slice::from_raw_parts(ptr, key.len() + 1 + value.count_bytes()) });
buf[offset..offset + key.len()].copy_from_slice(key);
offset += key.len();
buf[offset] = b'=';
offset += 1;
let value_len = value.count_bytes() + 1;
buf[offset..offset + value_len].copy_from_slice(value.to_bytes_with_nul());
offset += value_len;
}
(buf, slices)
}
}
#[derive(Debug, Clone, Copy)]
pub(super) struct RawEnv {
args: NonNull<[&'static [u8]]>,
}
impl RawEnv {
pub const fn new(args: Option<NonNull<[&'static [u8]]>>) -> Self {
Self {
args: match args {
Some(args) => args,
None => unsafe { NonNull::new_unchecked(&mut []) },
},
}
}
const unsafe fn into_slice(self) -> &'static [&'static [u8]] {
unsafe { self.args.as_ref() }
}
}
pub(super) struct RawEnvStatic(UnsafeCell<MaybeUninit<RawEnv>>);
unsafe impl Sync for RawEnvStatic {}
impl RawEnvStatic {
pub const fn new() -> Self {
Self(UnsafeCell::new(MaybeUninit::uninit()))
}
pub unsafe fn init(&self, env: RawEnv) {
unsafe {
self.0.get().write(MaybeUninit::new(env));
}
}
const unsafe fn get_unchecked(&self) -> &mut RawEnv {
(*self.0.get()).assume_init_mut()
}
pub const unsafe fn as_slice(&self) -> &'static [&'static [u8]] {
unsafe {
let raw = self.get_unchecked();
raw.into_slice()
}
}
}
pub(super) static RAW_ENV: RawEnvStatic = RawEnvStatic::new();
static ENV: LazyCell<Mutex<EnvVars>> = LazyCell::new(|| {
let mut env = EnvVars::new();
unsafe { env.insert_raw(RAW_ENV.as_slice()) };
Mutex::new(env)
});
#[inline]
pub fn env_get_all() -> Vec<(Box<[u8]>, Box<CStr>)> {
let env = ENV.lock();
env.env.clone()
}
#[inline]
pub fn env_get(key: &[u8]) -> Option<Box<[u8]>> {
let env = ENV.lock();
env.get(key).map(|v| v.to_vec().into_boxed_slice())
}
#[inline]
pub fn env_set(key: &[u8], value: &[u8]) {
let mut env = ENV.lock();
env.set(key, value);
}
#[inline]
pub fn env_remove(key: &[u8]) {
let mut env = ENV.lock();
env.remove(key);
}
#[inline]
pub(crate) unsafe fn duplicate_env() -> (Box<[u8]>, Vec<Slice<u8>>) {
let env = ENV.lock();
env.duplicate()
}
#[inline]
pub fn env_clear() {
let mut env = ENV.lock();
env.clear();
}
#[cfg_attr(
not(any(feature = "std", feature = "rustc-dep-of-std")),
unsafe(no_mangle)
)]
pub unsafe extern "C" fn sysenv_get(key: OptZero<Slice<u8>>) -> OptZero<Slice<u8>> {
unsafe {
let Some(key) = key.into_option() else {
return OptZero::none();
};
ENV.lock()
.get(key.as_slice_unchecked())
.map(|slice| Slice::from_slice(slice))
.into()
}
}
#[cfg_attr(
not(any(feature = "std", feature = "rustc-dep-of-std")),
unsafe(no_mangle)
)]
pub extern "C" fn sysenv_set(key: Slice<u8>, value: OptZero<Slice<u8>>) {
unsafe {
let key = key
.try_as_slice()
.expect("invalid key passed to sysenv_set");
let value = if let Some(value) = value.into_option() {
value
.try_as_slice()
.expect("invalid value passed to sysenv_set")
} else {
&[]
};
env_set(key, value);
}
}
#[cfg_attr(
not(any(feature = "std", feature = "rustc-dep-of-std")),
unsafe(no_mangle)
)]
pub extern "C" fn sysenv_remove(key: Slice<u8>) {
unsafe {
let key = key
.try_as_slice()
.expect("invalid key passed to sysenv_remove");
env_remove(key);
}
}
#[cfg_attr(
not(any(feature = "std", feature = "rustc-dep-of-std")),
unsafe(no_mangle)
)]
pub extern "C" fn sysenv_clear() {
env_clear();
}