15
15
//! assert_eq!(Method::POST.as_str(), "POST");
16
16
//! ```
17
17
18
+ use extension:: StaticExtension ;
19
+
18
20
use self :: extension:: { AllocatedExtension , InlineExtension } ;
19
21
use self :: Inner :: * ;
20
22
@@ -64,6 +66,8 @@ enum Inner {
64
66
ExtensionInline ( InlineExtension ) ,
65
67
// Otherwise, allocate it
66
68
ExtensionAllocated ( AllocatedExtension ) ,
69
+ // Statically allocated data
70
+ ExtensionStatic ( StaticExtension ) ,
67
71
}
68
72
69
73
impl Method {
@@ -134,6 +138,30 @@ impl Method {
134
138
}
135
139
}
136
140
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
+
137
165
fn extension_inline ( src : & [ u8 ] ) -> Result < Method , InvalidMethod > {
138
166
let inline = InlineExtension :: new ( src) ?;
139
167
@@ -176,6 +204,7 @@ impl Method {
176
204
Patch => "PATCH" ,
177
205
ExtensionInline ( ref inline) => inline. as_str ( ) ,
178
206
ExtensionAllocated ( ref allocated) => allocated. as_str ( ) ,
207
+ ExtensionStatic ( ref s) => s. as_str ( ) ,
179
208
}
180
209
}
181
210
}
@@ -316,6 +345,9 @@ mod extension {
316
345
// Invariant: self.0 contains valid UTF-8.
317
346
pub struct AllocatedExtension ( Box < [ u8 ] > ) ;
318
347
348
+ #[ derive( Clone , PartialEq , Eq , Hash ) ]
349
+ pub struct StaticExtension ( & ' static [ u8 ] ) ;
350
+
319
351
impl InlineExtension {
320
352
// Method::from_bytes() assumes this is at least 7
321
353
pub const MAX : usize = 15 ;
@@ -330,6 +362,34 @@ mod extension {
330
362
Ok ( InlineExtension ( data, src. len ( ) as u8 ) )
331
363
}
332
364
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
+
333
393
pub fn as_str ( & self ) -> & str {
334
394
let InlineExtension ( ref data, len) = self ;
335
395
// Safety: the invariant of InlineExtension ensures that the first
@@ -356,6 +416,32 @@ mod extension {
356
416
}
357
417
}
358
418
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
+
359
445
// From the RFC 9110 HTTP Semantics, section 9.1, the HTTP method is case-sensitive and can
360
446
// contain the following characters:
361
447
//
@@ -436,6 +522,38 @@ mod test {
436
522
assert_eq ! ( Method :: GET , & Method :: GET ) ;
437
523
}
438
524
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
+
439
557
#[ test]
440
558
fn test_invalid_method ( ) {
441
559
assert ! ( Method :: from_str( "" ) . is_err( ) ) ;
0 commit comments