use super::storage::Footprint;
use crate::defensive;
use alloc::vec::Vec;
use codec::{Decode, DecodeWithMemTracking, Encode, FullCodec, MaxEncodedLen};
use core::{cmp::Ordering, fmt::Debug, marker::PhantomData};
use scale_info::TypeInfo;
use subsoil::core::{ConstU32, Get, TypedGet};
use subsoil::runtime::{traits::Convert, BoundedSlice};
use subsoil::weights::{Weight, WeightMeter};
#[derive(Copy, Clone, Eq, PartialEq, Encode, Decode, DecodeWithMemTracking, TypeInfo, Debug)]
pub enum ProcessMessageError {
BadFormat,
Corrupt,
Unsupported,
Overweight(Weight),
Yield,
StackLimitReached,
}
pub trait ProcessMessage {
type Origin: FullCodec + MaxEncodedLen + Clone + Eq + PartialEq + TypeInfo + Debug;
fn process_message(
message: &[u8],
origin: Self::Origin,
meter: &mut WeightMeter,
id: &mut [u8; 32],
) -> Result<bool, ProcessMessageError>;
}
#[derive(Eq, PartialEq, Debug)]
pub enum ExecuteOverweightError {
NotFound,
AlreadyProcessed,
InsufficientWeight,
QueuePaused,
Other,
RecursiveDisallowed,
}
pub trait ServiceQueues {
type OverweightMessageAddress;
fn service_queues(weight_limit: Weight) -> Weight;
fn execute_overweight(
_weight_limit: Weight,
_address: Self::OverweightMessageAddress,
) -> Result<Weight, ExecuteOverweightError> {
Err(ExecuteOverweightError::NotFound)
}
}
pub struct NoopServiceQueues<OverweightAddr>(PhantomData<OverweightAddr>);
impl<OverweightAddr> ServiceQueues for NoopServiceQueues<OverweightAddr> {
type OverweightMessageAddress = OverweightAddr;
fn service_queues(_: Weight) -> Weight {
Weight::zero()
}
}
pub trait EnqueueMessage<Origin: MaxEncodedLen> {
type MaxMessageLen: Get<u32>;
fn enqueue_message(message: BoundedSlice<u8, Self::MaxMessageLen>, origin: Origin);
fn enqueue_messages<'a>(
messages: impl Iterator<Item = BoundedSlice<'a, u8, Self::MaxMessageLen>>,
origin: Origin,
);
fn sweep_queue(origin: Origin);
}
impl<Origin: MaxEncodedLen> EnqueueMessage<Origin> for () {
type MaxMessageLen = ConstU32<0>;
fn enqueue_message(_: BoundedSlice<u8, Self::MaxMessageLen>, _: Origin) {}
fn enqueue_messages<'a>(
_: impl Iterator<Item = BoundedSlice<'a, u8, Self::MaxMessageLen>>,
_: Origin,
) {
}
fn sweep_queue(_: Origin) {}
}
#[derive(Default, Copy, Clone, Eq, PartialEq, Debug)]
pub struct QueueFootprint {
pub pages: u32,
pub ready_pages: u32,
pub storage: Footprint,
}
#[derive(Default, Copy, Clone, PartialEq, Debug)]
pub struct BatchFootprint {
pub msgs_count: usize,
pub size_in_bytes: usize,
pub new_pages_count: u32,
}
#[derive(Default, Debug)]
pub struct BatchesFootprints {
pub first_page_pos: usize,
pub footprints: Vec<BatchFootprint>,
}
impl BatchesFootprints {
pub fn push(&mut self, msg: &[u8], new_page: bool) {
let previous_footprint =
self.footprints.last().map(|footprint| *footprint).unwrap_or_default();
let mut new_pages_count = previous_footprint.new_pages_count;
if new_page {
new_pages_count = new_pages_count.saturating_add(1);
}
self.footprints.push(BatchFootprint {
msgs_count: previous_footprint.msgs_count.saturating_add(1),
size_in_bytes: previous_footprint.size_in_bytes.saturating_add(msg.len()),
new_pages_count,
});
}
pub fn search_best_by<F>(&self, f: F) -> &BatchFootprint
where
F: FnMut(&BatchFootprint) -> Ordering,
{
let maybe_best_idx = match self.footprints.binary_search_by(f) {
Ok(last_ok_idx) => Some(last_ok_idx),
Err(first_err_idx) => first_err_idx.checked_sub(1),
};
if let Some(best_idx) = maybe_best_idx {
match self.footprints.get(best_idx) {
Some(best_footprint) => return best_footprint,
None => {
defensive!("Invalid best_batch_idx: {}", best_idx);
},
}
}
&BatchFootprint { msgs_count: 0, size_in_bytes: 0, new_pages_count: 0 }
}
}
pub trait QueueFootprintQuery<Origin> {
type MaxMessageLen: Get<u32>;
fn footprint(origin: Origin) -> QueueFootprint;
fn get_batches_footprints<'a>(
origin: Origin,
msgs: impl Iterator<Item = BoundedSlice<'a, u8, Self::MaxMessageLen>>,
total_pages_limit: u32,
) -> BatchesFootprints;
}
impl<Origin: MaxEncodedLen> QueueFootprintQuery<Origin> for () {
type MaxMessageLen = ConstU32<0>;
fn footprint(_: Origin) -> QueueFootprint {
QueueFootprint::default()
}
fn get_batches_footprints<'a>(
_origin: Origin,
_msgs: impl Iterator<Item = BoundedSlice<'a, u8, Self::MaxMessageLen>>,
_total_pages_limit: u32,
) -> BatchesFootprints {
BatchesFootprints::default()
}
}
pub struct TransformOrigin<E, O, N, C>(PhantomData<(E, O, N, C)>);
impl<E: EnqueueMessage<O>, O: MaxEncodedLen, N: MaxEncodedLen, C: Convert<N, O>> EnqueueMessage<N>
for TransformOrigin<E, O, N, C>
{
type MaxMessageLen = E::MaxMessageLen;
fn enqueue_message(message: BoundedSlice<u8, Self::MaxMessageLen>, origin: N) {
E::enqueue_message(message, C::convert(origin));
}
fn enqueue_messages<'a>(
messages: impl Iterator<Item = BoundedSlice<'a, u8, Self::MaxMessageLen>>,
origin: N,
) {
E::enqueue_messages(messages, C::convert(origin));
}
fn sweep_queue(origin: N) {
E::sweep_queue(C::convert(origin));
}
}
impl<E: QueueFootprintQuery<O>, O: MaxEncodedLen, N: MaxEncodedLen, C: Convert<N, O>>
QueueFootprintQuery<N> for TransformOrigin<E, O, N, C>
{
type MaxMessageLen = E::MaxMessageLen;
fn footprint(origin: N) -> QueueFootprint {
E::footprint(C::convert(origin))
}
fn get_batches_footprints<'a>(
origin: N,
msgs: impl Iterator<Item = BoundedSlice<'a, u8, Self::MaxMessageLen>>,
total_pages_limit: u32,
) -> BatchesFootprints {
E::get_batches_footprints(C::convert(origin), msgs, total_pages_limit)
}
}
pub trait HandleMessage {
type MaxMessageLen: Get<u32>;
fn handle_message(message: BoundedSlice<u8, Self::MaxMessageLen>);
fn handle_messages<'a>(
messages: impl Iterator<Item = BoundedSlice<'a, u8, Self::MaxMessageLen>>,
);
fn sweep_queue();
}
pub struct EnqueueWithOrigin<E, O>(PhantomData<(E, O)>);
impl<E: EnqueueMessage<O::Type>, O: TypedGet> HandleMessage for EnqueueWithOrigin<E, O>
where
O::Type: MaxEncodedLen,
{
type MaxMessageLen = E::MaxMessageLen;
fn handle_message(message: BoundedSlice<u8, Self::MaxMessageLen>) {
E::enqueue_message(message, O::get());
}
fn handle_messages<'a>(
messages: impl Iterator<Item = BoundedSlice<'a, u8, Self::MaxMessageLen>>,
) {
E::enqueue_messages(messages, O::get());
}
fn sweep_queue() {
E::sweep_queue(O::get());
}
}
pub trait QueuePausedQuery<Origin> {
fn is_paused(origin: &Origin) -> bool;
}
#[impl_trait_for_tuples::impl_for_tuples(8)]
impl<Origin> QueuePausedQuery<Origin> for Tuple {
fn is_paused(origin: &Origin) -> bool {
for_tuples!( #(
if Tuple::is_paused(origin) {
return true;
}
)* );
false
}
}