use {
agave_scheduler_bindings::{
TransactionResponseRegion,
worker_message_types::{
self, CHECK_RESPONSE, CheckResponse, EXECUTION_RESPONSE, ExecutionResponse,
},
},
rts_alloc::Allocator,
std::ptr::NonNull,
};
pub fn execution_responses_from_iter(
allocator: &Allocator,
iter: impl ExactSizeIterator<Item = ExecutionResponse>,
) -> Option<TransactionResponseRegion> {
unsafe { from_iterator(allocator, EXECUTION_RESPONSE, iter) }
}
pub fn resolve_responses_from_iter(
allocator: &Allocator,
iter: impl ExactSizeIterator<Item = CheckResponse>,
) -> Option<TransactionResponseRegion> {
unsafe { from_iterator(allocator, CHECK_RESPONSE, iter) }
}
pub fn allocate_check_response_region(
allocator: &Allocator,
num_transaction_responses: usize,
) -> Option<(NonNull<CheckResponse>, TransactionResponseRegion)> {
unsafe {
allocate_response_region::<CheckResponse>(
allocator,
CHECK_RESPONSE,
num_transaction_responses,
)
}
}
unsafe fn allocate_response_region<T: Sized>(
allocator: &Allocator,
tag: u8,
num_transaction_responses: usize,
) -> Option<(NonNull<T>, TransactionResponseRegion)> {
let size = num_transaction_responses.wrapping_mul(core::mem::size_of::<T>());
let response_ptr = allocator.allocate(size as u32)?.cast::<T>();
debug_assert!(
response_ptr.is_aligned(),
"allocator should guarantee alignment for the response types of interest"
);
let transaction_responses_offset = unsafe { allocator.offset(response_ptr.cast()) };
Some((
response_ptr,
TransactionResponseRegion {
tag,
num_transaction_responses: num_transaction_responses as u8,
transaction_responses_offset,
},
))
}
unsafe fn from_iterator<T: Sized>(
allocator: &Allocator,
tag: u8,
iter: impl ExactSizeIterator<Item = T>,
) -> Option<TransactionResponseRegion> {
let num_transaction_responses = iter.len();
let (response_ptr, region) =
unsafe { allocate_response_region(allocator, tag, num_transaction_responses)? };
for (index, response) in iter.enumerate() {
unsafe { response_ptr.add(index).write(response) };
}
Some(region)
}
#[derive(Debug)]
pub struct CheckResponsesPtr {
ptr: NonNull<CheckResponse>,
count: usize,
}
impl CheckResponsesPtr {
pub unsafe fn from_raw_parts(ptr: NonNull<CheckResponse>, count: usize) -> Self {
Self { ptr, count }
}
pub unsafe fn from_transaction_response_region(
transaction_response_region: &TransactionResponseRegion,
allocator: &Allocator,
) -> Self {
debug_assert!(transaction_response_region.tag == worker_message_types::CHECK_RESPONSE);
Self {
ptr: unsafe {
allocator.ptr_from_offset(transaction_response_region.transaction_responses_offset)
}
.cast(),
count: transaction_response_region.num_transaction_responses as usize,
}
}
pub const fn len(&self) -> usize {
self.count
}
pub const fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn iter(&self) -> impl Iterator<Item = &CheckResponse> {
unsafe { core::slice::from_raw_parts(self.ptr.as_ptr(), self.count) }.iter()
}
pub unsafe fn free(self, allocator: &Allocator) {
unsafe { allocator.free(self.ptr.cast()) }
}
}
#[derive(Debug)]
pub struct ExecutionResponsesPtr {
ptr: NonNull<ExecutionResponse>,
count: usize,
}
impl ExecutionResponsesPtr {
pub unsafe fn from_raw_parts(ptr: NonNull<ExecutionResponse>, count: usize) -> Self {
Self { ptr, count }
}
pub unsafe fn from_transaction_response_region(
transaction_response_region: &TransactionResponseRegion,
allocator: &Allocator,
) -> Self {
debug_assert!(transaction_response_region.tag == worker_message_types::EXECUTION_RESPONSE);
Self {
ptr: unsafe {
allocator.ptr_from_offset(transaction_response_region.transaction_responses_offset)
}
.cast(),
count: transaction_response_region.num_transaction_responses as usize,
}
}
pub const fn len(&self) -> usize {
self.count
}
pub const fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn iter(&self) -> impl Iterator<Item = &ExecutionResponse> {
unsafe { core::slice::from_raw_parts(self.ptr.as_ptr(), self.count) }.iter()
}
pub unsafe fn free(self, allocator: &Allocator) {
unsafe { allocator.free(self.ptr.cast()) }
}
}