#![allow(
clippy::unwrap_used,
clippy::expect_used,
clippy::indexing_slicing,
clippy::panic
)]
use std::alloc::{GlobalAlloc, Layout, System};
use std::sync::atomic::{AtomicUsize, Ordering};
use ws_kit::codec::Frame;
use ws_kit::hub::BroadcastHub;
static ALLOCATIONS: AtomicUsize = AtomicUsize::new(0);
struct Counting;
unsafe impl GlobalAlloc for Counting {
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
ALLOCATIONS.fetch_add(1, Ordering::Relaxed);
unsafe { System.alloc(layout) }
}
unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
unsafe { System.dealloc(ptr, layout) }
}
unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 {
ALLOCATIONS.fetch_add(1, Ordering::Relaxed);
unsafe { System.alloc_zeroed(layout) }
}
unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 {
ALLOCATIONS.fetch_add(1, Ordering::Relaxed);
unsafe { System.realloc(ptr, layout, new_size) }
}
}
#[global_allocator]
static GLOBAL: Counting = Counting;
fn allocations() -> usize {
ALLOCATIONS.load(Ordering::Relaxed)
}
const PAYLOAD: &str = "alloc-probe payload";
const ITERATIONS: usize = 100;
#[test]
fn frame_rx_allocation_profile() {
let text = Frame::from(PAYLOAD.to_string());
let bin = Frame::Binary(bytes::Bytes::from_static(b"owned"));
let before = allocations();
let moved_text = text; let moved_bin = bin.clone();
std::hint::black_box((&moved_text, &moved_bin));
assert_eq!(
allocations(),
before,
"owned-payload Frame conversion/move must not allocate"
);
let hub = BroadcastHub::<Frame>::new(64);
let mut rx = hub.subscribe();
assert!(hub
.broadcast(Frame::Binary(bytes::Bytes::from_static(b"warm")))
.is_ok());
assert!(rx.try_recv().is_ok(), "warm-up recv must deliver");
let before = allocations();
for _ in 0..ITERATIONS {
assert!(hub.broadcast(bin.clone()).is_ok());
assert!(rx.try_recv().is_ok());
}
assert_eq!(
allocations(),
before,
"binary rx steady state must not allocate (before: {before}, after: {})",
allocations()
);
let mut rx = hub.subscribe();
assert!(hub.broadcast(Frame::from("warm")).is_ok());
assert!(rx.try_recv().is_ok(), "warm-up recv must deliver");
let before = allocations();
for _ in 0..ITERATIONS {
assert!(hub.broadcast(Frame::from(PAYLOAD.to_string())).is_ok());
}
while rx.try_recv().is_ok() {}
assert_eq!(
allocations(),
before + ITERATIONS,
"broadcast must allocate only the caller's message value (1 per iteration)"
);
let mut fresh = hub.subscribe();
assert!(hub.broadcast(Frame::from(PAYLOAD.to_string())).is_ok());
let before = allocations();
assert!(fresh.try_recv().is_ok());
assert_eq!(
allocations(),
before + 1,
"text recv = exactly one allocation (the String clone)"
);
}
#[test]
fn allocation_counter_sanity() {
let before = allocations();
let leak = format!("fresh-{before}");
assert!(
allocations() > before,
"String::from must allocate (counter sanity check)"
);
std::hint::black_box(leak);
}