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