use crate::component::bindings::fastly::compute::types;
use crate::error::Error;
use http::{HeaderMap, HeaderName};
use std::cmp::min;
type MultiValueCursor = u32;
const MAX_HEADER_NAME_LEN: usize = 1 << (16 - 1);
fn write_values<I, T>(
iter: I,
terminator: u8,
max_len: u64,
cursor_start: MultiValueCursor,
) -> Result<(Vec<u8>, Option<MultiValueCursor>), types::Error>
where
I: Iterator<Item = T>,
T: AsRef<[u8]>,
{
let mut buf = Vec::with_capacity(min(max_len, 0x1_0000) as usize);
let mut cursor = cursor_start;
let mut finished = true;
let skip_amt = usize::try_from(cursor).expect("u32 can fit in usize");
for item in iter.skip(skip_amt) {
let bytes = item.as_ref();
let needed = buf.len() as u64 + bytes.len() as u64 + 1;
if needed > max_len {
if cursor == cursor_start {
return Err(types::Error::BufferLen(needed));
}
finished = false;
break;
}
buf.extend(bytes);
buf.push(terminator);
cursor += 1
}
let cursor = if finished { None } else { Some(cursor) };
Ok((buf, cursor))
}
fn write_names<I, T>(
iter: I,
terminator: char,
max_len: u64,
cursor_start: MultiValueCursor,
) -> Result<(String, Option<u32>), types::Error>
where
I: Iterator<Item = T>,
T: AsRef<str>,
{
let mut buf = String::with_capacity(min(max_len, 0x1_0000) as usize);
let mut cursor = cursor_start;
let mut finished = true;
let skip_amt = usize::try_from(cursor).expect("u32 can fit in usize");
for item in iter.skip(skip_amt) {
let key = item.as_ref();
let needed = buf.len() as u64 + key.len() as u64 + 1;
if needed > max_len {
if cursor == cursor_start {
return Err(types::Error::BufferLen(needed));
}
finished = false;
break;
}
buf += key;
buf.push(terminator);
cursor += 1
}
let cursor = if finished { None } else { Some(cursor) };
debug_assert!(!buf.is_empty() || cursor.is_none());
Ok((buf, cursor))
}
pub fn get_names<Keys, T>(
keys: Keys,
max_len: u64,
cursor: u32,
) -> Result<(String, Option<u32>), types::Error>
where
Keys: Iterator<Item = T>,
T: AsRef<str>,
{
write_names(keys, '\0', max_len, cursor)
}
pub fn get_values(
headers: &HeaderMap,
name: &str,
max_len: u64,
cursor: u32,
) -> Result<(Vec<u8>, Option<u32>), types::Error> {
if name.len() > MAX_HEADER_NAME_LEN {
return Err(Error::InvalidArgument.into());
}
let values = headers.get_all(HeaderName::try_from(name)?);
write_values(values.into_iter(), b'\0', max_len, cursor)
}