Skip to content

Commit

Permalink
Buffer pool examples
Browse files Browse the repository at this point in the history
  • Loading branch information
rrybarczyk committed Dec 10, 2024
1 parent 59b2a40 commit 869d56f
Show file tree
Hide file tree
Showing 3 changed files with 178 additions and 0 deletions.
40 changes: 40 additions & 0 deletions utils/buffer/examples/basic_buffer_pool.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// # Simple `BufferPool` Usage
//
// This example showcases how to:
// 1. Creating a `BufferPool`.
// 2. Obtaining a writable buffer.
// 3. Writing data into the buffer.
// 4. Retrieving the data as a referenced slice.
// 5. Retrieving the data as an owned slice.
//
// # Run
//
// ```
// cargo run --example basic_buffer_pool
// ```

use buffer_sv2::{Buffer, BufferPool};

fn main() {
// Create a new BufferPool with a capacity of 32 bytes
let mut buffer_pool = BufferPool::new(32);

// Get a writable buffer from the pool
let data_to_write = b"Ciao, mundo!"; // 12 bytes
let writable = buffer_pool.get_writable(data_to_write.len());

// Write data (12 bytes) into the buffer.
writable.copy_from_slice(data_to_write);
assert_eq!(buffer_pool.len(), 12);

// Retrieve the data as a referenced slice
let _data_slice = buffer_pool.get_data_by_ref(12);
assert_eq!(buffer_pool.len(), 12);

// Retrieve the data as an owned slice
let data_slice = buffer_pool.get_data_owned();
assert_eq!(buffer_pool.len(), 0);

let expect = [67, 105, 97, 111, 44, 32, 109, 117, 110, 100, 111, 33]; // "Ciao, mundo!" ASCII
assert_eq!(data_slice.as_ref(), expect);
}
80 changes: 80 additions & 0 deletions utils/buffer/examples/buffer_pool_exhaustion.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
// # Handling Buffer Pool Exhaustion and Heap Allocation
//
// This example demonstrates how a buffer pool is filled. The back slots of the buffer pool are
// exhausted first, followed by the front of the buffer pool. Once both the back and front are
// exhausted, data is allocated on the heap at a performance decrease.
//
// 1. Fills up the back slots of the buffer pool until they’re exhausted.
// 2. Releases one slot to allow the buffer pool to switch to front mode.
// 3. Fully fills the front slots of the buffer pool.
// 4. Switches to alloc mode for direct heap allocation when both the buffer pool's back and front
// slots are at capacity.
//
// Below is a visual representation of how the buffer pool evolves as the example progresses:
//
// -------- BACK MODE
// a------- BACK MODE (add "a" via loop)
// aa------ BACK MODE (add "a" via loop)
// aaa----- BACK MODE (add "a" via loop)
// aaaa---- BACK MODE (add "a" via loop)
// -aaa---- BACK MODE (pop front)
// -aaab--- BACK MODE (add "b")
// -aaabc-- BACK MODE (add "c" via loop)
// -aaabcc- BACK MODE (add "c" via loop)
// -aaabccc BACK MODE (add "c" via loop)
// caaabccc BACK MODE (add "c" via loop, which gets added via front mode)
// caaabccc ALLOC MODE (add "d", allocated in a new space in the heap)
//
// # Run
//
// ```
// cargo run --example buffer_pool_exhaustion
// ```

use buffer_sv2::{Buffer, BufferPool};
use std::collections::VecDeque;

fn main() {
// 8 byte capacity
let mut buffer_pool = BufferPool::new(8);
let mut slices = VecDeque::new();

// Write data to fill back slots
for _ in 0..4 {
let data_bytes = b"a"; // 1 byte
let writable = buffer_pool.get_writable(data_bytes.len()); // Mutable slice to internal
// buffer
writable.copy_from_slice(data_bytes);
let data_slice = buffer_pool.get_data_owned(); // Take ownership of allocated segment
slices.push_back(data_slice);
}
assert!(buffer_pool.is_back_mode());

// Release one slice and add another in the back (one slice in back mode must be free to switch
// to front mode)
slices.pop_front(); // Free the slice's associated segment in the buffer pool
let data_bytes = b"b"; // 1 byte
let writable = buffer_pool.get_writable(data_bytes.len());
writable.copy_from_slice(data_bytes);
let data_slice = buffer_pool.get_data_owned();
slices.push_back(data_slice);
assert!(buffer_pool.is_back_mode()); // Still in back mode

// Write data to switch to front mode
for _ in 0..4 {
let data_bytes = b"c"; // 1 byte
let writable = buffer_pool.get_writable(data_bytes.len());
writable.copy_from_slice(data_bytes);
let data_slice = buffer_pool.get_data_owned();
slices.push_back(data_slice);
}
assert!(buffer_pool.is_front_mode()); // Confirm front mode

// Add another slice, causing a switch to alloc mode
let data_bytes = b"d"; // 1 byte
let writable = buffer_pool.get_writable(data_bytes.len());
writable.copy_from_slice(data_bytes);
let data_slice = buffer_pool.get_data_owned();
slices.push_back(data_slice);
assert!(buffer_pool.is_alloc_mode());
}
58 changes: 58 additions & 0 deletions utils/buffer/examples/variable_sized_messages.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// # Handling Variable-Sized Messages
//
// This example demonstrates how to the `BufferPool` handles messages of varying sizes.
//
// # Run
//
// ```
// cargo run --example variable_sized_messages
// ```

use buffer_sv2::{Buffer, BufferPool};
use std::collections::VecDeque;

fn main() {
// Initialize a BufferPool with a capacity of 32 bytes
let mut buffer_pool = BufferPool::new(32);
let mut slices = VecDeque::new();

// Function to write data to the buffer pool and store the slice
let write_data = |pool: &mut BufferPool<_>, data: &[u8], slices: &mut VecDeque<_>| {
let writable = pool.get_writable(data.len());
writable.copy_from_slice(data);
let data_slice = pool.get_data_owned();
slices.push_back(data_slice);
println!("{:?}", &pool);
println!("");
};

// Write a small message to the first slot
let small_message = b"Hello";
write_data(&mut buffer_pool, small_message, &mut slices);
assert!(buffer_pool.is_back_mode());
assert_eq!(slices.back().unwrap().as_ref(), small_message);

// Write a medium-sized message to the second slot
let medium_message = b"Rust programming";
write_data(&mut buffer_pool, medium_message, &mut slices);
assert!(buffer_pool.is_back_mode());
assert_eq!(slices.back().unwrap().as_ref(), medium_message);

// Write a large message that exceeds the remaining pool capacity
let large_message = b"This message is larger than the remaining buffer pool capacity.";
write_data(&mut buffer_pool, large_message, &mut slices);
assert!(buffer_pool.is_alloc_mode());
assert_eq!(slices.back().unwrap().as_ref(), large_message);

while let Some(slice) = slices.pop_front() {
drop(slice);
}

// Write another small message
let another_small_message = b"Hi";
write_data(&mut buffer_pool, another_small_message, &mut slices);
assert_eq!(slices.back().unwrap().as_ref(), another_small_message);

// Verify that the buffer pool has returned to back mode for the last write
assert!(buffer_pool.is_back_mode());
}

0 comments on commit 869d56f

Please sign in to comment.