134 template <
typename stream_type,
135 typename seq_legal_alph_type,
136 typename stream_pos_type,
142 stream_pos_type & position_buffer,
145 qual_type & qualities);
147 template <
typename stream_type,
155 qual_type && qualities);
157 template <
typename stream_type,
158 typename seq_legal_alph_type,
159 typename ref_seqs_type,
160 typename ref_ids_type,
161 typename stream_pos_type,
164 typename offset_type,
165 typename ref_seq_type,
166 typename ref_id_type,
167 typename ref_offset_type,
173 typename tag_dict_type,
174 typename e_value_type,
175 typename bit_score_type>
178 ref_seqs_type & ref_seqs,
180 stream_pos_type & position_buffer,
184 offset_type & offset,
185 ref_seq_type & SEQAN3_DOXYGEN_ONLY(ref_seq),
186 ref_id_type & ref_id,
187 ref_offset_type & ref_offset,
188 cigar_type & cigar_vector,
192 tag_dict_type & tag_dict,
193 e_value_type & SEQAN3_DOXYGEN_ONLY(e_value),
194 bit_score_type & SEQAN3_DOXYGEN_ONLY(bit_score));
196 template <
typename stream_type,
197 typename header_type,
200 typename ref_seq_type,
201 typename ref_id_type,
204 typename tag_dict_type,
205 typename e_value_type,
206 typename bit_score_type>
209 header_type && header,
213 int32_t
const offset,
214 ref_seq_type && SEQAN3_DOXYGEN_ONLY(ref_seq),
215 ref_id_type && ref_id,
221 tag_dict_type && tag_dict,
222 e_value_type && SEQAN3_DOXYGEN_ONLY(e_value),
223 bit_score_type && SEQAN3_DOXYGEN_ONLY(bit_score));
233 sam_file_header<> default_header{};
236 bool ref_info_present_in_header{
false};
245 template <
typename t>
246 decltype(
auto) default_or(t && v)
const noexcept
248 return std::forward<t>(v);
251 template <
typename stream_view_type, arithmetic value_type>
255 template <
typename stream_view_type>
258 template <
typename stream_view_type>
259 void read_sam_dict_field(stream_view_type && stream_view, sam_tag_dictionary & target);
261 template <
typename stream_it_t, std::ranges::forward_range field_type>
262 void write_range_or_asterisk(stream_it_t & stream_it, field_type && field_value);
264 template <
typename stream_it_t>
265 void write_range_or_asterisk(stream_it_t & stream_it,
char const *
const field_value);
267 template <
typename stream_it_t>
268 void write_tag_fields(stream_it_t & stream, sam_tag_dictionary
const & tag_dict,
char const separator);
272template <
typename stream_type,
273 typename seq_legal_alph_type,
274 typename stream_pos_type,
280 stream_pos_type & position_buffer,
283 qual_type & qualities)
309 if constexpr (!detail::decays_to_ignore_v<seq_type>)
310 if (std::ranges::distance(
sequence) == 0)
311 throw parse_error{
"The sequence information must not be empty."};
312 if constexpr (!detail::decays_to_ignore_v<id_type>)
313 if (std::ranges::distance(
id) == 0)
314 throw parse_error{
"The id information must not be empty."};
317 id =
id | detail::take_until_and_consume(
is_space) | ranges::to<id_type>();
321template <
typename stream_type,
329 qual_type && qualities)
339 default_or(qualities),
355template <
typename stream_type,
356 typename seq_legal_alph_type,
357 typename ref_seqs_type,
358 typename ref_ids_type,
359 typename stream_pos_type,
362 typename offset_type,
363 typename ref_seq_type,
364 typename ref_id_type,
365 typename ref_offset_type,
371 typename tag_dict_type,
372 typename e_value_type,
373 typename bit_score_type>
377 ref_seqs_type & ref_seqs,
379 stream_pos_type & position_buffer,
383 offset_type & offset,
384 ref_seq_type & SEQAN3_DOXYGEN_ONLY(ref_seq),
385 ref_id_type & ref_id,
386 ref_offset_type & ref_offset,
387 cigar_type & cigar_vector,
391 tag_dict_type & tag_dict,
392 e_value_type & SEQAN3_DOXYGEN_ONLY(e_value),
393 bit_score_type & SEQAN3_DOXYGEN_ONLY(bit_score))
395 static_assert(detail::decays_to_ignore_v<ref_offset_type>
396 || detail::is_type_specialisation_of_v<ref_offset_type, std::optional>,
397 "The ref_offset must be a specialisation of std::optional.");
399 auto stream_view = detail::istreambuf(stream);
400 auto field_view = stream_view | detail::take_until_or_throw_and_consume(is_char<'\t'>);
402 int32_t ref_offset_tmp{};
403 std::ranges::range_value_t<
decltype(header.
ref_ids())> ref_id_tmp{};
409 read_header(stream_view, header, ref_seqs);
416 position_buffer = stream.tellg();
420 if constexpr (!detail::decays_to_ignore_v<id_type>)
421 read_forward_range_field(field_view,
id);
423 detail::consume(field_view);
425 uint16_t flag_integral{};
426 read_arithmetic_field(field_view, flag_integral);
429 read_forward_range_field(field_view, ref_id_tmp);
430 check_and_assign_ref_id(
ref_id, ref_id_tmp, header, ref_seqs);
432 read_arithmetic_field(field_view, ref_offset_tmp);
435 if (ref_offset_tmp == -1)
437 else if (ref_offset_tmp > -1)
439 else if (ref_offset_tmp < -1)
440 throw format_error{
"No negative values are allowed for field::ref_offset."};
442 if constexpr (!detail::decays_to_ignore_v<mapq_type>)
443 read_arithmetic_field(field_view,
mapq);
445 detail::consume(field_view);
449 if constexpr (!detail::decays_to_ignore_v<cigar_type>)
453 int32_t ref_length{0}, seq_length{0};
454 std::tie(cigar_vector, ref_length, seq_length) = detail::parse_cigar(field_view);
455 int32_t soft_clipping_end{};
456 int32_t offset_tmp{};
457 transfer_soft_clipping_to(cigar_vector, offset_tmp, soft_clipping_end);
467 detail::consume(field_view);
472 if constexpr (!detail::decays_to_ignore_v<mate_type>)
474 std::ranges::range_value_t<
decltype(header.
ref_ids())> tmp_mate_ref_id{};
475 read_forward_range_field(field_view, tmp_mate_ref_id);
477 if (tmp_mate_ref_id ==
"=")
479 if constexpr (!detail::decays_to_ignore_v<ref_id_type>)
482 check_and_assign_ref_id(get<0>(
mate), ref_id_tmp, header, ref_seqs);
486 check_and_assign_ref_id(get<0>(
mate), tmp_mate_ref_id, header, ref_seqs);
490 read_arithmetic_field(field_view, tmp_pnext);
493 get<1>(
mate) = --tmp_pnext;
494 else if (tmp_pnext < 0)
495 throw format_error{
"No negative values are allowed at the mate mapping position."};
498 read_arithmetic_field(field_view, get<2>(
mate));
502 for (
size_t i = 0; i < 3u; ++i)
504 detail::consume(field_view);
512 constexpr auto is_legal_alph = char_is_valid_for<seq_legal_alph_type>;
516 [is_legal_alph](
char const c)
518 if (!is_legal_alph(c))
520 + detail::type_name_as_string<seq_legal_alph_type>
521 +
"> evaluated to false on " + detail::make_printable(c)};
525 if constexpr (detail::decays_to_ignore_v<seq_type>)
527 detail::consume(seq_stream);
531 read_forward_range_field(seq_stream,
seq);
541 auto const tab_or_end = is_char<'\t'> || is_char<'\r'> || is_char<'\n'>;
542 auto qual_view = stream_view | detail::take_until_or_throw(tab_or_end);
543 if constexpr (!detail::decays_to_ignore_v<qual_type>)
544 read_forward_range_field(qual_view,
qual);
546 detail::consume(qual_view);
548 if constexpr (!detail::decays_to_ignore_v<seq_type> && !detail::decays_to_ignore_v<qual_type>)
550 if (std::ranges::distance(
seq) != 0 && std::ranges::distance(
qual) != 0
551 && std::ranges::distance(
seq) != std::ranges::distance(
qual))
553 throw format_error{detail::to_string(
"Sequence length (",
554 std::ranges::distance(
seq),
555 ") and quality length (",
556 std::ranges::distance(
qual),
557 ") must be the same.")};
566 auto stream_until_tab_or_end = stream_view | detail::take_until_or_throw(tab_or_end);
567 if constexpr (!detail::decays_to_ignore_v<tag_dict_type>)
568 read_sam_dict_field(stream_until_tab_or_end, tag_dict);
570 detail::consume(stream_until_tab_or_end);
573 detail::consume(stream_view | detail::take_until(!(is_char<'\r'> || is_char<'\n'>)));
577template <
typename stream_type,
578 typename header_type,
581 typename ref_seq_type,
582 typename ref_id_type,
585 typename tag_dict_type,
586 typename e_value_type,
587 typename bit_score_type>
590 header_type && header,
594 int32_t
const offset,
595 ref_seq_type && SEQAN3_DOXYGEN_ONLY(ref_seq),
596 ref_id_type && ref_id,
602 tag_dict_type && tag_dict,
603 e_value_type && SEQAN3_DOXYGEN_ONLY(e_value),
604 bit_score_type && SEQAN3_DOXYGEN_ONLY(bit_score))
623 "The seq object must be a std::ranges::forward_range over "
624 "letters that model seqan3::alphabet.");
627 "The id object must be a std::ranges::forward_range over "
628 "letters that model seqan3::alphabet.");
630 if constexpr (!detail::decays_to_ignore_v<ref_id_type>)
632 static_assert((std::ranges::forward_range<ref_id_type> || std::integral<std::remove_reference_t<ref_id_type>>
633 || detail::is_type_specialisation_of_v<std::remove_cvref_t<ref_id_type>,
std::optional>),
634 "The ref_id object must be a std::ranges::forward_range "
635 "over letters that model seqan3::alphabet.");
637 if constexpr (std::integral<std::remove_cvref_t<ref_id_type>>
638 || detail::is_type_specialisation_of_v<std::remove_cvref_t<ref_id_type>,
std::optional>)
639 static_assert(!detail::decays_to_ignore_v<header_type>,
640 "If you give indices as reference id information the header must also be present.");
644 "The qual object must be a std::ranges::forward_range "
645 "over letters that model seqan3::alphabet.");
648 "The mate object must be a std::tuple of size 3 with "
649 "1) a std::ranges::forward_range with a value_type modelling seqan3::alphabet, "
650 "2) a std::integral or std::optional<std::integral>, and "
651 "3) a std::integral.");
654 ((std::ranges::forward_range<decltype(std::get<0>(
mate))>
656 || detail::is_type_specialisation_of_v<
658 std::optional>)&&(std::integral<std::remove_cvref_t<decltype(std::get<1>(
mate))>>
659 || detail::is_type_specialisation_of_v<
661 std::optional>)&&std::integral<std::remove_cvref_t<decltype(std::get<2>(
mate))>>),
662 "The mate object must be a std::tuple of size 3 with "
663 "1) a std::ranges::forward_range with a value_type modelling seqan3::alphabet, "
664 "2) a std::integral or std::optional<std::integral>, and "
665 "3) a std::integral.");
667 if constexpr (std::integral<std::remove_cvref_t<decltype(std::get<0>(
mate))>>
670 static_assert(!detail::decays_to_ignore_v<header_type>,
671 "If you give indices as mate reference id information the header must also be present.");
674 "The tag_dict object must be of type seqan3::sam_tag_dictionary.");
679 if constexpr (!detail::decays_to_ignore_v<header_type> && !detail::decays_to_ignore_v<ref_id_type>
680 && !std::integral<std::remove_reference_t<ref_id_type>>
681 && !detail::is_type_specialisation_of_v<std::remove_reference_t<ref_id_type>,
std::optional>)
688 if constexpr (std::ranges::contiguous_range<
decltype(
ref_id)> && std::ranges::sized_range<
decltype(
ref_id)>
689 && std::ranges::borrowed_range<
decltype(
ref_id)>)
698 "The ref_id type is not convertible to the reference id information stored in the "
699 "reference dictionary of the header object.");
707 "' was not in the list of references:",
713 throw format_error{
"The ref_offset object must be a std::integral >= 0."};
718 if constexpr (!detail::decays_to_ignore_v<header_type>)
722 write_header(stream, options, header);
723 header_was_written =
true;
731 detail::fast_ostreambuf_iterator stream_it{*stream.rdbuf()};
732 constexpr char separator{
'\t'};
734 write_range_or_asterisk(stream_it,
id);
735 *stream_it = separator;
737 stream_it.write_number(
static_cast<uint16_t
>(
flag));
738 *stream_it = separator;
740 if constexpr (!detail::decays_to_ignore_v<ref_id_type>)
742 if constexpr (std::integral<std::remove_reference_t<ref_id_type>>)
744 write_range_or_asterisk(stream_it, (header.
ref_ids())[
ref_id]);
746 else if constexpr (detail::is_type_specialisation_of_v<std::remove_reference_t<ref_id_type>,
std::optional>)
749 write_range_or_asterisk(stream_it, (header.
ref_ids())[
ref_id.value()]);
755 write_range_or_asterisk(stream_it,
ref_id);
763 *stream_it = separator;
766 stream_it.write_number(
ref_offset.value_or(-1) + 1);
767 *stream_it = separator;
769 stream_it.write_number(
static_cast<unsigned>(
mapq));
770 *stream_it = separator;
772 if (!std::ranges::empty(cigar_vector))
774 for (
auto & c : cigar_vector)
775 stream_it.write_range(c.to_string());
782 *stream_it = separator;
784 if constexpr (std::integral<std::remove_reference_t<decltype(get<0>(
mate))>>)
786 write_range_or_asterisk(stream_it, (header.
ref_ids())[get<0>(
mate)]);
788 else if constexpr (detail::is_type_specialisation_of_v<std::remove_reference_t<decltype(get<0>(
mate))>,
791 if (get<0>(
mate).has_value())
792 write_range_or_asterisk(stream_it, header.
ref_ids()[get<0>(
mate).value()]);
798 write_range_or_asterisk(stream_it, get<0>(
mate));
801 *stream_it = separator;
803 if constexpr (detail::is_type_specialisation_of_v<std::remove_cvref_t<decltype(get<1>(
mate))>,
std::optional>)
806 stream_it.write_number(get<1>(
mate).value_or(-1) + 1);
807 *stream_it = separator;
811 stream_it.write_number(get<1>(
mate));
812 *stream_it = separator;
815 stream_it.write_number(get<2>(
mate));
816 *stream_it = separator;
818 write_range_or_asterisk(stream_it,
seq);
819 *stream_it = separator;
821 write_range_or_asterisk(stream_it,
qual);
823 write_tag_fields(stream_it, tag_dict, separator);
845template <
typename stream_view_type, arithmetic value_type>
847 stream_view_type && stream_view,
853 read_arithmetic_field(stream_view | detail::take_until(is_char<','>), value);
859 variant = std::move(tmp_vector);
875template <
typename stream_view_type>
885 read_byte_field(stream_view | detail::take_exactly_or_throw(2), value);
889 throw format_error{
"Hexadecimal tag has an uneven number of digits!"};
895 variant = std::move(tmp_vector);
915template <
typename stream_view_type>
916inline void format_sam::read_sam_dict_field(stream_view_type && stream_view, sam_tag_dictionary & target)
943 read_arithmetic_field(stream_view, tmp);
950 read_arithmetic_field(stream_view, tmp);
956 target[tag] = stream_view | ranges::to<std::string>();
961 read_sam_byte_vector(target[tag], stream_view);
970 switch (array_value_type_id)
973 read_sam_dict_vector(target[tag], stream_view, int8_t{});
976 read_sam_dict_vector(target[tag], stream_view, uint8_t{});
979 read_sam_dict_vector(target[tag], stream_view, int16_t{});
982 read_sam_dict_vector(target[tag], stream_view, uint16_t{});
985 read_sam_dict_vector(target[tag], stream_view, int32_t{});
988 read_sam_dict_vector(target[tag], stream_view, uint32_t{});
991 read_sam_dict_vector(target[tag], stream_view,
float{});
994 throw format_error{
std::string(
"The first character in the numerical ")
995 +
"id of a SAM tag must be one of [cCsSiIf] but '" + array_value_type_id
1001 throw format_error{
std::string(
"The second character in the numerical id of a "
1002 "SAM tag must be one of [A,i,Z,H,B,f] but '")
1003 + type_id +
"' was given."};
1014template <
typename stream_it_t, std::ranges::forward_range field_type>
1015inline void format_sam::write_range_or_asterisk(stream_it_t & stream_it, field_type && field_value)
1017 if (std::ranges::empty(field_value))
1023 if constexpr (std::same_as<std::remove_cvref_t<std::ranges::range_reference_t<field_type>>,
char>)
1024 stream_it.write_range(field_value);
1036template <
typename stream_it_t>
1037inline void format_sam::write_range_or_asterisk(stream_it_t & stream_it,
char const *
const field_value)
1049template <
typename stream_it_t>
1051format_sam::write_tag_fields(stream_it_t & stream_it, sam_tag_dictionary
const & tag_dict,
char const separator)
1053 auto const stream_variant_fn = [&stream_it](
auto && arg)
1057 if constexpr (std::ranges::input_range<T>)
1059 if constexpr (std::same_as<std::remove_cvref_t<std::ranges::range_reference_t<T>>,
char>)
1061 stream_it.write_range(arg);
1063 else if constexpr (std::same_as<std::remove_cvref_t<std::ranges::range_reference_t<T>>,
std::byte>)
1065 if (!std::ranges::empty(arg))
1072 stream_it.write_number(std::to_integer<uint8_t>(elem));
1078 if (!std::ranges::empty(arg))
1085 stream_it.write_number(elem);
1090 else if constexpr (std::same_as<std::remove_cvref_t<T>,
char>)
1096 stream_it.write_number(arg);
1100 for (
auto & [tag, variant] : tag_dict)
1102 *stream_it = separator;
1104 char const char0 = tag / 256;
1105 char const char1 = tag % 256;
1110 *stream_it = detail::sam_tag_type_char[variant.
index()];
1113 if (detail::sam_tag_type_char_extra[variant.
index()] !=
'\0')
1115 *stream_it = detail::sam_tag_type_char_extra[variant.
index()];
Core alphabet concept and free function/type trait wrappers.
The SAM tag dictionary class that stores all optional SAM fields.
Definition: sam_tag_dictionary.hpp:343
Provides seqan3::detail::fast_ostreambuf_iterator.
auto const to_char
A view that calls seqan3::to_char() on each element in the input range.
Definition: to_char.hpp:63
sam_flag
An enum flag that describes the properties of an aligned read (given as a SAM record).
Definition: sam_flag.hpp:76
@ none
None of the flags below are set.
@ flag
The alignment flag (bit information), uint16_t value.
@ ref_offset
Sequence (seqan3::field::ref_seq) relative start position (0-based), unsigned value.
@ mapq
The mapping quality of the seqan3::field::seq alignment, usually a Phred-scaled score.
@ offset
Sequence (seqan3::field::seq) relative start position (0-based), unsigned value.
@ mate
The mate pair information given as a std::tuple of reference name, offset and template length.
@ ref_id
The identifier of the (reference) sequence that seqan3::field::seq was aligned to.
@ seq
The "sequence", usually a range of nucleotides or amino acids.
@ qual
The qualities, usually in Phred score notation.
constexpr auto is_space
Checks whether c is a space character.
Definition: predicate.hpp:125
typename decltype(detail::split_after< i >(list_t{}))::second_type drop
Return a seqan3::type_list of the types in the input type list, except the first n.
Definition: type_list/traits.hpp:395
decltype(detail::transform< trait_t >(list_t{})) transform
Apply a transformation trait to every type in the list and return a seqan3::type_list of the results.
Definition: type_list/traits.hpp:470
constexpr size_t size
The size of a type pack.
Definition: type_pack/traits.hpp:146
The generic alphabet concept that covers most data types used in ranges.
Checks whether from can be implicityly converted to to.
The generic concept for a (biological) sequence.
Whether a type behaves like a tuple.
Auxiliary functions for the alignment IO.
Provides seqan3::detail::istreambuf.
The main SeqAn3 namespace.
Definition: aligned_sequence_concept.hpp:29
Provides seqan3::sam_file_output_options.
Provides helper data structures for the seqan3::sam_file_output.
Provides the seqan3::sam_tag_dictionary class and auxiliaries.
Provides seqan3::sequence_file_output_options.
Provides seqan3::views::slice.
Thrown if there is a parse error, such as reading an unexpected character from an input stream.
Definition: io/exception.hpp:48
The options type defines various option members that influence the behavior of all or some formats.
Definition: sam_file/output_options.hpp:26
bool add_carriage_return
The default plain text line-ending is "\n", but on Windows an additional carriage return is recommend...
Definition: sam_file/output_options.hpp:30
bool sam_require_header
Whether to require a header for SAM files.
Definition: sam_file/output_options.hpp:44
The options type defines various option members that influence the behaviour of all or some formats.
Definition: sequence_file/output_options.hpp:26
Provides seqan3::views::take_until and seqan3::views::take_until_or_throw.
Provides seqan3::ranges::to.
Provides seqan3::views::to_char.
Provides traits to inspect some information of a type, for example its name.
Provides seqan3::tuple_like.