use std::{
collections::HashMap,
error, fmt,
io::{self, Write},
};
use byteorder::WriteBytesExt;
use noodles_bam as bam;
use noodles_core::Position;
use noodles_sam::{
self as sam,
record::{quality_scores::Score, sequence::Base},
};
use super::num::write_itf8;
use crate::{
container::block,
data_container::{
compression_header::{
data_series_encoding_map::DataSeries,
encoding::codec::{Byte, ByteArray, Integer},
preservation_map::tag_ids_dictionary,
Encoding,
},
CompressionHeader, ReferenceSequenceContext,
},
record::{
feature::{self, substitution},
Feature, Flags, NextMateFlags,
},
BitWriter, Record,
};
#[allow(clippy::enum_variant_names)]
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum WriteRecordError {
MissingDataSeriesEncoding(DataSeries),
MissingTagEncoding(tag_ids_dictionary::Key),
MissingExternalBlock(block::ContentId),
}
impl error::Error for WriteRecordError {}
impl fmt::Display for WriteRecordError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::MissingDataSeriesEncoding(data_series) => {
write!(f, "missing data series encoding: {:?}", data_series)
}
Self::MissingTagEncoding(key) => write!(f, "missing tag encoding: {:?}", key),
Self::MissingExternalBlock(block_content_id) => {
write!(f, "missing external block: {}", block_content_id)
}
}
}
}
pub struct Writer<'a, W, X> {
compression_header: &'a CompressionHeader,
core_data_writer: &'a mut BitWriter<W>,
external_data_writers: &'a mut HashMap<block::ContentId, X>,
reference_sequence_context: ReferenceSequenceContext,
prev_alignment_start: Option<Position>,
}
impl<'a, W, X> Writer<'a, W, X>
where
W: Write,
X: Write,
{
pub fn new(
compression_header: &'a CompressionHeader,
core_data_writer: &'a mut BitWriter<W>,
external_data_writers: &'a mut HashMap<block::ContentId, X>,
reference_sequence_context: ReferenceSequenceContext,
) -> Self {
let initial_alignment_start = match reference_sequence_context {
ReferenceSequenceContext::Some(context) => Some(context.alignment_start()),
_ => None,
};
Self {
compression_header,
core_data_writer,
external_data_writers,
reference_sequence_context,
prev_alignment_start: initial_alignment_start,
}
}
pub fn write_record(&mut self, record: &Record) -> io::Result<()> {
self.write_bam_bit_flags(record.bam_flags())?;
self.write_cram_bit_flags(record.cram_flags())?;
self.write_positional_data(record)?;
let preservation_map = self.compression_header.preservation_map();
if preservation_map.read_names_included() {
self.write_read_name(record.read_name())?;
}
self.write_mate_data(record)?;
self.write_tag_data(record)?;
if record.bam_flags().is_unmapped() {
self.write_unmapped_read(record)?;
} else {
self.write_mapped_read(record)?;
}
self.prev_alignment_start = record.alignment_start();
Ok(())
}
fn write_bam_bit_flags(&mut self, bam_flags: sam::record::Flags) -> io::Result<()> {
let encoding = self
.compression_header
.data_series_encoding_map()
.bam_bit_flags_encoding();
let bam_bit_flags = i32::from(u16::from(bam_flags));
encode_itf8(
encoding,
self.core_data_writer,
self.external_data_writers,
bam_bit_flags,
)
}
fn write_cram_bit_flags(&mut self, flags: Flags) -> io::Result<()> {
let encoding = self
.compression_header
.data_series_encoding_map()
.cram_bit_flags_encoding();
let cram_bit_flags = i32::from(u8::from(flags));
encode_itf8(
encoding,
self.core_data_writer,
self.external_data_writers,
cram_bit_flags,
)
}
fn write_positional_data(&mut self, record: &Record) -> io::Result<()> {
if self.reference_sequence_context.is_many() {
self.write_reference_id(record.reference_sequence_id())?;
}
self.write_read_length(record.read_length())?;
self.write_alignment_start(record.alignment_start)?;
self.write_read_group(record.read_group_id())?;
Ok(())
}
fn write_reference_id(&mut self, reference_sequence_id: Option<usize>) -> io::Result<()> {
const UNMAPPED: i32 = -1;
let encoding = self
.compression_header
.data_series_encoding_map()
.reference_id_encoding()
.ok_or_else(|| {
io::Error::new(
io::ErrorKind::InvalidData,
WriteRecordError::MissingDataSeriesEncoding(DataSeries::ReferenceId),
)
})?;
let reference_id = if let Some(id) = reference_sequence_id {
i32::try_from(id).map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e))?
} else {
UNMAPPED
};
encode_itf8(
encoding,
self.core_data_writer,
self.external_data_writers,
reference_id,
)
}
fn write_read_length(&mut self, read_length: usize) -> io::Result<()> {
let encoding = self
.compression_header
.data_series_encoding_map()
.read_lengths_encoding();
let len = i32::try_from(read_length)
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
encode_itf8(
encoding,
self.core_data_writer,
self.external_data_writers,
len,
)
}
fn write_alignment_start(&mut self, alignment_start: Option<Position>) -> io::Result<()> {
let ap_data_series_delta = self
.compression_header
.preservation_map()
.ap_data_series_delta();
let encoding = self
.compression_header
.data_series_encoding_map()
.in_seq_positions_encoding();
let alignment_start_or_delta = if ap_data_series_delta {
match (alignment_start, self.prev_alignment_start) {
(None, None) => 0,
(Some(alignment_start), Some(prev_alignment_start)) => {
let alignment_start = i32::try_from(usize::from(alignment_start))
.map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e))?;
let prev_alignment_start = i32::try_from(usize::from(prev_alignment_start))
.map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e))?;
alignment_start - prev_alignment_start
}
_ => {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
format!(
"invalid alignment start ({:?}) or previous alignment start ({:?})",
alignment_start, self.prev_alignment_start
),
));
}
}
} else {
i32::try_from(alignment_start.map(usize::from).unwrap_or_default())
.map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e))?
};
encode_itf8(
encoding,
self.core_data_writer,
self.external_data_writers,
alignment_start_or_delta,
)
}
fn write_read_group(&mut self, read_group_id: Option<usize>) -> io::Result<()> {
const MISSING: i32 = -1;
let encoding = self
.compression_header
.data_series_encoding_map()
.read_groups_encoding();
let read_group = if let Some(id) = read_group_id {
i32::try_from(id).map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e))?
} else {
MISSING
};
encode_itf8(
encoding,
self.core_data_writer,
self.external_data_writers,
read_group,
)
}
fn write_read_name(&mut self, read_name: Option<&sam::record::ReadName>) -> io::Result<()> {
use sam::record::read_name::MISSING;
let encoding = self
.compression_header
.data_series_encoding_map()
.read_names_encoding()
.ok_or_else(|| {
io::Error::new(
io::ErrorKind::InvalidData,
WriteRecordError::MissingDataSeriesEncoding(DataSeries::ReadNames),
)
})?;
let read_name = read_name.map(|name| name.as_ref()).unwrap_or(MISSING);
encode_byte_array(
encoding,
self.core_data_writer,
self.external_data_writers,
read_name,
)
}
fn write_mate_data(&mut self, record: &Record) -> io::Result<()> {
if record.cram_flags().is_detached() {
self.write_next_mate_bit_flags(record.next_mate_flags())?;
let preservation_map = self.compression_header.preservation_map();
if !preservation_map.read_names_included() {
self.write_read_name(record.read_name())?;
}
self.write_next_fragment_reference_sequence_id(
record.next_fragment_reference_sequence_id(),
)?;
self.write_next_mate_alignment_start(record.next_mate_alignment_start())?;
self.write_template_size(record.template_size())?;
} else if let Some(distance_to_next_fragment) = record.distance_to_next_fragment() {
self.write_distance_to_next_fragment(distance_to_next_fragment)?;
}
Ok(())
}
fn write_next_mate_bit_flags(&mut self, next_mate_flags: NextMateFlags) -> io::Result<()> {
self.compression_header
.data_series_encoding_map()
.next_mate_bit_flags_encoding()
.ok_or_else(|| {
io::Error::new(
io::ErrorKind::InvalidData,
WriteRecordError::MissingDataSeriesEncoding(DataSeries::NextMateBitFlags),
)
})
.and_then(|encoding| {
let next_mate_bit_flags = i32::from(u8::from(next_mate_flags));
encode_itf8(
encoding,
self.core_data_writer,
self.external_data_writers,
next_mate_bit_flags,
)
})
}
fn write_next_fragment_reference_sequence_id(
&mut self,
next_fragment_reference_sequence_id: Option<usize>,
) -> io::Result<()> {
const UNMAPPED: i32 = -1;
let encoding = self
.compression_header
.data_series_encoding_map()
.next_fragment_reference_sequence_id_encoding()
.ok_or_else(|| {
io::Error::new(
io::ErrorKind::InvalidData,
WriteRecordError::MissingDataSeriesEncoding(
DataSeries::NextFragmentReferenceSequenceId,
),
)
})?;
let raw_next_fragment_reference_sequence_id =
if let Some(id) = next_fragment_reference_sequence_id {
i32::try_from(id).map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e))?
} else {
UNMAPPED
};
encode_itf8(
encoding,
self.core_data_writer,
self.external_data_writers,
raw_next_fragment_reference_sequence_id,
)
}
fn write_next_mate_alignment_start(
&mut self,
next_mate_alignment_start: Option<Position>,
) -> io::Result<()> {
let encoding = self
.compression_header
.data_series_encoding_map()
.next_mate_alignment_start_encoding()
.ok_or_else(|| {
io::Error::new(
io::ErrorKind::InvalidData,
WriteRecordError::MissingDataSeriesEncoding(DataSeries::NextMateAlignmentStart),
)
})?;
let position = i32::try_from(
next_mate_alignment_start
.map(usize::from)
.unwrap_or_default(),
)
.map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e))?;
encode_itf8(
encoding,
self.core_data_writer,
self.external_data_writers,
position,
)
}
fn write_template_size(&mut self, template_size: i32) -> io::Result<()> {
self.compression_header
.data_series_encoding_map()
.template_size_encoding()
.ok_or_else(|| {
io::Error::new(
io::ErrorKind::InvalidData,
WriteRecordError::MissingDataSeriesEncoding(DataSeries::TemplateSize),
)
})
.and_then(|encoding| {
encode_itf8(
encoding,
self.core_data_writer,
self.external_data_writers,
template_size,
)
})
}
fn write_distance_to_next_fragment(
&mut self,
distance_to_next_fragment: usize,
) -> io::Result<()> {
let encoding = self
.compression_header
.data_series_encoding_map()
.distance_to_next_fragment_encoding()
.ok_or_else(|| {
io::Error::new(
io::ErrorKind::InvalidData,
WriteRecordError::MissingDataSeriesEncoding(DataSeries::DistanceToNextFragment),
)
})?;
let n = i32::try_from(distance_to_next_fragment)
.map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e))?;
encode_itf8(
encoding,
self.core_data_writer,
self.external_data_writers,
n,
)
}
fn write_tag_data(&mut self, record: &Record) -> io::Result<()> {
use bam::writer::record::data::field::put_value;
let preservation_map = self.compression_header.preservation_map();
let tag_ids_dictionary = preservation_map.tag_ids_dictionary();
let keys: Vec<_> = record.tags().values().map(|field| field.into()).collect();
let tag_line = tag_ids_dictionary
.iter()
.enumerate()
.find(|(_, k)| **k == keys)
.map(|(i, _)| i)
.ok_or_else(|| {
io::Error::new(
io::ErrorKind::InvalidInput,
"tag line not in tag IDs dictionary",
)
})?;
self.write_tag_line(tag_line)?;
let tag_encoding_map = self.compression_header.tag_encoding_map();
let mut buf = Vec::new();
for field in record.tags().values() {
let key: tag_ids_dictionary::Key = field.into();
let id = block::ContentId::from(key);
let encoding = tag_encoding_map.get(&id).ok_or_else(|| {
io::Error::new(
io::ErrorKind::InvalidInput,
WriteRecordError::MissingTagEncoding(key),
)
})?;
buf.clear();
put_value(&mut buf, field.value())?;
encode_byte_array(
encoding,
self.core_data_writer,
self.external_data_writers,
&buf,
)?;
}
Ok(())
}
fn write_tag_line(&mut self, tag_line: usize) -> io::Result<()> {
let encoding = self
.compression_header
.data_series_encoding_map()
.tag_ids_encoding();
let n =
i32::try_from(tag_line).map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e))?;
encode_itf8(
encoding,
self.core_data_writer,
self.external_data_writers,
n,
)
}
fn write_mapped_read(&mut self, record: &Record) -> io::Result<()> {
self.write_number_of_read_features(record.features().len())?;
let mut prev_position = 0;
for feature in record.features().iter() {
let position = usize::from(feature.position()) - prev_position;
self.write_feature(feature, position)?;
prev_position = usize::from(feature.position());
}
self.write_mapping_quality(record.mapping_quality())?;
if record.cram_flags().are_quality_scores_stored_as_array() {
for &score in record.quality_scores().as_ref() {
self.write_quality_score(score)?;
}
}
Ok(())
}
fn write_number_of_read_features(&mut self, feature_count: usize) -> io::Result<()> {
let encoding = self
.compression_header
.data_series_encoding_map()
.number_of_read_features_encoding()
.ok_or_else(|| {
io::Error::new(
io::ErrorKind::InvalidData,
WriteRecordError::MissingDataSeriesEncoding(DataSeries::NumberOfReadFeatures),
)
})?;
let number_of_read_features = i32::try_from(feature_count)
.map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e))?;
encode_itf8(
encoding,
self.core_data_writer,
self.external_data_writers,
number_of_read_features,
)
}
fn write_feature(&mut self, feature: &Feature, position: usize) -> io::Result<()> {
self.write_feature_code(feature.code())?;
self.write_feature_position(position)?;
match feature {
Feature::Bases(_, bases) => {
self.write_stretches_of_bases(bases)?;
}
Feature::Scores(_, quality_scores) => {
self.write_stretches_of_quality_scores(quality_scores)?;
}
Feature::ReadBase(_, base, quality_score) => {
self.write_base(*base)?;
self.write_quality_score(*quality_score)?;
}
Feature::Substitution(_, value) => {
self.write_base_substitution_code(*value)?;
}
Feature::Insertion(_, bases) => {
self.write_insertion(bases)?;
}
Feature::Deletion(_, len) => {
self.write_deletion_length(*len)?;
}
Feature::InsertBase(_, base) => {
self.write_base(*base)?;
}
Feature::QualityScore(_, score) => {
self.write_quality_score(*score)?;
}
Feature::ReferenceSkip(_, len) => {
self.write_reference_skip_length(*len)?;
}
Feature::SoftClip(_, bases) => {
self.write_soft_clip(bases)?;
}
Feature::Padding(_, len) => {
self.write_padding(*len)?;
}
Feature::HardClip(_, len) => {
self.write_hard_clip(*len)?;
}
}
Ok(())
}
fn write_feature_code(&mut self, code: feature::Code) -> io::Result<()> {
self.compression_header
.data_series_encoding_map()
.read_features_codes_encoding()
.ok_or_else(|| {
io::Error::new(
io::ErrorKind::InvalidData,
WriteRecordError::MissingDataSeriesEncoding(DataSeries::ReadFeaturesCodes),
)
})
.and_then(|encoding| {
let feature_code = u8::from(code);
encode_byte(
encoding,
self.core_data_writer,
self.external_data_writers,
feature_code,
)
})
}
fn write_feature_position(&mut self, position: usize) -> io::Result<()> {
let encoding = self
.compression_header
.data_series_encoding_map()
.in_read_positions_encoding()
.ok_or_else(|| {
io::Error::new(
io::ErrorKind::InvalidData,
WriteRecordError::MissingDataSeriesEncoding(DataSeries::InReadPositions),
)
})?;
let position =
i32::try_from(position).map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e))?;
encode_itf8(
encoding,
self.core_data_writer,
self.external_data_writers,
position,
)
}
fn write_stretches_of_bases(&mut self, bases: &[Base]) -> io::Result<()> {
let encoding = self
.compression_header
.data_series_encoding_map()
.stretches_of_bases_encoding()
.ok_or_else(|| {
io::Error::new(
io::ErrorKind::InvalidData,
WriteRecordError::MissingDataSeriesEncoding(DataSeries::StretchesOfBases),
)
})?;
let raw_bases: Vec<_> = bases.iter().copied().map(u8::from).collect();
encode_byte_array(
encoding,
self.core_data_writer,
self.external_data_writers,
&raw_bases,
)
}
fn write_stretches_of_quality_scores(&mut self, quality_scores: &[Score]) -> io::Result<()> {
let encoding = self
.compression_header
.data_series_encoding_map()
.stretches_of_quality_scores_encoding()
.ok_or_else(|| {
io::Error::new(
io::ErrorKind::InvalidData,
WriteRecordError::MissingDataSeriesEncoding(
DataSeries::StretchesOfQualityScores,
),
)
})?;
let scores: Vec<_> = quality_scores.iter().copied().map(u8::from).collect();
encode_byte_array(
encoding,
self.core_data_writer,
self.external_data_writers,
&scores,
)
}
fn write_base(&mut self, base: Base) -> io::Result<()> {
let encoding = self
.compression_header
.data_series_encoding_map()
.bases_encoding()
.ok_or_else(|| {
io::Error::new(
io::ErrorKind::InvalidData,
WriteRecordError::MissingDataSeriesEncoding(DataSeries::Bases),
)
})?;
let raw_base = u8::from(base);
encode_byte(
encoding,
self.core_data_writer,
self.external_data_writers,
raw_base,
)
}
fn write_quality_score(&mut self, quality_score: Score) -> io::Result<()> {
let encoding = self
.compression_header
.data_series_encoding_map()
.quality_scores_encoding()
.ok_or_else(|| {
io::Error::new(
io::ErrorKind::InvalidData,
WriteRecordError::MissingDataSeriesEncoding(DataSeries::QualityScores),
)
})?;
let score = u8::from(quality_score);
encode_byte(
encoding,
self.core_data_writer,
self.external_data_writers,
score,
)
}
fn write_base_substitution_code(&mut self, value: substitution::Value) -> io::Result<()> {
let encoding = self
.compression_header
.data_series_encoding_map()
.base_substitution_codes_encoding()
.ok_or_else(|| {
io::Error::new(
io::ErrorKind::InvalidData,
WriteRecordError::MissingDataSeriesEncoding(DataSeries::BaseSubstitutionCodes),
)
})?;
let substitution_matrix = self
.compression_header
.preservation_map()
.substitution_matrix();
let code = match value {
substitution::Value::Bases(reference_base, read_base) => {
substitution_matrix.find_code(reference_base, read_base)
}
substitution::Value::Code(_) => {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
"base substitution cannot be a code on write",
));
}
};
encode_byte(
encoding,
self.core_data_writer,
self.external_data_writers,
code,
)
}
fn write_insertion(&mut self, bases: &[Base]) -> io::Result<()> {
let encoding = self
.compression_header
.data_series_encoding_map()
.insertion_encoding()
.ok_or_else(|| {
io::Error::new(
io::ErrorKind::InvalidData,
WriteRecordError::MissingDataSeriesEncoding(DataSeries::Insertion),
)
})?;
let raw_bases: Vec<_> = bases.iter().copied().map(u8::from).collect();
encode_byte_array(
encoding,
self.core_data_writer,
self.external_data_writers,
&raw_bases,
)
}
fn write_deletion_length(&mut self, len: usize) -> io::Result<()> {
let encoding = self
.compression_header
.data_series_encoding_map()
.deletion_lengths_encoding()
.ok_or_else(|| {
io::Error::new(
io::ErrorKind::InvalidData,
WriteRecordError::MissingDataSeriesEncoding(DataSeries::DeletionLengths),
)
})?;
let n = i32::try_from(len).map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e))?;
encode_itf8(
encoding,
self.core_data_writer,
self.external_data_writers,
n,
)
}
fn write_reference_skip_length(&mut self, len: usize) -> io::Result<()> {
let encoding = self
.compression_header
.data_series_encoding_map()
.reference_skip_length_encoding()
.ok_or_else(|| {
io::Error::new(
io::ErrorKind::InvalidData,
WriteRecordError::MissingDataSeriesEncoding(DataSeries::ReferenceSkipLength),
)
})?;
let n = i32::try_from(len).map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e))?;
encode_itf8(
encoding,
self.core_data_writer,
self.external_data_writers,
n,
)
}
fn write_soft_clip(&mut self, bases: &[Base]) -> io::Result<()> {
let encoding = self
.compression_header
.data_series_encoding_map()
.soft_clip_encoding()
.ok_or_else(|| {
io::Error::new(
io::ErrorKind::InvalidData,
WriteRecordError::MissingDataSeriesEncoding(DataSeries::SoftClip),
)
})?;
let raw_bases: Vec<_> = bases.iter().copied().map(u8::from).collect();
encode_byte_array(
encoding,
self.core_data_writer,
self.external_data_writers,
&raw_bases,
)
}
fn write_padding(&mut self, len: usize) -> io::Result<()> {
let encoding = self
.compression_header
.data_series_encoding_map()
.padding_encoding()
.ok_or_else(|| {
io::Error::new(
io::ErrorKind::InvalidData,
WriteRecordError::MissingDataSeriesEncoding(DataSeries::Padding),
)
})?;
let n = i32::try_from(len).map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e))?;
encode_itf8(
encoding,
self.core_data_writer,
self.external_data_writers,
n,
)
}
fn write_hard_clip(&mut self, len: usize) -> io::Result<()> {
let encoding = self
.compression_header
.data_series_encoding_map()
.hard_clip_encoding()
.ok_or_else(|| {
io::Error::new(
io::ErrorKind::InvalidData,
WriteRecordError::MissingDataSeriesEncoding(DataSeries::HardClip),
)
})?;
let n = i32::try_from(len).map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e))?;
encode_itf8(
encoding,
self.core_data_writer,
self.external_data_writers,
n,
)
}
fn write_mapping_quality(
&mut self,
mapping_quality: Option<sam::record::MappingQuality>,
) -> io::Result<()> {
let encoding = self
.compression_header
.data_series_encoding_map()
.mapping_qualities_encoding()
.ok_or_else(|| {
io::Error::new(
io::ErrorKind::InvalidData,
WriteRecordError::MissingDataSeriesEncoding(DataSeries::MappingQualities),
)
})?;
let mapping_quality = i32::from(
mapping_quality
.map(u8::from)
.unwrap_or(sam::record::mapping_quality::MISSING),
);
encode_itf8(
encoding,
self.core_data_writer,
self.external_data_writers,
mapping_quality,
)
}
fn write_unmapped_read(&mut self, record: &Record) -> io::Result<()> {
for &base in record.bases().as_ref() {
self.write_base(base)?;
}
if record.cram_flags().are_quality_scores_stored_as_array() {
for &score in record.quality_scores().as_ref() {
self.write_quality_score(score)?;
}
}
Ok(())
}
}
fn encode_byte<W, X>(
encoding: &Encoding<Byte>,
_core_data_writer: &mut BitWriter<W>,
external_data_writers: &mut HashMap<block::ContentId, X>,
value: u8,
) -> io::Result<()>
where
W: Write,
X: Write,
{
match encoding.get() {
Byte::External(block_content_id) => {
let writer = external_data_writers
.get_mut(block_content_id)
.ok_or_else(|| {
io::Error::new(
io::ErrorKind::InvalidData,
WriteRecordError::MissingExternalBlock(*block_content_id),
)
})?;
writer.write_u8(value)
}
_ => todo!("encode_byte: {:?}", encoding),
}
}
fn encode_itf8<W, X>(
encoding: &Encoding<Integer>,
_core_data_writer: &mut BitWriter<W>,
external_data_writers: &mut HashMap<block::ContentId, X>,
value: i32,
) -> io::Result<()>
where
W: Write,
X: Write,
{
match encoding.get() {
Integer::External(block_content_id) => {
let writer = external_data_writers
.get_mut(block_content_id)
.ok_or_else(|| {
io::Error::new(
io::ErrorKind::InvalidData,
WriteRecordError::MissingExternalBlock(*block_content_id),
)
})?;
write_itf8(writer, value)
}
_ => todo!("encode_itf8: {:?}", encoding),
}
}
fn encode_byte_array<W, X>(
encoding: &Encoding<ByteArray>,
core_data_writer: &mut BitWriter<W>,
external_data_writers: &mut HashMap<block::ContentId, X>,
data: &[u8],
) -> io::Result<()>
where
W: Write,
X: Write,
{
match encoding.get() {
ByteArray::ByteArrayLen(len_encoding, value_encoding) => {
let len = i32::try_from(data.len())
.map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e))?;
encode_itf8(len_encoding, core_data_writer, external_data_writers, len)?;
for &value in data {
encode_byte(
value_encoding,
core_data_writer,
external_data_writers,
value,
)?;
}
Ok(())
}
ByteArray::ByteArrayStop(stop_byte, block_content_id) => {
let writer = external_data_writers
.get_mut(block_content_id)
.ok_or_else(|| {
io::Error::new(
io::ErrorKind::InvalidInput,
WriteRecordError::MissingExternalBlock(*block_content_id),
)
})?;
writer.write_all(data)?;
writer.write_u8(*stop_byte)?;
Ok(())
}
}
}