1use crate::dataset::{DataToken, SeqTokenType};
10use crate::stateful::encode::StatefulEncoder;
11use dicom_core::header::Header;
12use dicom_core::{DataElementHeader, Length, Tag, VR};
13use dicom_encoding::encode::EncodeTo;
14use dicom_encoding::text::SpecificCharacterSet;
15use dicom_encoding::transfer_syntax::DynEncoder;
16use dicom_encoding::TransferSyntax;
17use snafu::{Backtrace, OptionExt, ResultExt, Snafu};
18use std::io::Write;
19
20#[derive(Debug, Snafu)]
21#[non_exhaustive]
22pub enum Error {
23 #[snafu(display("Unsupported transfer syntax {} ({})", ts_uid, ts_alias))]
25 UnsupportedTransferSyntax {
26 ts_uid: &'static str,
27 ts_alias: &'static str,
28 backtrace: Backtrace,
29 },
30 #[snafu(display("Unsupported character set {:?}", charset))]
32 UnsupportedCharacterSet {
33 charset: SpecificCharacterSet,
34 backtrace: Backtrace,
35 },
36 #[snafu(display("Unexpected token {:?} without element header", token))]
38 UnexpectedToken {
39 token: DataToken,
40 backtrace: Backtrace,
41 },
42 #[snafu(display("Could not write element header tagged {}", tag))]
43 WriteHeader {
44 tag: Tag,
45 #[snafu(backtrace)]
46 source: crate::stateful::encode::Error,
47 },
48 #[snafu(display("Could not write item header"))]
49 WriteItemHeader {
50 #[snafu(backtrace)]
51 source: crate::stateful::encode::Error,
52 },
53
54 #[snafu(display("Could not write sequence delimiter"))]
55 WriteSequenceDelimiter {
56 #[snafu(backtrace)]
57 source: crate::stateful::encode::Error,
58 },
59
60 #[snafu(display("Could not write item delimiter"))]
61 WriteItemDelimiter {
62 #[snafu(backtrace)]
63 source: crate::stateful::encode::Error,
64 },
65
66 #[snafu(display("Could not write element value"))]
67 WriteValue {
68 #[snafu(backtrace)]
69 source: crate::stateful::encode::Error,
70 },
71 #[snafu(display("Could not flush buffer"))]
72 FlushBuffer {
73 source: std::io::Error,
74 backtrace: Backtrace,
75 },
76}
77
78pub type Result<T> = std::result::Result<T, Error>;
79
80#[derive(Debug)]
82struct SeqToken {
83 typ: SeqTokenType,
85 len: Length,
88}
89
90#[derive(Debug, Default, Copy, Clone, Eq, Hash, PartialEq)]
93#[non_exhaustive]
94pub enum ExplicitLengthSqItemStrategy {
95 #[default]
106 SetUndefined,
107 NoChange,
117 }
122
123#[derive(Debug, Default, Copy, Clone, Eq, Hash, PartialEq)]
125#[non_exhaustive]
126pub struct DataSetWriterOptions {
127 pub explicit_length_sq_item_strategy: ExplicitLengthSqItemStrategy,
129}
130
131impl DataSetWriterOptions {
132 pub fn explicit_length_sq_item_strategy(
134 mut self,
135 exp_length: ExplicitLengthSqItemStrategy,
136 ) -> Self {
137 self.explicit_length_sq_item_strategy = exp_length;
138 self
139 }
140}
141
142#[derive(Debug)]
146pub struct DataSetWriter<W, E, T = SpecificCharacterSet> {
147 printer: StatefulEncoder<W, E, T>,
148 seq_tokens: Vec<SeqToken>,
149 last_de: Option<DataElementHeader>,
150 options: DataSetWriterOptions,
151}
152
153impl<'w, W: 'w> DataSetWriter<W, DynEncoder<'w, W>>
154where
155 W: Write,
156{
157 pub fn with_ts(to: W, ts: &TransferSyntax) -> Result<Self> {
162 Self::with_ts_options(to, ts, DataSetWriterOptions::default())
163 }
164
165 pub fn with_ts_cs(to: W, ts: &TransferSyntax, charset: SpecificCharacterSet) -> Result<Self> {
171 Self::with_ts_cs_options(to, ts, charset, DataSetWriterOptions::default())
172 }
173
174 pub fn with_ts_options(
178 to: W,
179 ts: &TransferSyntax,
180 options: DataSetWriterOptions,
181 ) -> Result<Self> {
182 let encoder = ts.encoder_for().context(UnsupportedTransferSyntaxSnafu {
183 ts_uid: ts.uid(),
184 ts_alias: ts.name(),
185 })?;
186 Ok(DataSetWriter::new_with_codec_options(
187 to,
188 encoder,
189 SpecificCharacterSet::default(),
190 options,
191 ))
192 }
193
194 pub fn with_ts_cs_options(
198 to: W,
199 ts: &TransferSyntax,
200 charset: SpecificCharacterSet,
201 options: DataSetWriterOptions,
202 ) -> Result<Self> {
203 let encoder = ts.encoder_for().context(UnsupportedTransferSyntaxSnafu {
204 ts_uid: ts.uid(),
205 ts_alias: ts.name(),
206 })?;
207 Ok(DataSetWriter::new_with_codec_options(
208 to, encoder, charset, options,
209 ))
210 }
211}
212
213impl<W, E> DataSetWriter<W, E> {
214 #[inline]
217 pub fn new(to: W, encoder: E) -> Self {
218 DataSetWriter::new_with_options(to, encoder, DataSetWriterOptions::default())
219 }
220
221 #[inline]
224 pub fn new_with_options(to: W, encoder: E, options: DataSetWriterOptions) -> Self {
225 DataSetWriter {
226 printer: StatefulEncoder::new(to, encoder, SpecificCharacterSet::default()),
227 seq_tokens: Vec::new(),
228 last_de: None,
229 options,
230 }
231 }
232}
233
234impl<W, E, T> DataSetWriter<W, E, T> {
235 #[inline]
238 pub fn new_with_codec(to: W, encoder: E, text: T) -> Self {
239 DataSetWriter::new_with_codec_options(to, encoder, text, DataSetWriterOptions::default())
240 }
241
242 #[inline]
245 pub fn new_with_codec_options(
246 to: W,
247 encoder: E,
248 text: T,
249 options: DataSetWriterOptions,
250 ) -> Self {
251 DataSetWriter {
252 printer: StatefulEncoder::new(to, encoder, text),
253 seq_tokens: Vec::new(),
254 last_de: None,
255 options,
256 }
257 }
258}
259
260impl<W, E> DataSetWriter<W, E>
261where
262 W: Write,
263 E: EncodeTo<W>,
264{
265 #[inline]
267 pub fn write_sequence<I>(&mut self, tokens: I) -> Result<()>
268 where
269 I: IntoIterator<Item = DataToken>,
270 {
271 for token in tokens {
272 self.write(token)?;
273 }
274
275 Ok(())
276 }
277
278 pub fn write(&mut self, token: DataToken) -> Result<()> {
280 match token {
281 DataToken::SequenceStart { tag, len, .. } => {
282 match self.options.explicit_length_sq_item_strategy {
283 ExplicitLengthSqItemStrategy::SetUndefined => {
284 self.seq_tokens.push(SeqToken {
285 typ: SeqTokenType::Sequence,
286 len: Length::UNDEFINED,
287 });
288 self.write_impl(&DataToken::SequenceStart {
289 tag,
290 len: Length::UNDEFINED,
291 })?;
292 }
293 ExplicitLengthSqItemStrategy::NoChange => {
294 self.seq_tokens.push(SeqToken {
295 typ: SeqTokenType::Sequence,
296 len,
297 });
298 self.write_impl(&token)?;
299 }
300 }
301 Ok(())
302 }
303 DataToken::ItemStart { len } => {
304 match self.options.explicit_length_sq_item_strategy {
305 ExplicitLengthSqItemStrategy::SetUndefined => {
306 let len = if self
309 .last_de
310 .map(|h| h.is_encapsulated_pixeldata())
311 .unwrap_or(false)
312 {
313 len
314 } else {
315 Length::UNDEFINED
316 };
317 self.seq_tokens.push(SeqToken {
318 typ: SeqTokenType::Item,
319 len,
320 });
321 self.write_impl(&DataToken::ItemStart { len })?;
322 }
323 ExplicitLengthSqItemStrategy::NoChange => {
324 self.seq_tokens.push(SeqToken {
325 typ: SeqTokenType::Item,
326 len,
327 });
328 self.write_impl(&token)?;
329 }
330 }
331 Ok(())
332 }
333 DataToken::ItemEnd => {
334 if let Some(seq_start) = self.seq_tokens.pop() {
336 if seq_start.typ == SeqTokenType::Item && seq_start.len.is_undefined() {
337 self.write_impl(&token)?;
338 }
339 }
340 Ok(())
341 }
342 DataToken::SequenceEnd => {
343 if let Some(seq_start) = self.seq_tokens.pop() {
345 if seq_start.typ == SeqTokenType::Sequence && seq_start.len.is_undefined() {
346 self.write_impl(&token)?;
347 }
348 }
349 Ok(())
350 }
351 DataToken::ElementHeader(de) => {
352 self.last_de = Some(de);
354
355 Ok(())
357 }
358 token @ DataToken::PixelSequenceStart => {
359 self.last_de = Some(DataElementHeader {
362 tag: Tag(0x7fe0, 0x0010),
363 vr: VR::OB,
364 len: Length::UNDEFINED,
365 });
366
367 self.seq_tokens.push(SeqToken {
368 typ: SeqTokenType::Sequence,
369 len: Length::UNDEFINED,
370 });
371 self.write_impl(&token)
372 }
373 token @ DataToken::ItemValue(_)
374 | token @ DataToken::PrimitiveValue(_)
375 | token @ DataToken::OffsetTable(_) => self.write_impl(&token),
376 }
377 }
378
379 fn write_impl(&mut self, token: &DataToken) -> Result<()> {
380 match token {
381 DataToken::ElementHeader(header) => {
382 self.printer
383 .encode_element_header(*header)
384 .context(WriteHeaderSnafu { tag: header.tag })?;
385 }
386 DataToken::SequenceStart { tag, len } => {
387 self.printer
388 .encode_element_header(DataElementHeader::new(*tag, VR::SQ, *len))
389 .context(WriteHeaderSnafu { tag: *tag })?;
390 }
391 DataToken::PixelSequenceStart => {
392 let tag = Tag(0x7fe0, 0x0010);
393 self.printer
394 .encode_element_header(DataElementHeader::new(tag, VR::OB, Length::UNDEFINED))
395 .context(WriteHeaderSnafu { tag })?;
396 }
397 DataToken::SequenceEnd => {
398 self.printer
399 .encode_sequence_delimiter()
400 .context(WriteSequenceDelimiterSnafu)?;
401 }
402 DataToken::ItemStart { len } => {
403 self.printer
404 .encode_item_header(len.0)
405 .context(WriteItemHeaderSnafu)?;
406 }
407 DataToken::ItemEnd => {
408 self.printer
409 .encode_item_delimiter()
410 .context(WriteItemDelimiterSnafu)?;
411 }
412 DataToken::PrimitiveValue(ref value) => {
413 let last_de = self.last_de.take().with_context(|| UnexpectedTokenSnafu {
414 token: token.clone(),
415 })?;
416
417 self.printer
418 .encode_primitive_element(&last_de, value)
419 .context(WriteValueSnafu)?;
420 self.last_de = None;
421 }
422 DataToken::OffsetTable(table) => {
423 self.printer
424 .encode_offset_table(table)
425 .context(WriteValueSnafu)?;
426 }
427 DataToken::ItemValue(data) => {
428 self.printer.write_bytes(data).context(WriteValueSnafu)?;
429 }
430 }
431 Ok(())
432 }
433
434 pub fn flush(&mut self) -> Result<()> {
436 self.printer.flush().context(FlushBufferSnafu)
437 }
438}
439
440#[cfg(test)]
441mod tests {
442 use super::super::DataToken;
443 use super::{DataSetWriter, DataSetWriterOptions, ExplicitLengthSqItemStrategy};
444 use dicom_core::{
445 header::{DataElementHeader, Length},
446 value::PrimitiveValue,
447 Tag, VR,
448 };
449 use dicom_encoding::encode::{explicit_le::ExplicitVRLittleEndianEncoder, EncoderFor};
450
451 fn validate_dataset_writer<I>(
452 tokens: I,
453 ground_truth: &[u8],
454 writer_options: DataSetWriterOptions,
455 ) where
456 I: IntoIterator<Item = DataToken>,
457 {
458 let mut raw_out: Vec<u8> = vec![];
459 let encoder = EncoderFor::new(ExplicitVRLittleEndianEncoder::default());
460 let mut dset_writer =
461 DataSetWriter::new_with_options(&mut raw_out, encoder, writer_options);
462
463 dset_writer.write_sequence(tokens).unwrap();
464
465 assert_eq!(raw_out, ground_truth);
466 }
467
468 #[test]
469 fn write_sequence_explicit() {
470 let tokens = vec![
471 DataToken::SequenceStart {
472 tag: Tag(0x0018, 0x6011),
473 len: Length(46),
474 },
475 DataToken::ItemStart { len: Length(20) },
476 DataToken::ElementHeader(DataElementHeader {
477 tag: Tag(0x0018, 0x6012),
478 vr: VR::US,
479 len: Length(2),
480 }),
481 DataToken::PrimitiveValue(PrimitiveValue::U16([1].as_ref().into())),
482 DataToken::ElementHeader(DataElementHeader {
483 tag: Tag(0x0018, 0x6014),
484 vr: VR::US,
485 len: Length(2),
486 }),
487 DataToken::PrimitiveValue(PrimitiveValue::U16([2].as_ref().into())),
488 DataToken::ItemEnd,
489 DataToken::ItemStart { len: Length(10) },
490 DataToken::ElementHeader(DataElementHeader {
491 tag: Tag(0x0018, 0x6012),
492 vr: VR::US,
493 len: Length(2),
494 }),
495 DataToken::PrimitiveValue(PrimitiveValue::U16([4].as_ref().into())),
496 DataToken::ItemEnd,
497 DataToken::SequenceEnd,
498 DataToken::ElementHeader(DataElementHeader {
499 tag: Tag(0x0020, 0x4000),
500 vr: VR::LT,
501 len: Length(4),
502 }),
503 DataToken::PrimitiveValue(PrimitiveValue::Str("TEST".into())),
504 ];
505
506 #[rustfmt::skip]
507 static GROUND_TRUTH_LENGTH_UNDEFINED: &[u8] = &[
508 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', ];
531
532 #[rustfmt::skip]
533 static GROUND_TRUTH_NO_CHANGE: &[u8] = &[
534 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', ];
554
555 let no_change = DataSetWriterOptions {
556 explicit_length_sq_item_strategy: ExplicitLengthSqItemStrategy::NoChange,
557 };
558 validate_dataset_writer(tokens.clone(), GROUND_TRUTH_NO_CHANGE, no_change);
559 validate_dataset_writer(
560 tokens,
561 GROUND_TRUTH_LENGTH_UNDEFINED,
562 DataSetWriterOptions::default(),
563 );
564 }
565
566 #[test]
567 fn write_element_overrides_len() {
568 let tokens = vec![
569 DataToken::ElementHeader(DataElementHeader {
570 tag: Tag(0x0008, 0x0005),
572 vr: VR::CS,
573 len: Length(10),
574 }),
575 DataToken::PrimitiveValue(PrimitiveValue::from("ISO_IR 100")),
576 DataToken::ElementHeader(DataElementHeader {
577 tag: Tag(0x0008, 0x0090),
579 vr: VR::PN,
580 len: Length("Simões^João".len() as u32),
582 }),
583 DataToken::PrimitiveValue(PrimitiveValue::from("Simões^João")),
584 ];
585
586 #[rustfmt::skip]
587 static GROUND_TRUTH: &[u8] = &[
588 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' '
599 ];
600 validate_dataset_writer(tokens, GROUND_TRUTH, DataSetWriterOptions::default());
601 }
602
603 #[test]
604 fn write_sequence_implicit() {
605 let tokens = vec![
606 DataToken::SequenceStart {
607 tag: Tag(0x0018, 0x6011),
608 len: Length::UNDEFINED,
609 },
610 DataToken::ItemStart {
611 len: Length::UNDEFINED,
612 },
613 DataToken::ElementHeader(DataElementHeader {
614 tag: Tag(0x0018, 0x6012),
615 vr: VR::US,
616 len: Length(2),
617 }),
618 DataToken::PrimitiveValue(PrimitiveValue::U16([1].as_ref().into())),
619 DataToken::ElementHeader(DataElementHeader {
620 tag: Tag(0x0018, 0x6014),
621 vr: VR::US,
622 len: Length(2),
623 }),
624 DataToken::PrimitiveValue(PrimitiveValue::U16([2].as_ref().into())),
625 DataToken::ItemEnd,
626 DataToken::ItemStart {
627 len: Length::UNDEFINED,
628 },
629 DataToken::ElementHeader(DataElementHeader {
630 tag: Tag(0x0018, 0x6012),
631 vr: VR::US,
632 len: Length(2),
633 }),
634 DataToken::PrimitiveValue(PrimitiveValue::U16([4].as_ref().into())),
635 DataToken::ItemEnd,
636 DataToken::SequenceEnd,
637 DataToken::ElementHeader(DataElementHeader {
638 tag: Tag(0x0020, 0x4000),
639 vr: VR::LT,
640 len: Length(4),
641 }),
642 DataToken::PrimitiveValue(PrimitiveValue::Str("TEST".into())),
643 ];
644
645 #[rustfmt::skip]
646 static GROUND_TRUTH: &[u8] = &[
647 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', ];
673
674 let no_change = DataSetWriterOptions {
675 explicit_length_sq_item_strategy: ExplicitLengthSqItemStrategy::NoChange,
676 };
677 validate_dataset_writer(tokens.clone(), GROUND_TRUTH, no_change);
678 validate_dataset_writer(tokens, GROUND_TRUTH, DataSetWriterOptions::default());
679 }
680
681 #[test]
682 fn write_sequence_explicit_with_implicit_item_len() {
683 let tokens = vec![
684 DataToken::SequenceStart {
685 tag: Tag(0x0018, 0x6011),
686 len: Length(60),
687 },
688 DataToken::ItemStart {
689 len: Length::UNDEFINED,
690 },
691 DataToken::ElementHeader(DataElementHeader {
692 tag: Tag(0x0018, 0x6012),
693 vr: VR::US,
694 len: Length(2),
695 }),
696 DataToken::PrimitiveValue(PrimitiveValue::U16([1].as_ref().into())),
697 DataToken::ElementHeader(DataElementHeader {
698 tag: Tag(0x0018, 0x6014),
699 vr: VR::US,
700 len: Length(2),
701 }),
702 DataToken::PrimitiveValue(PrimitiveValue::U16([2].as_ref().into())),
703 DataToken::ItemEnd,
704 DataToken::ItemStart {
705 len: Length::UNDEFINED,
706 },
707 DataToken::ElementHeader(DataElementHeader {
708 tag: Tag(0x0018, 0x6012),
709 vr: VR::US,
710 len: Length(2),
711 }),
712 DataToken::PrimitiveValue(PrimitiveValue::U16([4].as_ref().into())),
713 DataToken::ItemEnd,
714 DataToken::SequenceEnd,
715 DataToken::ElementHeader(DataElementHeader {
716 tag: Tag(0x0020, 0x4000),
717 vr: VR::LT,
718 len: Length(4),
719 }),
720 DataToken::PrimitiveValue(PrimitiveValue::Str("TEST".into())),
721 ];
722
723 #[rustfmt::skip]
724 static GROUND_TRUTH_LENGTH_UNDEFINED: &[u8] = &[
725 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', ];
742
743 #[rustfmt::skip]
744 static GROUND_TRUTH_NO_CHANGE: &[u8] = &[
745 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', ];
769 let no_change = DataSetWriterOptions {
770 explicit_length_sq_item_strategy: ExplicitLengthSqItemStrategy::NoChange,
771 };
772
773 validate_dataset_writer(tokens.clone(), GROUND_TRUTH_NO_CHANGE, no_change);
774 validate_dataset_writer(
775 tokens,
776 GROUND_TRUTH_LENGTH_UNDEFINED,
777 DataSetWriterOptions::default(),
778 );
779 }
780
781 #[test]
782 fn write_encapsulated_pixeldata() {
783 let tokens = vec![
784 DataToken::PixelSequenceStart,
785 DataToken::ItemStart { len: Length(0) },
786 DataToken::ItemEnd,
787 DataToken::ItemStart { len: Length(32) },
788 DataToken::ItemValue(vec![0x99; 32]),
789 DataToken::ItemEnd,
790 DataToken::SequenceEnd,
791 DataToken::ElementHeader(DataElementHeader::new(
792 Tag(0xfffc, 0xfffc),
793 VR::OB,
794 Length(8),
795 )),
796 DataToken::PrimitiveValue(PrimitiveValue::U8([0x00; 8].as_ref().into())),
797 ];
798
799 #[rustfmt::skip]
803 static GROUND_TRUTH: &[u8] = &[
804 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,
816 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,
817 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,
818 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,
819 0xfe, 0xff, 0xdd, 0xe0, 0x00, 0x00, 0x00, 0x00,
822 0xfc, 0xff, 0xfc, 0xff, b'O', b'B', 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
828 ];
829 let no_change = DataSetWriterOptions {
830 explicit_length_sq_item_strategy: ExplicitLengthSqItemStrategy::NoChange,
831 };
832 validate_dataset_writer(tokens.clone(), GROUND_TRUTH, no_change);
833 validate_dataset_writer(tokens, GROUND_TRUTH, DataSetWriterOptions::default());
834 }
835}