1515//! assert_eq!(Method::POST.as_str(), "POST");
1616//! ```
1717
18+ use extension:: StaticExtension ;
19+
1820use self :: extension:: { AllocatedExtension , InlineExtension } ;
1921use self :: Inner :: * ;
2022
@@ -64,6 +66,8 @@ enum Inner {
6466 ExtensionInline ( InlineExtension ) ,
6567 // Otherwise, allocate it
6668 ExtensionAllocated ( AllocatedExtension ) ,
69+ // Statically allocated data
70+ ExtensionStatic ( StaticExtension ) ,
6771}
6872
6973impl Method {
@@ -134,6 +138,30 @@ impl Method {
134138 }
135139 }
136140
141+ /// Convert static bytes into a `Method`.
142+ pub const fn from_static ( src : & ' static [ u8 ] ) -> Method {
143+ match src {
144+ b"OPTIONS" => Method :: OPTIONS ,
145+ b"GET" => Method :: GET ,
146+ b"POST" => Method :: POST ,
147+ b"PUT" => Method :: PUT ,
148+ b"DELETE" => Method :: DELETE ,
149+ b"HEAD" => Method :: HEAD ,
150+ b"TRACE" => Method :: TRACE ,
151+ b"CONNECT" => Method :: CONNECT ,
152+ b"PATCH" => Method :: PATCH ,
153+ src => {
154+ if src. len ( ) <= 15 {
155+ let inline = InlineExtension :: from_static ( src) ;
156+ Method ( ExtensionInline ( inline) )
157+ } else {
158+ let allocated = StaticExtension :: from_static ( src) ;
159+ Method ( ExtensionStatic ( allocated) )
160+ }
161+ }
162+ }
163+ }
164+
137165 fn extension_inline ( src : & [ u8 ] ) -> Result < Method , InvalidMethod > {
138166 let inline = InlineExtension :: new ( src) ?;
139167
@@ -176,6 +204,7 @@ impl Method {
176204 Patch => "PATCH" ,
177205 ExtensionInline ( ref inline) => inline. as_str ( ) ,
178206 ExtensionAllocated ( ref allocated) => allocated. as_str ( ) ,
207+ ExtensionStatic ( ref s) => s. as_str ( ) ,
179208 }
180209 }
181210}
@@ -316,6 +345,9 @@ mod extension {
316345 // Invariant: self.0 contains valid UTF-8.
317346 pub struct AllocatedExtension ( Box < [ u8 ] > ) ;
318347
348+ #[ derive( Clone , PartialEq , Eq , Hash ) ]
349+ pub struct StaticExtension ( & ' static [ u8 ] ) ;
350+
319351 impl InlineExtension {
320352 // Method::from_bytes() assumes this is at least 7
321353 pub const MAX : usize = 15 ;
@@ -330,6 +362,34 @@ mod extension {
330362 Ok ( InlineExtension ( data, src. len ( ) as u8 ) )
331363 }
332364
365+ /// Convert static bytes into an `InlineExtension`.
366+ ///
367+ /// # Panics
368+ ///
369+ /// If the input bytes are not a valid method name or if the method name is over 15 bytes.
370+ pub const fn from_static ( src : & ' static [ u8 ] ) -> InlineExtension {
371+ let mut i = 0 ;
372+ let mut dst = [ 0u8 ; 15 ] ;
373+ if src. len ( ) > 15 {
374+ // panicking in const requires Rust 1.57.0
375+ #[ allow( unconditional_panic) ]
376+ ( [ ] as [ u8 ; 0 ] ) [ 0 ] ;
377+ }
378+ while i < src. len ( ) {
379+ let byte = src[ i] ;
380+ let v = METHOD_CHARS [ byte as usize ] ;
381+ if v == 0 {
382+ // panicking in const requires Rust 1.57.0
383+ #[ allow( unconditional_panic) ]
384+ ( [ ] as [ u8 ; 0 ] ) [ 0 ] ;
385+ }
386+ dst[ i] = byte;
387+ i += 1 ;
388+ }
389+
390+ InlineExtension ( dst, i as u8 )
391+ }
392+
333393 pub fn as_str ( & self ) -> & str {
334394 let InlineExtension ( ref data, len) = self ;
335395 // Safety: the invariant of InlineExtension ensures that the first
@@ -356,6 +416,32 @@ mod extension {
356416 }
357417 }
358418
419+ impl StaticExtension {
420+ pub const fn from_static ( src : & ' static [ u8 ] ) -> StaticExtension {
421+ let mut i = 0 ;
422+ while i < src. len ( ) {
423+ let byte = src[ i] ;
424+ let v = METHOD_CHARS [ byte as usize ] ;
425+ if v == 0 {
426+ // panicking in const requires Rust 1.57.0
427+ #[ allow( unconditional_panic) ]
428+ ( [ ] as [ u8 ; 0 ] ) [ 0 ] ;
429+ }
430+ i += 1 ;
431+ }
432+
433+ // Invariant: data is exactly src.len() long and write_checked
434+ // ensures that the first src.len() bytes of data are valid UTF-8.
435+ StaticExtension ( src)
436+ }
437+
438+ pub fn as_str ( & self ) -> & str {
439+ // Safety: the invariant of StaticExtension ensures that self.0
440+ // contains valid UTF-8.
441+ unsafe { str:: from_utf8_unchecked ( & self . 0 ) }
442+ }
443+ }
444+
359445 // From the RFC 9110 HTTP Semantics, section 9.1, the HTTP method is case-sensitive and can
360446 // contain the following characters:
361447 //
@@ -436,6 +522,38 @@ mod test {
436522 assert_eq ! ( Method :: GET , & Method :: GET ) ;
437523 }
438524
525+ #[ test]
526+ fn test_from_static ( ) {
527+ // First class variant
528+ assert_eq ! (
529+ Method :: from_static( b"GET" ) ,
530+ Method :: from_bytes( b"GET" ) . unwrap( )
531+ ) ;
532+ // Inline, len < 15
533+ assert_eq ! (
534+ Method :: from_static( b"PROPFIND" ) ,
535+ Method :: from_bytes( b"PROPFIND" ) . unwrap( )
536+ ) ;
537+ // Inline, len == 15
538+ assert_eq ! ( Method :: from_static( b"GET" ) , Method :: GET ) ;
539+ assert_eq ! (
540+ Method :: from_static( b"123456789012345" ) . to_string( ) ,
541+ "123456789012345" . to_string( )
542+ ) ;
543+ // Ref, len > 15
544+ Method :: from_static ( b"1234567890123456" ) ;
545+ assert_eq ! (
546+ Method :: from_static( b"1234567890123456" ) . to_string( ) ,
547+ "1234567890123456" . to_string( )
548+ ) ;
549+ }
550+
551+ #[ test]
552+ #[ should_panic]
553+ fn test_from_static_bad ( ) {
554+ Method :: from_static ( b"\0 " ) ;
555+ }
556+
439557 #[ test]
440558 fn test_invalid_method ( ) {
441559 assert ! ( Method :: from_str( "" ) . is_err( ) ) ;
0 commit comments