1use core::{cell::UnsafeCell, ffi::CStr, mem::MaybeUninit, ptr::NonNull};
5
6#[cfg(not(any(feature = "std", feature = "rustc-dep-of-std")))]
7extern crate alloc;
8
9#[cfg(feature = "std")]
10use std as alloc;
11
12use alloc::boxed::Box;
13use alloc::vec::Vec;
14use safa_abi::ffi::option::OptZero;
15use safa_abi::ffi::slice::Slice;
16
17use alloc::ffi::CString;
18
19use crate::sync::cell::LazyCell;
20use crate::sync::locks::Mutex;
21
22struct EnvVars {
25 env: Vec<(Box<[u8]>, Box<CStr>)>,
26 size_hint: usize,
29}
30
31impl EnvVars {
32 pub const fn new() -> Self {
33 Self {
34 env: Vec::new(),
35 size_hint: 0,
36 }
37 }
38
39 pub fn get(&self, key: &[u8]) -> Option<&[u8]> {
40 for (k, v) in &self.env {
41 if &**k == key {
42 return Some(v.to_bytes());
43 }
44 }
45 None
46 }
47
48 #[inline(always)]
52 pub unsafe fn push(&mut self, key: &[u8], value: &[u8]) {
53 let cstr = CString::new(value)
54 .unwrap_or_else(|_| CStr::from_bytes_until_nul(value).unwrap().into());
55
56 self.env
57 .push((key.to_vec().into_boxed_slice(), cstr.into_boxed_c_str()));
58
59 self.size_hint += key.len() + value.len() + 2;
61 }
62
63 #[inline(always)]
64 pub fn set(&mut self, key: &[u8], value: &[u8]) {
65 for (k, v) in &mut self.env {
66 if &**k == key {
67 let old_len = v.count_bytes();
68
69 let new_value = CString::new(value)
70 .unwrap_or_else(|_| CStr::from_bytes_until_nul(value).unwrap().into());
71 *v = new_value.into_boxed_c_str();
72 self.size_hint -= old_len + 1;
73 self.size_hint += value.len() + 1;
75 return;
76 }
77 }
78
79 unsafe {
80 self.push(key, value);
81 }
82 }
83
84 #[inline(always)]
85 pub fn remove(&mut self, key: &[u8]) {
86 for (i, (k, v)) in self.env.iter().enumerate() {
87 if &**k == key {
88 self.size_hint -= key.len() + 1 + v.count_bytes();
90 self.env.swap_remove(i);
91 return;
92 }
93 }
94 }
95
96 unsafe fn insert_raw(&mut self, raw: &[&[u8]]) {
100 self.env.reserve(raw.len());
101
102 for slice in raw {
103 let mut spilt = slice.splitn(2, |c| *c == b'=');
104
105 let Some(key) = spilt.next() else {
106 continue;
107 };
108
109 let value = spilt.next();
110 let value = value.unwrap_or_default();
111
112 self.push(key, value);
113 }
114 }
115
116 pub fn clear(&mut self) {
117 self.env.clear();
118 self.size_hint = 0;
119 }
120
121 fn duplicate(&self) -> (Box<[u8]>, Vec<Slice<u8>>) {
122 let mut buf: Vec<u8> = Vec::with_capacity(self.size_hint);
125 unsafe { buf.set_len(buf.capacity()) };
126 let mut buf = buf.into_boxed_slice();
127 let mut offset = 0;
128
129 let mut slices = Vec::with_capacity(self.env.len());
130
131 for (key, value) in &self.env {
132 let ptr = unsafe { buf.as_mut_ptr().add(offset) };
133 slices.push(unsafe { Slice::from_raw_parts(ptr, key.len() + 1 + value.count_bytes()) });
134
135 buf[offset..offset + key.len()].copy_from_slice(key);
136 offset += key.len();
137
138 buf[offset] = b'=';
139 offset += 1;
140
141 let value_len = value.count_bytes() + 1;
142 buf[offset..offset + value_len].copy_from_slice(value.to_bytes_with_nul());
143 offset += value_len;
144 }
145
146 (buf, slices)
147 }
148}
149
150#[derive(Debug, Clone, Copy)]
151pub(super) struct RawEnv {
152 args: NonNull<[&'static [u8]]>,
153}
154
155impl RawEnv {
156 pub const fn new(args: Option<NonNull<[&'static [u8]]>>) -> Self {
157 Self {
158 args: match args {
159 Some(args) => args,
160 None => unsafe { NonNull::new_unchecked(&mut []) },
161 },
162 }
163 }
164
165 const unsafe fn into_slice(self) -> &'static [&'static [u8]] {
166 unsafe { self.args.as_ref() }
167 }
168}
169
170pub(super) struct RawEnvStatic(UnsafeCell<MaybeUninit<RawEnv>>);
171unsafe impl Sync for RawEnvStatic {}
172
173impl RawEnvStatic {
174 pub const fn new() -> Self {
175 Self(UnsafeCell::new(MaybeUninit::uninit()))
176 }
177
178 pub unsafe fn init(&self, env: RawEnv) {
179 unsafe {
180 self.0.get().write(MaybeUninit::new(env));
181 }
182 }
183
184 const unsafe fn get_unchecked(&self) -> &mut RawEnv {
185 (*self.0.get()).assume_init_mut()
186 }
187
188 pub const unsafe fn as_slice(&self) -> &'static [&'static [u8]] {
189 unsafe {
190 let raw = self.get_unchecked();
191 raw.into_slice()
192 }
193 }
194}
195
196pub(super) static RAW_ENV: RawEnvStatic = RawEnvStatic::new();
198
199static ENV: LazyCell<Mutex<EnvVars>> = LazyCell::new(|| {
201 let mut env = EnvVars::new();
202 unsafe { env.insert_raw(RAW_ENV.as_slice()) };
203 Mutex::new(env)
204});
205
206#[inline]
208pub fn env_get_all() -> Vec<(Box<[u8]>, Box<CStr>)> {
209 let env = ENV.lock();
210 env.env.clone()
211}
212
213#[inline]
214pub fn env_get(key: &[u8]) -> Option<Box<[u8]>> {
215 let env = ENV.lock();
216 env.get(key).map(|v| v.to_vec().into_boxed_slice())
217}
218
219#[inline]
220pub fn env_set(key: &[u8], value: &[u8]) {
221 let mut env = ENV.lock();
222 env.set(key, value);
223}
224
225#[inline]
226pub fn env_remove(key: &[u8]) {
227 let mut env = ENV.lock();
228 env.remove(key);
229}
230
231#[inline]
237pub(crate) unsafe fn duplicate_env() -> (Box<[u8]>, Vec<Slice<u8>>) {
238 let env = ENV.lock();
239 env.duplicate()
240}
241
242#[inline]
243pub fn env_clear() {
244 let mut env = ENV.lock();
245 env.clear();
246}
247
248#[cfg_attr(
249 not(any(feature = "std", feature = "rustc-dep-of-std")),
250 unsafe(no_mangle)
251)]
252
253pub unsafe extern "C" fn sysenv_get(key: OptZero<Slice<u8>>) -> OptZero<Slice<u8>> {
258 unsafe {
259 let Some(key) = key.into_option() else {
260 return OptZero::none();
261 };
262
263 ENV.lock()
264 .get(key.as_slice_unchecked())
265 .map(|slice| Slice::from_slice(slice))
266 .into()
267 }
268}
269
270#[cfg_attr(
271 not(any(feature = "std", feature = "rustc-dep-of-std")),
272 unsafe(no_mangle)
273)]
274pub extern "C" fn sysenv_set(key: Slice<u8>, value: OptZero<Slice<u8>>) {
276 unsafe {
277 let key = key
278 .try_as_slice()
279 .expect("invalid key passed to sysenv_set");
280
281 let value = if let Some(value) = value.into_option() {
282 value
283 .try_as_slice()
284 .expect("invalid value passed to sysenv_set")
285 } else {
286 &[]
287 };
288
289 env_set(key, value);
290 }
291}
292
293#[cfg_attr(
294 not(any(feature = "std", feature = "rustc-dep-of-std")),
295 unsafe(no_mangle)
296)]
297pub extern "C" fn sysenv_remove(key: Slice<u8>) {
299 unsafe {
300 let key = key
301 .try_as_slice()
302 .expect("invalid key passed to sysenv_remove");
303
304 env_remove(key);
305 }
306}
307
308#[cfg_attr(
309 not(any(feature = "std", feature = "rustc-dep-of-std")),
310 unsafe(no_mangle)
311)]
312pub extern "C" fn sysenv_clear() {
314 env_clear();
315}