1use core::{cell::UnsafeCell, ffi::CStr};
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::raw::{NonNullSlice, Optional, RawSlice};
15
16use crate::Lazy;
17use alloc::ffi::CString;
18
19use super::args::RawArgsStatic;
20
21struct EnvVars {
24 env: Vec<(Box<[u8]>, Box<CStr>)>,
25 size_hint: usize,
28}
29
30impl EnvVars {
31 pub const fn new() -> Self {
32 Self {
33 env: Vec::new(),
34 size_hint: 0,
35 }
36 }
37
38 pub fn get(&self, key: &[u8]) -> Option<&[u8]> {
39 for (k, v) in &self.env {
40 if &**k == key {
41 return Some(v.to_bytes());
42 }
43 }
44 None
45 }
46
47 #[inline(always)]
51 pub unsafe fn push(&mut self, key: &[u8], value: &[u8]) {
52 let cstr = CString::new(value)
53 .unwrap_or_else(|_| CStr::from_bytes_until_nul(value).unwrap().into());
54
55 self.env
56 .push((key.to_vec().into_boxed_slice(), cstr.into_boxed_c_str()));
57
58 self.size_hint += key.len() + value.len() + 1;
59 }
60
61 #[inline(always)]
62 pub fn set(&mut self, key: &[u8], value: &[u8]) {
63 for (k, v) in &mut self.env {
64 if &**k == key {
65 let old_len = v.count_bytes();
66
67 let new_value = CString::new(value)
68 .unwrap_or_else(|_| CStr::from_bytes_until_nul(value).unwrap().into());
69 *v = new_value.into_boxed_c_str();
70 self.size_hint -= old_len;
71 self.size_hint += value.len();
72 return;
73 }
74 }
75
76 unsafe {
77 self.push(key, value);
78 }
79 }
80
81 #[inline(always)]
82 pub fn remove(&mut self, key: &[u8]) {
83 for (i, (k, v)) in self.env.iter().enumerate() {
84 if &**k == key {
85 self.size_hint -= key.len() + 1 + v.count_bytes();
87 self.env.swap_remove(i);
88 return;
89 }
90 }
91 }
92
93 unsafe fn insert_raw(&mut self, raw: &[NonNullSlice<u8>]) {
97 self.env.reserve(raw.len());
98
99 for slice in raw {
100 let slice = slice.into_slice_mut();
101 let mut spilt = slice.splitn(2, |c| *c == b'=');
102
103 let Some(key) = spilt.next() else {
104 continue;
105 };
106
107 let value = spilt.next();
108 let value = value.unwrap_or_default();
109
110 self.push(key, value);
111 }
112 }
113
114 pub fn clear(&mut self) {
115 self.env.clear();
116 self.size_hint = 0;
117 }
118
119 fn duplicate(&self) -> (Vec<u8>, Vec<RawSlice<u8>>) {
120 let mut buf: Vec<u8> = Vec::with_capacity(self.size_hint);
121 let mut slices = Vec::with_capacity(self.env.len());
122
123 for (key, value) in &self.env {
124 let ptr = unsafe { buf.as_mut_ptr().add(buf.len()) };
125 slices.push(unsafe {
126 RawSlice::from_raw_parts(ptr, key.len() + 1 + value.count_bytes())
127 });
128
129 buf.extend_from_slice(key);
130 buf.push(b'=');
131 buf.extend_from_slice(value.to_bytes_with_nul());
132 }
133
134 (buf, slices)
135 }
136}
137
138pub(super) static RAW_ENV: RawArgsStatic = RawArgsStatic::new();
140
141static ENV: Lazy<UnsafeCell<EnvVars>> = Lazy::new(|| {
143 let mut env = EnvVars::new();
144 unsafe { env.insert_raw(RAW_ENV.as_slice()) };
145 UnsafeCell::new(env)
146});
147
148#[inline]
151pub fn env_get_all() -> &'static [(Box<[u8]>, Box<CStr>)] {
152 let env = unsafe { &*ENV.get() };
153 &env.env
154}
155
156#[inline]
157pub fn env_get(key: &[u8]) -> Option<&[u8]> {
158 let env = unsafe { &*ENV.get() };
159 env.get(key)
160}
161
162#[inline]
163pub fn env_set(key: &[u8], value: &[u8]) {
164 let env = unsafe { &mut *ENV.get() };
165 env.set(key, value);
166}
167
168#[inline]
169pub fn env_remove(key: &[u8]) {
170 let env = unsafe { &mut *ENV.get() };
171 env.remove(key);
172}
173
174#[inline]
180pub(crate) unsafe fn duplicate_env() -> (Vec<u8>, Vec<RawSlice<u8>>) {
181 let env = unsafe { &*ENV.get() };
182 env.duplicate()
183}
184
185#[inline]
186pub fn env_clear() {
187 let env = unsafe { &mut *ENV.get() };
188 env.clear();
189}
190
191#[cfg_attr(
192 not(any(feature = "std", feature = "rustc-dep-of-std")),
193 unsafe(no_mangle)
194)]
195pub extern "C" fn sysenv_get(key: RawSlice<u8>) -> Optional<RawSlice<u8>> {
197 unsafe {
198 let Some(key) = key.into_slice() else {
199 return Optional::None;
200 };
201
202 env_get(key).map(|slice| RawSlice::from_slice(slice)).into()
203 }
204}
205
206#[cfg_attr(
207 not(any(feature = "std", feature = "rustc-dep-of-std")),
208 unsafe(no_mangle)
209)]
210pub extern "C" fn sysenv_set(key: RawSlice<u8>, value: RawSlice<u8>) {
212 unsafe {
213 let Some(key) = key.into_slice() else {
214 return;
215 };
216 let value = if let Some(value) = value.into_slice() {
217 value
218 } else {
219 &[]
220 };
221
222 env_set(key, value);
223 }
224}
225
226#[cfg_attr(
227 not(any(feature = "std", feature = "rustc-dep-of-std")),
228 unsafe(no_mangle)
229)]
230pub extern "C" fn sysenv_remove(key: RawSlice<u8>) {
232 unsafe {
233 let Some(key) = key.into_slice() else {
234 return;
235 };
236
237 env_remove(key);
238 }
239}
240
241#[cfg_attr(
242 not(any(feature = "std", feature = "rustc-dep-of-std")),
243 unsafe(no_mangle)
244)]
245pub extern "C" fn sysenv_clear() {
247 env_clear();
248}