Skip to content

Commit

Permalink
comments for sendable and stream
Browse files Browse the repository at this point in the history
  • Loading branch information
quackitsquinn committed May 16, 2024
1 parent 739a1d1 commit 51cedc4
Show file tree
Hide file tree
Showing 2 changed files with 38 additions and 9 deletions.
33 changes: 27 additions & 6 deletions lazuli_core/src/sendable.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,16 @@
//! A module for types that can be sent over the network.
//!
//! This module contains the Sendable trait, which is used to convert types to bytes that can be sent over the network.
//! The Sendable trait is implemented for all primitive types, and can be derived for custom types.
//!
//! Why use a Trait?
//! - The Sendable trait allows for any type that has stuff such as heap allocations to be sent over the network.
//! - The Sendable trait allows for optimization of the size of the type when sent over the network.
//! - This in particularly useful for Option<T> because it can be sent as a single byte.
//! - The Sendable trait allows for the type to be converted to bytes in a way that is easy to implement.
//!
//!
use core::slice;
use std::{
io::{self, Read},
Expand All @@ -10,21 +23,27 @@ use crate::header::PacketHeader;
use crate::Result;

/// A trait for types that can be sent over the network.
///
/// Sendable has the Debug bound because it is internally useful, and can be helpful for debugging.
pub trait Sendable: Sized + std::fmt::Debug {
/// Returns the header of the packet.
fn header(&self) -> PacketHeader<Self> {
unsafe { PacketHeader::new(self.size()) }
}

/// Returns the size of the type.
///
/// **This does not return the size of the type in memory, but the size of the type when sent over the network.**
fn size(&self) -> u32 {
std::mem::size_of::<Self>() as u32
}

/// Converts the type to a Vec<u8> that can be sent over the network.
fn send(&self) -> Vec<u8>;

/// Converts an incoming stream of bytes to the type.
fn recv(data: &mut dyn Read) -> Result<Self>;

/// Converts the type to a function that can be used to convert incoming data to the type.
/// Returns a Vec<u8> that is the type's representation in memory.
/// This is used as a hacky way to convert the type if the type cant be known at runtime.
Expand All @@ -33,12 +52,13 @@ pub trait Sendable: Sized + std::fmt::Debug {
let conversion = Box::new(Self::recv(data)?);
trace!("Converted to bytes: {:?}", conversion);
let as_slice_bytes = unsafe {
// We use a slice to get the bytes of the type. This is safe because we are using the size of the type to get the slice.
slice::from_raw_parts(
Box::leak(conversion) as *mut Self as *mut u8,
mem::size_of::<Self>(),
)
};
Ok(as_slice_bytes.into()) // well its good to know that impl Into<Box<[u8]>> for &[u8] is implemented.
Ok(as_slice_bytes.into())
}
}
}
Expand All @@ -48,13 +68,13 @@ macro_rules! impl_sendable_number {
impl Sendable for $t {
fn send(&self) -> Vec<u8> {
// Follow the standard of big-endian
<$t>::to_ne_bytes(*self).to_vec()
<$t>::to_be_bytes(*self).to_vec()
}

fn recv(data: &mut dyn Read,) -> Result<Self> {
let mut buffer = [0; std::mem::size_of::<$t>()];
data.read_exact(&mut buffer)?;
Ok(<$t>::from_ne_bytes(buffer))
Ok(<$t>::from_be_bytes(buffer))
}
}
};
Expand Down Expand Up @@ -85,6 +105,7 @@ impl Sendable for bool {
Ok(buffer[0] != 0)
}
}

impl<T> Sendable for Vec<T>
where
T: Sendable,
Expand All @@ -98,8 +119,7 @@ where
for item in self {
size += item.size();
}
// Safety: We have just calculated the size of the payload.
size + 4 // Add 4 bytes for the length of the vector.
size + 4
}

fn send(&self) -> Vec<u8> {
Expand Down Expand Up @@ -239,7 +259,8 @@ macro_rules! impl_sendable_tuple {
}
};
}
// gross
// Implement the Sendable trait for tuples of size 0 to 12.
// We don't go above 12 because A. A tuple of size 12 is already pretty big, and B. Debug implements up to 12.
impl_sendable_tuple!(A);
impl_sendable_tuple!(A B);
impl_sendable_tuple!(A B C);
Expand Down
14 changes: 11 additions & 3 deletions lazuli_core/src/stream.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
//! This module contains the Stream struct, which is used to receive data from a socket.
//!
//! Streams are how data is received. You create a stream for the type you want to receive, and then call recv() on the client or server to get the data.
//! Streams are used to receive data from a socket because they allow for type safety and easy access to the data.
use std::{
mem::{self, ManuallyDrop},
sync::{Arc, Mutex},
};

/// A stream of data received from a socket.
/// A stream of data received from a socket. This is used to receive data from a socket.
#[derive(Debug)]
pub struct Stream<T> {
data: Arc<Mutex<Vec<T>>>,
Expand All @@ -15,14 +20,15 @@ impl<T> Stream<T>
where
T: 'static,
{
/// Creates a new stream.
pub(crate) fn new() -> Self {
Stream {
data: Arc::new(Mutex::new(vec![])),
grew: Arc::new(Mutex::new(0)),
ptr: Arc::new(Mutex::new(std::ptr::null_mut())),
}
}

/// Checks if the stream has grown and replaces the vec with the new vec.
fn check_vec(&mut self) {
let mut grew_by = self.grew.lock().unwrap();
// Check if the stream was given any data.
Expand All @@ -46,13 +52,15 @@ where
/// Gets one item from the stream.
pub fn get(&mut self) -> Option<T> {
self.check_vec();
// We will *always* pop the data. If we retain ownership, things can go very wrong because of the way the stream is designed.
self.data.lock().unwrap().pop()
}

/// Gets the count of items in the stream.
pub fn len(&self) -> usize {
self.data.lock().unwrap().len() + *self.grew.lock().unwrap()
}
/// Gets a pointer to the underlying buffer.
/// Gets the underlying vec.
pub fn get_vec(&self) -> Arc<Mutex<Vec<T>>> {
self.data.clone()
}
Expand Down

0 comments on commit 51cedc4

Please sign in to comment.