Vendor things

This commit is contained in:
John Doty 2024-03-08 11:03:01 -08:00
parent 5deceec006
commit 977e3c17e5
19434 changed files with 10682014 additions and 0 deletions

View file

@ -0,0 +1,201 @@
//! <https://infra.spec.whatwg.org/#forgiving-base64-decode>
use alloc::vec::Vec;
use core::fmt;
#[derive(Debug)]
pub struct InvalidBase64(InvalidBase64Details);
impl fmt::Display for InvalidBase64 {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.0 {
InvalidBase64Details::UnexpectedSymbol(code_point) => {
write!(f, "symbol with codepoint {} not expected", code_point)
}
InvalidBase64Details::AlphabetSymbolAfterPadding => {
write!(f, "alphabet symbol present after padding")
}
InvalidBase64Details::LoneAlphabetSymbol => write!(f, "lone alphabet symbol present"),
InvalidBase64Details::Padding => write!(f, "incorrect padding"),
}
}
}
#[cfg(feature = "std")]
impl std::error::Error for InvalidBase64 {}
#[derive(Debug)]
enum InvalidBase64Details {
UnexpectedSymbol(u8),
AlphabetSymbolAfterPadding,
LoneAlphabetSymbol,
Padding,
}
#[derive(Debug)]
pub enum DecodeError<E> {
InvalidBase64(InvalidBase64),
WriteError(E),
}
impl<E: fmt::Display> fmt::Display for DecodeError<E> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::InvalidBase64(inner) => write!(f, "base64 not valid: {}", inner),
Self::WriteError(err) => write!(f, "write error: {}", err),
}
}
}
#[cfg(feature = "std")]
impl<E: std::error::Error> std::error::Error for DecodeError<E> {}
impl<E> From<InvalidBase64Details> for DecodeError<E> {
fn from(e: InvalidBase64Details) -> Self {
DecodeError::InvalidBase64(InvalidBase64(e))
}
}
pub(crate) enum Impossible {}
impl From<DecodeError<Impossible>> for InvalidBase64 {
fn from(e: DecodeError<Impossible>) -> Self {
match e {
DecodeError::InvalidBase64(e) => e,
DecodeError::WriteError(e) => match e {},
}
}
}
/// `input` is assumed to be in an ASCII-compatible encoding
pub fn decode_to_vec(input: &[u8]) -> Result<Vec<u8>, InvalidBase64> {
let mut v = Vec::new();
{
let mut decoder = Decoder::new(|bytes| {
v.extend_from_slice(bytes);
Ok(())
});
decoder.feed(input)?;
decoder.finish()?;
}
Ok(v)
}
/// <https://infra.spec.whatwg.org/#forgiving-base64-decode>
pub struct Decoder<F, E>
where
F: FnMut(&[u8]) -> Result<(), E>,
{
write_bytes: F,
bit_buffer: u32,
buffer_bit_length: u8,
padding_symbols: u8,
}
impl<F, E> Decoder<F, E>
where
F: FnMut(&[u8]) -> Result<(), E>,
{
pub fn new(write_bytes: F) -> Self {
Self {
write_bytes,
bit_buffer: 0,
buffer_bit_length: 0,
padding_symbols: 0,
}
}
/// Feed to the decoder partial input in an ASCII-compatible encoding
pub fn feed(&mut self, input: &[u8]) -> Result<(), DecodeError<E>> {
for &byte in input.iter() {
let value = BASE64_DECODE_TABLE[byte as usize];
if value < 0 {
// A character thats not part of the alphabet
// Remove ASCII whitespace
if matches!(byte, b' ' | b'\t' | b'\n' | b'\r' | b'\x0C') {
continue;
}
if byte == b'=' {
self.padding_symbols = self.padding_symbols.saturating_add(1);
continue;
}
return Err(InvalidBase64Details::UnexpectedSymbol(byte).into());
}
if self.padding_symbols > 0 {
return Err(InvalidBase64Details::AlphabetSymbolAfterPadding.into());
}
self.bit_buffer <<= 6;
self.bit_buffer |= value as u32;
// 18 before incrementing means weve just reached 24
if self.buffer_bit_length < 18 {
self.buffer_bit_length += 6;
} else {
// Weve accumulated four times 6 bits, which equals three times 8 bits.
let byte_buffer = [
(self.bit_buffer >> 16) as u8,
(self.bit_buffer >> 8) as u8,
self.bit_buffer as u8,
];
(self.write_bytes)(&byte_buffer).map_err(DecodeError::WriteError)?;
self.buffer_bit_length = 0;
// No need to reset bit_buffer,
// since next time were only gonna read relevant bits.
}
}
Ok(())
}
/// Call this to signal the end of the input
pub fn finish(mut self) -> Result<(), DecodeError<E>> {
match (self.buffer_bit_length, self.padding_symbols) {
(0, 0) => {
// A multiple of four of alphabet symbols, and nothing else.
}
(12, 2) | (12, 0) => {
// A multiple of four of alphabet symbols, followed by two more symbols,
// optionally followed by two padding characters (which make a total multiple of four).
let byte_buffer = [(self.bit_buffer >> 4) as u8];
(self.write_bytes)(&byte_buffer).map_err(DecodeError::WriteError)?;
}
(18, 1) | (18, 0) => {
// A multiple of four of alphabet symbols, followed by three more symbols,
// optionally followed by one padding character (which make a total multiple of four).
let byte_buffer = [(self.bit_buffer >> 10) as u8, (self.bit_buffer >> 2) as u8];
(self.write_bytes)(&byte_buffer).map_err(DecodeError::WriteError)?;
}
(6, _) => return Err(InvalidBase64Details::LoneAlphabetSymbol.into()),
_ => return Err(InvalidBase64Details::Padding.into()),
}
Ok(())
}
}
/// Generated by `make_base64_decode_table.py` based on "Table 1: The Base 64 Alphabet"
/// at <https://tools.ietf.org/html/rfc4648#section-4>
///
/// Array indices are the byte value of symbols.
/// Array values are their positions in the base64 alphabet,
/// or -1 for symbols not in the alphabet.
/// The position contributes 6 bits to the decoded bytes.
#[rustfmt::skip]
const BASE64_DECODE_TABLE: [i8; 256] = [
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63,
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1,
-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1,
-1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
];

