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() + 2;
60 }
61
62 #[inline(always)]
63 pub fn set(&mut self, key: &[u8], value: &[u8]) {
64 for (k, v) in &mut self.env {
65 if &**k == key {
66 let old_len = v.count_bytes();
67
68 let new_value = CString::new(value)
69 .unwrap_or_else(|_| CStr::from_bytes_until_nul(value).unwrap().into());
70 *v = new_value.into_boxed_c_str();
71 self.size_hint -= old_len + 1;
72 self.size_hint += value.len() + 1;
74 return;
75 }
76 }
77
78 unsafe {
79 self.push(key, value);
80 }
81 }
82
83 #[inline(always)]
84 pub fn remove(&mut self, key: &[u8]) {
85 for (i, (k, v)) in self.env.iter().enumerate() {
86 if &**k == key {
87 self.size_hint -= key.len() + 1 + v.count_bytes();
89 self.env.swap_remove(i);
90 return;
91 }
92 }
93 }
94
95 unsafe fn insert_raw(&mut self, raw: &[NonNullSlice<u8>]) {
99 self.env.reserve(raw.len());
100
101 for slice in raw {
102 let slice = slice.into_slice_mut();
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<RawSlice<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 {
134 RawSlice::from_raw_parts(ptr, key.len() + 1 + value.count_bytes())
135 });
136
137 buf[offset..offset + key.len()].copy_from_slice(key);
138 offset += key.len();
139
140 buf[offset] = b'=';
141 offset += 1;
142
143 let value_len = value.count_bytes() + 1;
144 buf[offset..offset + value_len].copy_from_slice(value.to_bytes_with_nul());
145 offset += value_len;
146 }
147
148 (buf, slices)
149 }
150}
151
152pub(super) static RAW_ENV: RawArgsStatic = RawArgsStatic::new();
154
155static ENV: Lazy<UnsafeCell<EnvVars>> = Lazy::new(|| {
157 let mut env = EnvVars::new();
158 unsafe { env.insert_raw(RAW_ENV.as_slice()) };
159 UnsafeCell::new(env)
160});
161
162#[inline]
165pub fn env_get_all() -> &'static [(Box<[u8]>, Box<CStr>)] {
166 let env = unsafe { &*ENV.get() };
167 &env.env
168}
169
170#[inline]
171pub fn env_get(key: &[u8]) -> Option<&[u8]> {
172 let env = unsafe { &*ENV.get() };
173 env.get(key)
174}
175
176#[inline]
177pub fn env_set(key: &[u8], value: &[u8]) {
178 let env = unsafe { &mut *ENV.get() };
179 env.set(key, value);
180}
181
182#[inline]
183pub fn env_remove(key: &[u8]) {
184 let env = unsafe { &mut *ENV.get() };
185 env.remove(key);
186}
187
188#[inline]
194pub(crate) unsafe fn duplicate_env() -> (Box<[u8]>, Vec<RawSlice<u8>>) {
195 let env = unsafe { &*ENV.get() };
196 env.duplicate()
197}
198
199#[inline]
200pub fn env_clear() {
201 let env = unsafe { &mut *ENV.get() };
202 env.clear();
203}
204
205#[cfg_attr(
206 not(any(feature = "std", feature = "rustc-dep-of-std")),
207 unsafe(no_mangle)
208)]
209pub extern "C" fn sysenv_get(key: RawSlice<u8>) -> Optional<RawSlice<u8>> {
211 unsafe {
212 let Some(key) = key.into_slice() else {
213 return Optional::None;
214 };
215
216 env_get(key).map(|slice| RawSlice::from_slice(slice)).into()
217 }
218}
219
220#[cfg_attr(
221 not(any(feature = "std", feature = "rustc-dep-of-std")),
222 unsafe(no_mangle)
223)]
224pub extern "C" fn sysenv_set(key: RawSlice<u8>, value: RawSlice<u8>) {
226 unsafe {
227 let Some(key) = key.into_slice() else {
228 return;
229 };
230 let value = if let Some(value) = value.into_slice() {
231 value
232 } else {
233 &[]
234 };
235
236 env_set(key, value);
237 }
238}
239
240#[cfg_attr(
241 not(any(feature = "std", feature = "rustc-dep-of-std")),
242 unsafe(no_mangle)
243)]
244pub extern "C" fn sysenv_remove(key: RawSlice<u8>) {
246 unsafe {
247 let Some(key) = key.into_slice() else {
248 return;
249 };
250
251 env_remove(key);
252 }
253}
254
255#[cfg_attr(
256 not(any(feature = "std", feature = "rustc-dep-of-std")),
257 unsafe(no_mangle)
258)]
259pub extern "C" fn sysenv_clear() {
261 env_clear();
262}