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 }