347
third-party/vendor/data-url/src/lib.rs vendored Normal file
View file

@ -0,0 +1,347 @@
//! Processing of `data:` URLs according to the Fetch Standard:
//! <https://fetch.spec.whatwg.org/#data-urls>
//! but starting from a string rather than a parsed URL to avoid extra copies.
//!
//! ```rust
//! use data_url::{DataUrl, mime};
//!
//! let url = DataUrl::process("data:,Hello%20World!").unwrap();
//! let (body, fragment) = url.decode_to_vec().unwrap();
//!
//! assert_eq!(url.mime_type().type_, "text");
//! assert_eq!(url.mime_type().subtype, "plain");
//! assert_eq!(url.mime_type().get_parameter("charset"), Some("US-ASCII"));
//! assert_eq!(body, b"Hello World!");
//! assert!(fragment.is_none());
//! ```
#![no_std]
// For forwards compatibility
#[cfg(feature = "std")]
extern crate std;
#[macro_use]
extern crate alloc;
#[cfg(not(feature = "alloc"))]
compile_error!("the `alloc` feature must be enabled");
use alloc::{string::String, vec::Vec};
use core::fmt;
macro_rules! require {
($condition: expr) => {
if !$condition {
return None;
}
};
}
pub mod forgiving_base64;
pub mod mime;
pub struct DataUrl<'a> {
mime_type: mime::Mime,
base64: bool,
encoded_body_plus_fragment: &'a str,
}
#[derive(Debug)]
pub enum DataUrlError {
NotADataUrl,
NoComma,
}
impl fmt::Display for DataUrlError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::NotADataUrl => write!(f, "not a valid data url"),
Self::NoComma => write!(
f,
"data url is missing comma delimiting attributes and body"
),
}
}
}
#[cfg(feature = "std")]
impl std::error::Error for DataUrlError {}
impl<'a> DataUrl<'a> {
/// <https://fetch.spec.whatwg.org/#data-url-processor>
/// but starting from a string rather than a parsed `Url`, to avoid extra string copies.
pub fn process(input: &'a str) -> Result<Self, DataUrlError> {
use crate::DataUrlError::*;
let after_colon = pretend_parse_data_url(input).ok_or(NotADataUrl)?;
let (from_colon_to_comma, encoded_body_plus_fragment) =
find_comma_before_fragment(after_colon).ok_or(NoComma)?;
let (mime_type, base64) = parse_header(from_colon_to_comma);
Ok(DataUrl {
mime_type,
base64,
encoded_body_plus_fragment,
})
}
pub fn mime_type(&self) -> &mime::Mime {
&self.mime_type
}
/// Streaming-decode the data URLs body to `write_body_bytes`,
/// and return the URLs fragment identifier if it has one.
pub fn decode<F, E>(
&self,
write_body_bytes: F,
) -> Result<Option<FragmentIdentifier<'a>>, forgiving_base64::DecodeError<E>>
where
F: FnMut(&[u8]) -> Result<(), E>,
{
if self.base64 {
decode_with_base64(self.encoded_body_plus_fragment, write_body_bytes)
} else {
decode_without_base64(self.encoded_body_plus_fragment, write_body_bytes)
.map_err(forgiving_base64::DecodeError::WriteError)
}
}
/// Return the decoded body, and the URLs fragment identifier if it has one.
pub fn decode_to_vec(
&self,
) -> Result<(Vec<u8>, Option<FragmentIdentifier<'a>>), forgiving_base64::InvalidBase64> {
let mut body = Vec::new();
let fragment = self.decode(|bytes| {
body.extend_from_slice(bytes);
Ok(())
})?;
Ok((body, fragment))
}
}
/// The URLs fragment identifier (after `#`)
pub struct FragmentIdentifier<'a>(&'a str);
impl<'a> FragmentIdentifier<'a> {
/// Like in a parsed URL
pub fn to_percent_encoded(&self) -> String {
let mut string = String::new();
for byte in self.0.bytes() {
match byte {
// Ignore ASCII tabs or newlines like the URL parser would
b'\t' | b'\n' | b'\r' => continue,
// https://url.spec.whatwg.org/#fragment-percent-encode-set
b'\0'..=b' ' | b'"' | b'<' | b'>' | b'`' | b'\x7F'..=b'\xFF' => {
percent_encode(byte, &mut string)
}
// Printable ASCII
_ => string.push(byte as char),
}
}
string
}
}
/// Similar to <https://url.spec.whatwg.org/#concept-basic-url-parser>
/// followed by <https://url.spec.whatwg.org/#concept-url-serializer>
///
/// * `None`: not a data URL.
///
/// * `Some(s)`: sort of the result of serialization, except:
///
/// - `data:` prefix removed
/// - The fragment is included
/// - Other components are **not** UTF-8 percent-encoded
/// - ASCII tabs and newlines in the middle are **not** removed
fn pretend_parse_data_url(input: &str) -> Option<&str> {
// Trim C0 control or space
let left_trimmed = input.trim_start_matches(|ch| ch <= ' ');
let mut bytes = left_trimmed.bytes();
{
// Ignore ASCII tabs or newlines like the URL parser would
let mut iter = bytes
.by_ref()
.filter(|&byte| !matches!(byte, b'\t' | b'\n' | b'\r'));
require!(iter.next()?.to_ascii_lowercase() == b'd');
require!(iter.next()?.to_ascii_lowercase() == b'a');
require!(iter.next()?.to_ascii_lowercase() == b't');
require!(iter.next()?.to_ascii_lowercase() == b'a');
require!(iter.next()? == b':');
}
let bytes_consumed = left_trimmed.len() - bytes.len();
let after_colon = &left_trimmed[bytes_consumed..];
// Trim C0 control or space
Some(after_colon.trim_end_matches(|ch| ch <= ' '))
}
fn find_comma_before_fragment(after_colon: &str) -> Option<(&str, &str)> {
for (i, byte) in after_colon.bytes().enumerate() {
if byte == b',' {
return Some((&after_colon[..i], &after_colon[i + 1..]));
}
if byte == b'#' {
break;
}
}
None
}
fn parse_header(from_colon_to_comma: &str) -> (mime::Mime, bool) {
// "Strip leading and trailing ASCII whitespace"
// \t, \n, and \r would have been filtered by the URL parser
// \f percent-encoded by the URL parser
// space is the only remaining ASCII whitespace
let trimmed = from_colon_to_comma.trim_matches(|c| matches!(c, ' ' | '\t' | '\n' | '\r'));
let without_base64_suffix = remove_base64_suffix(trimmed);
let base64 = without_base64_suffix.is_some();
let mime_type = without_base64_suffix.unwrap_or(trimmed);
let mut string = String::new();
if mime_type.starts_with(';') {
string.push_str("text/plain")
}
let mut in_query = false;
for byte in mime_type.bytes() {
match byte {
// Ignore ASCII tabs or newlines like the URL parser would
b'\t' | b'\n' | b'\r' => continue,
// https://url.spec.whatwg.org/#c0-control-percent-encode-set
b'\0'..=b'\x1F' | b'\x7F'..=b'\xFF' => percent_encode(byte, &mut string),
// Bytes other than the C0 percent-encode set that are percent-encoded
// by the URL parser in the query state.
// '#' is also in that list but cannot occur here
// since it indicates the start of the URLs fragment.
b' ' | b'"' | b'<' | b'>' if in_query => percent_encode(byte, &mut string),
b'?' => {
in_query = true;
string.push('?')
}
// Printable ASCII
_ => string.push(byte as char),
}
}
// FIXME: does Mime::from_str match the MIME Sniffing Standards parsing algorithm?
// <https://mimesniff.spec.whatwg.org/#parse-a-mime-type>
let mime_type = string.parse().unwrap_or_else(|_| mime::Mime {
type_: String::from("text"),
subtype: String::from("plain"),
parameters: vec![(String::from("charset"), String::from("US-ASCII"))],
});
(mime_type, base64)
}
/// None: no base64 suffix
#[allow(clippy::skip_while_next)]
fn remove_base64_suffix(s: &str) -> Option<&str> {
let mut bytes = s.bytes();
{
// Ignore ASCII tabs or newlines like the URL parser would
let iter = bytes
.by_ref()
.filter(|&byte| !matches!(byte, b'\t' | b'\n' | b'\r'));
// Search from the end
let mut iter = iter.rev();
require!(iter.next()? == b'4');
require!(iter.next()? == b'6');
require!(iter.next()?.to_ascii_lowercase() == b'e');
require!(iter.next()?.to_ascii_lowercase() == b's');
require!(iter.next()?.to_ascii_lowercase() == b'a');
require!(iter.next()?.to_ascii_lowercase() == b'b');
require!(iter.skip_while(|&byte| byte == b' ').next()? == b';');
}
Some(&s[..bytes.len()])
}
fn percent_encode(byte: u8, string: &mut String) {
const HEX_UPPER: [u8; 16] = *b"0123456789ABCDEF";
string.push('%');
string.push(HEX_UPPER[(byte >> 4) as usize] as char);
string.push(HEX_UPPER[(byte & 0x0f) as usize] as char);
}
/// This is <https://url.spec.whatwg.org/#string-percent-decode> while also:
///
/// * Ignoring ASCII tab or newlines
/// * Stopping at the first '#' (which indicates the start of the fragment)
///
/// Anything that would have been UTF-8 percent-encoded by the URL parser
/// would be percent-decoded here.
/// We skip that round-trip and pass it through unchanged.
fn decode_without_base64<F, E>(
encoded_body_plus_fragment: &str,
mut write_bytes: F,
) -> Result<Option<FragmentIdentifier<'_>>, E>
where
F: FnMut(&[u8]) -> Result<(), E>,
{
let bytes = encoded_body_plus_fragment.as_bytes();
let mut slice_start = 0;
for (i, &byte) in bytes.iter().enumerate() {
// We only need to look for 5 different "special" byte values.
// For everything else we make slices as large as possible, borrowing the input,
// in order to make fewer write_all() calls.
if matches!(byte, b'%' | b'#' | b'\t' | b'\n' | b'\r') {
// Write everything (if anything) "non-special" weve accumulated
// before this special byte
if i > slice_start {
write_bytes(&bytes[slice_start..i])?;
}
// Then deal with the special byte.
match byte {
b'%' => {
let l = bytes.get(i + 2).and_then(|&b| (b as char).to_digit(16));
let h = bytes.get(i + 1).and_then(|&b| (b as char).to_digit(16));
if let (Some(h), Some(l)) = (h, l) {
// '%' followed by two ASCII hex digits
let one_byte = h as u8 * 0x10 + l as u8;
write_bytes(&[one_byte])?;
slice_start = i + 3;
} else {
// Do nothing. Leave slice_start unchanged.
// The % sign will be part of the next slice.
}
}
b'#' => {
let fragment_start = i + 1;
let fragment = &encoded_body_plus_fragment[fragment_start..];
return Ok(Some(FragmentIdentifier(fragment)));
}
// Ignore over '\t' | '\n' | '\r'
_ => slice_start = i + 1,
}
}
}
write_bytes(&bytes[slice_start..])?;
Ok(None)
}
/// `decode_without_base64()` composed with
/// <https://infra.spec.whatwg.org/#isomorphic-decode> composed with
/// <https://infra.spec.whatwg.org/#forgiving-base64-decode>.
fn decode_with_base64<F, E>(
encoded_body_plus_fragment: &str,
write_bytes: F,
) -> Result<Option<FragmentIdentifier<'_>>, forgiving_base64::DecodeError<E>>
where
F: FnMut(&[u8]) -> Result<(), E>,
{
let mut decoder = forgiving_base64::Decoder::new(write_bytes);
let fragment = decode_without_base64(encoded_body_plus_fragment, |bytes| decoder.feed(bytes))?;
decoder.finish()?;
Ok(fragment)
}

