use coap_message::{MessageOption, MutableWritableMessage, ReadableMessage};
use coap_numbers::{code, option};
use windowed_infinity::WindowedInfinity;
pub fn codeconvert<O: TryFrom<u8>>(code: u8) -> O {
code.try_into()
.or_else(|_| code::INTERNAL_SERVER_ERROR.try_into())
.map_err(|_| "Responde type can't even express tha simplest error codes")
.unwrap()
}
pub(crate) fn optconvert<O: TryFrom<u16>>(option: u16) -> O {
option
.try_into()
.map_err(|_| "Response type can't express options required by handler")
.unwrap()
}
pub struct Block2RequestData {
blknum: u32,
szx: u8,
}
#[derive(Debug)]
pub struct BadBlock2Option;
impl Block2RequestData {
pub fn from_message(message: &impl ReadableMessage) -> Result<Self, BadBlock2Option> {
let mut b2options = message.options().filter(|o| o.number() == option::BLOCK2);
match b2options.next() {
None => Ok(Self::default()),
Some(o) => {
if b2options.next().is_none() {
Self::from_option(&o)
} else {
Err(BadBlock2Option)
}
}
}
}
pub fn from_option(option: &impl MessageOption) -> Result<Self, BadBlock2Option> {
debug_assert!(option.number() == option::BLOCK2);
let o: u32 = option.value_uint().ok_or(BadBlock2Option)?;
if o >= 0x1000000 {
return Err(BadBlock2Option);
}
Ok(Self {
szx: (o as u8) & 0x7,
blknum: o >> 4,
})
}
pub fn to_option_value(&self, more: bool) -> u32 {
(self.blknum << 4) | if more { 0x08 } else { 0 } | self.szx as u32
}
pub fn size(&self) -> u16 {
1 << (4 + self.szx)
}
pub fn before(&self) -> u32 {
self.size() as u32 * self.blknum
}
pub fn shrink(mut self, size: u16) -> Option<Self> {
while self.size() > size {
if self.szx == 0 {
return None;
}
self.szx -= 1;
self.blknum *= 2;
}
Some(self)
}
}
impl Default for Block2RequestData {
fn default() -> Self {
Self { szx: 6, blknum: 0 }
}
}
pub fn block2_write<F, R>(
block2: Block2RequestData,
response: &mut impl MutableWritableMessage,
f: F,
) -> R
where
F: FnOnce(&mut WindowedInfinityWithETag) -> R,
{
block2_write_with_cf(block2, response, f, None)
}
#[derive(PartialEq)]
enum Characterization {
Underflow,
Inside,
Overflow,
}
use Characterization::*;
pub(crate) fn block2_write_with_cf<F, R>(
block2: Block2RequestData,
response: &mut impl MutableWritableMessage,
f: F,
cf: Option<u16>,
) -> R
where
F: FnOnce(&mut WindowedInfinityWithETag) -> R,
{
let estimated_option_size = 25; let payload_budget = response.available_space() - estimated_option_size;
let block2 = block2
.shrink(payload_budget as u16)
.expect("Tiny buffer allocated");
response.add_option(optconvert(option::ETAG), &[0, 0, 0, 0, 0, 0, 0, 0]);
if let Some(cf) = cf {
if let Ok(cfopt) = option::CONTENT_FORMAT.try_into() {
response.add_option_uint(cfopt, cf);
}
}
response.add_option_uint(optconvert(option::BLOCK2), block2.to_option_value(false));
let (characterization, written, etag, ret) = {
let full_payload = response.payload_mut_with_len(block2.size().into());
let mut writer = WindowedInfinityWithETag::new(
&mut full_payload[..block2.size() as usize],
-(block2.before() as isize),
);
let ret = f(&mut writer);
(
writer.characterize_cursor(),
writer.buffer.get_written().len(),
writer.etag(),
ret,
)
};
response.truncate(written);
if characterization == Underflow {
unimplemented!("Report out-of-band seek");
}
response.mutate_options(|optnum, value| {
match optnum.into() {
option::ETAG => {
value.copy_from_slice(&etag);
}
option::BLOCK2 if characterization == Overflow => {
value[value.len() - 1] |= 0x08;
}
_ => (),
};
});
ret
}
pub struct WindowedInfinityWithETag<'a> {
buffer: WindowedInfinity<'a>,
etag: u64,
}
impl<'a> WindowedInfinityWithETag<'a> {
fn new(data: &'a mut [u8], cursor: isize) -> Self {
let etag = 0;
let buffer = WindowedInfinity::new(data, cursor);
Self { buffer, etag }
}
fn characterize_cursor(&self) -> Characterization {
match usize::try_from(self.buffer.get_cursor()) {
Err(_) => Underflow,
Ok(i) if i == self.buffer.get_written().len() => Inside,
_ => Overflow,
}
}
pub fn write(&mut self, s: &[u8]) {
self.etag = crc::crc64::update(self.etag, &crc::crc64::ECMA_TABLE, s);
self.buffer.write(s);
}
pub fn etag(&self) -> [u8; 8] {
self.etag.to_le_bytes()
}
}
impl<'a> ::core::fmt::Write for WindowedInfinityWithETag<'a> {
fn write_str(&mut self, s: &str) -> ::core::fmt::Result {
self.write(s.as_bytes());
Ok(())
}
}
impl<'a> serde_cbor::ser::Write for WindowedInfinityWithETag<'a> {
type Error = serde_cbor::error::Error;
fn write_all(&mut self, buf: &[u8]) -> Result<(), serde_cbor::error::Error> {
Ok(self.write(buf))
}
}
#[cfg(feature = "minicbor")]
impl<'a> minicbor::encode::Write for WindowedInfinityWithETag<'a> {
type Error = core::convert::Infallible;
fn write_all(&mut self, buf: &[u8]) -> Result<(), Self::Error> {
Ok(self.write(buf))
}
}
#[cfg(feature = "ciborium-io")]
impl<'a> ciborium_io::Write for WindowedInfinityWithETag<'a> {
type Error = core::convert::Infallible;
fn write_all(&mut self, buf: &[u8]) -> Result<(), Self::Error> {
Ok(self.write(buf))
}
fn flush(&mut self) -> Result<(), Self::Error> {
Ok(())
}
}
pub struct MaskingUriUpToPath<'m, M: ReadableMessage>(pub &'m M);
impl<'m, M: ReadableMessage> ReadableMessage for MaskingUriUpToPath<'m, M> {
type Code = M::Code;
type MessageOption<'a> = M::MessageOption<'a>
where
Self: 'a,
;
type OptionsIter<'a> = MaskingUriUpToPathIter<M::OptionsIter<'a>>
where
Self: 'a,
;
fn options(&self) -> Self::OptionsIter<'_> {
MaskingUriUpToPathIter(self.0.options())
}
fn code(&self) -> M::Code {
self.0.code()
}
fn payload(&self) -> &[u8] {
self.0.payload()
}
}
pub struct MaskingUriUpToPathIter<I>(I);
impl<MO: MessageOption, I: Iterator<Item = MO>> Iterator for MaskingUriUpToPathIter<I> {
type Item = MO;
fn next(&mut self) -> Option<MO> {
loop {
let result = self.0.next()?;
match result.number() {
coap_numbers::option::URI_HOST => continue,
coap_numbers::option::URI_PATH => continue,
_ => return Some(result),
}
}
}
}
pub(crate) struct MaskingUriUpToPathN<'m, M: ReadableMessage> {
message: &'m M,
strip_paths: usize,
}
impl<'m, M: ReadableMessage> MaskingUriUpToPathN<'m, M> {
pub(crate) fn new(message: &'m M, strip_paths: usize) -> Self {
Self {
message,
strip_paths,
}
}
}
impl<'m, M: ReadableMessage> ReadableMessage for MaskingUriUpToPathN<'m, M> {
type Code = M::Code;
type MessageOption<'a> = M::MessageOption<'a>
where
Self: 'a,
;
type OptionsIter<'a> = MaskingUriUpToPathNIter<M::OptionsIter<'a>>
where
Self: 'a,
;
fn options(&self) -> Self::OptionsIter<'_> {
MaskingUriUpToPathNIter {
inner: self.message.options(),
remaining_strip: self.strip_paths,
}
}
fn code(&self) -> M::Code {
self.message.code()
}
fn payload(&self) -> &[u8] {
self.message.payload()
}
}
pub struct MaskingUriUpToPathNIter<I> {
inner: I,
remaining_strip: usize,
}
impl<MO: MessageOption, I: Iterator<Item = MO>> Iterator for MaskingUriUpToPathNIter<I> {
type Item = MO;
fn next(&mut self) -> Option<MO> {
loop {
let result = self.inner.next()?;
match result.number() {
coap_numbers::option::URI_HOST => continue,
coap_numbers::option::URI_PATH if self.remaining_strip > 0 => {
self.remaining_strip -= 1;
continue;
}
_ => return Some(result),
}
}
}
}