@@ -38,15 +38,46 @@ impl<'a> From<Vec<u8>> for Cmdline<'a> {
3838///
3939/// This is created by the `iter` method on `Cmdline`.
4040#[ derive( Debug ) ]
41- pub struct CmdlineIter < ' a > ( & ' a [ u8 ] ) ;
41+ pub struct CmdlineIter < ' a > ( CmdlineIterBytes < ' a > ) ;
4242
4343impl < ' a > Iterator for CmdlineIter < ' a > {
4444 type Item = Parameter < ' a > ;
4545
4646 fn next ( & mut self ) -> Option < Self :: Item > {
47- let ( param, rest) = Parameter :: parse_one ( self . 0 ) ;
47+ self . 0 . next ( ) . and_then ( Parameter :: parse_internal)
48+ }
49+ }
50+
51+ /// An iterator over kernel command line parameters as byte slices.
52+ ///
53+ /// This is created by the `iter_bytes` method on `Cmdline`.
54+ #[ derive( Debug ) ]
55+ pub struct CmdlineIterBytes < ' a > ( & ' a [ u8 ] ) ;
56+
57+ impl < ' a > Iterator for CmdlineIterBytes < ' a > {
58+ type Item = & ' a [ u8 ] ;
59+
60+ fn next ( & mut self ) -> Option < Self :: Item > {
61+ let input = self . 0 . trim_ascii_start ( ) ;
62+
63+ if input. is_empty ( ) {
64+ self . 0 = input;
65+ return None ;
66+ }
67+
68+ let mut in_quotes = false ;
69+ let end = input. iter ( ) . position ( move |c| {
70+ if * c == b'"' {
71+ in_quotes = !in_quotes;
72+ }
73+ !in_quotes && c. is_ascii_whitespace ( )
74+ } ) ;
75+
76+ let end = end. unwrap_or ( input. len ( ) ) ;
77+ let ( param, rest) = input. split_at ( end) ;
4878 self . 0 = rest;
49- param
79+
80+ Some ( param)
5081 }
5182}
5283
@@ -71,7 +102,15 @@ impl<'a> Cmdline<'a> {
71102 /// unquoted whitespace characters. Parameters are parsed as either
72103 /// key-only switches or key=value pairs.
73104 pub fn iter ( & ' a self ) -> CmdlineIter < ' a > {
74- CmdlineIter ( & self . 0 )
105+ CmdlineIter ( self . iter_bytes ( ) )
106+ }
107+
108+ /// Returns an iterator over all parameters in the command line as byte slices.
109+ ///
110+ /// This is similar to `iter()` but yields `&[u8]` directly instead of `Parameter`,
111+ /// which can be more convenient when you just need the raw byte representation.
112+ pub fn iter_bytes ( & self ) -> CmdlineIterBytes < ' _ > {
113+ CmdlineIterBytes ( & self . 0 )
75114 }
76115
77116 /// Returns an iterator over all parameters in the command line
@@ -369,51 +408,27 @@ impl<'a> Parameter<'a> {
369408 /// be constructed from the input. This occurs when the input is
370409 /// either empty or contains only whitespace.
371410 ///
372- /// Any remaining bytes not consumed from the input are discarded.
411+ /// If the input contains multiple parameters, only the first one
412+ /// is parsed and the rest is discarded.
373413 pub fn parse < T : AsRef < [ u8 ] > + ?Sized > ( input : & ' a T ) -> Option < Self > {
374- Self :: parse_one ( input) . 0
414+ CmdlineIterBytes ( input. as_ref ( ) )
415+ . next ( )
416+ . and_then ( Self :: parse_internal)
375417 }
376418
377- /// Attempt to parse a single command line parameter from a slice
378- /// of bytes.
379- ///
380- /// The first tuple item contains the parsed parameter, or `None`
381- /// if a Parameter could not be constructed from the input. This
382- /// occurs when the input is either empty or contains only
383- /// whitespace.
419+ /// Parse a parameter from a byte slice that contains exactly one parameter.
384420 ///
385- /// Any remaining bytes not consumed from the input are returned
386- /// as the second tuple item.
387- pub fn parse_one < T : AsRef < [ u8 ] > + ?Sized > ( input : & ' a T ) -> ( Option < Self > , & ' a [ u8 ] ) {
388- let input = input. as_ref ( ) . trim_ascii_start ( ) ;
389-
390- if input. is_empty ( ) {
391- return ( None , input) ;
392- }
393-
394- let mut in_quotes = false ;
395- let end = input. iter ( ) . position ( move |c| {
396- if * c == b'"' {
397- in_quotes = !in_quotes;
398- }
399- !in_quotes && c. is_ascii_whitespace ( )
400- } ) ;
401-
402- let end = match end {
403- Some ( end) => end,
404- None => input. len ( ) ,
405- } ;
406-
407- let ( input, rest) = input. split_at ( end) ;
408-
421+ /// This is an internal method that assumes the input has already been
422+ /// split into a single parameter (e.g., by CmdlineIterBytes).
423+ fn parse_internal ( input : & ' a [ u8 ] ) -> Option < Self > {
409424 let equals = input. iter ( ) . position ( |b| * b == b'=' ) ;
410425
411- let ret = match equals {
412- None => Self {
426+ match equals {
427+ None => Some ( Self {
413428 parameter : input,
414429 key : ParameterKey ( input) ,
415430 value : None ,
416- } ,
431+ } ) ,
417432 Some ( i) => {
418433 let ( key, mut value) = input. split_at ( i) ;
419434 let key = ParameterKey ( key) ;
@@ -428,15 +443,13 @@ impl<'a> Parameter<'a> {
428443 v. strip_suffix ( b"\" " ) . unwrap_or ( v)
429444 } ;
430445
431- Self {
446+ Some ( Self {
432447 parameter : input,
433448 key,
434449 value : Some ( value) ,
435- }
450+ } )
436451 }
437- } ;
438-
439- ( Some ( ret) , rest)
452+ }
440453 }
441454
442455 /// Returns the key part of the parameter
@@ -479,27 +492,19 @@ mod tests {
479492 }
480493
481494 #[ test]
482- fn test_parameter_parse_one ( ) {
483- let ( p, rest) = Parameter :: parse_one ( b"foo" ) ;
484- let p = p. unwrap ( ) ;
495+ fn test_parameter_parse ( ) {
496+ let p = Parameter :: parse ( b"foo" ) . unwrap ( ) ;
485497 assert_eq ! ( p. key. 0 , b"foo" ) ;
486498 assert_eq ! ( p. value, None ) ;
487- assert_eq ! ( rest, "" . as_bytes( ) ) ;
488499
489- // should consume one parameter and return the rest of the input
490- let ( p, rest) = Parameter :: parse_one ( b"foo=bar baz" ) ;
491- let p = p. unwrap ( ) ;
500+ // should parse only the first parameter and discard the rest of the input
501+ let p = Parameter :: parse ( b"foo=bar baz" ) . unwrap ( ) ;
492502 assert_eq ! ( p. key. 0 , b"foo" ) ;
493503 assert_eq ! ( p. value, Some ( b"bar" . as_slice( ) ) ) ;
494- assert_eq ! ( rest, " baz" . as_bytes( ) ) ;
495504
496505 // should return None on empty or whitespace inputs
497- let ( p, rest) = Parameter :: parse_one ( b"" ) ;
498- assert ! ( p. is_none( ) ) ;
499- assert_eq ! ( rest, b"" . as_slice( ) ) ;
500- let ( p, rest) = Parameter :: parse_one ( b" " ) ;
501- assert ! ( p. is_none( ) ) ;
502- assert_eq ! ( rest, b"" . as_slice( ) ) ;
506+ assert ! ( Parameter :: parse( b"" ) . is_none( ) ) ;
507+ assert ! ( Parameter :: parse( b" " ) . is_none( ) ) ;
503508 }
504509
505510 #[ test]
@@ -534,11 +539,10 @@ mod tests {
534539
535540 #[ test]
536541 fn test_parameter_internal_key_whitespace ( ) {
537- let ( p , rest ) = Parameter :: parse_one ( "foo bar=baz" . as_bytes ( ) ) ;
538- let p = p . unwrap ( ) ;
542+ // parse should only consume the first parameter
543+ let p = Parameter :: parse ( "foo bar=baz" . as_bytes ( ) ) . unwrap ( ) ;
539544 assert_eq ! ( p. key. 0 , b"foo" ) ;
540545 assert_eq ! ( p. value, None ) ;
541- assert_eq ! ( rest, b" bar=baz" ) ;
542546 }
543547
544548 #[ test]
@@ -923,4 +927,54 @@ mod tests {
923927 assert_eq ! ( params[ 1 ] , param( "baz=qux" ) ) ;
924928 assert_eq ! ( params[ 2 ] , param( "wiz" ) ) ;
925929 }
930+
931+ #[ test]
932+ fn test_iter_bytes_simple ( ) {
933+ let kargs = Cmdline :: from ( b"foo bar baz" ) ;
934+ let params: Vec < _ > = kargs. iter_bytes ( ) . collect ( ) ;
935+
936+ assert_eq ! ( params. len( ) , 3 ) ;
937+ assert_eq ! ( params[ 0 ] , b"foo" ) ;
938+ assert_eq ! ( params[ 1 ] , b"bar" ) ;
939+ assert_eq ! ( params[ 2 ] , b"baz" ) ;
940+ }
941+
942+ #[ test]
943+ fn test_iter_bytes_with_values ( ) {
944+ let kargs = Cmdline :: from ( b"foo=bar baz=qux wiz" ) ;
945+ let params: Vec < _ > = kargs. iter_bytes ( ) . collect ( ) ;
946+
947+ assert_eq ! ( params. len( ) , 3 ) ;
948+ assert_eq ! ( params[ 0 ] , b"foo=bar" ) ;
949+ assert_eq ! ( params[ 1 ] , b"baz=qux" ) ;
950+ assert_eq ! ( params[ 2 ] , b"wiz" ) ;
951+ }
952+
953+ #[ test]
954+ fn test_iter_bytes_with_quotes ( ) {
955+ let kargs = Cmdline :: from ( b"foo=\" bar baz\" qux" ) ;
956+ let params: Vec < _ > = kargs. iter_bytes ( ) . collect ( ) ;
957+
958+ assert_eq ! ( params. len( ) , 2 ) ;
959+ assert_eq ! ( params[ 0 ] , b"foo=\" bar baz\" " ) ;
960+ assert_eq ! ( params[ 1 ] , b"qux" ) ;
961+ }
962+
963+ #[ test]
964+ fn test_iter_bytes_extra_whitespace ( ) {
965+ let kargs = Cmdline :: from ( b" foo bar " ) ;
966+ let params: Vec < _ > = kargs. iter_bytes ( ) . collect ( ) ;
967+
968+ assert_eq ! ( params. len( ) , 2 ) ;
969+ assert_eq ! ( params[ 0 ] , b"foo" ) ;
970+ assert_eq ! ( params[ 1 ] , b"bar" ) ;
971+ }
972+
973+ #[ test]
974+ fn test_iter_bytes_empty ( ) {
975+ let kargs = Cmdline :: from ( b"" ) ;
976+ let params: Vec < _ > = kargs. iter_bytes ( ) . collect ( ) ;
977+
978+ assert_eq ! ( params. len( ) , 0 ) ;
979+ }
926980}
0 commit comments