use crate::*;
use hrx::View;
pub struct DeviceExclusions {
bitmap: Buffer,
rows: usize,
device: usize,
update: Kernel,
}
impl DeviceExclusions {
pub fn new(stream: &Stream, rows: usize) -> Result<Self> {
if rows > MAX_ROWS {
return Err(invalid("exclusion set exceeds 2^30 rows"));
}
let compiler = hrx::loom::Compiler::for_stream(None, stream)?;
let (update, _) = compile(
&compiler,
stream,
include_str!("../kernels/exclusions.loom"),
kernels::named_spec("update_exclusions"),
)?;
let bitmap = stream.allocate_zeroed(rows.div_ceil(32) * 4)?;
Ok(Self {
bitmap,
rows,
device: stream.device_id(),
update,
})
}
pub fn binding(&self) -> View<'_> {
self.bitmap.binding()
}
pub fn len(&self) -> usize {
self.rows
}
pub fn is_empty(&self) -> bool {
self.rows == 0
}
pub fn memory_usage(&self) -> usize {
self.bitmap.bytes()
}
fn check(&self, stream: &Stream) -> Result<()> {
if stream.device_id() != self.device {
return Err(invalid("exclusion set belongs to another device"));
}
Ok(())
}
pub fn clear(&mut self, stream: &Stream) -> Result<()> {
self.check(stream)?;
stream.fill(self.bitmap.binding(), 0)
}
pub fn insert(&mut self, stream: &mut Stream, ids: &[u32]) -> Result<()> {
self.update_host(stream, ids, false)
}
pub fn remove(&mut self, stream: &mut Stream, ids: &[u32]) -> Result<()> {
self.update_host(stream, ids, true)
}
fn update_host(&mut self, stream: &mut Stream, ids: &[u32], remove: bool) -> Result<()> {
self.check(stream)?;
if ids.len() > MAX_ROWS || ids.iter().any(|&id| id as usize >= self.rows) {
return Err(invalid("excluded ID is outside the corpus"));
}
if ids.is_empty() {
return Ok(());
}
let buffer = stream.allocate(ids.len() * 4)?;
stream.upload(
buffer.binding(),
&ids.iter()
.flat_map(|id| id.to_le_bytes())
.collect::<Vec<_>>(),
)?;
self.update_device(stream, buffer.binding(), ids.len(), remove)
}
pub fn insert_device(&mut self, stream: &Stream, ids: View<'_>, count: usize) -> Result<()> {
self.update_device(stream, ids, count, false)
}
pub fn remove_device(&mut self, stream: &Stream, ids: View<'_>, count: usize) -> Result<()> {
self.update_device(stream, ids, count, true)
}
fn update_device(
&mut self,
stream: &Stream,
ids: View<'_>,
count: usize,
remove: bool,
) -> Result<()> {
self.check(stream)?;
if count > MAX_ROWS {
return Err(invalid("too many exclusion updates"));
}
let ids = ids.slice(0, count * 4)?;
if count == 0 || self.rows == 0 {
return Ok(());
}
let mut c = Constants::new();
c.push(count as u32)?;
c.push(self.rows as u32)?;
c.push(u32::from(remove))?;
unsafe {
stream.dispatch(
&self.update,
[count.div_ceil(256) as u32, 1, 1],
[256, 1, 1],
&c,
&[ids, self.bitmap.binding()],
)
}
}
}