Skip to content

Commit be05ed2

Browse files
dekellumquininer
andauthored
Allow StatusCode to support 600-999 range
Additionally, reduces the size of the static memory included, and reduces some of the complicated macro usage resulting in faster compiles! Co-authored-by: quininer <quininer@live.com> Co-authored-by: David Kellum <dek-oss@gravitext.com>
1 parent b00473c commit be05ed2

2 files changed

Lines changed: 96 additions & 104 deletions

File tree

src/status.rs

Lines changed: 87 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -15,19 +15,22 @@
1515
//! ```
1616
1717
use std::convert::TryFrom;
18+
use std::num::NonZeroU16;
1819
use std::error::Error;
1920
use std::fmt;
2021
use std::str::FromStr;
2122

2223
/// An HTTP status code (`status-code` in RFC 7230 et al.).
2324
///
24-
/// This type contains constants for all common status codes.
25-
/// It allows status codes in the range [100, 599].
25+
/// Constants are provided for known status codes, including those in the IANA
26+
/// [HTTP Status Code Registry](
27+
/// https://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml).
2628
///
27-
/// IANA maintain the [Hypertext Transfer Protocol (HTTP) Status Code
28-
/// Registry](http://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml) which is
29-
/// the source for this enum (with one exception, 418 I'm a teapot, which is
30-
/// inexplicably not in the register).
29+
/// Status code values in the range 100-999 (inclusive) are supported by this
30+
/// type. Values in the range 100-599 are semantically classified by the most
31+
/// significant digit. See [`StatusCode::is_success`], etc. Values above 599
32+
/// are unclassified but allowed for legacy compatibility, though their use is
33+
/// discouraged. Applications may interpret such values as protocol errors.
3134
///
3235
/// # Examples
3336
///
@@ -39,12 +42,12 @@ use std::str::FromStr;
3942
/// assert!(StatusCode::OK.is_success());
4043
/// ```
4144
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
42-
pub struct StatusCode(u16);
45+
pub struct StatusCode(NonZeroU16);
4346

