Skip to content

Commit 56602da

Browse files
RenTrieuphip1611
andcommitted
uefi: Changing get_envs() to return an Iterator
Co-authored-by: Philipp Schuster <[email protected]>
1 parent ad4e343 commit 56602da

File tree

2 files changed

+133
-53
lines changed

2 files changed

+133
-53
lines changed

uefi-test-runner/src/proto/shell.rs

Lines changed: 37 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
use uefi::boot::ScopedProtocol;
44
use uefi::proto::shell::Shell;
5-
use uefi::{Error, Status, boot, cstr16, CStr16};
5+
use uefi::{Error, Status, boot, cstr16};
66

77
/// Test `current_dir()` and `set_current_dir()`
88
pub fn test_current_dir(shell: &ScopedProtocol<Shell>) {
@@ -100,27 +100,20 @@ pub fn test_current_dir(shell: &ScopedProtocol<Shell>) {
100100
assert_eq!(cur_fs_str, expected_fs_str);
101101
}
102102

103-
/// Test ``get_env()``, ``get_envs()``, and ``set_env()``
103+
/// Test `get_env()`, `get_envs()`, and `set_env()`
104104
pub fn test_env(shell: &ScopedProtocol<Shell>) {
105-
let mut test_buf = [0u16; 128];
106-
107105
/* Test retrieving list of environment variable names */
106+
let mut cur_env_vec = shell.get_envs();
107+
assert_eq!(cur_env_vec.next().unwrap(), cstr16!("path"),);
108+
// check pre-defined shell variables; see UEFI Shell spec
109+
assert_eq!(cur_env_vec.next().unwrap(), cstr16!("nonesting"),);
108110
let cur_env_vec = shell.get_envs();
109-
assert_eq!(
110-
*cur_env_vec.first().unwrap(),
111-
CStr16::from_str_with_buf("path", &mut test_buf).unwrap()
112-
);
113-
assert_eq!(
114-
*cur_env_vec.get(1).unwrap(),
115-
CStr16::from_str_with_buf("nonesting", &mut test_buf).unwrap()
116-
);
117-
let default_len = cur_env_vec.len();
111+
let default_len = cur_env_vec.count();
118112

119113
/* Test setting and getting a specific environment variable */
120-
let mut test_env_buf = [0u16; 32];
121-
let test_var = CStr16::from_str_with_buf("test_var", &mut test_env_buf).unwrap();
122-
let mut test_val_buf = [0u16; 32];
123-
let test_val = CStr16::from_str_with_buf("test_val", &mut test_val_buf).unwrap();
114+
let cur_env_vec = shell.get_envs();
115+
let test_var = cstr16!("test_var");
116+
let test_val = cstr16!("test_val");
124117
assert!(shell.get_env(test_var).is_none());
125118
let status = shell.set_env(test_var, test_val, false);
126119
assert_eq!(status, Status::SUCCESS);
@@ -129,20 +122,41 @@ pub fn test_env(shell: &ScopedProtocol<Shell>) {
129122
.expect("Could not get environment variable");
130123
assert_eq!(cur_env_str, test_val);
131124

132-
assert!(!cur_env_vec.contains(&test_var));
125+
let mut found_var = false;
126+
for env_var in cur_env_vec {
127+
if env_var == test_var {
128+
found_var = true;
129+
}
130+
}
131+
assert!(!found_var);
132+
let cur_env_vec = shell.get_envs();
133+
let mut found_var = false;
134+
for env_var in cur_env_vec {
135+
if env_var == test_var {
136+
found_var = true;
137+
}
138+
}
139+
assert!(found_var);
140+
133141
let cur_env_vec = shell.get_envs();
134-
assert!(cur_env_vec.contains(&test_var));
135-
assert_eq!(cur_env_vec.len(), default_len + 1);
142+
assert_eq!(cur_env_vec.count(), default_len + 1);
136143

137144
/* Test deleting environment variable */
138-
let test_val = CStr16::from_str_with_buf("", &mut test_val_buf).unwrap();
145+
let test_val = cstr16!("");
139146
let status = shell.set_env(test_var, test_val, false);
140147
assert_eq!(status, Status::SUCCESS);
141148
assert!(shell.get_env(test_var).is_none());
142149

143150
let cur_env_vec = shell.get_envs();
144-
assert!(!cur_env_vec.contains(&test_var));
145-
assert_eq!(cur_env_vec.len(), default_len);
151+
let mut found_var = false;
152+
for env_var in cur_env_vec {
153+
if env_var == test_var {
154+
found_var = true;
155+
}
156+
}
157+
assert!(!found_var);
158+
let cur_env_vec = shell.get_envs();
159+
assert_eq!(cur_env_vec.count(), default_len);
146160
}
147161

148162
pub fn test() {

uefi/src/proto/shell/mod.rs

Lines changed: 96 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,45 @@
44
55
use crate::proto::unsafe_protocol;
66
use crate::{CStr16, Char16, Error, Result, Status, StatusExt};
7+
use core::marker::PhantomData;
78
use core::ptr;
89
use uefi_raw::protocol::shell::ShellProtocol;
9-
use alloc::vec::Vec;
1010

1111
/// Shell Protocol
1212
#[derive(Debug)]
1313
#[repr(transparent)]
1414
#[unsafe_protocol(ShellProtocol::GUID)]
1515
pub struct Shell(ShellProtocol);
1616

17+
/// Iterator over the names of environmental variables obtained from the Shell protocol.
18+
#[derive(Debug)]
19+
pub struct Vars<'a> {
20+
/// Char16 containing names of environment variables
21+
inner: *const Char16,
22+
/// Placeholder to attach a lifetime to `Vars`
23+
placeholder: PhantomData<&'a CStr16>,
24+
}
25+
26+
impl<'a> Iterator for Vars<'a> {
27+
type Item = &'a CStr16;
28+
// We iterate a list of NUL terminated CStr16s.
29+
// The list is terminated with a double NUL.
30+
fn next(&mut self) -> Option<Self::Item> {
31+
let cur_start = self.inner;
32+
let mut cur_len = 0;
33+
unsafe {
34+
if *(cur_start) == Char16::from_u16_unchecked(0) {
35+
return None;
36+
}
37+
while *(cur_start.add(cur_len)) != Char16::from_u16_unchecked(0) {
38+
cur_len += 1;
39+
}
40+
self.inner = self.inner.add(cur_len + 1);
41+
Some(CStr16::from_ptr(cur_start))
42+
}
43+
}
44+
}
45+
1746
impl Shell {
1847
/// Returns the current directory on the specified device.
1948
///
@@ -85,36 +114,12 @@ impl Shell {
85114
///
86115
/// * `Vec<env_names>` - Vector of environment variable names
87116
#[must_use]
88-
pub fn get_envs(&self) -> Vec<&CStr16> {
89-
let mut env_vec: Vec<&CStr16> = Vec::new();
90-
let cur_env_ptr = unsafe { (self.0.get_env)(ptr::null()) };
91-
92-
let mut cur_start = cur_env_ptr;
93-
let mut cur_len = 0;
94-
95-
let mut i = 0;
96-
let mut null_count = 0;
97-
unsafe {
98-
while null_count <= 1 {
99-
if (*(cur_env_ptr.add(i))) == Char16::from_u16_unchecked(0).into() {
100-
if cur_len > 0 {
101-
env_vec.push(CStr16::from_char16_with_nul(
102-
&(*ptr::slice_from_raw_parts(cur_start.cast(), cur_len + 1)),
103-
).unwrap());
104-
}
105-
cur_len = 0;
106-
null_count += 1;
107-
} else {
108-
if null_count > 0 {
109-
cur_start = cur_env_ptr.add(i);
110-
}
111-
null_count = 0;
112-
cur_len += 1;
113-
}
114-
i += 1;
115-
}
117+
pub fn get_envs(&self) -> Vars {
118+
let env_ptr = unsafe { (self.0.get_env)(ptr::null()) };
119+
Vars {
120+
inner: env_ptr.cast::<Char16>(),
121+
placeholder: PhantomData,
116122
}
117-
env_vec
118123
}
119124

120125
/// Sets the environment variable
@@ -135,3 +140,64 @@ impl Shell {
135140
unsafe { (self.0.set_env)(name_ptr.cast(), value_ptr.cast(), volatile) }
136141
}
137142
}
143+
144+
#[cfg(test)]
145+
mod tests {
146+
use super::*;
147+
use alloc::vec::Vec;
148+
use uefi::cstr16;
149+
150+
/// Testing Vars struct
151+
#[test]
152+
fn test_vars() {
153+
// Empty Vars
154+
let mut vars_mock = Vec::<u16>::new();
155+
vars_mock.push(0);
156+
vars_mock.push(0);
157+
let mut vars = Vars {
158+
inner: vars_mock.as_ptr().cast(),
159+
placeholder: PhantomData,
160+
};
161+
assert!(vars.next().is_none());
162+
163+
// One environment variable in Vars
164+
let mut vars_mock = Vec::<u16>::new();
165+
vars_mock.push(b'f' as u16);
166+
vars_mock.push(b'o' as u16);
167+
vars_mock.push(b'o' as u16);
168+
vars_mock.push(0);
169+
vars_mock.push(0);
170+
let vars = Vars {
171+
inner: vars_mock.as_ptr().cast(),
172+
placeholder: PhantomData,
173+
};
174+
assert_eq!(vars.collect::<Vec<_>>(), Vec::from([cstr16!("foo")]));
175+
176+
// Multiple environment variables in Vars
177+
let mut vars_mock = Vec::<u16>::new();
178+
vars_mock.push(b'f' as u16);
179+
vars_mock.push(b'o' as u16);
180+
vars_mock.push(b'o' as u16);
181+
vars_mock.push(b'1' as u16);
182+
vars_mock.push(0);
183+
vars_mock.push(b'b' as u16);
184+
vars_mock.push(b'a' as u16);
185+
vars_mock.push(b'r' as u16);
186+
vars_mock.push(0);
187+
vars_mock.push(b'b' as u16);
188+
vars_mock.push(b'a' as u16);
189+
vars_mock.push(b'z' as u16);
190+
vars_mock.push(b'2' as u16);
191+
vars_mock.push(0);
192+
vars_mock.push(0);
193+
194+
let vars = Vars {
195+
inner: vars_mock.as_ptr().cast(),
196+
placeholder: PhantomData,
197+
};
198+
assert_eq!(
199+
vars.collect::<Vec<_>>(),
200+
Vec::from([cstr16!("foo1"), cstr16!("bar"), cstr16!("baz2")])
201+
);
202+
}
203+
}

0 commit comments

Comments
 (0)