1 module bio.std.sff.writer; 2 3 import bio.std.sff.constants; 4 import bio.std.sff.utils.roundup; 5 6 import bio.core.utils.stream; 7 import contrib.undead.stream; 8 import std.system; 9 10 /// Class for outputting SFF files 11 class SffWriter { 12 13 /// Create new writer. 14 this(string filename, string flow_order, string key_sequence) 15 { 16 _filename = filename; 17 _flow_order = flow_order; 18 _key_seq = key_sequence; 19 20 auto f = new bio.core.utils.stream.File(filename, "wb+"); 21 auto stream = new BufferedStream(f, 1024576); 22 _endian_stream = new EndianStream(stream, Endian.bigEndian); 23 24 writeHeader(); 25 } 26 27 /// Flow order 28 string flow_order() @property const { 29 return _flow_order; 30 } 31 32 /// Key sequence 33 string key_sequence() @property const { 34 return _key_seq; 35 } 36 37 /// Add a read to the end of file 38 void append(R)(R sff_read) { 39 // compute read_header_length 40 ushort exact_read_header_length = cast(ushort)(16 + sff_read.name.length); 41 ushort read_header_length = roundup(exact_read_header_length); 42 43 _endian_stream.write(read_header_length); 44 _endian_stream.write(cast(ushort)sff_read.name.length); 45 _endian_stream.write(cast(uint)sff_read.bases.length); 46 _endian_stream.write(sff_read.clip_qual_left); 47 _endian_stream.write(sff_read.clip_qual_right); 48 _endian_stream.write(sff_read.clip_adapter_left); 49 _endian_stream.write(sff_read.clip_adapter_right); 50 _endian_stream.writeExact(sff_read.name.ptr, sff_read.name.length); 51 for (size_t i = 0; i < read_header_length - exact_read_header_length; ++i) 52 _endian_stream.write(cast(ubyte)0); 53 54 for (size_t i = 0; i < _flow_order.length; ++i) 55 _endian_stream.write(sff_read.flowgram_values[i]); 56 57 auto n_bases = sff_read.bases.length; 58 _endian_stream.writeExact(sff_read.flow_index_per_base.ptr, n_bases); 59 _endian_stream.writeExact(sff_read.bases.ptr, n_bases); 60 _endian_stream.writeExact(sff_read.quality_scores.ptr, n_bases); 61 62 auto k = 2 * _flow_order.length + 3 * n_bases; 63 auto padding = roundup(k) - k; 64 65 for (size_t i = 0; i < padding; ++i) 66 _endian_stream.write(cast(ubyte)0); 67 68 ++_n_reads; 69 } 70 71 /// Flush all buffers and update number of reads in the file header 72 void finish() { 73 updateNumberOfReads(); 74 _endian_stream.close(); 75 } 76 77 private { 78 string _filename; 79 string _flow_order; 80 string _key_seq; 81 Stream _endian_stream; 82 83 uint _n_reads; 84 85 ushort _exact_header_len() @property const { 86 return cast(ushort)(31 + _flow_order.length + _key_seq.length); 87 } 88 89 ushort _header_len() @property const { 90 return roundup(_exact_header_len); 91 } 92 93 void writeHeader() { 94 _endian_stream.write(SFF_MAGIC); 95 _endian_stream.writeExact(SFF_VERSION.ptr, 4); 96 _endian_stream.write(0UL); 97 _endian_stream.write(0U); 98 _endian_stream.write(_n_reads); 99 _endian_stream.write(_header_len); 100 _endian_stream.write(cast(ushort)_key_seq.length); 101 _endian_stream.write(cast(ushort)_flow_order.length); 102 _endian_stream.write(cast(ubyte)1); 103 _endian_stream.writeExact(_flow_order.ptr, _flow_order.length); 104 _endian_stream.writeExact(_key_seq.ptr, _key_seq.length); 105 for (size_t i = 0; i < _header_len - _exact_header_len; ++i) 106 _endian_stream.write(cast(ubyte)0); 107 } 108 109 void updateNumberOfReads() { 110 auto old_pos = _endian_stream.position; 111 _endian_stream.position = 20; 112 _endian_stream.write(_n_reads); 113 _endian_stream.position = old_pos; 114 } 115 } 116 }