#![deprecated]
#![allow(
clippy::redundant_pattern_matching,
clippy::needless_lifetimes,
clippy::result_unit_err
)]
use core::convert::{TryFrom, TryInto};
use coap_message::{MessageOption, MutableWritableMessage, ReadableMessage};
use coap_numbers::{code, option};
pub(crate) 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,
}
impl Block2RequestData {
pub fn from_message(message: &impl ReadableMessage) -> Result<Self, ()> {
let mut b2options = message.options().filter(|o| o.number() == option::BLOCK2);
match b2options.next() {
None => Ok(Self::default()),
Some(o) => {
if let None = b2options.next() {
Self::from_option(&o)
} else {
Err(())
}
}
}
}
pub fn from_option<'a>(option: &'a impl MessageOption) -> Result<Self, ()> {
debug_assert!(option.number() == option::BLOCK2);
let o: u32 = option.value_uint().ok_or(())?;
if o >= 0x1000000 {
return Err(());
}
Ok(Self {
szx: (o as u8) & 0x7,
blknum: o >> 4,
})
}
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 BlockWriter) -> R,
{
block2_write_with_cf(block2, response, f, None)
}
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 BlockWriter) -> 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.blknum << 4) | block2.szx as u32,
);
let (cursor, etag, ret) = {
let full_payload = response.payload_mut_with_len(block2.size().into());
let mut writer = BlockWriter {
data: &mut full_payload[..block2.size() as usize],
cursor: -(block2.before() as isize),
etag: 0,
};
let ret = f(&mut writer);
(writer.cursor, writer.etag, ret)
};
let set_more;
if cursor > block2.size() as isize {
response.truncate(block2.size() as usize);
set_more = true;
} else if cursor < 0 {
unimplemented!("Report out-of-band seek");
} else {
response.truncate(cursor as usize);
set_more = false;
}
response.mutate_options(|optnum, value| {
match optnum.into() {
option::ETAG => {
use byteorder::ByteOrder;
byteorder::NetworkEndian::write_u64(value, etag);
}
option::BLOCK2 if set_more => {
value[value.len() - 1] |= 0x08;
}
_ => (),
};
});
ret
}
pub struct BlockWriter<'a> {
data: &'a mut [u8],
cursor: isize,
etag: u64,
}
impl<'a> ::core::fmt::Write for BlockWriter<'a> {
fn write_str(&mut self, s: &str) -> ::core::fmt::Result {
let mut s = s.as_bytes();
self.etag = crc::crc64::update(self.etag, &crc::crc64::ECMA_TABLE, s);
if self.cursor >= self.data.len() as isize {
self.cursor += s.len() as isize;
return Ok(());
}
if self.cursor < -(s.len() as isize) {
self.cursor += s.len() as isize;
return Ok(());
}
if self.cursor < 0 {
s = &s[(-self.cursor) as usize..];
self.cursor = 0;
}
let mut s_to_copy = s;
if self.cursor as usize + s.len() > self.data.len() {
let copy_bytes = self.data.len() - self.cursor as usize;
s_to_copy = &s[..copy_bytes]
}
self.data[self.cursor as usize..self.cursor as usize + s_to_copy.len()]
.copy_from_slice(s_to_copy);
self.cursor += s.len() as isize;
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<'a>(&'a self) -> Self::OptionsIter<'a> {
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),
}
}
}
}