use bincode::{Decode, Encode, config, decode_from_slice, serde::encode_to_vec};
use half::f16;
use indexmap::IndexSet;
use lz4_flex::{compress_prepend_size, decompress_size_prepended};
use memmap2::MmapOptions;
use rayon::prelude::*;
use rustc_hash::{FxHashMap, FxHashSet};
use serde::{Deserialize, Serialize};
use std::{
fs::File,
io::{BufWriter, Seek, SeekFrom, Write},
marker::Sync,
path::Path,
sync::Arc,
};
use crate::prelude::*;
#[derive(Clone, Debug)]
#[allow(dead_code)]
pub struct CellQuality {
pub cell_indices: Vec<usize>,
pub gene_indices: Vec<usize>,
pub lib_size: Vec<usize>,
pub no_genes: Vec<usize>,
}
impl CellQuality {
pub fn set_cell_indices(&mut self, cell_indices: &[usize]) {
self.cell_indices = cell_indices.to_vec();
}
pub fn set_gene_indices(&mut self, gene_indices: &[usize]) {
self.gene_indices = gene_indices.to_vec();
}
}
#[derive(Clone, Debug)]
pub struct MinCellQuality {
pub min_unique_genes: usize,
pub min_lib_size: usize,
pub min_cells: usize,
pub target_size: f32,
}
#[derive(Debug, Clone)]
pub struct CellOnFileQuality {
pub cells_to_keep: Vec<usize>,
pub genes_to_keep: Vec<usize>,
pub cells_to_keep_set: FxHashSet<usize>,
pub genes_to_keep_set: FxHashSet<usize>,
pub cell_old_to_new: FxHashMap<usize, usize>,
pub gene_old_to_new: FxHashMap<usize, usize>,
}
impl CellOnFileQuality {
pub fn new(cells_to_keep: Vec<usize>, genes_to_keep: Vec<usize>) -> Self {
Self {
cells_to_keep,
genes_to_keep,
cells_to_keep_set: FxHashSet::default(),
genes_to_keep_set: FxHashSet::default(),
cell_old_to_new: FxHashMap::default(),
gene_old_to_new: FxHashMap::default(),
}
}
pub fn generate_maps_sets(&mut self) {
let cells_to_keep_set: FxHashSet<usize> = self.cells_to_keep.iter().cloned().collect();
let genes_to_keep_set: FxHashSet<usize> = self.genes_to_keep.iter().cloned().collect();
let cell_old_to_new: FxHashMap<usize, usize> = self
.cells_to_keep
.iter()
.enumerate()
.map(|(new_idx, &old_idx)| (old_idx, new_idx))
.collect();
let gene_old_to_new: FxHashMap<usize, usize> = self
.genes_to_keep
.iter()
.enumerate()
.map(|(new_idx, &old_idx)| (old_idx, new_idx))
.collect();
self.cells_to_keep_set = cells_to_keep_set;
self.genes_to_keep_set = genes_to_keep_set;
self.cell_old_to_new = cell_old_to_new;
self.gene_old_to_new = gene_old_to_new
}
}
#[derive(Debug)]
pub struct CsrCellChunk {
pub data_raw: Vec<u16>,
pub data_norm: Vec<F16>,
pub library_size: usize,
pub indices: Vec<u16>,
pub original_index: usize,
pub to_keep: bool,
}
impl CsrCellChunk {
pub fn from_data<T, U>(
data: &[T],
col_idx: &[U],
original_index: usize,
size_factor: f32,
to_keep: bool,
) -> Self
where
T: ToF32AndU16,
U: ToF32AndU16,
{
let data_f32 = data.iter().map(|&x| x.to_f32()).collect::<Vec<f32>>();
let sum = data_f32.iter().sum::<f32>();
let data_norm: Vec<F16> = data_f32
.into_iter()
.map(|x| {
let norm = (x / sum * size_factor).ln_1p();
F16::from(f16::from_f32(norm))
})
.collect();
Self {
data_raw: data.iter().map(|&x| x.to_u16()).collect::<Vec<u16>>(),
data_norm,
library_size: sum as usize,
indices: col_idx.iter().map(|&x| x.to_u16()).collect::<Vec<u16>>(),
original_index,
to_keep,
}
}
pub fn write_to_bytes(&self, writer: &mut impl Write) -> std::io::Result<()> {
writer.write_all(&(self.data_raw.len() as u32).to_le_bytes())?;
writer.write_all(&(self.data_norm.len() as u32).to_le_bytes())?;
writer.write_all(&(self.indices.len() as u32).to_le_bytes())?;
writer.write_all(&(self.library_size as u64).to_le_bytes())?;
writer.write_all(&(self.original_index as u64).to_le_bytes())?;
writer.write_all(&[self.to_keep as u8, 0, 0, 0])?;
let data_raw_bytes = unsafe {
std::slice::from_raw_parts(self.data_raw.as_ptr() as *const u8, self.data_raw.len() * 2)
};
writer.write_all(data_raw_bytes)?;
let data_norm_bytes = unsafe {
std::slice::from_raw_parts(
self.data_norm.as_ptr() as *const u8,
self.data_norm.len() * 2,
)
};
writer.write_all(data_norm_bytes)?;
let col_indices_bytes = unsafe {
std::slice::from_raw_parts(self.indices.as_ptr() as *const u8, self.indices.len() * 2)
};
writer.write_all(col_indices_bytes)?;
Ok(())
}
pub fn read_from_buffer(buffer: &[u8]) -> std::io::Result<Self> {
if buffer.len() < 32 {
return Err(std::io::Error::new(
std::io::ErrorKind::InvalidData,
"Buffer too small for header",
));
}
let header = &buffer[0..32];
let data_raw_len =
u32::from_le_bytes([header[0], header[1], header[2], header[3]]) as usize;
let data_norm_len =
u32::from_le_bytes([header[4], header[5], header[6], header[7]]) as usize;
let col_indices_len =
u32::from_le_bytes([header[8], header[9], header[10], header[11]]) as usize;
let library_size = u64::from_le_bytes([
header[12], header[13], header[14], header[15], header[16], header[17], header[18],
header[19],
]) as usize;
let original_index = u64::from_le_bytes([
header[20], header[21], header[22], header[23], header[24], header[25], header[26],
header[27],
]) as usize;
let to_keep = header[28] != 0;
let data_start = 32;
let data_end = data_start + data_raw_len * 2;
let norm_end = data_end + data_norm_len * 2;
let data_raw = unsafe {
let ptr = buffer.as_ptr().add(data_start) as *const u16;
std::slice::from_raw_parts(ptr, data_raw_len).to_vec()
};
let data_norm: Vec<F16> = unsafe {
let ptr = buffer.as_ptr().add(data_end) as *const u16;
std::slice::from_raw_parts(ptr, data_norm_len)
.iter()
.map(|&bits| F16::from_bits(bits))
.collect()
};
let indices = unsafe {
let ptr = buffer.as_ptr().add(norm_end) as *const u16;
std::slice::from_raw_parts(ptr, col_indices_len).to_vec()
};
Ok(Self {
data_raw,
data_norm,
library_size,
indices,
original_index,
to_keep,
})
}
pub fn generate_chunks_sparse_data<T, U>(
sparse_data: CompressedSparseData<T, U>,
cell_qc: MinCellQuality,
) -> (Vec<CsrCellChunk>, CellQuality)
where
T: Clone + Default + Into<u32> + Sync,
U: Clone + Default,
{
let n_cells = sparse_data.indptr.len() - 1;
let n_genes = sparse_data.shape.1;
let mut no_cells_exp_gene = vec![0usize; n_genes];
for i in 0..n_cells {
let start_i = sparse_data.indptr[i];
let end_i = sparse_data.indptr[i + 1];
for &gene_idx in &sparse_data.indices[start_i..end_i] {
no_cells_exp_gene[gene_idx] += 1;
}
}
let genes_to_keep: Vec<usize> = (0..n_genes)
.filter(|&i| no_cells_exp_gene[i] >= cell_qc.min_cells)
.collect();
let mut gene_index_map = vec![None; n_genes];
for (new_idx, &old_idx) in genes_to_keep.iter().enumerate() {
gene_index_map[old_idx] = Some(new_idx);
}
let results: Vec<_> = (0..n_cells)
.into_par_iter()
.map(|i| {
let start_i = sparse_data.indptr[i];
let end_i = sparse_data.indptr[i + 1];
let mut filtered_data = Vec::new();
let mut filtered_indices = Vec::new();
for idx in start_i..end_i {
let old_gene_idx = sparse_data.indices[idx];
if let Some(new_gene_idx) = gene_index_map[old_gene_idx] {
filtered_data.push(sparse_data.data[idx].clone().into());
filtered_indices.push(new_gene_idx);
}
}
let sum_data_i = filtered_data.iter().sum::<u32>() as usize;
let nnz_i = filtered_indices.len();
let to_keep_i =
(nnz_i >= cell_qc.min_unique_genes) && (sum_data_i >= cell_qc.min_lib_size);
let chunk_i = CsrCellChunk::from_data(
&filtered_data,
&filtered_indices,
i,
cell_qc.target_size,
to_keep_i,
);
(chunk_i, sum_data_i, nnz_i, to_keep_i, i)
})
.collect();
let mut res = Vec::with_capacity(n_cells);
let mut lib_size = Vec::new();
let mut nnz = Vec::new();
let mut cells_to_keep = Vec::new();
for (chunk, lib, n, keep, idx) in results {
res.push(chunk);
if keep {
lib_size.push(lib);
nnz.push(n);
cells_to_keep.push(idx);
}
}
let qc_data = CellQuality {
cell_indices: cells_to_keep,
gene_indices: genes_to_keep,
lib_size,
no_genes: nnz,
};
(res, qc_data)
}
pub fn get_qc_info(&self) -> (usize, usize) {
(self.indices.len(), self.library_size)
}
pub fn filter_genes(&mut self, genes_to_keep: &[bool]) {
let mut index_map = vec![None; genes_to_keep.len()];
let mut new_idx = 0;
for (old_idx, &keep) in genes_to_keep.iter().enumerate() {
if keep {
index_map[old_idx] = Some(new_idx);
new_idx += 1;
}
}
let mut new_indices = Vec::new();
let mut new_raw = Vec::new();
let mut new_norm = Vec::new();
for (i, &gene_idx) in self.indices.iter().enumerate() {
if let Some(new_gene_idx) = index_map[gene_idx as usize] {
new_indices.push(new_gene_idx as u16);
new_raw.push(self.data_raw[i]);
new_norm.push(self.data_norm[i]);
}
}
self.indices = new_indices;
self.data_raw = new_raw;
self.data_norm = new_norm;
}
}
#[derive(Encode, Decode, Serialize, Deserialize, Debug)]
pub struct CscGeneChunk {
pub data_raw: Vec<u16>,
pub data_norm: Vec<F16>,
pub avg_exp: F16,
pub nnz: usize,
pub indices: Vec<u32>,
pub original_index: usize,
pub to_keep: bool,
}
impl CscGeneChunk {
pub fn from_conversion(
data_raw: &[u16],
data_norm: &[F16],
col_idx: &[usize],
original_index: usize,
to_keep: bool,
) -> Self {
let avg_exp = data_norm.iter().sum::<F16>();
let nnz = data_raw.len();
Self {
data_raw: data_raw.to_vec(),
data_norm: data_norm.to_vec(),
avg_exp,
nnz,
indices: col_idx.iter().map(|x| *x as u32).collect::<Vec<u32>>(),
original_index,
to_keep,
}
}
pub fn write_to_bytes(&self, writer: &mut impl Write) -> std::io::Result<()> {
writer.write_all(&(self.data_raw.len() as u32).to_le_bytes())?;
writer.write_all(&(self.data_norm.len() as u32).to_le_bytes())?;
writer.write_all(&(self.indices.len() as u32).to_le_bytes())?;
writer.write_all(&self.avg_exp.to_le_bytes())?;
writer.write_all(&[0, 0])?;
writer.write_all(&(self.nnz as u64).to_le_bytes())?;
writer.write_all(&(self.original_index as u64).to_le_bytes())?;
writer.write_all(&[self.to_keep as u8, 0, 0, 0])?;
let data_raw_bytes = unsafe {
std::slice::from_raw_parts(self.data_raw.as_ptr() as *const u8, self.data_raw.len() * 2)
};
writer.write_all(data_raw_bytes)?;
let data_norm_bytes = unsafe {
std::slice::from_raw_parts(
self.data_norm.as_ptr() as *const u8,
self.data_norm.len() * 2,
)
};
writer.write_all(data_norm_bytes)?;
let row_indices_bytes = unsafe {
std::slice::from_raw_parts(self.indices.as_ptr() as *const u8, self.indices.len() * 4)
};
writer.write_all(row_indices_bytes)?;
Ok(())
}
pub fn read_from_buffer(buffer: &[u8]) -> std::io::Result<Self> {
if buffer.len() < 36 {
return Err(std::io::Error::new(
std::io::ErrorKind::InvalidData,
"Buffer too small for header",
));
}
let header = &buffer[0..36];
let data_raw_len =
u32::from_le_bytes([header[0], header[1], header[2], header[3]]) as usize;
let data_norm_len =
u32::from_le_bytes([header[4], header[5], header[6], header[7]]) as usize;
let row_indices_len =
u32::from_le_bytes([header[8], header[9], header[10], header[11]]) as usize;
let avg_exp = F16::from_le_bytes([header[12], header[13]]);
let nnz = u64::from_le_bytes([
header[16], header[17], header[18], header[19], header[20], header[21], header[22],
header[23],
]) as usize;
let original_index = u64::from_le_bytes([
header[24], header[25], header[26], header[27], header[28], header[29], header[30],
header[31],
]) as usize;
let to_keep = header[32] != 0;
let data_start = 36;
let data_end = data_start + data_raw_len * 2;
let norm_end = data_end + data_norm_len * 2;
let data_raw = unsafe {
let ptr = buffer.as_ptr().add(data_start) as *const u16;
std::slice::from_raw_parts(ptr, data_raw_len).to_vec()
};
let data_norm = unsafe {
let ptr = buffer.as_ptr().add(data_end) as *const u16;
let slice = std::slice::from_raw_parts(ptr, data_norm_len);
let mut norm = Vec::with_capacity(data_norm_len);
norm.extend(slice.iter().map(|&bits| F16::from_bits(bits)));
norm
};
let indices = unsafe {
let ptr = buffer.as_ptr().add(norm_end) as *const u32;
std::slice::from_raw_parts(ptr, row_indices_len).to_vec()
};
Ok(Self {
data_raw,
data_norm,
avg_exp,
nnz,
indices,
original_index,
to_keep,
})
}
pub fn filter_selected_cells(&mut self, cells_to_keep: &IndexSet<u32>) {
let cell_positions: FxHashMap<u32, usize> = self
.indices
.iter()
.enumerate()
.map(|(pos, &cell_id)| (cell_id, pos))
.collect();
let mut new_data_raw = Vec::new();
let mut new_data_norm = Vec::new();
let mut new_row_indices = Vec::new();
for (new_row_idx, &cell_index) in cells_to_keep.iter().enumerate() {
if let Some(&pos) = cell_positions.get(&cell_index) {
new_data_raw.push(self.data_raw[pos]);
new_data_norm.push(self.data_norm[pos]);
new_row_indices.push(new_row_idx as u32);
}
}
let nnz = new_data_raw.len();
self.data_raw = new_data_raw;
self.data_norm = new_data_norm;
self.indices = new_row_indices;
self.nnz = nnz;
}
pub fn calculate_avg_exp(&self, cells_to_keep: &IndexSet<u32>) -> (usize, f32) {
let sum: f32 = self
.indices
.iter()
.zip(self.data_norm.iter())
.filter(|(cell_idx, _)| cells_to_keep.contains(*cell_idx))
.map(|(_, val)| val.to_f32())
.sum();
let avg = sum / cells_to_keep.len() as f32;
(self.original_index, avg)
}
}
#[derive(Encode, Decode, Serialize, Deserialize, Clone)]
pub struct SparseDataHeader {
pub total_cells: usize,
pub total_genes: usize,
pub cell_based: bool,
pub no_chunks: usize,
pub chunk_offsets: Vec<u64>,
pub index_map: FxHashMap<usize, usize>,
}
#[repr(C)]
#[derive(Encode, Decode, Serialize, Deserialize)]
struct FileHeader {
magic: [u8; 8],
version: u32,
main_header_offset: u64,
cell_based: bool,
_reserved_1: [u8; 32],
_reserved_2: [u8; 3],
}
impl FileHeader {
fn new(cell_based: bool) -> Self {
Self {
magic: *b"SCRNASEQ",
version: SC_FILE_VERSION,
main_header_offset: 0,
cell_based,
_reserved_1: [0; 32],
_reserved_2: [0; 3],
}
}
}
pub struct CellGeneSparseWriter {
header: SparseDataHeader,
writer: BufWriter<File>,
chunks_start_pos: u64,
cell_based: bool,
chunks_since_flush: usize,
flush_frequency: usize,
}
impl CellGeneSparseWriter {
pub fn new<P: AsRef<Path>>(
path_f: P,
cell_based: bool,
total_cells: usize,
total_genes: usize,
) -> std::io::Result<Self> {
let file = File::create(path_f)?;
let mut writer = BufWriter::with_capacity(128 * 1024 * 1024, file);
let file_header = FileHeader::new(cell_based);
let file_header_enc = encode_to_vec(&file_header, config::standard()).unwrap();
if file_header_enc.len() < 64 {
writer.write_all(&file_header_enc)?;
writer.write_all(&vec![0u8; 64 - file_header_enc.len()])?;
} else {
writer.write_all(&file_header_enc[..64])?;
}
writer.flush()?;
let chunks_start_pos = 64;
let flush_frequency = if cell_based { 100000_usize } else { 1000_usize };
let header = SparseDataHeader {
total_cells,
total_genes,
cell_based,
no_chunks: 0,
chunk_offsets: Vec::new(),
index_map: FxHashMap::default(),
};
Ok(Self {
header,
writer,
chunks_start_pos,
cell_based,
chunks_since_flush: 0_usize,
flush_frequency,
})
}
pub fn write_cell_chunk(&mut self, cell_chunk: CsrCellChunk) -> std::io::Result<()> {
assert!(
self.cell_based,
"The writer is not set to write in a cell-based manner!"
);
let current_pos = self.writer.stream_position()?;
let chunk_offset = current_pos - self.chunks_start_pos;
self.header.chunk_offsets.push(chunk_offset);
self.header
.index_map
.insert(cell_chunk.original_index, self.header.no_chunks);
let mut buffer = Vec::new();
cell_chunk.write_to_bytes(&mut buffer)?;
let compressed = compress_prepend_size(&buffer);
self.writer
.write_all(&(compressed.len() as u64).to_le_bytes())?;
self.writer.write_all(&compressed)?;
self.header.no_chunks += 1;
Ok(())
}
pub fn write_gene_chunk(&mut self, gene_chunk: CscGeneChunk) -> std::io::Result<()> {
assert!(
!self.cell_based,
"The writer is not set to write in a gene-based manner!"
);
let current_pos = self.writer.stream_position()?;
let chunk_offset = current_pos - self.chunks_start_pos;
self.header.chunk_offsets.push(chunk_offset);
self.header
.index_map
.insert(gene_chunk.original_index, self.header.no_chunks);
let mut buffer = Vec::new();
gene_chunk.write_to_bytes(&mut buffer)?;
let compressed = compress_prepend_size(&buffer);
self.writer
.write_all(&(compressed.len() as u64).to_le_bytes())?;
self.writer.write_all(&compressed)?;
self.header.no_chunks += 1;
self.chunks_since_flush += 1;
if self.chunks_since_flush >= self.flush_frequency {
self.writer.flush()?;
self.chunks_since_flush = 0;
}
Ok(())
}
pub fn finalise(mut self) -> std::io::Result<()> {
let main_header_offset = self.writer.stream_position()?;
let header_data = encode_to_vec(&self.header, config::standard()).unwrap();
let header_size = header_data.len() as u64;
self.writer.write_all(&header_size.to_le_bytes())?;
self.writer.write_all(&header_data)?;
self.writer.seek(SeekFrom::Start(0))?;
let mut file_header = FileHeader::new(self.cell_based);
file_header.main_header_offset = main_header_offset;
let file_header_enc = encode_to_vec(&file_header, config::standard()).unwrap();
if file_header_enc.len() < 64 {
self.writer.write_all(&file_header_enc)?;
self.writer
.write_all(&vec![0u8; 64 - file_header_enc.len()])?;
} else {
self.writer.write_all(&file_header_enc[..64])?;
}
self.writer.flush()?;
Ok(())
}
pub fn update_header_no_cells(&mut self, no_cells: usize) {
self.header.total_cells = no_cells;
}
pub fn update_header_no_genes(&mut self, no_genes: usize) {
self.header.total_genes = no_genes;
}
}
pub struct ParallelSparseReader {
header: SparseDataHeader,
mmap: Arc<memmap2::Mmap>,
chunks_start: u64,
}
impl ParallelSparseReader {
pub fn new(f_path: &str) -> std::io::Result<Self> {
let file = File::open(f_path)?;
let file_size = file.metadata()?.len();
let mmap = unsafe {
let mut opts = MmapOptions::new();
if file_size <= 8 * 1024 * 1024 * 1024 {
opts.populate();
}
opts.map(&file)?
};
let file_header_bytes = &mmap[0..64];
let (file_header, _) = decode_from_slice::<FileHeader, _>(
file_header_bytes,
config::standard(),
)
.map_err(|_| {
std::io::Error::new(std::io::ErrorKind::InvalidData, "File header decode failed")
})?;
assert!(
file_header.version == SC_FILE_VERSION,
"File version mismatch: expected {}, got {}. Please check the version you are using",
SC_FILE_VERSION,
file_header.version
);
let main_header_offset = file_header.main_header_offset as usize;
let header_size = u64::from_le_bytes(
mmap[main_header_offset..main_header_offset + 8]
.try_into()
.unwrap(),
) as usize;
let header_bytes = &mmap[main_header_offset + 8..main_header_offset + 8 + header_size];
let (header, _) =
decode_from_slice::<SparseDataHeader, _>(header_bytes, config::standard()).map_err(
|_| std::io::Error::new(std::io::ErrorKind::InvalidData, "Header decode failed"),
)?;
Ok(Self {
header,
mmap: Arc::new(mmap),
chunks_start: 64,
})
}
pub fn read_cells_parallel(&self, indices: &[usize]) -> Vec<CsrCellChunk> {
assert!(
self.header.cell_based,
"The file is not set up for CellChunks."
);
indices
.par_iter()
.map(|&original_index| {
let chunk_index = *self.header.index_map.get(&original_index).unwrap();
let chunk_offset =
(self.chunks_start + self.header.chunk_offsets[chunk_index]) as usize;
let compressed_size = u64::from_le_bytes(
self.mmap[chunk_offset..chunk_offset + 8]
.try_into()
.unwrap(),
) as usize;
let compressed = &self.mmap[chunk_offset + 8..chunk_offset + 8 + compressed_size];
let decompressed = decompress_size_prepended(compressed).unwrap();
CsrCellChunk::read_from_buffer(&decompressed).unwrap()
})
.collect()
}
pub fn read_cell(&self, index: usize) -> CsrCellChunk {
self.read_cells_parallel(&[index])
.into_iter()
.next()
.unwrap()
}
pub fn read_gene_parallel(&self, indices: &[usize]) -> Vec<CscGeneChunk> {
assert!(
!self.header.cell_based,
"The file is not set up for CellChunks."
);
indices
.par_iter()
.map(|&original_index| {
let chunk_index = *self.header.index_map.get(&original_index).unwrap();
let chunk_offset =
(self.chunks_start + self.header.chunk_offsets[chunk_index]) as usize;
let compressed_size = u64::from_le_bytes(
self.mmap[chunk_offset..chunk_offset + 8]
.try_into()
.unwrap(),
) as usize;
let compressed = &self.mmap[chunk_offset + 8..chunk_offset + 8 + compressed_size];
let decompressed = decompress_size_prepended(compressed).unwrap();
CscGeneChunk::read_from_buffer(&decompressed).unwrap()
})
.collect()
}
pub fn get_all_cells(&self) -> Vec<CsrCellChunk> {
let iter: Vec<usize> = (0..self.header.total_cells).collect();
self.read_cells_parallel(&iter)
}
pub fn get_all_genes(&self) -> Vec<CscGeneChunk> {
let iter: Vec<usize> = (0..self.header.total_genes).collect();
self.read_gene_parallel(&iter)
}
pub fn get_header(&self) -> SparseDataHeader {
self.header.clone()
}
pub fn read_cells_range(&self, start: usize, end: usize) -> Vec<CsrCellChunk> {
assert!(self.header.cell_based, "File not cell-based");
let indices: Vec<usize> = (start..end).collect();
self.read_cells_parallel(&indices)
}
#[allow(dead_code)]
pub fn read_cell_library_sizes(&self, indices: &[usize]) -> Vec<usize> {
assert!(self.header.cell_based, "File not cell-based");
indices
.par_iter()
.map(|&original_index| {
let chunk_index = *self.header.index_map.get(&original_index).unwrap();
let chunk_offset =
(self.chunks_start + self.header.chunk_offsets[chunk_index]) as usize;
let compressed_size = u64::from_le_bytes(
self.mmap[chunk_offset..chunk_offset + 8]
.try_into()
.unwrap(),
) as usize;
let compressed = &self.mmap[chunk_offset + 8..chunk_offset + 8 + compressed_size];
let decompressed = decompress_size_prepended(compressed).unwrap();
u64::from_le_bytes([
decompressed[12],
decompressed[13],
decompressed[14],
decompressed[15],
decompressed[16],
decompressed[17],
decompressed[18],
decompressed[19],
]) as usize
})
.collect()
}
pub fn read_gene_nnz(&self, indices: &[usize]) -> Vec<usize> {
assert!(!self.header.cell_based, "File not gene-based");
indices
.par_iter()
.map(|&original_index| {
let chunk_index = *self.header.index_map.get(&original_index).unwrap();
let chunk_offset =
(self.chunks_start + self.header.chunk_offsets[chunk_index]) as usize;
let compressed_size = u64::from_le_bytes(
self.mmap[chunk_offset..chunk_offset + 8]
.try_into()
.unwrap(),
) as usize;
let compressed = &self.mmap[chunk_offset + 8..chunk_offset + 8 + compressed_size];
let decompressed = decompress_size_prepended(compressed).unwrap();
u64::from_le_bytes([
decompressed[16],
decompressed[17],
decompressed[18],
decompressed[19],
decompressed[20],
decompressed[21],
decompressed[22],
decompressed[23],
]) as usize
})
.collect()
}
pub fn get_all_gene_nnz(&self) -> Vec<usize> {
let iter: Vec<usize> = (0..self.header.total_genes).collect();
self.read_gene_nnz(&iter)
}
}