@@ -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,39 @@ 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
375- }
376-
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.
384- ///
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 ] ) {
388414 let input = input. as_ref ( ) . trim_ascii_start ( ) ;
389415
390- if input . is_empty ( ) {
391- return ( None , input) ;
392- }
416+ // Use iter_bytes to determine the length of the first parameter
417+ let temp_cmdline = Cmdline :: from ( input) ;
418+ let param_len = temp_cmdline . iter_bytes ( ) . next ( ) . map ( |p| p . len ( ) ) ;
393419
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- } ) ;
420+ // Explicitly drop to ensure no lifetime leakage
421+ drop ( temp_cmdline) ;
401422
402- let end = match end {
403- Some ( end) => end,
404- None => input. len ( ) ,
405- } ;
423+ let end = param_len?;
406424
407- let ( input, rest) = input. split_at ( end) ;
425+ // Now split at the determined length from the trimmed input
426+ let param_bytes = & input[ ..end] ;
408427
428+ Self :: parse_internal ( param_bytes)
429+ }
430+
431+ /// Parse a parameter from a byte slice that contains exactly one parameter.
432+ ///
433+ /// This is an internal method that assumes the input has already been
434+ /// split into a single parameter (e.g., by CmdlineIterBytes).
435+ fn parse_internal ( input : & ' a [ u8 ] ) -> Option < Self > {
409436 let equals = input. iter ( ) . position ( |b| * b == b'=' ) ;
410437
411- let ret = match equals {
412- None => Self {
438+ match equals {
439+ None => Some ( Self {
413440 parameter : input,
414441 key : ParameterKey ( input) ,
415442 value : None ,
416- } ,
443+ } ) ,
417444 Some ( i) => {
418445 let ( key, mut value) = input. split_at ( i) ;
419446 let key = ParameterKey ( key) ;
@@ -428,15 +455,13 @@ impl<'a> Parameter<'a> {
428455 v. strip_suffix ( b"\" " ) . unwrap_or ( v)
429456 } ;
430457
431- Self {
458+ Some ( Self {
432459 parameter : input,
433460 key,
434461 value : Some ( value) ,
435- }
462+ } )
436463 }
437- } ;
438-
439- ( Some ( ret) , rest)
464+ }
440465 }
441466
442467 /// Returns the key part of the parameter
@@ -479,27 +504,19 @@ mod tests {
479504 }
480505
481506 #[ test]
482- fn test_parameter_parse_one ( ) {
483- let ( p, rest) = Parameter :: parse_one ( b"foo" ) ;
484- let p = p. unwrap ( ) ;
507+ fn test_parameter_parse ( ) {
508+ let p = Parameter :: parse ( b"foo" ) . unwrap ( ) ;
485509 assert_eq ! ( p. key. 0 , b"foo" ) ;
486510 assert_eq ! ( p. value, None ) ;
487- assert_eq ! ( rest, "" . as_bytes( ) ) ;
488511
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 ( ) ;
512+ // should parse only the first parameter and discard the rest of the input
513+ let p = Parameter :: parse ( b"foo=bar baz" ) . unwrap ( ) ;
492514 assert_eq ! ( p. key. 0 , b"foo" ) ;
493515 assert_eq ! ( p. value, Some ( b"bar" . as_slice( ) ) ) ;
494- assert_eq ! ( rest, " baz" . as_bytes( ) ) ;
495516
496517 // 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( ) ) ;
518+ assert ! ( Parameter :: parse( b"" ) . is_none( ) ) ;
519+ assert ! ( Parameter :: parse( b" " ) . is_none( ) ) ;
503520 }
504521
505522 #[ test]
@@ -534,11 +551,10 @@ mod tests {
534551
535552 #[ test]
536553 fn test_parameter_internal_key_whitespace ( ) {
537- let ( p , rest ) = Parameter :: parse_one ( "foo bar=baz" . as_bytes ( ) ) ;
538- let p = p . unwrap ( ) ;
554+ // parse should only consume the first parameter
555+ let p = Parameter :: parse ( "foo bar=baz" . as_bytes ( ) ) . unwrap ( ) ;
539556 assert_eq ! ( p. key. 0 , b"foo" ) ;
540557 assert_eq ! ( p. value, None ) ;
541- assert_eq ! ( rest, b" bar=baz" ) ;
542558 }
543559
544560 #[ test]
@@ -923,4 +939,54 @@ mod tests {
923939 assert_eq ! ( params[ 1 ] , param( "baz=qux" ) ) ;
924940 assert_eq ! ( params[ 2 ] , param( "wiz" ) ) ;
925941 }
942+
943+ #[ test]
944+ fn test_iter_bytes_simple ( ) {
945+ let kargs = Cmdline :: from ( b"foo bar baz" ) ;
946+ let params: Vec < _ > = kargs. iter_bytes ( ) . collect ( ) ;
947+
948+ assert_eq ! ( params. len( ) , 3 ) ;
949+ assert_eq ! ( params[ 0 ] , b"foo" ) ;
950+ assert_eq ! ( params[ 1 ] , b"bar" ) ;
951+ assert_eq ! ( params[ 2 ] , b"baz" ) ;
952+ }
953+
954+ #[ test]
955+ fn test_iter_bytes_with_values ( ) {
956+ let kargs = Cmdline :: from ( b"foo=bar baz=qux wiz" ) ;
957+ let params: Vec < _ > = kargs. iter_bytes ( ) . collect ( ) ;
958+
959+ assert_eq ! ( params. len( ) , 3 ) ;
960+ assert_eq ! ( params[ 0 ] , b"foo=bar" ) ;
961+ assert_eq ! ( params[ 1 ] , b"baz=qux" ) ;
962+ assert_eq ! ( params[ 2 ] , b"wiz" ) ;
963+ }
964+
965+ #[ test]
966+ fn test_iter_bytes_with_quotes ( ) {
967+ let kargs = Cmdline :: from ( b"foo=\" bar baz\" qux" ) ;
968+ let params: Vec < _ > = kargs. iter_bytes ( ) . collect ( ) ;
969+
970+ assert_eq ! ( params. len( ) , 2 ) ;
971+ assert_eq ! ( params[ 0 ] , b"foo=\" bar baz\" " ) ;
972+ assert_eq ! ( params[ 1 ] , b"qux" ) ;
973+ }
974+
975+ #[ test]
976+ fn test_iter_bytes_extra_whitespace ( ) {
977+ let kargs = Cmdline :: from ( b" foo bar " ) ;
978+ let params: Vec < _ > = kargs. iter_bytes ( ) . collect ( ) ;
979+
980+ assert_eq ! ( params. len( ) , 2 ) ;
981+ assert_eq ! ( params[ 0 ] , b"foo" ) ;
982+ assert_eq ! ( params[ 1 ] , b"bar" ) ;
983+ }
984+
985+ #[ test]
986+ fn test_iter_bytes_empty ( ) {
987+ let kargs = Cmdline :: from ( b"" ) ;
988+ let params: Vec < _ > = kargs. iter_bytes ( ) . collect ( ) ;
989+
990+ assert_eq ! ( params. len( ) , 0 ) ;
991+ }
926992}
0 commit comments