Skip to content

Commit af715af

Browse files
d-sonuganicholasbishop
authored andcommitted
Added the unicode collation protocol
1 parent 89f6d6f commit af715af

File tree

3 files changed

+195
-0
lines changed

3 files changed

+195
-0
lines changed

src/proto/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,3 +74,4 @@ pub mod pi;
7474
pub mod rng;
7575
pub mod security;
7676
pub mod shim;
77+
pub mod string;

src/proto/string/mod.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
//! String protocols
2+
//!
3+
//! The protocols provide some string operations like
4+
//! lexical comparison.
5+
6+
pub mod unicode_collation;

src/proto/string/unicode_collation.rs

Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
//! The Unicode Collation Protocol
2+
//!
3+
//! Used in the boot services environment to perform
4+
//! lexical comparison functions on Unicode strings for given languages
5+
6+
use core::cmp::Ordering;
7+
use uefi_macros::{unsafe_guid, Protocol};
8+
use uefi::data_types::{Char16, CStr16, Char8, CStr8};
9+
10+
/// The Unicode Collation Protocol
11+
///
12+
/// Used to perform case-insensitive comaprisons of strings
13+
#[repr(C)]
14+
#[unsafe_guid("a4c751fc-23ae-4c3e-92e9-4964cf63f349")]
15+
#[derive(Protocol)]
16+
pub struct UnicodeCollation {
17+
stri_coll: extern "efiapi" fn(
18+
this: &Self,
19+
s1: *const Char16,
20+
s2: *const Char16
21+
) -> isize,
22+
metai_match: extern "efiapi" fn(
23+
this: &Self,
24+
string: *const Char16,
25+
pattern: *const Char16
26+
) -> bool,
27+
str_lwr: extern "efiapi" fn(
28+
this: &Self,
29+
s: *mut Char16
30+
),
31+
str_upr: extern "efiapi" fn(
32+
this: &Self,
33+
s: *mut Char16
34+
),
35+
fat_to_str: extern "efiapi" fn(
36+
this: &Self,
37+
fat_size: usize,
38+
fat: *const Char8,
39+
s: *mut Char16
40+
),
41+
str_to_fat: extern "efiapi" fn(
42+
this: &Self,
43+
s: *const Char16,
44+
fat_size: usize,
45+
fat: *mut Char8
46+
) -> bool
47+
}
48+
49+
impl UnicodeCollation {
50+
/// Performs a case insensitive comparison of two
51+
/// null-terminated strings
52+
pub fn stri_coll(&self, s1: &CStr16, s2: &CStr16) -> Ordering {
53+
let order = (self.stri_coll)(
54+
self,
55+
s1.as_ptr(),
56+
s2.as_ptr()
57+
);
58+
if order == 0 {
59+
Ordering::Equal
60+
} else if order < 0 {
61+
Ordering::Less
62+
} else {
63+
Ordering::Greater
64+
}
65+
}
66+
67+
/// Performs a case insensitive comparison between a null terminated
68+
/// pattern string and a null terminated string
69+
///
70+
/// This function checks if character pattern described in `pattern`
71+
/// is found in `string`. If the pattern match succeeds, true is returned.
72+
/// Otherwise, false is returned
73+
///
74+
/// The following syntax can be used to build the string `pattern`:
75+
///
76+
/// |Pattern Character |Meaning |
77+
/// |-----------------------------|--------------------------------------------------|
78+
/// |* | Match 0 or more characters |
79+
/// |? | Match any one character |
80+
/// |[`char1` `char2`...`charN`]| Match any character in the set |
81+
/// |[`char1`-`char2`] | Match any character between `char1` and `char2`|
82+
/// |`char` | Match the character `char` |
83+
///
84+
/// For example, the pattern "*.Fw" will match all strings that end
85+
/// in ".FW", ".fw", ".Fw" or ".fW". The pattern "[a-z]" will match any
86+
/// letter in the alphabet. The pattern "z" will match the letter "z".
87+
/// The pattern "d?.*" will match the character "D" or "d" followed by
88+
/// any single character followed by a "." followed by any string
89+
pub fn metai_match(&self, s: &CStr16, pattern: &CStr16) -> bool {
90+
(self.metai_match)(
91+
self,
92+
s.as_ptr(),
93+
pattern.as_ptr()
94+
)
95+
}
96+
97+
/// Converts the characters in `s` to lower case characters
98+
pub fn str_lwr<'a>(&self, s: &CStr16, buf: &'a mut [u16]) -> Result<&'a CStr16, StrConversionError> {
99+
let mut last_index = 0;
100+
for (i, c) in s.iter().enumerate() {
101+
*buf.get_mut(i)
102+
.ok_or(StrConversionError::BufferTooSmall)? = (*c).into();
103+
last_index = i;
104+
}
105+
*buf.get_mut(last_index + 1)
106+
.ok_or(StrConversionError::BufferTooSmall)? = 0;
107+
108+
(self.str_lwr)(
109+
self,
110+
buf.as_ptr() as *mut _
111+
);
112+
113+
Ok(unsafe { CStr16::from_u16_with_nul_unchecked(buf) })
114+
}
115+
116+
/// Coverts the characters in `s` to upper case characters
117+
pub fn str_upr<'a>(&self, s: &CStr16, buf: &'a mut [u16]) -> Result<&'a CStr16, StrConversionError> {
118+
let mut last_index = 0;
119+
for (i, c) in s.iter().enumerate() {
120+
*buf.get_mut(i)
121+
.ok_or(StrConversionError::BufferTooSmall)? = (*c).into();
122+
last_index = i;
123+
}
124+
*buf.get_mut(last_index + 1)
125+
.ok_or(StrConversionError::BufferTooSmall)? = 0;
126+
127+
(self.str_upr)(
128+
self,
129+
buf.as_ptr() as *mut _
130+
);
131+
132+
Ok(unsafe { CStr16::from_u16_with_nul_unchecked(buf) })
133+
}
134+
135+
/// Converts the 8.3 FAT file name `fat` to a null terminated string
136+
pub fn fat_to_str<'a>(&self, fat: &CStr8, buf: &'a mut [u16]) -> Result<&'a CStr16, StrConversionError> {
137+
if buf.len() < fat.to_bytes_with_nul().len() {
138+
return Err(StrConversionError::BufferTooSmall);
139+
}
140+
(self.fat_to_str)(
141+
self,
142+
fat.to_bytes_with_nul().len(),
143+
fat.as_ptr(),
144+
buf.as_ptr() as *mut _
145+
);
146+
Ok(unsafe { CStr16::from_u16_with_nul_unchecked(buf) })
147+
}
148+
149+
/// Converts the null terminated string `s` to legal characters in a FAT file name
150+
pub fn str_to_fat<'a>(&self, s: &CStr16, buf: &'a mut [u8]) -> Result<&'a CStr8, StrConversionError> {
151+
if s.as_slice_with_nul().len() > buf.len() {
152+
return Err(StrConversionError::BufferTooSmall);
153+
}
154+
let failed = (self.str_to_fat)(
155+
self,
156+
s.as_ptr(),
157+
s.as_slice_with_nul().len(),
158+
buf.as_ptr() as *mut _
159+
);
160+
if failed {
161+
Err(StrConversionError::ConversionFailed)
162+
} else {
163+
// After the conversion, there is a possibility that the converted string
164+
// is smaller than the original `s` string.
165+
// When the converted string is smaller, there will be a bunch of trailing
166+
// nulls.
167+
// To remove all those trailing nulls:
168+
let mut last_null_index = buf.len() - 1;
169+
for i in (0..buf.len()).rev() {
170+
if buf[i] != 0 {
171+
last_null_index = i + 1;
172+
break;
173+
}
174+
}
175+
let buf = unsafe { core::slice::from_raw_parts(buf.as_ptr(), last_null_index + 1) };
176+
Ok(unsafe { CStr8::from_bytes_with_nul_unchecked(buf) })
177+
}
178+
}
179+
}
180+
181+
/// Errors returned by [`UnicodeCollation::str_lwr`] and [`UnicodeCollation::str_upr`]
182+
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
183+
pub enum StrConversionError {
184+
/// The conversion failed
185+
ConversionFailed,
186+
/// The buffer given is too small to hold the string
187+
BufferTooSmall
188+
}

0 commit comments

Comments
 (0)