use o3::buffer::{Owned, Shared};
use super::super::{HotTextInner, TextItemInner};
use super::headers::HeaderNameToken;
use crate::http::request::LocalFrameBytesRef;
#[derive(Clone)]
pub enum HeaderValueInner<'req> {
Static(&'static [u8]),
Inline(InlineHeaderValue),
Shared(Shared),
Local(LocalFrameBytesRef<'req>),
}
#[derive(Clone, Copy)]
pub struct InlineHeaderValue {
bytes: [u8; 31],
len: u8,
}
impl InlineHeaderValue {
pub fn new(value: &[u8]) -> Self {
assert!(
value.len() <= 31,
"inline header value overflow: max 31, got {}",
value.len()
);
let mut bytes = [0u8; 31];
bytes[..value.len()].copy_from_slice(value);
Self {
bytes,
len: value.len() as u8,
}
}
pub fn from_decimal(value: usize) -> Self {
let mut raw = [0u8; 20];
let n = crate::http::codec::Wire::write_dec(value, &mut raw);
Self::new(&raw[..n])
}
pub fn as_bytes(&self) -> &[u8] {
&self.bytes[..usize::from(self.len)]
}
}
impl std::fmt::Debug for InlineHeaderValue {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("InlineHeaderValue")
.field("len", &self.len)
.finish()
}
}
impl<'req> std::fmt::Debug for HeaderValueInner<'req> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Static(value) => f
.debug_struct("HeaderValue::Static")
.field("len", &value.len())
.finish(),
Self::Inline(value) => f
.debug_struct("HeaderValue::Inline")
.field("len", &value.as_bytes().len())
.finish(),
Self::Shared(value) => f
.debug_struct("HeaderValue::Shared")
.field("len", &value.len())
.finish(),
Self::Local(value) => f
.debug_struct("HeaderValue::Local")
.field("len", &value.len())
.finish(),
}
}
}
impl<'req> HeaderValueInner<'req> {
pub fn as_bytes(&self) -> &[u8] {
match self {
Self::Static(value) => value,
Self::Inline(value) => value.as_bytes(),
Self::Shared(value) => value.as_ref(),
Self::Local(value) => value.as_bytes(),
}
}
pub fn len(&self) -> usize {
self.as_bytes().len()
}
pub fn is_empty(&self) -> bool {
self.as_bytes().is_empty()
}
}
#[derive(Clone, Debug)]
pub struct HeaderItemInner<'req> {
pub(super) name: HeaderNameToken,
pub(super) value: HeaderValueInner<'req>,
}
pub type HeaderItem = HeaderItemInner<'static>;
impl<'req> HeaderItemInner<'req> {
pub fn from_value<V>(name: HeaderNameToken, value: V) -> Self
where
V: IntoHeaderValue<'req>,
{
Self {
name,
value: value.into_header_value(),
}
}
pub(crate) fn placeholder() -> Self {
Self {
name: HeaderNameToken::empty_placeholder(),
value: HeaderValueInner::Static(&[]),
}
}
pub fn wire_len(&self) -> usize {
self.name.as_bytes().len() + 2 + self.value.len() + 2
}
pub fn name_bytes(&self) -> &'static [u8] {
self.name.as_bytes()
}
pub fn value_bytes(&self) -> &[u8] {
self.value.as_bytes()
}
}
pub trait IntoHeaderValue<'req> {
fn into_header_value(self) -> HeaderValueInner<'req>;
}
impl<'req> IntoHeaderValue<'req> for HeaderValueInner<'req> {
fn into_header_value(self) -> Self {
self
}
}
impl<'req> IntoHeaderValue<'req> for InlineHeaderValue {
fn into_header_value(self) -> HeaderValueInner<'req> {
HeaderValueInner::Inline(self)
}
}
impl<'req> IntoHeaderValue<'req> for LocalFrameBytesRef<'req> {
fn into_header_value(self) -> HeaderValueInner<'req> {
HeaderValueInner::Local(self)
}
}
impl<'req> IntoHeaderValue<'req> for Shared {
fn into_header_value(self) -> HeaderValueInner<'req> {
HeaderValueInner::Shared(self)
}
}
impl<'req> IntoHeaderValue<'req> for Owned {
fn into_header_value(self) -> HeaderValueInner<'req> {
HeaderValueInner::Shared(self.freeze())
}
}
impl<'req> IntoHeaderValue<'req> for String {
fn into_header_value(self) -> HeaderValueInner<'req> {
HeaderValueInner::Shared(Shared::from(self.into_bytes()))
}
}
impl<'req> IntoHeaderValue<'req> for &'static str {
fn into_header_value(self) -> HeaderValueInner<'req> {
HeaderValueInner::Static(self.as_bytes())
}
}
pub trait TextSpec<'req> {
fn into_hot_text(self) -> HotTextInner<'req>;
}
impl<'req, const N: usize> TextSpec<'req> for [TextItemInner<'req>; N] {
fn into_hot_text(self) -> HotTextInner<'req> {
HotTextInner::from_items(self)
}
}
impl<'req> TextSpec<'req> for HotTextInner<'req> {
fn into_hot_text(self) -> Self {
self
}
}