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