#![cfg(unix)]
mod common;
use readcon_core::iterators::{frames_from_bytes, frames_from_text};
use readcon_core::types::ConFrame;
use std::alloc::Layout;
use std::path::Path;
use std::ptr::NonNull;
const COPIES: usize = 64;
const THREADS: [usize; 3] = [1, 2, 8];
struct PageAligned {
ptr: NonNull<u8>,
layout: Layout,
}
impl PageAligned {
fn copy_of(bytes: &[u8]) -> Self {
let page = unsafe { libc::sysconf(libc::_SC_PAGESIZE) };
let page = usize::try_from(page).expect("page size");
let layout = Layout::from_size_align(bytes.len(), page).expect("layout");
assert_ne!(layout.size(), 0);
let ptr = NonNull::new(unsafe { std::alloc::alloc(layout) }).expect("allocation");
unsafe { std::ptr::copy_nonoverlapping(bytes.as_ptr(), ptr.as_ptr(), bytes.len()) };
Self { ptr, layout }
}
fn bytes(&self) -> &[u8] {
unsafe { std::slice::from_raw_parts(self.ptr.as_ptr(), self.layout.size()) }
}
}
impl Drop for PageAligned {
fn drop(&mut self) {
unsafe { std::alloc::dealloc(self.ptr.as_ptr(), self.layout) };
}
}
fn check(what: &str, frames: Result<Vec<ConFrame>, impl std::fmt::Debug>, expected: &[ConFrame]) {
let frames = frames.unwrap_or_else(|e| panic!("{what}: {e:?}"));
assert_eq!(frames.len(), expected.len(), "{what}");
for (i, (got, want)) in frames.iter().zip(expected).enumerate() {
assert!(got == want, "{what}: frame {i} differs");
}
}
fn changed_bytes(buffer: &[u8], original: &[u8]) -> usize {
buffer.iter().zip(original).filter(|(a, b)| a != b).count()
}
#[test]
fn readers_leave_a_page_aligned_buffer_unchanged() {
let text = std::fs::read_to_string(test_case!("cuh2.con"))
.expect("fixture")
.repeat(COPIES);
let expected = frames_from_text(&text, Some(1)).expect("one-thread string read");
assert_eq!(expected.len(), COPIES);
let buffer = PageAligned::copy_of(text.as_bytes());
assert_eq!(buffer.bytes().as_ptr() as usize % buffer.layout.align(), 0);
for threads in THREADS {
let what = format!("byte read, {threads} threads");
let frames = frames_from_bytes(buffer.bytes(), Some(threads));
assert_eq!(changed_bytes(buffer.bytes(), text.as_bytes()), 0, "{what}");
check(&what, frames, &expected);
let what = format!("string read, {threads} threads");
let borrowed = std::str::from_utf8(buffer.bytes()).expect("utf8");
let frames = frames_from_text(borrowed, Some(threads));
assert_eq!(changed_bytes(buffer.bytes(), text.as_bytes()), 0, "{what}");
check(&what, frames, &expected);
}
}