View file

@ -0,0 +1,19 @@
"""
Generate the BASE64_DECODE_TABLE constant. See its doc-comment.
"""
import string
# https://tools.ietf.org/html/rfc4648#section-4
alphabet = string.ascii_uppercase + string.ascii_lowercase + string.digits + "+/"
assert len(alphabet) == 64
reverse_table = [-1] * 256
for i, symbol in enumerate(alphabet):
reverse_table[ord(symbol)] = i
print("[")
per_line = 16
for line in range(0, 256, per_line):
print(" " + "".join(" %2s," % value for value in reverse_table[line:][:per_line]))
print("]")

207
third-party/vendor/data-url/src/mime.rs vendored Normal file
View file

@ -0,0 +1,207 @@
use alloc::{borrow::ToOwned, string::String, vec::Vec};
use core::fmt::{self, Write};
use core::str::FromStr;
/// <https://mimesniff.spec.whatwg.org/#mime-type-representation>
#[derive(Debug, PartialEq, Eq)]
pub struct Mime {
pub type_: String,
pub subtype: String,
/// (name, value)
pub parameters: Vec<(String, String)>,
}
impl Mime {
pub fn get_parameter<P>(&self, name: &P) -> Option<&str>
where
P: ?Sized + PartialEq<str>,
{
self.parameters
.iter()
.find(|&(n, _)| name == &**n)
.map(|(_, v)| &**v)
}
}
#[derive(Debug)]
pub struct MimeParsingError(());
impl fmt::Display for MimeParsingError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "invalid mime type")
}
}
#[cfg(feature = "std")]
impl std::error::Error for MimeParsingError {}
/// <https://mimesniff.spec.whatwg.org/#parsing-a-mime-type>
impl FromStr for Mime {
type Err = MimeParsingError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
parse(s).ok_or(MimeParsingError(()))
}
}
fn parse(s: &str) -> Option<Mime> {
let trimmed = s.trim_matches(http_whitespace);
let (type_, rest) = split2(trimmed, '/');
require!(only_http_token_code_points(type_) && !type_.is_empty());
let (subtype, rest) = split2(rest?, ';');
let subtype = subtype.trim_end_matches(http_whitespace);
require!(only_http_token_code_points(subtype) && !subtype.is_empty());
let mut parameters = Vec::new();
if let Some(rest) = rest {
parse_parameters(rest, &mut parameters)
}
Some(Mime {
type_: type_.to_ascii_lowercase(),
subtype: subtype.to_ascii_lowercase(),
parameters,
})
}
fn split2(s: &str, separator: char) -> (&str, Option<&str>) {
let mut iter = s.splitn(2, separator);
let first = iter.next().unwrap();
(first, iter.next())
}
fn parse_parameters(s: &str, parameters: &mut Vec<(String, String)>) {
let mut semicolon_separated = s.split(';');
while let Some(piece) = semicolon_separated.next() {
let piece = piece.trim_start_matches(http_whitespace);
let (name, value) = split2(piece, '=');
// We can not early return on an invalid name here, because the value
// parsing later may consume more semicolon seperated pieces.
let name_valid =
!name.is_empty() && only_http_token_code_points(name) && !contains(parameters, name);
if let Some(value) = value {
let value = if let Some(stripped) = value.strip_prefix('"') {
let max_len = stripped.len().saturating_sub(1); // without end quote
let mut unescaped_value = String::with_capacity(max_len);
let mut chars = stripped.chars();
'until_closing_quote: loop {
while let Some(c) = chars.next() {
match c {
'"' => break 'until_closing_quote,
'\\' => unescaped_value.push(chars.next().unwrap_or_else(|| {
semicolon_separated
.next()
.map(|piece| {
// A semicolon inside a quoted value is not a separator
// for the next parameter, but part of the value.
chars = piece.chars();
';'
})
.unwrap_or('\\')
})),
_ => unescaped_value.push(c),
}
}
if let Some(piece) = semicolon_separated.next() {
// A semicolon inside a quoted value is not a separator
// for the next parameter, but part of the value.
unescaped_value.push(';');
chars = piece.chars()
} else {
break;
}
}
if !name_valid || !valid_value(value) {
continue;
}
unescaped_value
} else {
let value = value.trim_end_matches(http_whitespace);
if value.is_empty() {
continue;
}
if !name_valid || !valid_value(value) {
continue;
}
value.to_owned()
};
parameters.push((name.to_ascii_lowercase(), value))
}
}
}
fn contains(parameters: &[(String, String)], name: &str) -> bool {
parameters.iter().any(|(n, _)| n == name)
}
fn valid_value(s: &str) -> bool {
s.chars().all(|c| {
// <https://mimesniff.spec.whatwg.org/#http-quoted-string-token-code-point>
matches!(c, '\t' | ' '..='~' | '\u{80}'..='\u{FF}')
})
}
/// <https://mimesniff.spec.whatwg.org/#serializing-a-mime-type>
impl fmt::Display for Mime {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(&self.type_)?;
f.write_str("/")?;
f.write_str(&self.subtype)?;
for (name, value) in &self.parameters {
f.write_str(";")?;
f.write_str(name)?;
f.write_str("=")?;
if only_http_token_code_points(value) && !value.is_empty() {
f.write_str(value)?
} else {
f.write_str("\"")?;
for c in value.chars() {
if c == '"' || c == '\\' {
f.write_str("\\")?
}
f.write_char(c)?
}
f.write_str("\"")?
}
}
Ok(())
}
}
fn http_whitespace(c: char) -> bool {
matches!(c, ' ' | '\t' | '\n' | '\r')
}
fn only_http_token_code_points(s: &str) -> bool {
s.bytes().all(|byte| IS_HTTP_TOKEN[byte as usize])
}
macro_rules! byte_map {
($($flag:expr,)*) => ([
$($flag != 0,)*
])
}
// Copied from https://github.com/hyperium/mime/blob/v0.3.5/src/parse.rs#L293
#[rustfmt::skip]
static IS_HTTP_TOKEN: [bool; 256] = byte_map![
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
];