1use crate as libradicl;
33
34use self::libradicl::rad_types::{MappedFragmentOrientation, RadIntId};
35use self::libradicl::record::AlevinFryReadRecord;
36use self::libradicl::record::AtacSeqReadRecord;
37use self::libradicl::record::{
38 CollatableMappedRecord, CollatableRecordHeader, ConvertiblePrimitiveInteger, KnownSize,
39 MappedRecord, RecordHeader,
40};
41use self::libradicl::schema::{CollateKey, TempCellInfo};
42#[allow(unused_imports)]
43use ahash::{AHasher, RandomState};
44use bio_types::strand::*;
45use scroll::Pread;
46use serde::{Deserialize, Serialize};
47
48use std::collections::HashMap;
49use std::fs::File;
50use std::io::{BufReader, Cursor, Read, Seek, SeekFrom};
51use std::io::{BufWriter, Write};
52use std::sync::atomic::{AtomicU32, AtomicU64, Ordering};
53use std::sync::{Arc, Mutex};
54use std::vec::Vec;
55
56pub mod chunk;
57pub mod codec;
58pub mod collation;
59pub mod constants;
60pub mod exit_codes;
61pub mod header;
62pub mod io;
63pub mod rad_types;
64pub mod readers;
65pub mod record;
66pub mod schema;
67pub mod unmapped;
68pub mod utils;
69pub mod writers;
70pub use chunk::ChunkBuf;
71pub use codec::{CHUNK_CODEC_TAG, ChunkCodec};
72pub use libradicl_macros::UmiTagged;
73pub use writers::{ConcurrentChunkWriter, RadFileWriter};
74
75#[macro_use]
76mod macros;
77
78static LIB_NAME: &str = "libradicl";
80
81pub fn lib_name() -> &'static str {
82 LIB_NAME
83}
84
85#[derive(Serialize, Deserialize, Debug)]
86#[allow(dead_code)]
87pub struct BarcodeLookupMap {
88 pub barcodes: Vec<u64>,
89 offsets: Vec<usize>,
91 bclen: u32,
92 prefix_len: u32,
93 suffix_len: u32,
94}
95
96impl BarcodeLookupMap {
97 pub fn new(mut kv: Vec<u64>, bclen: u32) -> BarcodeLookupMap {
98 let prefix_len = bclen.div_ceil(2) as u64;
99 let suffix_len = bclen - prefix_len as u32;
100
101 let _prefix_bits = 2 * prefix_len;
102 let suffix_bits = 2 * suffix_len;
103
104 kv.sort_unstable();
105
106 let pref_mask = ((4usize.pow(prefix_len as u32) - 1) as u64) << (suffix_bits);
107 let mut offsets = vec![0; 4usize.pow(prefix_len as u32) + 1];
108 let mut prev_ind = 0xFFFF;
109
110 for (n, &v) in kv.iter().enumerate() {
111 let ind = ((v & pref_mask) >> (suffix_bits)) as usize;
112 if ind != prev_ind {
113 for item in offsets.iter_mut().take(ind).skip(prev_ind + 1) {
114 *item = n;
115 }
116 offsets[ind] = n;
117 prev_ind = ind;
118 }
119 }
120 for item in offsets.iter_mut().skip(prev_ind + 1) {
121 *item = kv.len();
122 }
123
124 BarcodeLookupMap {
126 barcodes: kv,
127 offsets,
129 bclen,
130 prefix_len: prefix_len as u32,
131 suffix_len,
132 }
133 }
134
135 #[allow(dead_code)]
136 pub fn barcode_for_idx(&self, idx: usize) -> u64 {
137 self.barcodes[idx]
138 }
139
140 pub fn find_exact(&self, query: u64) -> Option<usize> {
141 let mut ret: Option<usize> = None;
142
143 let suffix_bits = 2 * self.suffix_len;
145 let query_pref = query >> suffix_bits;
146
147 let qrange = std::ops::Range {
149 start: self.offsets[query_pref as usize],
150 end: self.offsets[(query_pref + 1) as usize],
151 };
152
153 let qs = qrange.start;
154
155 if let Ok(res) = self.barcodes[qrange].binary_search(&query) {
157 ret = Some(qs + res);
158 }
159 ret
160 }
161
162 pub fn find_neighbors(&self, query: u64, try_exact: bool) -> (Option<usize>, usize) {
174 let mut ret: Option<usize> = None;
175
176 let pref_bits = 2 * self.prefix_len;
178 let suffix_bits = 2 * self.suffix_len;
179 let mut query_pref = query >> suffix_bits;
180 let mut num_neighbors = 0usize;
181
182 let qrange = std::ops::Range {
184 start: self.offsets[query_pref as usize],
185 end: self.offsets[(query_pref + 1) as usize],
186 };
187
188 let qs = qrange.start;
189
190 if try_exact {
191 if let Ok(res) = self.barcodes[qrange.clone()].binary_search(&query) {
194 ret = Some(qs + res);
195 num_neighbors += 1;
196 return (ret, num_neighbors);
197 }
198 }
199
200 if !(std::ops::Range::<usize>::is_empty(&qrange)) {
208 let qs = qrange.start;
210
211 for i in (0..suffix_bits).step_by(2) {
213 let bit_mask = 3 << (i);
214
215 for nmod in 1..4 {
217 let nucl = 0x3 & ((query >> i) + nmod);
218 let nquery = (query & (!bit_mask)) | (nucl << i);
219
220 if let Ok(res) = self.barcodes[qrange.clone()].binary_search(&nquery) {
221 ret = Some(qs + res);
222 num_neighbors += 1;
223 if num_neighbors >= 2 {
224 return (ret, num_neighbors);
225 }
226 }
227 }
228 }
229 }
230
231 {
232 for i in (suffix_bits..(suffix_bits + pref_bits)).step_by(2) {
237 let bit_mask = 3 << i;
238
239 for nmod in 1..4 {
241 let nucl = 0x3 & ((query >> i) + nmod);
242 let nquery = (query & (!bit_mask)) | (nucl << i);
243
244 query_pref = nquery >> suffix_bits;
245
246 let qrange = std::ops::Range {
247 start: self.offsets[query_pref as usize],
248 end: self.offsets[(query_pref + 1) as usize],
249 };
250 let qs = qrange.start;
251 if let Ok(res) = self.barcodes[qrange].binary_search(&nquery) {
252 ret = Some(qs + res);
253 num_neighbors += 1;
254 if num_neighbors >= 2 {
255 return (ret, num_neighbors);
256 }
257 }
258 }
259 }
260 }
261
262 (ret, num_neighbors)
263 }
264}
265
266#[derive(Debug, Clone)]
267pub struct GlobalEqCellList {
268 cell_ids: Vec<usize>,
269 count: u32,
270}
271
272impl GlobalEqCellList {
273 pub fn from_umi_and_count(bc_mer: usize, count: u32) -> GlobalEqCellList {
274 let mut cc = GlobalEqCellList {
275 cell_ids: Vec::new(),
276 count: 0,
277 };
278 cc.cell_ids.push(bc_mer);
279 cc.count += count;
280 cc
281 }
282 pub fn add_element(&mut self, bc_mer: usize, count: u32) {
283 self.cell_ids.push(bc_mer);
284 self.count += count;
285 }
286}
287
288pub fn collate_temporary_bucket_twopass_generic<
307 B: ConvertiblePrimitiveInteger,
308 T: Read + Seek,
309 U: Write,
310 R: MappedRecord + KnownSize + CollatableMappedRecord<B>,
311>(
312 reader: &mut BufReader<T>,
313 rec_context: &<R as MappedRecord>::ParsingContext,
314 nrec: u32,
315 owriter: &Mutex<U>,
316 compress: bool,
317 cb_byte_map: &mut HashMap<u64, TempCellInfo, ahash::RandomState>,
318) -> usize
319where
320 u64: From<B>,
321{
322 let mut tbuf = vec![0u8; 65536];
323 let mut total_bytes = 0usize;
324 let chunk_header_size = 2 * std::mem::size_of::<u32>() as u64;
325 let size_of_aln = R::nbytes_aln(rec_context);
326
327 let calc_record_bytes = |num_aln: usize| -> usize { R::nbytes(num_aln as u32, rec_context) };
328
329 for _ in 0..(nrec as usize) {
331 let tup =
335 <R as CollatableMappedRecord<B>>::from_bytes_collatable_header(reader, rec_context)
336 .expect("can read header");
337
338 let v = cb_byte_map
340 .entry(tup.collation_group_key(rec_context))
341 .or_insert(TempCellInfo {
342 offset: chunk_header_size,
343 nbytes: chunk_header_size as u32,
344 nrec: 0_u32,
345 });
346
347 let na = tup.naln() as usize;
349 let req_size = size_of_aln * na;
350 if tbuf.len() < req_size {
351 tbuf.resize(req_size, 0);
352 }
353 reader.read_exact(&mut tbuf[0..(size_of_aln * na)]).unwrap();
354 let nbytes = calc_record_bytes(na);
356 v.offset += nbytes as u64;
357 v.nbytes += nbytes as u32;
358 v.nrec += 1;
359 total_bytes += nbytes;
360 }
361
362 total_bytes += cb_byte_map.len() * chunk_header_size as usize;
364 let mut output_buffer = Cursor::new(vec![0u8; total_bytes]);
365
366 let mut next_offset = 0u64;
371 for (_, v) in cb_byte_map.iter_mut() {
372 output_buffer.set_position(next_offset);
375 let cell_bytes = v.nbytes;
376 let cell_rec = v.nrec;
377 output_buffer.write_all(&cell_bytes.to_le_bytes()).unwrap();
378 output_buffer.write_all(&cell_rec.to_le_bytes()).unwrap();
379 v.offset = output_buffer.position();
381 let nbytes = v.nbytes as u64;
383 next_offset += nbytes;
385 }
386
387 reader
390 .get_mut()
391 .seek(SeekFrom::Start(0))
392 .expect("could not get read pointer.");
393
394 for _ in 0..(nrec as usize) {
396 let tup =
400 <R as CollatableMappedRecord<B>>::from_bytes_collatable_header(reader, rec_context)
401 .expect("can read header");
402
403 if let Some(v) = cb_byte_map.get_mut(&tup.collation_group_key(rec_context)) {
405 output_buffer.set_position(v.offset);
406
407 let na = tup.naln() as usize;
408 tup.write_fields(&mut output_buffer, rec_context)
410 .expect("could write header");
411
412 let bytes_for_aln_rec = R::nbytes_aln(rec_context);
413 reader
415 .read_exact(&mut tbuf[0..(bytes_for_aln_rec * na)])
416 .unwrap();
417 output_buffer
418 .write_all(&tbuf[..(bytes_for_aln_rec * na)])
419 .unwrap();
420
421 v.offset = output_buffer.position();
422 } else {
423 panic!("should not have any barcodes we can't find");
424 }
425 }
426
427 output_buffer.set_position(0);
428
429 if compress {
430 let mut compressed_output =
432 snap::write::FrameEncoder::new(Cursor::new(Vec::<u8>::with_capacity(total_bytes)));
433 compressed_output
434 .write_all(output_buffer.get_ref())
435 .expect("could not compress the output chunk.");
436
437 output_buffer = compressed_output
438 .into_inner()
439 .expect("couldn't unwrap the FrameEncoder.");
440 output_buffer.set_position(0);
441 }
442
443 owriter
444 .lock()
445 .unwrap()
446 .write_all(output_buffer.get_ref())
447 .unwrap();
448
449 cb_byte_map.len()
450}
451
452#[deprecated(
459 since = "0.10.0",
460 note = "This function is highly-specalized and works only with the AlevinFryReadRecordT<u64> type. \
461 This function has been deprecated in favor of the more generic `collate_temporary_bucket_twopass_generic`. \
462 Please use that function instead."
463)]
464pub fn collate_temporary_bucket_twopass<T: Read + Seek, U: Write>(
465 reader: &mut BufReader<T>,
466 bct: &RadIntId,
467 umit: &RadIntId,
468 nrec: u32,
469 owriter: &Mutex<U>,
470 compress: bool,
471 cb_byte_map: &mut HashMap<u64, TempCellInfo, ahash::RandomState>,
472) -> usize {
473 let mut tbuf = vec![0u8; 65536];
474 let mut total_bytes = 0usize;
475 let chunk_header_size = 2 * std::mem::size_of::<u32>() as u64;
476 let size_of_u32 = std::mem::size_of::<u32>();
477 let size_of_bc = bct.bytes_for_type();
478 let size_of_umi = umit.bytes_for_type();
479
480 let calc_record_bytes = |num_aln: usize| -> usize {
481 size_of_u32 + size_of_bc + size_of_umi + (size_of_u32 * num_aln)
482 };
483
484 for _ in 0..(nrec as usize) {
486 let tup = AlevinFryReadRecord::from_bytes_record_header(reader, bct, umit);
490
491 let v = cb_byte_map.entry(tup.0).or_insert(TempCellInfo {
493 offset: chunk_header_size,
494 nbytes: chunk_header_size as u32,
495 nrec: 0_u32,
496 });
497
498 let na = tup.2 as usize;
500 let req_size = size_of_u32 * na;
501 if tbuf.len() < req_size {
502 tbuf.resize(req_size, 0);
503 }
504 reader.read_exact(&mut tbuf[0..(size_of_u32 * na)]).unwrap();
505 let nbytes = calc_record_bytes(na);
507 v.offset += nbytes as u64;
508 v.nbytes += nbytes as u32;
509 v.nrec += 1;
510 total_bytes += nbytes;
511 }
512
513 total_bytes += cb_byte_map.len() * chunk_header_size as usize;
515 let mut output_buffer = Cursor::new(vec![0u8; total_bytes]);
516
517 let mut next_offset = 0u64;
522 for (_, v) in cb_byte_map.iter_mut() {
523 output_buffer.set_position(next_offset);
526 let cell_bytes = v.nbytes;
527 let cell_rec = v.nrec;
528 output_buffer.write_all(&cell_bytes.to_le_bytes()).unwrap();
529 output_buffer.write_all(&cell_rec.to_le_bytes()).unwrap();
530 v.offset = output_buffer.position();
532 let nbytes = v.nbytes as u64;
534 next_offset += nbytes;
536 }
537
538 reader
541 .get_mut()
542 .seek(SeekFrom::Start(0))
543 .expect("could not get read pointer.");
544
545 for _ in 0..(nrec as usize) {
547 let tup = AlevinFryReadRecord::from_bytes_record_header(reader, bct, umit);
551
552 if let Some(v) = cb_byte_map.get_mut(&tup.0) {
554 output_buffer.set_position(v.offset);
555
556 let na = tup.2 as usize;
558 let nau32 = na as u32;
559 output_buffer.write_all(&nau32.to_le_bytes()).unwrap();
560
561 bct.write_to(tup.0, &mut output_buffer).unwrap();
563 umit.write_to(tup.1, &mut output_buffer).unwrap();
564
565 reader.read_exact(&mut tbuf[0..(size_of_u32 * na)]).unwrap();
567 output_buffer
569 .write_all(&tbuf[..(size_of_u32 * na)])
570 .unwrap();
571
572 v.offset = output_buffer.position();
573 } else {
574 panic!("should not have any barcodes we can't find");
575 }
576 }
577
578 output_buffer.set_position(0);
579
580 if compress {
581 let mut compressed_output =
583 snap::write::FrameEncoder::new(Cursor::new(Vec::<u8>::with_capacity(total_bytes)));
584 compressed_output
585 .write_all(output_buffer.get_ref())
586 .expect("could not compress the output chunk.");
587
588 output_buffer = compressed_output
589 .into_inner()
590 .expect("couldn't unwrap the FrameEncoder.");
591 output_buffer.set_position(0);
592 }
593
594 owriter
595 .lock()
596 .unwrap()
597 .write_all(output_buffer.get_ref())
598 .unwrap();
599
600 cb_byte_map.len()
601}
602
603pub fn collate_temporary_bucket_twopass_atac<T: Read + Seek, U: Write>(
604 reader: &mut BufReader<T>,
605 bct: &RadIntId,
606 nrec: u32,
607 owriter: &Mutex<U>,
608 compress: bool,
609 cb_byte_map: &mut HashMap<u64, TempCellInfo, ahash::RandomState>,
610) -> usize {
611 let mut tbuf = vec![0u8; 65536];
612 let mut total_bytes = 0usize;
613 let header_size = 2 * std::mem::size_of::<u32>() as u64;
614 let size_of_bc = bct.bytes_for_type();
615 let size_of_rec = std::mem::size_of::<u32>()
616 + std::mem::size_of::<u8>()
617 + std::mem::size_of::<u32>()
618 + std::mem::size_of::<u16>();
619
620 let size_of_u32 = std::mem::size_of::<u32>();
621 let calc_record_bytes =
622 |num_aln: usize| -> usize { size_of_u32 + size_of_bc + (size_of_rec * num_aln) };
623
624 for _ in 0..(nrec as usize) {
626 let tup = AtacSeqReadRecord::from_bytes_record_header(reader, bct);
630
631 let v = cb_byte_map.entry(tup.0).or_insert(TempCellInfo {
633 offset: header_size,
634 nbytes: header_size as u32,
635 nrec: 0_u32,
636 });
637
638 let na = tup.1 as usize;
640 let req_size = size_of_rec * na;
641 if tbuf.len() < req_size {
642 tbuf.resize(req_size, 0);
643 }
644 reader.read_exact(&mut tbuf[0..(size_of_rec * na)]).unwrap();
645 let nbytes = calc_record_bytes(na);
647 v.offset += nbytes as u64;
648 v.nbytes += nbytes as u32;
649 v.nrec += 1;
650 total_bytes += nbytes;
651 }
652
653 total_bytes += cb_byte_map.len() * header_size as usize;
655 let mut output_buffer = Cursor::new(vec![0u8; total_bytes]);
656
657 let mut next_offset = 0u64;
662 for (_, v) in cb_byte_map.iter_mut() {
663 output_buffer.set_position(next_offset);
666 let cell_bytes = v.nbytes;
667 let cell_rec = v.nrec;
668 output_buffer.write_all(&cell_bytes.to_le_bytes()).unwrap();
669 output_buffer.write_all(&cell_rec.to_le_bytes()).unwrap();
670 v.offset = output_buffer.position();
672 let nbytes = v.nbytes as u64;
674 next_offset += nbytes;
676 }
677
678 reader
681 .get_mut()
682 .seek(SeekFrom::Start(0))
683 .expect("could not get read pointer.");
684
685 for _ in 0..(nrec as usize) {
687 let tup = AtacSeqReadRecord::from_bytes_record_header(reader, bct);
691
692 if let Some(v) = cb_byte_map.get_mut(&tup.0) {
694 output_buffer.set_position(v.offset);
695
696 let na = tup.1 as usize;
698 let nau32 = na as u32;
699 output_buffer.write_all(&nau32.to_le_bytes()).unwrap();
700
701 bct.write_to(tup.0, &mut output_buffer).unwrap();
703
704 reader.read_exact(&mut tbuf[0..(size_of_rec * na)]).unwrap();
706 output_buffer
708 .write_all(&tbuf[..(size_of_rec * na)])
709 .unwrap();
710
711 v.offset = output_buffer.position();
712 } else {
713 panic!("should not have any barcodes we can't find");
714 }
715 }
716
717 output_buffer.set_position(0);
718
719 if compress {
720 let mut compressed_output =
722 snap::write::FrameEncoder::new(Cursor::new(Vec::<u8>::with_capacity(total_bytes)));
723 compressed_output
724 .write_all(output_buffer.get_ref())
725 .expect("could not compress the output chunk.");
726
727 output_buffer = compressed_output
728 .into_inner()
729 .expect("couldn't unwrap the FrameEncoder.");
730 output_buffer.set_position(0);
731 }
732
733 owriter
734 .lock()
735 .unwrap()
736 .write_all(output_buffer.get_ref())
737 .unwrap();
738
739 cb_byte_map.len()
740}
741
742pub struct TempBucket {
849 pub bucket_id: u32,
850 pub bucket_writer: Arc<Mutex<BufWriter<File>>>,
851 pub num_chunks: u32,
852 pub num_records: u32,
853 pub num_records_written: AtomicU32,
854 pub num_bytes_written: AtomicU64,
855}
856
857impl TempBucket {
858 pub fn from_id_and_parent(bucket_id: u32, parent: &std::path::Path) -> Self {
859 TempBucket {
860 bucket_id,
861 bucket_writer: Arc::new(Mutex::new(BufWriter::with_capacity(
862 4096_usize,
863 File::create(parent.join(format!("bucket_{}.tmp", bucket_id))).unwrap(),
864 ))),
865 num_chunks: 0u32,
866 num_records: 0u32,
867 num_records_written: AtomicU32::new(0u32),
868 num_bytes_written: AtomicU64::new(0u64),
869 }
870 }
871}
872
873#[allow(clippy::too_many_arguments)]
879pub fn dump_corrected_cb_chunk_to_temp_file_generic<
880 B: ConvertiblePrimitiveInteger + std::convert::From<u64>,
881 T: Read,
882 R: MappedRecord + KnownSize + CollatableMappedRecord<B>,
883>(
884 reader: &mut BufReader<T>,
885 rec_context: &<R as MappedRecord>::ParsingContext,
886 correct_map: &HashMap<u64, u64>,
887 expected_ori: &Strand,
888 output_cache: &HashMap<u64, Arc<TempBucket>>,
889 local_buffers: &mut [Cursor<&mut [u8]>],
890 flush_limit: usize,
891) where
892 u64: From<B>,
893 <R as MappedRecord>::ParsingContext: std::fmt::Debug,
894{
895 let mut buf = [0u8; 8];
896 let mut tbuf = vec![0u8; 4096];
897 reader.read_exact(&mut buf).unwrap();
903 let _nbytes = buf.pread::<u32>(0).unwrap();
904 let nrec = buf.pread::<u32>(4).unwrap();
905
906 let expected_ori: MappedFragmentOrientation = expected_ori.into();
907
908 for _ in 0..(nrec as usize) {
910 let mut tup =
911 <R as CollatableMappedRecord<B>>::from_bytes_collatable_header(reader, rec_context)
912 .expect("could read header");
913
914 if let Some(corrected_id) = correct_map.get(&tup.collate_key().into()) {
916 let mut rr =
917 R::from_bytes_with_header_retain_ori(reader, &mut tup, rec_context, &expected_ori);
918
919 if rr.is_empty() {
920 continue;
921 }
922 if let Some(v) = output_cache.get(corrected_id) {
923 let na = tup.naln() as usize;
928 let nb = R::nbytes(na as u32, rec_context) as u64;
930
931 let buffidx = v.bucket_id as usize;
933 let bcursor = &mut local_buffers[buffidx];
935 let len = bcursor.position() as usize;
937
938 if len + nb as usize >= flush_limit {
942 let mut filebuf = v.bucket_writer.lock().unwrap();
943 filebuf.write_all(&bcursor.get_ref()[0..len]).unwrap();
944 bcursor.set_position(0);
946 }
947
948 rr.set_collate_key((*corrected_id).into());
950
951 let blen = bcursor.position() as usize;
952 rr.write(bcursor, rec_context).expect("can write record");
954 let alen = bcursor.position() as usize;
955 let actual = alen - blen;
956 let expected = R::nbytes(na as u32, rec_context);
957 assert_eq!(
958 expected, actual,
959 "Expected to write {} bytes, but wrote {}.",
960 expected, actual
961 );
962
963 v.num_records_written.fetch_add(1, Ordering::SeqCst);
965 v.num_bytes_written.fetch_add(nb, Ordering::SeqCst);
967 }
968 } else {
969 let req_len = R::nbytes_aln(rec_context) * tup.naln() as usize;
975 let do_resize = req_len > tbuf.len();
976
977 if do_resize {
978 tbuf.resize(req_len, 0);
979 }
980
981 reader.read_exact(&mut tbuf[0..req_len]).unwrap();
982
983 if do_resize {
984 tbuf.resize(4096, 0);
985 tbuf.shrink_to_fit();
986 }
987 }
988 }
989}
990
991#[deprecated(
997 since = "0.10.0",
998 note = "This function is highly-specalized and works only with the AlevinFryReadRecordT<u64> type. \
999 This function has been deprecated in favor of the more generic `dump_corrected_cb_chunk_to_temp_file_generic`. \
1000 Please use that function instead."
1001)]
1002#[allow(clippy::too_many_arguments)]
1003pub fn dump_corrected_cb_chunk_to_temp_file<T: Read>(
1004 reader: &mut BufReader<T>,
1005 bct: &RadIntId,
1006 umit: &RadIntId,
1007 correct_map: &HashMap<u64, u64>,
1008 expected_ori: &Strand,
1009 output_cache: &HashMap<u64, Arc<TempBucket>>,
1010 local_buffers: &mut [Cursor<&mut [u8]>],
1011 flush_limit: usize,
1012) {
1013 let mut buf = [0u8; 8];
1014 let mut tbuf = vec![0u8; 4096];
1015 reader.read_exact(&mut buf).unwrap();
1021 let _nbytes = buf.pread::<u32>(0).unwrap();
1022 let nrec = buf.pread::<u32>(4).unwrap();
1023
1024 let bc_bytes = bct.bytes_for_type();
1025 let umi_bytes = umit.bytes_for_type();
1026 let na_bytes = std::mem::size_of::<u32>();
1027 let target_id_bytes = std::mem::size_of::<u32>();
1028
1029 for _ in 0..(nrec as usize) {
1031 let tup = AlevinFryReadRecord::from_bytes_record_header(reader, bct, umit);
1032
1033 if let Some(corrected_id) = correct_map.get(&tup.0) {
1035 let rr = AlevinFryReadRecord::from_bytes_with_header_keep_ori(
1036 reader,
1037 tup.0,
1038 tup.1,
1039 tup.2,
1040 expected_ori,
1041 );
1042
1043 if rr.is_empty() {
1044 continue;
1045 }
1046 if let Some(v) = output_cache.get(corrected_id) {
1047 let nb = (rr.refs.len() * target_id_bytes + na_bytes + bc_bytes + umi_bytes) as u64;
1053
1054 let buffidx = v.bucket_id as usize;
1056 let bcursor = &mut local_buffers[buffidx];
1058 let len = bcursor.position() as usize;
1060
1061 if len + nb as usize >= flush_limit {
1065 let mut filebuf = v.bucket_writer.lock().unwrap();
1066 filebuf.write_all(&bcursor.get_ref()[0..len]).unwrap();
1067 bcursor.set_position(0);
1069 }
1070
1071 let na = rr.refs.len() as u32;
1073 bcursor.write_all(&na.to_le_bytes()).unwrap();
1074 bct.write_to(*corrected_id, bcursor).unwrap();
1075 umit.write_to(rr.umi, bcursor).unwrap();
1076 bcursor.write_all(as_u8_slice(&rr.refs[..])).unwrap();
1077
1078 v.num_records_written.fetch_add(1, Ordering::SeqCst);
1080 v.num_bytes_written.fetch_add(nb, Ordering::SeqCst);
1082 }
1083 } else {
1084 let req_len = target_id_bytes * (tup.2 as usize);
1088 let do_resize = req_len > tbuf.len();
1089
1090 if do_resize {
1091 tbuf.resize(req_len, 0);
1092 }
1093
1094 reader
1095 .read_exact(&mut tbuf[0..(target_id_bytes * (tup.2 as usize))])
1096 .unwrap();
1097
1098 if do_resize {
1099 tbuf.resize(4096, 0);
1100 tbuf.shrink_to_fit();
1101 }
1102 }
1103 }
1104}
1105
1106#[allow(clippy::too_many_arguments)]
1112pub fn dump_corrected_cb_chunk_to_temp_file_atac<T: Read>(
1113 reader: &mut BufReader<T>,
1114 bct: &RadIntId,
1115 correct_map: &HashMap<u64, u64>,
1116 output_cache: &HashMap<u64, Arc<TempBucket>>,
1117 local_buffers: &mut [Cursor<&mut [u8]>],
1118 flush_limit: usize,
1119 ck: CollateKey, )
1121where
1122{
1124 let mut buf = [0u8; 8];
1125 let mut tbuf = vec![0u8; 4096];
1126 reader.read_exact(&mut buf).unwrap();
1132 let _nbytes = buf.pread::<u32>(0).unwrap();
1133 let nrec = buf.pread::<u32>(4).unwrap();
1134
1135 let bc_bytes = bct.bytes_for_type();
1136 let na_bytes = std::mem::size_of::<u32>();
1137 let target_id_bytes = std::mem::size_of::<u32>() + std::mem::size_of::<u8>() + std::mem::size_of::<u32>() + std::mem::size_of::<u16>(); for _ in 0..(nrec as usize) {
1144 let tup = AtacSeqReadRecord::from_bytes_record_header(reader, bct);
1145
1146 if let Some(corrected_id) = correct_map.get(&tup.0) {
1148 let rr = AtacSeqReadRecord::from_bytes_with_header(reader, tup.0, tup.1);
1150
1151 if rr.is_empty() || tup.1 > 1 {
1152 continue;
1153 }
1154 let pos = rr.start_pos[0];
1155 let ref_id = rr.refs[0];
1156 let check_id = match ck {
1157 CollateKey::Barcode => *corrected_id,
1158 CollateKey::Pos(ref f) => f(pos, ref_id as usize) as u64,
1159 };
1160 if let Some(v) = output_cache.get(&check_id) {
1161 let nb = (rr.refs.len() * target_id_bytes + na_bytes + bc_bytes) as u64;
1167
1168 let buffidx = v.bucket_id as usize;
1170 let bcursor = &mut local_buffers[buffidx];
1172 let len = bcursor.position() as usize;
1174
1175 if len + nb as usize >= flush_limit {
1179 let mut filebuf = v.bucket_writer.lock().unwrap();
1180 filebuf.write_all(&bcursor.get_ref()[0..len]).unwrap();
1181 bcursor.set_position(0);
1183 }
1184
1185 let na = rr.refs.len() as u32;
1187 bcursor.write_all(&na.to_le_bytes()).unwrap();
1188 bct.write_to(*corrected_id, bcursor).unwrap();
1189 bcursor.write_all(as_u8_slice(&rr.refs[..])).unwrap();
1190 bcursor.write_all(as_u8_slice_u8(&rr.map_type[..])).unwrap();
1191 bcursor.write_all(as_u8_slice(&rr.start_pos[..])).unwrap();
1192 bcursor
1193 .write_all(as_u8_slice_u16(&rr.frag_lengths[..]))
1194 .unwrap();
1195
1196 v.num_records_written.fetch_add(1, Ordering::SeqCst);
1198 v.num_bytes_written.fetch_add(nb, Ordering::SeqCst);
1200 }
1201 } else {
1202 let req_len = target_id_bytes * (tup.1 as usize);
1206 let do_resize = req_len > tbuf.len();
1207
1208 if do_resize {
1209 tbuf.resize(req_len, 0);
1210 }
1211
1212 reader
1213 .read_exact(&mut tbuf[0..(target_id_bytes * (tup.1 as usize))])
1214 .unwrap();
1215
1216 if do_resize {
1217 tbuf.resize(4096, 0);
1218 tbuf.shrink_to_fit();
1219 }
1220 }
1221 }
1222}
1223
1224pub fn as_u8_slice(v: &[u32]) -> &[u8] {
1225 unsafe { std::slice::from_raw_parts(v.as_ptr() as *const u8, std::mem::size_of_val(v)) }
1226}
1227
1228pub fn as_u8_slice_u16(v: &[u16]) -> &[u8] {
1229 unsafe { std::slice::from_raw_parts(v.as_ptr() as *const u8, std::mem::size_of_val(v)) }
1230}
1231
1232pub fn as_u8_slice_u8(v: &[u8]) -> &[u8] {
1233 unsafe { std::slice::from_raw_parts(v.as_ptr(), std::mem::size_of_val(v)) }
1234}
1235
1236#[cfg(test)]
1237mod tests {
1238 use crate::BarcodeLookupMap;
1239
1240 #[test]
1241 fn test_barcode_lookup_map() {
1242 let barcode_sv_even = vec![
1243 b"AACC", b"AAGG", b"CAGT", b"CATT", b"GACC", b"GATA", b"TCAG", b"TCGT",
1244 ];
1245 let barcode_sv_odd = vec![
1246 b"AACCA", b"AAGGC", b"CAGTA", b"CATTG", b"GACCG", b"GATAC", b"TCAGA", b"TCGTG",
1247 ];
1248
1249 let mut barcode_even = Vec::with_capacity(barcode_sv_even.len());
1250 for b in barcode_sv_even.clone() {
1251 if let Some((_, km, _)) =
1252 needletail::bitkmer::BitNuclKmer::new(&b[..], b.len() as u8, false).next()
1253 {
1254 barcode_even.push(km.0);
1255 }
1256 }
1257 let mut barcode_odd = Vec::with_capacity(barcode_sv_odd.len());
1258 for b in barcode_sv_odd.clone() {
1259 if let Some((_, km, _)) =
1260 needletail::bitkmer::BitNuclKmer::new(&b[..], b.len() as u8, false).next()
1261 {
1262 barcode_odd.push(km.0);
1263 }
1264 }
1265
1266 let me = BarcodeLookupMap::new(barcode_even, 4);
1267 let mo = BarcodeLookupMap::new(barcode_odd, 5);
1268
1269 let x = b"CAGA";
1270 if let Some((_, et, _)) =
1271 needletail::bitkmer::BitNuclKmer::new(&x[..], x.len() as u8, false).next()
1272 {
1273 assert_eq!((Some(2), 1), me.find_neighbors(et.0, false));
1274 }
1275 let x = b"CAATG";
1276 if let Some((_, et, _)) =
1277 needletail::bitkmer::BitNuclKmer::new(&x[..], x.len() as u8, false).next()
1278 {
1279 assert_eq!((Some(3), 1), mo.find_neighbors(et.0, false));
1280 }
1281 }
1282}