1use crate::dataset::{DataToken, SeqTokenType};
10use crate::stateful::encode::StatefulEncoder;
11use dicom_core::{DataElementHeader, Length, Tag, VR};
12use dicom_encoding::encode::EncodeTo;
13use dicom_encoding::text::SpecificCharacterSet;
14use dicom_encoding::transfer_syntax::DynEncoder;
15use dicom_encoding::TransferSyntax;
16use snafu::{Backtrace, OptionExt, ResultExt, Snafu};
17use std::io::Write;
18
19#[derive(Debug, Snafu)]
20#[non_exhaustive]
21pub enum Error {
22 #[snafu(display("Unsupported transfer syntax {} ({})", ts_uid, ts_alias))]
24 UnsupportedTransferSyntax {
25 ts_uid: &'static str,
26 ts_alias: &'static str,
27 backtrace: Backtrace,
28 },
29 #[snafu(display("Unsupported character set {:?}", charset))]
31 UnsupportedCharacterSet {
32 charset: SpecificCharacterSet,
33 backtrace: Backtrace,
34 },
35 #[snafu(display("Unexpected token {:?} without element header", token))]
37 UnexpectedToken {
38 token: DataToken,
39 backtrace: Backtrace,
40 },
41 #[snafu(display("Could not write element header tagged {}", tag))]
42 WriteHeader {
43 tag: Tag,
44 #[snafu(backtrace)]
45 source: crate::stateful::encode::Error,
46 },
47 #[snafu(display("Could not write item header"))]
48 WriteItemHeader {
49 #[snafu(backtrace)]
50 source: crate::stateful::encode::Error,
51 },
52
53 #[snafu(display("Could not write sequence delimiter"))]
54 WriteSequenceDelimiter {
55 #[snafu(backtrace)]
56 source: crate::stateful::encode::Error,
57 },
58
59 #[snafu(display("Could not write item delimiter"))]
60 WriteItemDelimiter {
61 #[snafu(backtrace)]
62 source: crate::stateful::encode::Error,
63 },
64
65 #[snafu(display("Could not write element value"))]
66 WriteValue {
67 #[snafu(backtrace)]
68 source: crate::stateful::encode::Error,
69 },
70}
71
72pub type Result<T> = std::result::Result<T, Error>;
73
74#[derive(Debug)]
76struct SeqToken {
77 typ: SeqTokenType,
79 len: Length,
82}
83
84#[derive(Debug)]
88pub struct DataSetWriter<W, E, T = SpecificCharacterSet> {
89 printer: StatefulEncoder<W, E, T>,
90 seq_tokens: Vec<SeqToken>,
91 last_de: Option<DataElementHeader>,
92}
93
94impl<'w, W: 'w> DataSetWriter<W, DynEncoder<'w, W>>
95where
96 W: Write,
97{
98 pub fn with_ts(to: W, ts: &TransferSyntax) -> Result<Self> {
101 let encoder = ts.encoder_for().context(UnsupportedTransferSyntaxSnafu {
102 ts_uid: ts.uid(),
103 ts_alias: ts.name(),
104 })?;
105 Ok(DataSetWriter::new_with_codec(
106 to,
107 encoder,
108 SpecificCharacterSet::default(),
109 ))
110 }
111
112 pub fn with_ts_cs(to: W, ts: &TransferSyntax, charset: SpecificCharacterSet) -> Result<Self> {
120 let encoder = ts.encoder_for().context(UnsupportedTransferSyntaxSnafu {
121 ts_uid: ts.uid(),
122 ts_alias: ts.name(),
123 })?;
124 Ok(DataSetWriter::new_with_codec(to, encoder, charset))
125 }
126}
127
128impl<W, E> DataSetWriter<W, E> {
129 pub fn new(to: W, encoder: E) -> Self {
130 DataSetWriter {
131 printer: StatefulEncoder::new(to, encoder, SpecificCharacterSet::default()),
132 seq_tokens: Vec::new(),
133 last_de: None,
134 }
135 }
136}
137
138impl<W, E, T> DataSetWriter<W, E, T> {
139 pub fn new_with_codec(to: W, encoder: E, text: T) -> Self {
140 DataSetWriter {
141 printer: StatefulEncoder::new(to, encoder, text),
142 seq_tokens: Vec::new(),
143 last_de: None,
144 }
145 }
146}
147
148impl<W, E> DataSetWriter<W, E>
149where
150 W: Write,
151 E: EncodeTo<W>,
152{
153 #[inline]
155 pub fn write_sequence<I>(&mut self, tokens: I) -> Result<()>
156 where
157 I: IntoIterator<Item = DataToken>,
158 {
159 for token in tokens {
160 self.write(token)?;
161 }
162
163 Ok(())
164 }
165
166 pub fn write(&mut self, token: DataToken) -> Result<()> {
168 match token {
173 DataToken::SequenceStart { len, .. } => {
174 self.seq_tokens.push(SeqToken {
175 typ: SeqTokenType::Sequence,
176 len,
177 });
178 self.write_impl(&token)?;
179 Ok(())
180 }
181 DataToken::ItemStart { len } => {
182 self.seq_tokens.push(SeqToken {
183 typ: SeqTokenType::Item,
184 len,
185 });
186 self.write_impl(&token)?;
187 Ok(())
188 }
189 DataToken::ItemEnd => {
190 if let Some(seq_start) = self.seq_tokens.pop() {
192 if seq_start.typ == SeqTokenType::Item && seq_start.len.is_undefined() {
193 self.write_impl(&token)?;
194 }
195 }
196 Ok(())
197 }
198 DataToken::SequenceEnd => {
199 if let Some(seq_start) = self.seq_tokens.pop() {
201 if seq_start.typ == SeqTokenType::Sequence && seq_start.len.is_undefined() {
202 self.write_impl(&token)?;
203 }
204 }
205 Ok(())
206 }
207 DataToken::ElementHeader(de) => {
208 self.last_de = Some(de);
210
211 Ok(())
213 }
214 token @ DataToken::PixelSequenceStart => {
215 self.seq_tokens.push(SeqToken {
216 typ: SeqTokenType::Sequence,
217 len: Length::UNDEFINED,
218 });
219 self.write_impl(&token)
220 }
221 token @ DataToken::ItemValue(_)
222 | token @ DataToken::PrimitiveValue(_)
223 | token @ DataToken::OffsetTable(_) => self.write_impl(&token),
224 }
225 }
226
227 fn write_impl(&mut self, token: &DataToken) -> Result<()> {
228 match token {
229 DataToken::ElementHeader(header) => {
230 self.printer
231 .encode_element_header(*header)
232 .context(WriteHeaderSnafu { tag: header.tag })?;
233 }
234 DataToken::SequenceStart { tag, len } => {
235 self.printer
236 .encode_element_header(DataElementHeader::new(*tag, VR::SQ, *len))
237 .context(WriteHeaderSnafu { tag: *tag })?;
238 }
239 DataToken::PixelSequenceStart => {
240 let tag = Tag(0x7fe0, 0x0010);
241 self.printer
242 .encode_element_header(DataElementHeader::new(tag, VR::OB, Length::UNDEFINED))
243 .context(WriteHeaderSnafu { tag })?;
244 }
245 DataToken::SequenceEnd => {
246 self.printer
247 .encode_sequence_delimiter()
248 .context(WriteSequenceDelimiterSnafu)?;
249 }
250 DataToken::ItemStart { len } => {
251 self.printer
252 .encode_item_header(len.0)
253 .context(WriteItemHeaderSnafu)?;
254 }
255 DataToken::ItemEnd => {
256 self.printer
257 .encode_item_delimiter()
258 .context(WriteItemDelimiterSnafu)?;
259 }
260 DataToken::PrimitiveValue(ref value) => {
261 let last_de = self.last_de.take().with_context(|| UnexpectedTokenSnafu {
262 token: token.clone(),
263 })?;
264
265 self.printer
266 .encode_primitive_element(&last_de, value)
267 .context(WriteValueSnafu)?;
268 self.last_de = None;
269 }
270 DataToken::OffsetTable(table) => {
271 self.printer
272 .encode_offset_table(table)
273 .context(WriteValueSnafu)?;
274 }
275 DataToken::ItemValue(data) => {
276 self.printer.write_bytes(data).context(WriteValueSnafu)?;
277 }
278 }
279 Ok(())
280 }
281}
282
283#[cfg(test)]
284mod tests {
285 use super::super::DataToken;
286 use super::DataSetWriter;
287 use dicom_core::{
288 header::{DataElementHeader, Length},
289 value::PrimitiveValue,
290 Tag, VR,
291 };
292 use dicom_encoding::encode::{explicit_le::ExplicitVRLittleEndianEncoder, EncoderFor};
293
294 fn validate_dataset_writer<I>(tokens: I, ground_truth: &[u8])
295 where
296 I: IntoIterator<Item = DataToken>,
297 {
298 let mut raw_out: Vec<u8> = vec![];
299 let encoder = EncoderFor::new(ExplicitVRLittleEndianEncoder::default());
300 let mut dset_writer = DataSetWriter::new(&mut raw_out, encoder);
301
302 dset_writer.write_sequence(tokens).unwrap();
303
304 assert_eq!(raw_out, ground_truth);
305 }
306
307 #[test]
308 fn write_sequence_explicit() {
309 let tokens = vec![
310 DataToken::SequenceStart {
311 tag: Tag(0x0018, 0x6011),
312 len: Length(46),
313 },
314 DataToken::ItemStart { len: Length(20) },
315 DataToken::ElementHeader(DataElementHeader {
316 tag: Tag(0x0018, 0x6012),
317 vr: VR::US,
318 len: Length(2),
319 }),
320 DataToken::PrimitiveValue(PrimitiveValue::U16([1].as_ref().into())),
321 DataToken::ElementHeader(DataElementHeader {
322 tag: Tag(0x0018, 0x6014),
323 vr: VR::US,
324 len: Length(2),
325 }),
326 DataToken::PrimitiveValue(PrimitiveValue::U16([2].as_ref().into())),
327 DataToken::ItemEnd,
328 DataToken::ItemStart { len: Length(10) },
329 DataToken::ElementHeader(DataElementHeader {
330 tag: Tag(0x0018, 0x6012),
331 vr: VR::US,
332 len: Length(2),
333 }),
334 DataToken::PrimitiveValue(PrimitiveValue::U16([4].as_ref().into())),
335 DataToken::ItemEnd,
336 DataToken::SequenceEnd,
337 DataToken::ElementHeader(DataElementHeader {
338 tag: Tag(0x0020, 0x4000),
339 vr: VR::LT,
340 len: Length(4),
341 }),
342 DataToken::PrimitiveValue(PrimitiveValue::Str("TEST".into())),
343 ];
344
345 #[rustfmt::skip]
346 static GROUND_TRUTH: &[u8] = &[
347 0x18, 0x00, 0x11, 0x60, b'S', b'Q', 0x00, 0x00, 0x2e, 0x00, 0x00, 0x00, 0xfe, 0xff, 0x00, 0xe0, 0x14, 0x00, 0x00, 0x00, 0x18, 0x00, 0x12, 0x60, b'U', b'S', 0x02, 0x00, 0x01, 0x00, 0x18, 0x00, 0x14, 0x60, b'U', b'S', 0x02, 0x00, 0x02, 0x00, 0xfe, 0xff, 0x00, 0xe0, 0x0a, 0x00, 0x00, 0x00, 0x18, 0x00, 0x12, 0x60, b'U', b'S', 0x02, 0x00, 0x04, 0x00, 0x20, 0x00, 0x00, 0x40, b'L', b'T', 0x04, 0x00, b'T', b'E', b'S', b'T', ];
367
368 validate_dataset_writer(tokens, GROUND_TRUTH);
369 }
370
371 #[test]
372 fn write_element_overrides_len() {
373 let tokens = vec![
374 DataToken::ElementHeader(DataElementHeader {
375 tag: Tag(0x0008, 0x0005),
377 vr: VR::CS,
378 len: Length(10),
379 }),
380 DataToken::PrimitiveValue(PrimitiveValue::from("ISO_IR 100")),
381 DataToken::ElementHeader(DataElementHeader {
382 tag: Tag(0x0008, 0x0090),
384 vr: VR::PN,
385 len: Length("Simões^João".len() as u32),
387 }),
388 DataToken::PrimitiveValue(PrimitiveValue::from("Simões^João")),
389 ];
390
391 #[rustfmt::skip]
392 static GROUND_TRUTH: &[u8] = &[
393 0x08, 0x00, 0x05, 0x00, b'C', b'S', 0x0a, 0x00, b'I', b'S', b'O', b'_', b'I', b'R', b' ', b'1', b'0', b'0', 0x08, 0x00, 0x90, 0x00, b'P', b'N', 0x0c, 0x00, b'S', b'i', b'm', 0xF5, b'e', b's', b'^', b'J', b'o', 0xE3, b'o', b' '
404 ];
405
406 validate_dataset_writer(tokens, GROUND_TRUTH);
407 }
408
409 #[test]
410 fn write_sequence_implicit() {
411 let tokens = vec![
412 DataToken::SequenceStart {
413 tag: Tag(0x0018, 0x6011),
414 len: Length::UNDEFINED,
415 },
416 DataToken::ItemStart {
417 len: Length::UNDEFINED,
418 },
419 DataToken::ElementHeader(DataElementHeader {
420 tag: Tag(0x0018, 0x6012),
421 vr: VR::US,
422 len: Length(2),
423 }),
424 DataToken::PrimitiveValue(PrimitiveValue::U16([1].as_ref().into())),
425 DataToken::ElementHeader(DataElementHeader {
426 tag: Tag(0x0018, 0x6014),
427 vr: VR::US,
428 len: Length(2),
429 }),
430 DataToken::PrimitiveValue(PrimitiveValue::U16([2].as_ref().into())),
431 DataToken::ItemEnd,
432 DataToken::ItemStart {
433 len: Length::UNDEFINED,
434 },
435 DataToken::ElementHeader(DataElementHeader {
436 tag: Tag(0x0018, 0x6012),
437 vr: VR::US,
438 len: Length(2),
439 }),
440 DataToken::PrimitiveValue(PrimitiveValue::U16([4].as_ref().into())),
441 DataToken::ItemEnd,
442 DataToken::SequenceEnd,
443 DataToken::ElementHeader(DataElementHeader {
444 tag: Tag(0x0020, 0x4000),
445 vr: VR::LT,
446 len: Length(4),
447 }),
448 DataToken::PrimitiveValue(PrimitiveValue::Str("TEST".into())),
449 ];
450
451 #[rustfmt::skip]
452 static GROUND_TRUTH: &[u8] = &[
453 0x18, 0x00, 0x11, 0x60, b'S', b'Q', 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0x00, 0xe0, 0xff, 0xff, 0xff, 0xff, 0x18, 0x00, 0x12, 0x60, b'U', b'S', 0x02, 0x00, 0x01, 0x00, 0x18, 0x00, 0x14, 0x60, b'U', b'S', 0x02, 0x00, 0x02, 0x00, 0xfe, 0xff, 0x0d, 0xe0, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xff, 0x00, 0xe0, 0xff, 0xff, 0xff, 0xff, 0x18, 0x00, 0x12, 0x60, b'U', b'S', 0x02, 0x00, 0x04, 0x00, 0xfe, 0xff, 0x0d, 0xe0, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xff, 0xdd, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x40, b'L', b'T', 0x04, 0x00, b'T', b'E', b'S', b'T', ];
479
480 validate_dataset_writer(tokens, GROUND_TRUTH);
481 }
482
483 #[test]
484 fn write_sequence_explicit_with_implicit_item_len() {
485 let tokens = vec![
486 DataToken::SequenceStart {
487 tag: Tag(0x0018, 0x6011),
488 len: Length(60),
489 },
490 DataToken::ItemStart {
491 len: Length::UNDEFINED,
492 },
493 DataToken::ElementHeader(DataElementHeader {
494 tag: Tag(0x0018, 0x6012),
495 vr: VR::US,
496 len: Length(2),
497 }),
498 DataToken::PrimitiveValue(PrimitiveValue::U16([1].as_ref().into())),
499 DataToken::ElementHeader(DataElementHeader {
500 tag: Tag(0x0018, 0x6014),
501 vr: VR::US,
502 len: Length(2),
503 }),
504 DataToken::PrimitiveValue(PrimitiveValue::U16([2].as_ref().into())),
505 DataToken::ItemEnd,
506 DataToken::ItemStart {
507 len: Length::UNDEFINED,
508 },
509 DataToken::ElementHeader(DataElementHeader {
510 tag: Tag(0x0018, 0x6012),
511 vr: VR::US,
512 len: Length(2),
513 }),
514 DataToken::PrimitiveValue(PrimitiveValue::U16([4].as_ref().into())),
515 DataToken::ItemEnd,
516 DataToken::SequenceEnd,
517 DataToken::ElementHeader(DataElementHeader {
518 tag: Tag(0x0020, 0x4000),
519 vr: VR::LT,
520 len: Length(4),
521 }),
522 DataToken::PrimitiveValue(PrimitiveValue::Str("TEST".into())),
523 ];
524
525 #[rustfmt::skip]
526 static GROUND_TRUTH: &[u8] = &[
527 0x18, 0x00, 0x11, 0x60, b'S', b'Q', 0x00, 0x00, 0x3c, 0x00, 0x00, 0x00, 0xfe, 0xff, 0x00, 0xe0, 0xff, 0xff, 0xff, 0xff, 0x18, 0x00, 0x12, 0x60, b'U', b'S', 0x02, 0x00, 0x01, 0x00, 0x18, 0x00, 0x14, 0x60, b'U', b'S', 0x02, 0x00, 0x02, 0x00, 0xfe, 0xff, 0x0d, 0xe0, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xff, 0x00, 0xe0, 0xff, 0xff, 0xff, 0xff, 0x18, 0x00, 0x12, 0x60, b'U', b'S', 0x02, 0x00, 0x04, 0x00, 0xfe, 0xff, 0x0d, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x40, b'L', b'T', 0x04, 0x00, b'T', b'E', b'S', b'T', ];
551
552 validate_dataset_writer(tokens, GROUND_TRUTH);
553 }
554
555 #[test]
556 fn write_encapsulated_pixeldata() {
557 let tokens = vec![
558 DataToken::PixelSequenceStart,
559 DataToken::ItemStart { len: Length(0) },
560 DataToken::ItemEnd,
561 DataToken::ItemStart { len: Length(32) },
562 DataToken::ItemValue(vec![0x99; 32]),
563 DataToken::ItemEnd,
564 DataToken::SequenceEnd,
565 DataToken::ElementHeader(DataElementHeader::new(
566 Tag(0xfffc, 0xfffc),
567 VR::OB,
568 Length(8),
569 )),
570 DataToken::PrimitiveValue(PrimitiveValue::U8([0x00; 8].as_ref().into())),
571 ];
572
573 #[rustfmt::skip]
574 static GROUND_TRUTH: &[u8] = &[
575 0xe0, 0x7f, 0x10, 0x00, b'O', b'B', 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0x00, 0xe0, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xff, 0x00, 0xe0, 0x20, 0x00, 0x00, 0x00, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,
587 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,
588 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,
589 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,
590 0xfe, 0xff, 0xdd, 0xe0, 0x00, 0x00, 0x00, 0x00,
593 0xfc, 0xff, 0xfc, 0xff, b'O', b'B', 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
599 ];
600
601 validate_dataset_writer(tokens, GROUND_TRUTH);
602 }
603}