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