@@ -23,8 +23,8 @@ pub enum Error {
23
23
#[ error( "string contains non-printable ASCII character" ) ]
24
24
InvalidAscii ,
25
25
/// Operation would have made the command line too large.
26
- #[ error( "inserting string would make command line too long " ) ]
27
- TooLarge ,
26
+ #[ error( "command line length {0} exceeds maximum {1} " ) ]
27
+ TooLarge ( usize , usize ) ,
28
28
}
29
29
30
30
/// Specialized Result type for command line operations.
@@ -54,59 +54,36 @@ fn valid_element(s: &str) -> Result<()> {
54
54
}
55
55
}
56
56
57
- /// A builder for a kernel command line string that validates the string as its being built. A
58
- /// `CString` can be constructed from this directly using `CString::new`.
57
+ /// A builder for a kernel command line string that validates the string as it is built.
58
+ # [ derive ( Default ) ]
59
59
pub struct Cmdline {
60
60
line : String ,
61
- capacity : usize ,
62
61
}
63
62
64
63
impl Cmdline {
65
- /// Constructs an empty Cmdline with the given capacity, which includes the nul terminator.
66
- /// Capacity must be greater than 0.
67
- pub fn new ( capacity : usize ) -> Cmdline {
68
- assert_ne ! ( capacity, 0 ) ;
69
- Cmdline {
70
- line : String :: new ( ) ,
71
- capacity,
72
- }
73
- }
74
-
75
- fn has_capacity ( & self , more : usize ) -> Result < ( ) > {
76
- let needs_space = if self . line . is_empty ( ) { 0 } else { 1 } ;
77
- if self . line . len ( ) + more + needs_space < self . capacity {
78
- Ok ( ( ) )
79
- } else {
80
- Err ( Error :: TooLarge )
81
- }
64
+ /// Constructs an empty Cmdline.
65
+ pub fn new ( ) -> Cmdline {
66
+ Cmdline :: default ( )
82
67
}
83
68
84
- fn start_push ( & mut self ) {
69
+ fn push_space_if_needed ( & mut self ) {
85
70
if !self . line . is_empty ( ) {
86
71
self . line . push ( ' ' ) ;
87
72
}
88
73
}
89
74
90
- fn end_push ( & mut self ) {
91
- // This assert is always true because of the `has_capacity` check that each insert method
92
- // uses.
93
- assert ! ( self . line. len( ) < self . capacity) ;
94
- }
95
-
96
75
/// Validates and inserts a key value pair into this command line
97
76
pub fn insert < T : AsRef < str > > ( & mut self , key : T , val : T ) -> Result < ( ) > {
98
77
let k = key. as_ref ( ) ;
99
78
let v = val. as_ref ( ) ;
100
79
101
80
valid_element ( k) ?;
102
81
valid_element ( v) ?;
103
- self . has_capacity ( k. len ( ) + v. len ( ) + 1 ) ?;
104
82
105
- self . start_push ( ) ;
83
+ self . push_space_if_needed ( ) ;
106
84
self . line . push_str ( k) ;
107
85
self . line . push ( '=' ) ;
108
86
self . line . push_str ( v) ;
109
- self . end_push ( ) ;
110
87
111
88
Ok ( ( ) )
112
89
}
@@ -116,11 +93,8 @@ impl Cmdline {
116
93
let s = slug. as_ref ( ) ;
117
94
valid_str ( s) ?;
118
95
119
- self . has_capacity ( s. len ( ) ) ?;
120
-
121
- self . start_push ( ) ;
96
+ self . push_space_if_needed ( ) ;
122
97
self . line . push_str ( s) ;
123
- self . end_push ( ) ;
124
98
125
99
Ok ( ( ) )
126
100
}
@@ -129,42 +103,64 @@ impl Cmdline {
129
103
pub fn as_str ( & self ) -> & str {
130
104
self . line . as_str ( )
131
105
}
132
- }
133
106
134
- impl From < Cmdline > for Vec < u8 > {
135
- fn from ( c : Cmdline ) -> Vec < u8 > {
136
- c. line . into_bytes ( )
107
+ /// Returns the current command line as a string with a maximum length.
108
+ ///
109
+ /// # Arguments
110
+ ///
111
+ /// `max_len`: maximum number of bytes (not including NUL terminator)
112
+ pub fn as_str_with_max_len ( & self , max_len : usize ) -> Result < & str > {
113
+ let s = self . line . as_str ( ) ;
114
+ if s. len ( ) <= max_len {
115
+ Ok ( s)
116
+ } else {
117
+ Err ( Error :: TooLarge ( s. len ( ) , max_len) )
118
+ }
119
+ }
120
+
121
+ /// Converts the command line into a `Vec<u8>` with a maximum length.
122
+ ///
123
+ /// # Arguments
124
+ ///
125
+ /// `max_len`: maximum number of bytes (not including NUL terminator)
126
+ pub fn into_bytes_with_max_len ( self , max_len : usize ) -> Result < Vec < u8 > > {
127
+ let bytes: Vec < u8 > = self . line . into_bytes ( ) ;
128
+ if bytes. len ( ) <= max_len {
129
+ Ok ( bytes)
130
+ } else {
131
+ Err ( Error :: TooLarge ( bytes. len ( ) , max_len) )
132
+ }
137
133
}
138
134
}
139
135
140
136
#[ cfg( test) ]
141
137
mod tests {
142
- use std:: ffi:: CString ;
143
-
144
138
use super :: * ;
145
139
146
140
#[ test]
147
141
fn insert_hello_world ( ) {
148
- let mut cl = Cmdline :: new ( 100 ) ;
142
+ let mut cl = Cmdline :: new ( ) ;
149
143
assert_eq ! ( cl. as_str( ) , "" ) ;
150
144
assert ! ( cl. insert( "hello" , "world" ) . is_ok( ) ) ;
151
145
assert_eq ! ( cl. as_str( ) , "hello=world" ) ;
152
146
153
- let s = CString :: new ( cl) . expect ( "failed to create CString from Cmdline" ) ;
154
- assert_eq ! ( s, CString :: new( "hello=world" ) . unwrap( ) ) ;
147
+ let bytes = cl
148
+ . into_bytes_with_max_len ( 100 )
149
+ . expect ( "failed to convert Cmdline into bytes" ) ;
150
+ assert_eq ! ( bytes, b"hello=world" ) ;
155
151
}
156
152
157
153
#[ test]
158
154
fn insert_multi ( ) {
159
- let mut cl = Cmdline :: new ( 100 ) ;
155
+ let mut cl = Cmdline :: new ( ) ;
160
156
assert ! ( cl. insert( "hello" , "world" ) . is_ok( ) ) ;
161
157
assert ! ( cl. insert( "foo" , "bar" ) . is_ok( ) ) ;
162
158
assert_eq ! ( cl. as_str( ) , "hello=world foo=bar" ) ;
163
159
}
164
160
165
161
#[ test]
166
162
fn insert_space ( ) {
167
- let mut cl = Cmdline :: new ( 100 ) ;
163
+ let mut cl = Cmdline :: new ( ) ;
168
164
assert_eq ! ( cl. insert( "a " , "b" ) , Err ( Error :: HasSpace ) ) ;
169
165
assert_eq ! ( cl. insert( "a" , "b " ) , Err ( Error :: HasSpace ) ) ;
170
166
assert_eq ! ( cl. insert( "a " , "b " ) , Err ( Error :: HasSpace ) ) ;
@@ -174,7 +170,7 @@ mod tests {
174
170
175
171
#[ test]
176
172
fn insert_equals ( ) {
177
- let mut cl = Cmdline :: new ( 100 ) ;
173
+ let mut cl = Cmdline :: new ( ) ;
178
174
assert_eq ! ( cl. insert( "a=" , "b" ) , Err ( Error :: HasEquals ) ) ;
179
175
assert_eq ! ( cl. insert( "a" , "b=" ) , Err ( Error :: HasEquals ) ) ;
180
176
assert_eq ! ( cl. insert( "a=" , "b " ) , Err ( Error :: HasEquals ) ) ;
@@ -185,15 +181,15 @@ mod tests {
185
181
186
182
#[ test]
187
183
fn insert_emoji ( ) {
188
- let mut cl = Cmdline :: new ( 100 ) ;
184
+ let mut cl = Cmdline :: new ( ) ;
189
185
assert_eq ! ( cl. insert( "heart" , "💖" ) , Err ( Error :: InvalidAscii ) ) ;
190
186
assert_eq ! ( cl. insert( "💖" , "love" ) , Err ( Error :: InvalidAscii ) ) ;
191
187
assert_eq ! ( cl. as_str( ) , "" ) ;
192
188
}
193
189
194
190
#[ test]
195
191
fn insert_string ( ) {
196
- let mut cl = Cmdline :: new ( 13 ) ;
192
+ let mut cl = Cmdline :: new ( ) ;
197
193
assert_eq ! ( cl. as_str( ) , "" ) ;
198
194
assert ! ( cl. insert_str( "noapic" ) . is_ok( ) ) ;
199
195
assert_eq ! ( cl. as_str( ) , "noapic" ) ;
@@ -202,25 +198,25 @@ mod tests {
202
198
}
203
199
204
200
#[ test]
205
- fn insert_too_large ( ) {
206
- let mut cl = Cmdline :: new ( 4 ) ;
207
- assert_eq ! ( cl. insert( "hello" , "world" ) , Err ( Error :: TooLarge ) ) ;
208
- assert_eq ! ( cl. insert( "a" , "world" ) , Err ( Error :: TooLarge ) ) ;
209
- assert_eq ! ( cl. insert( "hello" , "b" ) , Err ( Error :: TooLarge ) ) ;
201
+ fn as_str_too_large ( ) {
202
+ let mut cl = Cmdline :: new ( ) ;
210
203
assert ! ( cl. insert( "a" , "b" ) . is_ok( ) ) ; // start off with 3.
211
- assert_eq ! ( cl. insert( "a" , "b" ) , Err ( Error :: TooLarge ) ) ; // adds 4. " a=b"
212
- assert_eq ! ( cl. insert_str( "a" ) , Err ( Error :: TooLarge ) ) ;
213
204
assert_eq ! ( cl. as_str( ) , "a=b" ) ;
205
+ assert_eq ! ( cl. as_str_with_max_len( 2 ) , Err ( Error :: TooLarge ( 3 , 2 ) ) ) ;
206
+ assert_eq ! ( cl. as_str_with_max_len( 3 ) , Ok ( "a=b" ) ) ;
214
207
215
- let mut cl = Cmdline :: new ( 10 ) ;
208
+ let mut cl = Cmdline :: new ( ) ;
216
209
assert ! ( cl. insert( "ab" , "ba" ) . is_ok( ) ) ; // adds 5 length
217
- assert_eq ! ( cl. insert( "c" , "da" ) , Err ( Error :: TooLarge ) ) ; // adds 5 (including space) length
218
210
assert ! ( cl. insert( "c" , "d" ) . is_ok( ) ) ; // adds 4 (including space) length
211
+ assert_eq ! ( cl. as_str( ) , "ab=ba c=d" ) ;
212
+ assert_eq ! ( cl. as_str_with_max_len( 8 ) , Err ( Error :: TooLarge ( 9 , 8 ) ) ) ;
213
+ assert_eq ! ( cl. as_str_with_max_len( 9 ) , Ok ( "ab=ba c=d" ) ) ;
219
214
220
- let mut cl = Cmdline :: new ( 10 ) ;
215
+ let mut cl = Cmdline :: new ( ) ;
221
216
assert ! ( cl. insert( "ab" , "ba" ) . is_ok( ) ) ; // adds 5 length
222
- assert_eq ! ( cl. insert_str( "1234" ) , Err ( Error :: TooLarge ) ) ; // adds 5 (including space) length
223
217
assert ! ( cl. insert_str( "123" ) . is_ok( ) ) ; // adds 4 (including space) length
224
218
assert_eq ! ( cl. as_str( ) , "ab=ba 123" ) ;
219
+ assert_eq ! ( cl. as_str_with_max_len( 8 ) , Err ( Error :: TooLarge ( 9 , 8 ) ) ) ;
220
+ assert_eq ! ( cl. as_str_with_max_len( 9 ) , Ok ( "ab=ba 123" ) ) ;
225
221
}
226
222
}
0 commit comments