4447
/// A possible error value when converting a `StatusCode` from a `u16` or `&str`
4548
///
4649
/// This error indicates that the supplied input was not a valid number, was less
47-
/// than 100, or was greater than 599.
50+
/// than 100, or was greater than 999.
4851
pub struct InvalidStatusCode {
4952
_priv: (),
5053
}
@@ -53,7 +56,7 @@ impl StatusCode {
5356
/// Converts a u16 to a status code.
5457
///
5558
/// The function validates the correctness of the supplied u16. It must be
56-
/// greater or equal to 100 but less than 600.
59+
/// greater or equal to 100 and less than 1000.
5760
///
5861
/// # Example
5962
///
@@ -68,11 +71,13 @@ impl StatusCode {
6871
/// ```
6972
#[inline]
7073
pub fn from_u16(src: u16) -> Result<StatusCode, InvalidStatusCode> {
71-
if src < 100 || src >= 600 {
74+
if src < 100 || src >= 1000 {
7275
return Err(InvalidStatusCode::new());
7376
}
7477

75-
Ok(StatusCode(src))
78+
NonZeroU16::new(src)
79+
.map(StatusCode)
80+
.ok_or_else(InvalidStatusCode::new)
7681
}
7782

7883
/// Converts a &[u8] to a status code
@@ -85,12 +90,14 @@ impl StatusCode {
8590
let b = src[1].wrapping_sub(b'0') as u16;
8691
let c = src[2].wrapping_sub(b'0') as u16;
8792

88-
if a == 0 || a > 5 || b > 9 || c > 9 {
93+
if a == 0 || a > 9 || b > 9 || c > 9 {
8994
return Err(InvalidStatusCode::new());
9095
}
9196

9297
let status = (a * 100) + (b * 10) + c;
93-
Ok(StatusCode(status))
98+
NonZeroU16::new(status)
99+
.map(StatusCode)
100+
.ok_or_else(InvalidStatusCode::new)
94101
}
95102

96103
/// Returns the `u16` corresponding to this `StatusCode`.
@@ -126,7 +133,17 @@ impl StatusCode {
126133
/// ```
127134
#[inline]
128135
pub fn as_str(&self) -> &str {
129-
CODES_AS_STR[(self.0 - 100) as usize]
136+
let offset = (self.0.get() - 100) as usize;
137+
let offset = offset * 3;
138+
139+
// Invariant: self has checked range [100, 999] and CODE_DIGITS is
140+
// ASCII-only, of length 900 * 3 = 2700 bytes
141+
142+
#[cfg(debug_assertions)]
143+
{ &CODE_DIGITS[offset..offset+3] }
144+
145+
#[cfg(not(debug_assertions))]
146+
unsafe { CODE_DIGITS.get_unchecked(offset..offset+3) }
130147
}
131148

132149
/// Get the standardised `reason-phrase` for this status code.
@@ -148,37 +165,37 @@ impl StatusCode {
148165
/// assert_eq!(status.canonical_reason(), Some("OK"));
149166
/// ```
150167
pub fn canonical_reason(&self) -> Option<&'static str> {
151-
canonical_reason(self.0)
168+
canonical_reason(self.0.get())
152169
}
153170

154171
/// Check if status is within 100-199.
155172
#[inline]
156173
pub fn is_informational(&self) -> bool {
157-
200 > self.0 && self.0 >= 100
174+
200 > self.0.get() && self.0.get() >= 100
158175
}
159176

160177
/// Check if status is within 200-299.
161178
#[inline]
162179
pub fn is_success(&self) -> bool {
163-
300 > self.0 && self.0 >= 200
180+
300 > self.0.get() && self.0.get() >= 200
164181
}
165182

166183
/// Check if status is within 300-399.
167184
#[inline]
168185
pub fn is_redirection(&self) -> bool {
169-
400 > self.0 && self.0 >= 300
186+
400 > self.0.get() && self.0.get() >= 300
170187
}
171188

172189
/// Check if status is within 400-499.
173190
#[inline]
174191
pub fn is_client_error(&self) -> bool {
175-
500 > self.0 && self.0 >= 400
192+
500 > self.0.get() && self.0.get() >= 400
176193
}
177194

178195
/// Check if status is within 500-599.
179196
#[inline]
180197
pub fn is_server_error(&self) -> bool {
181-
600 > self.0 && self.0 >= 500
198+
600 > self.0.get() && self.0.get() >= 500
182199
}
183200
}
184201

@@ -231,7 +248,7 @@ impl PartialEq<StatusCode> for u16 {
231248
impl From<StatusCode> for u16 {
232249
#[inline]
233250
fn from(status: StatusCode) -> u16 {
234-
status.0
251+
status.0.get()
235252
}
236253
}
237254

@@ -287,7 +304,7 @@ macro_rules! status_codes {
287304
impl StatusCode {
288305
$(
289306
$(#[$docs])*
290-
pub const $konst: StatusCode = StatusCode($num);
307+
pub const $konst: StatusCode = StatusCode(unsafe { NonZeroU16::new_unchecked($num) });
291308
)+
292309

293310
}
@@ -521,40 +538,51 @@ impl fmt::Display for InvalidStatusCode {
521538

522539
impl Error for InvalidStatusCode {}
523540

524-
macro_rules! status_code_strs {
525-
($($num:expr,)+) => {
526-
const CODES_AS_STR: [&'static str; 500] = [ $( stringify!($num), )+ ];
527-
}
528-
}
529-
530-
status_code_strs!(
531-
100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119,
532-
120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139,
533-
140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159,
534-
160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179,
535-
180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199,
536-
537-
200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219,
538-
220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239,
539-
240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, 256, 257, 258, 259,
540-
260, 261, 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, 278, 279,
541-
280, 281, 282, 283, 284, 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, 295, 296, 297, 298, 299,
542-
543-
300, 301, 302, 303, 304, 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, 319,
544-
320, 321, 322, 323, 324, 325, 326, 327, 328, 329, 330, 331, 332, 333, 334, 335, 336, 337, 338, 339,
545-
340, 341, 342, 343, 344, 345, 346, 347, 348, 349, 350, 351, 352, 353, 354, 355, 356, 357, 358, 359,
546-
360, 361, 362, 363, 364, 365, 366, 367, 368, 369, 370, 371, 372, 373, 374, 375, 376, 377, 378, 379,
547-
380, 381, 382, 383, 384, 385, 386, 387, 388, 389, 390, 391, 392, 393, 394, 395, 396, 397, 398, 399,
548-
549-
400, 401, 402, 403, 404, 405, 406, 407, 408, 409, 410, 411, 412, 413, 414, 415, 416, 417, 418, 419,
550-
420, 421, 422, 423, 424, 425, 426, 427, 428, 429, 430, 431, 432, 433, 434, 435, 436, 437, 438, 439,
551-
440, 441, 442, 443, 444, 445, 446, 447, 448, 449, 450, 451, 452, 453, 454, 455, 456, 457, 458, 459,
552-
460, 461, 462, 463, 464, 465, 466, 467, 468, 469, 470, 471, 472, 473, 474, 475, 476, 477, 478, 479,
553-
480, 481, 482, 483, 484, 485, 486, 487, 488, 489, 490, 491, 492, 493, 494, 495, 496, 497, 498, 499,
554-
555-
500, 501, 502, 503, 504, 505, 506, 507, 508, 509, 510, 511, 512, 513, 514, 515, 516, 517, 518, 519,
556-
520, 521, 522, 523, 524, 525, 526, 527, 528, 529, 530, 531, 532, 533, 534, 535, 536, 537, 538, 539,
557-
540, 541, 542, 543, 544, 545, 546, 547, 548, 549, 550, 551, 552, 553, 554, 555, 556, 557, 558, 559,
558-
560, 561, 562, 563, 564, 565, 566, 567, 568, 569, 570, 571, 572, 573, 574, 575, 576, 577, 578, 579,
559-
580, 581, 582, 583, 584, 585, 586, 587, 588, 589, 590, 591, 592, 593, 594, 595, 596, 597, 598, 599,
560-
);
541+
// A string of packed 3-ASCII-digit status code values for the supported range
542+
// of [100, 999] (900 codes, 2700 bytes).
543+
const CODE_DIGITS: &'static str = "\
544+
100101102103104105106107108109110111112113114115116117118119\
545+
120121122123124125126127128129130131132133134135136137138139\
546+
140141142143144145146147148149150151152153154155156157158159\
547+
160161162163164165166167168169170171172173174175176177178179\
548+
180181182183184185186187188189190191192193194195196197198199\
549+
200201202203204205206207208209210211212213214215216217218219\
550+
220221222223224225226227228229230231232233234235236237238239\
551+
240241242243244245246247248249250251252253254255256257258259\
552+
260261262263264265266267268269270271272273274275276277278279\
553+
280281282283284285286287288289290291292293294295296297298299\
554+
300301302303304305306307308309310311312313314315316317318319\
555+
320321322323324325326327328329330331332333334335336337338339\
556+
340341342343344345346347348349350351352353354355356357358359\
557+
360361362363364365366367368369370371372373374375376377378379\
558+
380381382383384385386387388389390391392393394395396397398399\
559+
400401402403404405406407408409410411412413414415416417418419\
560+
420421422423424425426427428429430431432433434435436437438439\
561+
440441442443444445446447448449450451452453454455456457458459\
562+
460461462463464465466467468469470471472473474475476477478479\
563+
480481482483484485486487488489490491492493494495496497498499\
564+
500501502503504505506507508509510511512513514515516517518519\
565+
520521522523524525526527528529530531532533534535536537538539\
566+
540541542543544545546547548549550551552553554555556557558559\
567+
560561562563564565566567568569570571572573574575576577578579\
568+
580581582583584585586587588589590591592593594595596597598599\
569+
600601602603604605606607608609610611612613614615616617618619\
570+
620621622623624625626627628629630631632633634635636637638639\
571+
640641642643644645646647648649650651652653654655656657658659\
572+
660661662663664665666667668669670671672673674675676677678679\
573+
680681682683684685686687688689690691692693694695696697698699\
574+
700701702703704705706707708709710711712713714715716717718719\
575+
720721722723724725726727728729730731732733734735736737738739\
576+
740741742743744745746747748749750751752753754755756757758759\
577+
760761762763764765766767768769770771772773774775776777778779\
578+
780781782783784785786787788789790791792793794795796797798799\
579+
800801802803804805806807808809810811812813814815816817818819\
580+
820821822823824825826827828829830831832833834835836837838839\
581+
840841842843844845846847848849850851852853854855856857858859\
582+
860861862863864865866867868869870871872873874875876877878879\
583+
880881882883884885886887888889890891892893894895896897898899\
584+
900901902903904905906907908909910911912913914915916917918919\
585+
920921922923924925926927928929930931932933934935936937938939\
586+
940941942943944945946947948949950951952953954955956957958959\
587+
960961962963964965966967968969970971972973974975976977978979\
588+
980981982983984985986987988989990991992993994995996997998999";

tests/status_code.rs

Lines changed: 9 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,13 @@ use http::*;
33
#[test]
44
fn from_bytes() {
55
for ok in &[
6-
"100", "101", "199", "200", "250", "299", "321", "399", "499", "599",
6+
"100", "101", "199", "200", "250", "299", "321", "399", "499", "599", "600", "999"
77
] {
88
assert!(StatusCode::from_bytes(ok.as_bytes()).is_ok());
99
}
1010

1111
for not_ok in &[
12-
"0", "00", "10", "40", "99", "000", "010", "099", "600", "610", "999",
12+
"0", "00", "10", "40", "99", "000", "010", "099", "1000", "1999",
1313
] {
1414
assert!(StatusCode::from_bytes(not_ok.as_bytes()).is_err());
1515
}
@@ -22,48 +22,12 @@ fn equates_with_u16() {
2222
assert_eq!(status, 200u16);
2323
}
2424

25-
macro_rules! test_round_trip {
26-
($($num:expr,)+) => {
27-
#[test]
28-
fn roundtrip() {
29-
$(
30-
let status = StatusCode::from_bytes(stringify!($num).as_bytes()).unwrap();
31-
let expect = $num;
32-
33-
assert_eq!(u16::from(status), expect);
34-
)+
35-
}
25+
#[test]
26+
fn roundtrip() {
27+
for s in 100..1000 {
28+
let sstr = s.to_string();
29+
let status = StatusCode::from_bytes(sstr.as_bytes()).unwrap();
30+
assert_eq!(s, u16::from(status));
31+
assert_eq!(sstr, status.as_str());
3632
}
3733
}
38-
39-
test_round_trip!(
40-
100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119,
41-
120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139,
42-
140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159,
43-
160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179,
44-
180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199,
45-
46-
200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219,
47-
220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239,
48-
240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, 256, 257, 258, 259,
49-
260, 261, 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, 278, 279,
50-
280, 281, 282, 283, 284, 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, 295, 296, 297, 298, 299,
51-
52-
300, 301, 302, 303, 304, 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, 319,
53-
320, 321, 322, 323, 324, 325, 326, 327, 328, 329, 330, 331, 332, 333, 334, 335, 336, 337, 338, 339,
54-
340, 341, 342, 343, 344, 345, 346, 347, 348, 349, 350, 351, 352, 353, 354, 355, 356, 357, 358, 359,
55-
360, 361, 362, 363, 364, 365, 366, 367, 368, 369, 370, 371, 372, 373, 374, 375, 376, 377, 378, 379,
56-
380, 381, 382, 383, 384, 385, 386, 387, 388, 389, 390, 391, 392, 393, 394, 395, 396, 397, 398, 399,
57-
58-
400, 401, 402, 403, 404, 405, 406, 407, 408, 409, 410, 411, 412, 413, 414, 415, 416, 417, 418, 419,
59-
420, 421, 422, 423, 424, 425, 426, 427, 428, 429, 430, 431, 432, 433, 434, 435, 436, 437, 438, 439,
60-
440, 441, 442, 443, 444, 445, 446, 447, 448, 449, 450, 451, 452, 453, 454, 455, 456, 457, 458, 459,
61-
460, 461, 462, 463, 464, 465, 466, 467, 468, 469, 470, 471, 472, 473, 474, 475, 476, 477, 478, 479,
62-
480, 481, 482, 483, 484, 485, 486, 487, 488, 489, 490, 491, 492, 493, 494, 495, 496, 497, 498, 499,
63-
64-
500, 501, 502, 503, 504, 505, 506, 507, 508, 509, 510, 511, 512, 513, 514, 515, 516, 517, 518, 519,
65-
520, 521, 522, 523, 524, 525, 526, 527, 528, 529, 530, 531, 532, 533, 534, 535, 536, 537, 538, 539,
66-
540, 541, 542, 543, 544, 545, 546, 547, 548, 549, 550, 551, 552, 553, 554, 555, 556, 557, 558, 559,
67-
560, 561, 562, 563, 564, 565, 566, 567, 568, 569, 570, 571, 572, 573, 574, 575, 576, 577, 578, 579,
68-
580, 581, 582, 583, 584, 585, 586, 587, 588, 589, 590, 591, 592, 593, 594, 595, 596, 597, 598, 599,
69-
);

0 commit comments

Comments
 (0)