use anyhow::Result;
use bytes::BytesMut;
pub trait RendezvousWrite {
fn write_chunk(&mut self, offset: usize, data: &[u8]) -> Result<()>;
fn capacity(&self) -> usize;
fn rdma_destination(&mut self) -> Option<RdmaDestination<'_>> {
None
}
}
pub struct RdmaDestination<'a> {
region_id: u64,
offset: u64,
capacity: u64,
#[cfg(all(target_os = "linux", feature = "ucx"))]
hold: Option<super::rdma::TransferHold>,
marker: std::marker::PhantomData<&'a mut [u8]>,
}
impl RdmaDestination<'_> {
#[cfg(all(target_os = "linux", feature = "ucx"))]
pub(crate) fn held(
region_id: u64,
offset: u64,
capacity: u64,
hold: super::rdma::TransferHold,
) -> Self {
Self {
region_id,
offset,
capacity,
hold: Some(hold),
marker: std::marker::PhantomData,
}
}
#[cfg(all(target_os = "linux", feature = "ucx"))]
pub(crate) fn take_hold(&mut self) -> Option<super::rdma::TransferHold> {
self.hold.take()
}
pub fn region_id(&self) -> u64 {
self.region_id
}
pub fn offset(&self) -> u64 {
self.offset
}
pub fn capacity(&self) -> u64 {
self.capacity
}
}
impl std::fmt::Debug for RdmaDestination<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("RdmaDestination")
.field("region_id", &self.region_id)
.field("offset", &self.offset)
.field("capacity", &self.capacity)
.finish()
}
}
impl RendezvousWrite for &mut [u8] {
fn write_chunk(&mut self, offset: usize, data: &[u8]) -> Result<()> {
let end = offset + data.len();
if end > self.len() {
anyhow::bail!(
"write_chunk out of bounds: offset={offset}, len={}, capacity={}",
data.len(),
self.len()
);
}
self[offset..end].copy_from_slice(data);
Ok(())
}
fn capacity(&self) -> usize {
self.len()
}
}
impl RendezvousWrite for BytesMut {
fn write_chunk(&mut self, offset: usize, data: &[u8]) -> Result<()> {
let end = offset + data.len();
if end > self.len() {
self.resize(end, 0);
}
self[offset..end].copy_from_slice(data);
Ok(())
}
fn capacity(&self) -> usize {
BytesMut::capacity(self)
}
}
impl RendezvousWrite for Vec<u8> {
fn write_chunk(&mut self, offset: usize, data: &[u8]) -> Result<()> {
let end = offset + data.len();
if end > self.len() {
self.resize(end, 0);
}
self[offset..end].copy_from_slice(data);
Ok(())
}
fn capacity(&self) -> usize {
Vec::capacity(self)
}
}
#[cfg(all(target_os = "linux", feature = "ucx"))]
pub struct PinnedWriter {
buf: super::rdma::PinnedBuf,
}
#[cfg(all(target_os = "linux", feature = "ucx"))]
impl PinnedWriter {
pub(crate) fn new(buf: super::rdma::PinnedBuf) -> Self {
Self { buf }
}
pub fn len(&self) -> usize {
self.buf.len()
}
pub fn is_empty(&self) -> bool {
self.buf.is_empty()
}
pub fn as_slice(&self) -> &[u8] {
&self.buf
}
pub fn into_inner(self) -> super::rdma::PinnedBuf {
self.buf
}
}
#[cfg(all(target_os = "linux", feature = "ucx"))]
impl std::ops::Deref for PinnedWriter {
type Target = [u8];
fn deref(&self) -> &[u8] {
&self.buf
}
}
#[cfg(all(target_os = "linux", feature = "ucx"))]
impl std::fmt::Debug for PinnedWriter {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("PinnedWriter")
.field("buf", &self.buf)
.finish()
}
}
#[cfg(all(target_os = "linux", feature = "ucx"))]
impl RendezvousWrite for PinnedWriter {
fn write_chunk(&mut self, offset: usize, data: &[u8]) -> Result<()> {
let end = offset
.checked_add(data.len())
.ok_or_else(|| anyhow::anyhow!("write_chunk offset overflow"))?;
if end > self.buf.len() {
anyhow::bail!(
"write_chunk out of bounds: offset={offset}, len={}, capacity={}",
data.len(),
self.buf.len()
);
}
self.buf[offset..end].copy_from_slice(data);
Ok(())
}
fn capacity(&self) -> usize {
self.buf.len()
}
fn rdma_destination(&mut self) -> Option<RdmaDestination<'_>> {
Some(RdmaDestination::held(
self.buf.backend_region_id(),
self.buf.arena_offset(),
self.buf.len() as u64,
self.buf.hold(),
))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn ordinary_destinations_offer_no_rdma_destination() {
let mut buf = vec![0u8; 16];
assert!(buf.rdma_destination().is_none());
let mut bytes = BytesMut::with_capacity(16);
assert!(bytes.rdma_destination().is_none());
let mut owned = vec![0u8; 16];
let mut slice: &mut [u8] = &mut owned;
assert!(slice.rdma_destination().is_none());
}
#[test]
fn test_slice_write_chunk() {
let mut buf = vec![0u8; 16];
let mut slice: &mut [u8] = &mut buf;
slice.write_chunk(0, &[1, 2, 3, 4]).unwrap();
slice.write_chunk(4, &[5, 6, 7, 8]).unwrap();
assert_eq!(&buf[..8], &[1, 2, 3, 4, 5, 6, 7, 8]);
}
#[test]
fn test_slice_write_out_of_bounds() {
let mut buf = vec![0u8; 4];
let mut slice: &mut [u8] = &mut buf;
assert!(slice.write_chunk(2, &[1, 2, 3]).is_err());
}
#[test]
fn test_bytesmut_write_chunk_auto_resize() {
let mut buf = BytesMut::with_capacity(4);
buf.resize(4, 0);
buf.write_chunk(0, &[1, 2]).unwrap();
buf.write_chunk(4, &[5, 6, 7, 8]).unwrap();
assert_eq!(&buf[..], &[1, 2, 0, 0, 5, 6, 7, 8]);
}
#[test]
fn test_vec_write_chunk_auto_resize() {
let mut buf = vec![0u8; 4];
buf.write_chunk(0, &[1, 2]).unwrap();
buf.write_chunk(4, &[5, 6]).unwrap();
assert_eq!(&buf, &[1, 2, 0, 0, 5, 6]);
}
}