1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
//! CRAM.
//!
//! Format reference: `docs/cram_format_v3.1.md`, with the entropy codecs in
//! `docs/cram_codecs_v3.1.md`. Versions 3.0 and 3.1 are read; 2.x and 1.0 are
//! refused by name — see [`container::FileDefinition::parse`].
//!
//! # What a CRAM reader is, in one paragraph
//!
//! A CRAM is a list of containers; a container is a compression header and some
//! slices; a slice is a set of *data series* — one for flags, one for mapping
//! qualities, one for each auxiliary tag — each stored in its own block and
//! each read column-wise rather than record-wise. Decoding a record means
//! taking one value from twenty-odd of those columns at once. On top of that,
//! mapped reads are stored as *differences* against a reference sequence, so
//! the bases are not in the file at all unless the read disagrees with the
//! genome.
//!
//! # Why this reader emits BAM records
//!
//! CRAM's record model is BAM's — the same flags, the same positions, the same
//! CIGAR operators, the same auxiliary tag types — encoded differently. So
//! the `record` module reconstructs a *BAM* record for each CRAM one, a whole
//! slice into one contiguous buffer, and hands them to the BAM record decoder.
//! Everything above that point is the BAM reader's, already written and already
//! tested: the lazily built fields, the entry filter, the tag walk, the Python
//! class. The cost is one write per record of what the file would have held had
//! it been a BAM; the alternative is a second record type and a second entry
//! class that must be kept saying the same things as the first.
//!
//! One thing that arrangement gives up: `sequence` cannot stay lazy, because
//! rebuilding it needs the slice's reference bases and its substitution matrix.
//! A slice is decoded whole in any case — mate resolution reaches across
//! records within one — so this costs a read-length write per record and not a
//! second pass.
//!
//! # The reference
//!
//! Coordinates, CIGARs, flags, names, mates, tags, soft clips and inserted
//! bases are all either stored verbatim or derivable from the read features
//! alone. Only `SEQ` needs the reference. A reader with none still answers
//! everything else and reports why through [`CramReader::reference_error`],
//! which is the same shape `BamReader::index_error` already has for a missing
//! index.
// `pub(crate)` on the parser modules rather than private, as in `bam/`:
// `crate::fuzz` runs every one of them over hostile bytes and is a sibling of
// these rather than a descendant.
pub
pub
pub
pub
pub
pub
pub
pub
pub
pub use ;
pub use ;
pub use ;
pub use Encoding;
pub use ;
pub use ;
pub use SliceHeader;
/// Checks against real CRAM files, which run only when those files are present.
///
/// A synthetic file exercises the paths its writer chose. A file `samtools`
/// wrote exercises the ones it chose, which are not the same — and for a format
/// with five entropy coders and thirty data series, the difference is most of
/// the format. These skip themselves when `local/test_data` is empty, so the
/// suite still runs anywhere.