mirror of https://github.com/fafhrd91/actix-web
optimise quality item parsing
This commit is contained in:
parent
2330ca60ec
commit
134b690364
|
@ -5,8 +5,8 @@ use std::{
|
||||||
|
|
||||||
use derive_more::{Display, Error};
|
use derive_more::{Display, Error};
|
||||||
|
|
||||||
pub(super) const MAX_QUALITY_INT: u16 = 1000;
|
const MAX_QUALITY_INT: u16 = 1000;
|
||||||
pub(super) const MAX_QUALITY_FLOAT: f32 = 1.0;
|
const MAX_QUALITY_FLOAT: f32 = 1.0;
|
||||||
|
|
||||||
/// Represents a quality used in q-factor values.
|
/// Represents a quality used in q-factor values.
|
||||||
///
|
///
|
||||||
|
@ -66,8 +66,6 @@ impl Default for Quality {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// In benchmarks this is twice as fast as a naive approach using something like
|
|
||||||
// `format!("{}").trim_end_matches('0')` for non-fast path quality values
|
|
||||||
impl fmt::Display for Quality {
|
impl fmt::Display for Quality {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
match self.0 {
|
match self.0 {
|
||||||
|
@ -78,8 +76,9 @@ impl fmt::Display for Quality {
|
||||||
x => {
|
x => {
|
||||||
f.write_str("0.")?;
|
f.write_str("0.")?;
|
||||||
|
|
||||||
// this implementation avoids string allocation otherwise required
|
// This implementation avoids string allocation for removing trailing zeroes.
|
||||||
// for `.trim_end_matches('0')`
|
// In benchmarks it is twice as fast as approach using something like
|
||||||
|
// `format!("{}").trim_end_matches('0')` for non-fast path quality values.
|
||||||
|
|
||||||
if x < 10 {
|
if x < 10 {
|
||||||
f.write_str("00")?;
|
f.write_str("00")?;
|
||||||
|
|
|
@ -1,11 +1,8 @@
|
||||||
use std::{cmp, fmt, str};
|
use std::{cmp, convert::TryFrom as _, fmt, str};
|
||||||
|
|
||||||
use crate::error::ParseError;
|
use crate::error::ParseError;
|
||||||
|
|
||||||
use super::{
|
use super::Quality;
|
||||||
quality::{MAX_QUALITY_FLOAT, MAX_QUALITY_INT},
|
|
||||||
Quality,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Represents an item with a quality value as defined
|
/// Represents an item with a quality value as defined
|
||||||
/// in [RFC 7231 §5.3.1](https://datatracker.ietf.org/doc/html/rfc7231#section-5.3.1).
|
/// in [RFC 7231 §5.3.1](https://datatracker.ietf.org/doc/html/rfc7231#section-5.3.1).
|
||||||
|
@ -74,9 +71,9 @@ impl<T: fmt::Display> fmt::Display for QualityItem<T> {
|
||||||
|
|
||||||
match self.quality {
|
match self.quality {
|
||||||
// q-factor value is implied for max value
|
// q-factor value is implied for max value
|
||||||
Quality(MAX_QUALITY_INT) => Ok(()),
|
Quality::MAX => Ok(()),
|
||||||
|
|
||||||
Quality(0) => f.write_str("; q=0"),
|
Quality::MIN => f.write_str("; q=0"),
|
||||||
|
|
||||||
q => write!(f, "; q={}", q),
|
q => write!(f, "; q={}", q),
|
||||||
}
|
}
|
||||||
|
@ -86,57 +83,55 @@ impl<T: fmt::Display> fmt::Display for QualityItem<T> {
|
||||||
impl<T: str::FromStr> str::FromStr for QualityItem<T> {
|
impl<T: str::FromStr> str::FromStr for QualityItem<T> {
|
||||||
type Err = ParseError;
|
type Err = ParseError;
|
||||||
|
|
||||||
fn from_str(qitem_str: &str) -> Result<Self, Self::Err> {
|
fn from_str(q_item_str: &str) -> Result<Self, Self::Err> {
|
||||||
if !qitem_str.is_ascii() {
|
if !q_item_str.is_ascii() {
|
||||||
return Err(ParseError::Header);
|
return Err(ParseError::Header);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set defaults used if parsing fails.
|
// set defaults used if quality-item parsing fails, i.e., item has no q-factor
|
||||||
let mut raw_item = qitem_str;
|
let mut raw_item = q_item_str;
|
||||||
let mut quality = 1f32;
|
let mut quality = Quality::MAX;
|
||||||
|
|
||||||
// TODO: MSRV(1.52): use rsplit_once
|
let parts = q_item_str
|
||||||
let parts: Vec<_> = qitem_str.rsplitn(2, ';').map(str::trim).collect();
|
.rsplit_once(';')
|
||||||
|
.map(|(item, q_attr)| (item.trim(), q_attr.trim()));
|
||||||
|
|
||||||
if parts.len() == 2 {
|
if let Some((val, q_attr)) = parts {
|
||||||
// example for item with q-factor:
|
// example for item with q-factor:
|
||||||
//
|
//
|
||||||
// gzip; q=0.65
|
// gzip;q=0.65
|
||||||
// ^^^^^^ parts[0]
|
// ^^^^ val
|
||||||
// ^^ start
|
// ^^^^^^ q_attr
|
||||||
// ^^^^ q_val
|
// ^^ q
|
||||||
// ^^^^ parts[1]
|
// ^^^^ q_val
|
||||||
|
|
||||||
if parts[0].len() < 2 {
|
if q_attr.len() < 2 {
|
||||||
// Can't possibly be an attribute since an attribute needs at least a name followed
|
// Can't possibly be an attribute since an attribute needs at least a name followed
|
||||||
// by an equals sign. And bare identifiers are forbidden.
|
// by an equals sign. And bare identifiers are forbidden.
|
||||||
return Err(ParseError::Header);
|
return Err(ParseError::Header);
|
||||||
}
|
}
|
||||||
|
|
||||||
let start = &parts[0][0..2];
|
let q = &q_attr[0..2];
|
||||||
|
|
||||||
if start == "q=" || start == "Q=" {
|
if q == "q=" || q == "Q=" {
|
||||||
let q_val = &parts[0][2..];
|
let q_val = &q_attr[2..];
|
||||||
if q_val.len() > 5 {
|
if q_val.len() > 5 {
|
||||||
// longer than 5 indicates an over-precise q-factor
|
// longer than 5 indicates an over-precise q-factor
|
||||||
return Err(ParseError::Header);
|
return Err(ParseError::Header);
|
||||||
}
|
}
|
||||||
|
|
||||||
let q_value = q_val.parse::<f32>().map_err(|_| ParseError::Header)?;
|
let q_value = q_val.parse::<f32>().map_err(|_| ParseError::Header)?;
|
||||||
|
let q_value =
|
||||||
|
Quality::try_from(q_value).map_err(|_| ParseError::Header)?;
|
||||||
|
|
||||||
if (0f32..=MAX_QUALITY_FLOAT).contains(&q_value) {
|
quality = q_value;
|
||||||
quality = q_value;
|
raw_item = val;
|
||||||
raw_item = parts[1];
|
|
||||||
} else {
|
|
||||||
return Err(ParseError::Header);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let item = raw_item.parse::<T>().map_err(|_| ParseError::Header)?;
|
let item = raw_item.parse::<T>().map_err(|_| ParseError::Header)?;
|
||||||
|
|
||||||
// we already checked above that the quality is within range
|
Ok(QualityItem::new(item, quality))
|
||||||
Ok(QualityItem::new(item, Quality::from_f32(quality)))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue