use std::cmp;
use std::error::Error;
use std::fmt;
use std::fmt::Debug;
use std::fmt::Write;
use std::hash::Hash;
use std::hash::Hasher;
use std::marker::PhantomData;
use std::str::FromStr;
use bytes::Bytes;
use bytes::BytesMut;
use super::encoding::Ascii;
use super::encoding::Binary;
use super::encoding::InvalidMetadataValue;
use super::encoding::InvalidMetadataValueBytes;
use super::encoding::ValueEncoding;
use crate::private;
#[derive(Clone)]
#[repr(transparent)]
pub struct MetadataValue<VE> {
inner: UnencodedHeaderValue,
_phantom: PhantomData<VE>,
}
#[derive(Clone)]
pub struct UnencodedHeaderValue {
data: Bytes,
is_sensitive: bool,
}
impl PartialEq for UnencodedHeaderValue {
fn eq(&self, other: &Self) -> bool {
self.data == other.data
}
}
impl Eq for UnencodedHeaderValue {}
impl UnencodedHeaderValue {
pub(crate) fn from_bytes(bytes: Bytes) -> Self {
UnencodedHeaderValue {
data: bytes,
is_sensitive: true,
}
}
pub(crate) fn into_bytes(self) -> Bytes {
self.data
}
pub(crate) fn as_bytes(&self) -> &Bytes {
&self.data
}
}
const fn is_visible_ascii(b: u8) -> bool {
b >= 32 && b < 127 || b == b'\t'
}
impl Debug for UnencodedHeaderValue {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.is_sensitive {
f.write_str("Sensitive")
} else {
f.write_str("\"")?;
let mut from = 0;
let bytes = self.as_bytes();
for (i, &b) in bytes.iter().enumerate() {
if !is_visible_ascii(b) || b == b'"' {
if from != i {
f.write_str(unsafe { str::from_utf8_unchecked(&bytes[from..i]) })?;
}
if b == b'"' {
f.write_str("\\\"")?;
} else {
write!(f, "\\x{:x}", b)?;
}
from = i + 1;
}
}
f.write_str(unsafe { str::from_utf8_unchecked(&bytes[from..]) })?;
f.write_str("\"")
}
}
}
#[derive(Debug)]
#[non_exhaustive]
pub struct ToStrError {}
pub type AsciiMetadataValue = MetadataValue<Ascii>;
pub type BinaryMetadataValue = MetadataValue<Binary>;
impl<VE> MetadataValue<VE> {
#[inline]
pub unsafe fn from_shared_unchecked(src: Bytes) -> Self {
MetadataValue {
inner: UnencodedHeaderValue::from_bytes(src),
_phantom: PhantomData,
}
}
#[inline]
pub fn set_sensitive(&mut self, val: bool) {
self.inner.is_sensitive = val;
}
#[inline]
pub fn is_sensitive(&self) -> bool {
self.inner.is_sensitive
}
#[inline]
pub(crate) fn unchecked_from_header_value(value: UnencodedHeaderValue) -> Self {
MetadataValue {
inner: value,
_phantom: PhantomData,
}
}
#[inline]
pub(crate) fn unchecked_from_header_value_ref(header_value: &UnencodedHeaderValue) -> &Self {
unsafe { &*(header_value as *const UnencodedHeaderValue as *const Self) }
}
#[inline]
pub(crate) fn unchecked_from_mut_header_value_ref(
header_value: &mut UnencodedHeaderValue,
) -> &mut Self {
unsafe { &mut *(header_value as *mut UnencodedHeaderValue as *mut Self) }
}
pub(crate) fn into_inner(self) -> UnencodedHeaderValue {
self.inner
}
}
impl<VE: ValueEncoding> MetadataValue<VE> {
pub(crate) fn encode(value: Bytes) -> Bytes {
VE::encode(value, private::Internal)
}
#[inline]
pub fn from_static(src: &'static str) -> Self {
MetadataValue {
inner: VE::from_static(src, private::Internal),
_phantom: PhantomData,
}
}
}
impl<VE: ValueEncoding> TryFrom<&[u8]> for MetadataValue<VE> {
type Error = InvalidMetadataValueBytes;
#[inline]
fn try_from(src: &[u8]) -> Result<Self, Self::Error> {
VE::from_bytes(src, private::Internal).map(|value| MetadataValue {
inner: value,
_phantom: PhantomData,
})
}
}
impl<VE: ValueEncoding, const N: usize> TryFrom<&[u8; N]> for MetadataValue<VE> {
type Error = InvalidMetadataValueBytes;
#[inline]
fn try_from(src: &[u8; N]) -> Result<Self, Self::Error> {
Self::try_from(src.as_ref())
}
}
impl<VE: ValueEncoding> TryFrom<Bytes> for MetadataValue<VE> {
type Error = InvalidMetadataValueBytes;
#[inline]
fn try_from(src: Bytes) -> Result<Self, Self::Error> {
VE::from_shared(src, private::Internal).map(|value| MetadataValue {
inner: value,
_phantom: PhantomData,
})
}
}
impl<VE: ValueEncoding> TryFrom<Vec<u8>> for MetadataValue<VE> {
type Error = InvalidMetadataValueBytes;
#[inline]
fn try_from(src: Vec<u8>) -> Result<Self, Self::Error> {
Self::try_from(src.as_slice())
}
}
impl<'a> TryFrom<&'a str> for MetadataValue<Ascii> {
type Error = InvalidMetadataValue;
#[inline]
fn try_from(s: &'a str) -> Result<Self, Self::Error> {
s.parse()
}
}
impl<'a> TryFrom<&'a String> for MetadataValue<Ascii> {
type Error = InvalidMetadataValue;
#[inline]
fn try_from(s: &'a String) -> Result<Self, Self::Error> {
s.parse()
}
}
impl TryFrom<String> for MetadataValue<Ascii> {
type Error = InvalidMetadataValue;
#[inline]
fn try_from(s: String) -> Result<Self, Self::Error> {
s.parse()
}
}
impl MetadataValue<Ascii> {
pub fn to_str(&self) -> &str {
unsafe { std::str::from_utf8_unchecked(self.inner.data.as_ref()) }
}
#[inline]
pub fn as_bytes(&self) -> &[u8] {
self.inner.data.as_ref()
}
}
impl MetadataValue<Binary> {
#[inline]
pub fn from_bytes(src: &[u8]) -> Self {
Self::try_from(src).unwrap()
}
}
impl<VE: ValueEncoding> Debug for MetadataValue<VE> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
Debug::fmt(&self.inner, f)
}
}
macro_rules! from_integers {
($($name:ident: $t:ident => $max_len:expr),*) => {$(
impl From<$t> for MetadataValue<Ascii> {
fn from(num: $t) -> MetadataValue<Ascii> {
let mut buf = BytesMut::with_capacity($max_len);
let _ = buf.write_str(itoa::Buffer::new().format(num));
let inner = UnencodedHeaderValue {
data: buf.freeze(),
is_sensitive: false,
};
MetadataValue {
inner,
_phantom: PhantomData,
}
}
}
#[test]
fn $name() {
let n: $t = 55;
let val = AsciiMetadataValue::from(n);
assert_eq!(val, &n.to_string());
let n = $t::MAX;
let val = AsciiMetadataValue::from(n);
assert_eq!(val, &n.to_string());
}
)*};
}
from_integers! {
from_u16: u16 => 5,
from_i16: i16 => 6,
from_u32: u32 => 10,
from_i32: i32 => 11,
from_u64: u64 => 20,
from_i64: i64 => 20
}
#[cfg(target_pointer_width = "16")]
from_integers! {
from_usize: usize => 5,
from_isize: isize => 6
}
#[cfg(target_pointer_width = "32")]
from_integers! {
from_usize: usize => 10,
from_isize: isize => 11
}
#[cfg(target_pointer_width = "64")]
from_integers! {
from_usize: usize => 20,
from_isize: isize => 20
}
impl FromStr for MetadataValue<Ascii> {
type Err = InvalidMetadataValue;
#[inline]
fn from_str(s: &str) -> Result<MetadataValue<Ascii>, Self::Err> {
AsciiMetadataValue::try_from(s.as_bytes()).map_err(|_| InvalidMetadataValue::new())
}
}
impl<VE: ValueEncoding> From<MetadataValue<VE>> for Bytes {
#[inline]
fn from(value: MetadataValue<VE>) -> Bytes {
value.inner.data
}
}
impl<'a, VE: ValueEncoding> From<&'a MetadataValue<VE>> for MetadataValue<VE> {
#[inline]
fn from(t: &'a MetadataValue<VE>) -> Self {
t.clone()
}
}
impl fmt::Display for ToStrError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("failed to convert metadata to a str")
}
}
impl Error for ToStrError {}
impl Hash for MetadataValue<Ascii> {
fn hash<H: Hasher>(&self, state: &mut H) {
self.inner.data.hash(state)
}
}
impl Hash for MetadataValue<Binary> {
fn hash<H: Hasher>(&self, state: &mut H) {
self.inner.data.hash(state)
}
}
impl<VE: ValueEncoding> PartialEq for MetadataValue<VE> {
#[inline]
fn eq(&self, other: &MetadataValue<VE>) -> bool {
VE::values_equal(&self.inner, &other.inner, private::Internal)
}
}
impl<VE: ValueEncoding> Eq for MetadataValue<VE> {}
impl<VE: ValueEncoding> PartialOrd for MetadataValue<VE> {
#[inline]
fn partial_cmp(&self, other: &MetadataValue<VE>) -> Option<cmp::Ordering> {
Some(self.cmp(other))
}
}
impl<VE: ValueEncoding> Ord for MetadataValue<VE> {
#[inline]
fn cmp(&self, other: &Self) -> cmp::Ordering {
self.inner.data.cmp(&other.inner.data)
}
}
impl<VE: ValueEncoding> PartialEq<str> for MetadataValue<VE> {
#[inline]
fn eq(&self, other: &str) -> bool {
VE::equals(&self.inner, other.as_bytes(), private::Internal)
}
}
impl<VE: ValueEncoding> PartialEq<[u8]> for MetadataValue<VE> {
#[inline]
fn eq(&self, other: &[u8]) -> bool {
VE::equals(&self.inner, other, private::Internal)
}
}
impl<VE: ValueEncoding> PartialOrd<str> for MetadataValue<VE> {
#[inline]
fn partial_cmp(&self, other: &str) -> Option<cmp::Ordering> {
self.inner.data.partial_cmp(other.as_bytes())
}
}
impl<VE: ValueEncoding> PartialOrd<[u8]> for MetadataValue<VE> {
#[inline]
fn partial_cmp(&self, other: &[u8]) -> Option<cmp::Ordering> {
self.inner.data.partial_cmp(other)
}
}
impl<VE: ValueEncoding> PartialEq<MetadataValue<VE>> for str {
#[inline]
fn eq(&self, other: &MetadataValue<VE>) -> bool {
*other == *self
}
}
impl<VE: ValueEncoding> PartialEq<MetadataValue<VE>> for [u8] {
#[inline]
fn eq(&self, other: &MetadataValue<VE>) -> bool {
*other == *self
}
}
impl<VE: ValueEncoding> PartialOrd<MetadataValue<VE>> for str {
#[inline]
fn partial_cmp(&self, other: &MetadataValue<VE>) -> Option<cmp::Ordering> {
self.as_bytes().partial_cmp(other.inner.data.as_ref())
}
}
impl<VE: ValueEncoding> PartialOrd<MetadataValue<VE>> for [u8] {
#[inline]
fn partial_cmp(&self, other: &MetadataValue<VE>) -> Option<cmp::Ordering> {
self.partial_cmp(other.inner.data.as_ref())
}
}
impl<VE: ValueEncoding> PartialEq<String> for MetadataValue<VE> {
#[inline]
fn eq(&self, other: &String) -> bool {
*self == other[..]
}
}
impl<VE: ValueEncoding> PartialOrd<String> for MetadataValue<VE> {
#[inline]
fn partial_cmp(&self, other: &String) -> Option<cmp::Ordering> {
self.inner.data.partial_cmp(other.as_bytes())
}
}
impl<VE: ValueEncoding> PartialEq<MetadataValue<VE>> for String {
#[inline]
fn eq(&self, other: &MetadataValue<VE>) -> bool {
*other == *self
}
}
impl<VE: ValueEncoding> PartialOrd<MetadataValue<VE>> for String {
#[inline]
fn partial_cmp(&self, other: &MetadataValue<VE>) -> Option<cmp::Ordering> {
self.as_bytes().partial_cmp(other.inner.data.as_ref())
}
}
impl<VE: ValueEncoding> PartialEq<MetadataValue<VE>> for &MetadataValue<VE> {
#[inline]
fn eq(&self, other: &MetadataValue<VE>) -> bool {
**self == *other
}
}
impl<VE: ValueEncoding> PartialOrd<MetadataValue<VE>> for &MetadataValue<VE> {
#[inline]
fn partial_cmp(&self, other: &MetadataValue<VE>) -> Option<cmp::Ordering> {
(**self).partial_cmp(other)
}
}
impl<'a, VE: ValueEncoding, T: ?Sized> PartialEq<&'a T> for MetadataValue<VE>
where
MetadataValue<VE>: PartialEq<T>,
{
#[inline]
fn eq(&self, other: &&'a T) -> bool {
*self == **other
}
}
impl<'a, VE: ValueEncoding, T: ?Sized> PartialOrd<&'a T> for MetadataValue<VE>
where
MetadataValue<VE>: PartialOrd<T>,
{
#[inline]
fn partial_cmp(&self, other: &&'a T) -> Option<cmp::Ordering> {
self.partial_cmp(*other)
}
}
impl<VE: ValueEncoding> PartialEq<MetadataValue<VE>> for &str {
#[inline]
fn eq(&self, other: &MetadataValue<VE>) -> bool {
*other == *self
}
}
impl<VE: ValueEncoding> PartialOrd<MetadataValue<VE>> for &str {
#[inline]
fn partial_cmp(&self, other: &MetadataValue<VE>) -> Option<cmp::Ordering> {
self.as_bytes().partial_cmp(other.inner.data.as_ref())
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_debug() {
let cases = &[
("hello", "\"hello\""),
("hello \"world\"", "\"hello \\\"world\\\"\""),
];
for &(value, expected) in cases {
let mut val = AsciiMetadataValue::try_from(value.as_bytes()).unwrap();
let actual = format!("{val:?}");
assert_eq!("Sensitive", actual);
val.set_sensitive(false);
let actual = format!("{val:?}");
assert_eq!(expected, actual);
}
}
#[test]
fn test_valid_metadata_values() {
assert!(MetadataValue::<Ascii>::try_from("".as_bytes()).is_err());
assert!(MetadataValue::<Ascii>::try_from(" ".as_bytes()).is_err()); assert!(MetadataValue::<Binary>::try_from("".as_bytes()).is_ok());
assert!(MetadataValue::<Ascii>::try_from("a".as_bytes()).is_ok());
assert!(MetadataValue::<Ascii>::try_from("abc".as_bytes()).is_ok());
assert!(MetadataValue::<Ascii>::try_from("\0".as_bytes()).is_err());
assert!(MetadataValue::<Ascii>::try_from("\n".as_bytes()).is_err());
assert!(MetadataValue::<Ascii>::try_from("\x7f".as_bytes()).is_err());
assert!(MetadataValue::<Binary>::try_from("\0".as_bytes()).is_ok());
assert!(MetadataValue::<Binary>::try_from("\n".as_bytes()).is_ok());
assert!(MetadataValue::<Ascii>::try_from("🦀".as_bytes()).is_err());
assert!(MetadataValue::<Ascii>::try_from("ü".as_bytes()).is_err());
assert!(MetadataValue::<Binary>::try_from("🦀".as_bytes()).is_ok());
assert!(MetadataValue::<Binary>::try_from("ü".as_bytes()).is_ok());
}
#[test]
fn test_value_eq_value() {
type Bmv = BinaryMetadataValue;
type Amv = AsciiMetadataValue;
assert_eq!(Amv::from_static("abc"), Amv::from_static("abc"));
assert_ne!(Amv::from_static("abc"), Amv::from_static("ABC"));
assert_eq!(Bmv::from_bytes(b"abc"), Bmv::from_bytes(b"abc"));
assert_ne!(Bmv::from_bytes(b"abc"), Bmv::from_bytes(b"ABC"));
unsafe {
assert_ne!(
Bmv::from_shared_unchecked(Bytes::from_static(b"..{}")),
Bmv::from_shared_unchecked(Bytes::from_static(b"{}.."))
);
}
}
#[test]
fn test_value_eq_ignores_sensitivity() {
let mut val1 = AsciiMetadataValue::from_static("abc");
let val2 = AsciiMetadataValue::from_static("abc");
assert_eq!(val1, val2);
val1.set_sensitive(false);
assert_eq!(val1, val2);
let mut val1 = BinaryMetadataValue::from_bytes(b"abc");
let val2 = BinaryMetadataValue::from_bytes(b"abc");
assert_eq!(val1, val2);
val1.set_sensitive(false);
assert_eq!(val1, val2);
}
#[test]
fn test_value_eq_str() {
type Bmv = BinaryMetadataValue;
type Amv = AsciiMetadataValue;
assert_eq!(Amv::from_static("abc"), "abc");
assert_ne!(Amv::from_static("abc"), "ABC");
assert_eq!("abc", Amv::from_static("abc"));
assert_ne!("ABC", Amv::from_static("abc"));
assert_eq!(Bmv::from_bytes(b"abc"), "abc");
assert_ne!(Bmv::from_bytes(b"abc"), "ABC");
assert_eq!("abc", Bmv::from_bytes(b"abc"));
assert_ne!("ABC", Bmv::from_bytes(b"abc"));
}
#[test]
fn test_value_eq_bytes() {
type Bmv = BinaryMetadataValue;
type Amv = AsciiMetadataValue;
assert_eq!(Amv::from_static("abc"), "abc".as_bytes());
assert_ne!(Amv::from_static("abc"), "ABC".as_bytes());
assert_eq!(*"abc".as_bytes(), Amv::from_static("abc"));
assert_ne!(*"ABC".as_bytes(), Amv::from_static("abc"));
assert_eq!(*"abc".as_bytes(), Bmv::from_bytes(b"abc"));
assert_ne!(*"ABC".as_bytes(), Bmv::from_bytes(b"abc"));
}
#[test]
fn test_ascii_value_hash() {
use std::collections::hash_map::DefaultHasher;
type Amv = AsciiMetadataValue;
fn hash(value: Amv) -> u64 {
let mut hasher = DefaultHasher::new();
value.hash(&mut hasher);
hasher.finish()
}
let value1 = Amv::from_static("abc");
let value2 = Amv::from_static("abc");
assert_eq!(value1, value2);
assert_eq!(hash(value1), hash(value2));
let value1 = Amv::from_static("abc");
let value2 = Amv::from_static("xyz");
assert_ne!(value1, value2);
assert_ne!(hash(value1), hash(value2));
}
#[test]
fn test_valid_binary_value_hash() {
use std::collections::hash_map::DefaultHasher;
type Bmv = BinaryMetadataValue;
fn hash(value: Bmv) -> u64 {
let mut hasher = DefaultHasher::new();
value.hash(&mut hasher);
hasher.finish()
}
let value1 = Bmv::from_bytes(b"abc");
let value2 = Bmv::from_bytes(b"abc");
assert_eq!(value1, value2);
assert_eq!(hash(value1), hash(value2));
let value1 = Bmv::from_bytes(b"abc");
let value2 = Bmv::from_bytes(b"xyz");
assert_ne!(value1, value2);
assert_ne!(hash(value1), hash(value2));
}
#[test]
fn test_invalid_binary_value_hash() {
use std::collections::hash_map::DefaultHasher;
type Bmv = BinaryMetadataValue;
fn hash(value: Bmv) -> u64 {
let mut hasher = DefaultHasher::new();
value.hash(&mut hasher);
hasher.finish()
}
unsafe {
let value1 = Bmv::from_shared_unchecked(Bytes::from_static(b"..{}"));
let value2 = Bmv::from_shared_unchecked(Bytes::from_static(b"{}.."));
assert_ne!(value1, value2);
assert_ne!(hash(value1), hash(value2));
}
unsafe {
let valid = Bmv::from_bytes(b"abc");
let invalid = Bmv::from_shared_unchecked(Bytes::from_static(b"{}.."));
assert_ne!(valid, invalid);
assert_ne!(hash(valid), hash(invalid));
}
}
}