1use std::{
105 borrow::Cow,
106 fmt,
107 fs::File,
108 io::{BufRead, BufReader, Read, Seek},
109 path::Path,
110};
111
112use dicom_core::{
113 header::HasLength,
114 value::{PixelFragmentSequence, C},
115 DataDictionary, DataElement, DicomValue, Length, Tag, VR,
116};
117use dicom_dictionary_std::{tags, StandardDataDictionary};
118use dicom_encoding::{decode::DecodeFrom, TransferSyntaxIndex};
119use dicom_parser::{
120 dataset::{
121 lazy_read::{LazyDataSetReader, LazyDataSetReaderOptions},
122 DataToken, LazyDataToken,
123 },
124 DynStatefulDecoder, StatefulDecode, StatefulDecoder,
125};
126use dicom_transfer_syntax_registry::TransferSyntaxRegistry;
127use snafu::prelude::*;
128use snafu::Backtrace;
129
130use crate::{
131 file::ReadPreamble,
132 mem::{InMemElement, InMemFragment},
133 FileMetaTable, InMemDicomObject,
134};
135
136pub use dicom_parser::dataset::read::OddLengthStrategy;
138pub use dicom_parser::stateful::decode::CharacterSetOverride;
139
140pub type Result<T, E = Error> = std::result::Result<T, E>;
141
142#[derive(Debug, Snafu)]
144pub struct Error(InnerError);
145
146#[derive(Debug, Snafu)]
148#[non_exhaustive]
149pub(crate) enum InnerError {
150 #[snafu(display("Could not open file '{}'", filename.display()))]
151 OpenFile {
152 filename: std::path::PathBuf,
153 backtrace: Backtrace,
154 source: std::io::Error,
155 },
156 ReadPreambleBytes {
158 backtrace: Backtrace,
159 source: std::io::Error,
160 },
161 CreateParser {
163 #[snafu(
164 backtrace,
165 source(from(dicom_parser::dataset::lazy_read::Error, Box::from))
166 )]
167 source: Box<dicom_parser::dataset::lazy_read::Error>,
168 },
169 ReadToken {
171 #[snafu(
172 backtrace,
173 source(from(dicom_parser::dataset::lazy_read::Error, Box::from))
174 )]
175 source: Box<dicom_parser::dataset::lazy_read::Error>,
176 },
177 IllegalStateStart { backtrace: Backtrace },
179 IllegalStateMeta { backtrace: Backtrace },
181 IllegalStateInPixel { backtrace: Backtrace },
183 MissingElementValue { backtrace: Backtrace },
185 UnrecognizedTransferSyntax {
187 ts_uid: String,
188 backtrace: Backtrace,
189 },
190 GuessTransferSyntax { backtrace: Backtrace },
192 #[snafu(display("Unexpected token {token:?}"))]
193 UnexpectedToken {
194 token: dicom_parser::dataset::LazyDataTokenRepr,
195 backtrace: Backtrace,
196 },
197 #[snafu(display("Unexpected data token {token:?}"))]
198 UnexpectedDataToken {
199 token: dicom_parser::dataset::DataToken,
200 backtrace: Backtrace,
201 },
202 #[snafu(display("Could not collect data in {tag}"))]
203 CollectDataValue {
204 tag: Tag,
205 #[snafu(backtrace, source(from(dicom_parser::dataset::Error, Box::from)))]
206 source: Box<dicom_parser::dataset::Error>,
207 },
208 PrematureEnd { backtrace: Backtrace },
210 BuildMetaTable {
212 #[snafu(backtrace, source(from(crate::meta::Error, Box::new)))]
213 source: Box<crate::meta::Error>,
214 },
215 ReadItem {
217 #[snafu(
218 backtrace,
219 source(from(dicom_parser::stateful::decode::Error, Box::from))
220 )]
221 source: Box<dicom_parser::stateful::decode::Error>,
222 },
223}
224
225#[derive(Debug, Default)]
229pub struct DicomCollectorOptions<D = StandardDataDictionary, R = TransferSyntaxRegistry> {
230 dict: D,
232 ts_index: R,
234 ts_hint: Option<Cow<'static, str>>,
236 read_preamble: ReadPreamble,
238 odd_length: OddLengthStrategy,
240 charset_override: CharacterSetOverride,
242}
243
244impl DicomCollectorOptions {
245 pub fn new() -> Self {
247 Self::default()
248 }
249}
250
251impl<D, R> DicomCollectorOptions<D, R> {
252 pub fn dict<D2>(self, dict: D2) -> DicomCollectorOptions<D2, R> {
259 DicomCollectorOptions {
260 dict,
261 ts_index: self.ts_index,
262 ts_hint: self.ts_hint,
263 read_preamble: self.read_preamble,
264 odd_length: self.odd_length,
265 charset_override: self.charset_override,
266 }
267 }
268
269 pub fn ts_index<R2>(self, ts_index: R2) -> DicomCollectorOptions<D, R2> {
276 DicomCollectorOptions {
277 dict: self.dict,
278 ts_index,
279 ts_hint: self.ts_hint,
280 read_preamble: self.read_preamble,
281 odd_length: self.odd_length,
282 charset_override: self.charset_override,
283 }
284 }
285
286 pub fn expected_ts(mut self, ts_uid: impl Into<Cow<'static, str>>) -> Self {
288 self.ts_hint = Some(ts_uid.into());
289 self
290 }
291
292 pub fn unset_expected_ts(mut self) -> Self {
294 self.ts_hint = None;
295 self
296 }
297
298 pub fn read_preamble(mut self, option: ReadPreamble) -> Self {
300 self.read_preamble = option;
301 self
302 }
303
304 pub fn odd_length_strategy(mut self, option: OddLengthStrategy) -> Self {
306 self.odd_length = option;
307 self
308 }
309
310 pub fn charset_override(mut self, option: CharacterSetOverride) -> Self {
312 self.charset_override = option;
313 self
314 }
315
316 pub fn open_file(
318 self,
319 filename: impl AsRef<Path>,
320 ) -> Result<DicomCollector<BufReader<File>, D, R>>
321 where
322 R: TransferSyntaxIndex,
323 {
324 let filename = filename.as_ref();
325 let reader = BufReader::new(File::open(filename).context(OpenFileSnafu { filename })?);
326
327 Ok(DicomCollector {
328 source: CollectionSource::new(
329 reader,
330 self.ts_index,
331 self.odd_length,
332 self.charset_override,
333 ),
334 dictionary: self.dict,
335 ts_hint: self.ts_hint,
336 file_meta: None,
337 read_preamble: self.read_preamble,
338 state: Default::default(),
339 })
340 }
341
342 pub fn from_reader<S>(self, reader: BufReader<S>) -> DicomCollector<BufReader<S>, D, R>
344 where
345 S: Read + Seek,
346 R: TransferSyntaxIndex,
347 {
348 DicomCollector {
349 source: CollectionSource::new(
350 reader,
351 self.ts_index,
352 self.odd_length,
353 self.charset_override,
354 ),
355 dictionary: self.dict,
356 ts_hint: self.ts_hint,
357 file_meta: None,
358 read_preamble: self.read_preamble,
359 state: Default::default(),
360 }
361 }
362}
363
364enum CollectionSource<S, R> {
365 Raw {
366 reader: Option<S>,
368 ts_index: R,
370 odd_length: OddLengthStrategy,
373 charset_override: CharacterSetOverride,
375 },
376 Parser(LazyDataSetReader<DynStatefulDecoder<S>>),
377}
378
379impl<S, R> fmt::Debug for CollectionSource<S, R>
380where
381 R: fmt::Debug,
382{
383 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
384 match self {
385 CollectionSource::Raw {
386 reader,
387 ts_index,
388 odd_length,
389 charset_override,
390 } => f
391 .debug_struct("Raw")
392 .field("ts_index", ts_index)
393 .field("odd_length", odd_length)
394 .field("charset_override", charset_override)
395 .field(
396 "reader",
397 &match reader {
398 Some(_) => "Some(_)",
399 None => "None",
400 },
401 )
402 .finish(),
403 CollectionSource::Parser(_) => f.write_str("Parser(..)"),
404 }
405 }
406}
407
408impl<S, R> CollectionSource<S, R>
409where
410 S: Read + Seek,
411 R: TransferSyntaxIndex,
412{
413 fn new(
414 raw_source: S,
415 ts_index: R,
416 odd_length: OddLengthStrategy,
417 charset_override: CharacterSetOverride,
418 ) -> Self {
419 CollectionSource::Raw {
420 reader: Some(raw_source),
421 ts_index,
422 odd_length,
423 charset_override,
424 }
425 }
426
427 fn has_parser(&self) -> bool {
428 matches!(self, CollectionSource::Parser(_))
429 }
430
431 fn raw_reader_mut(&mut self) -> &mut S {
432 match self {
433 CollectionSource::Raw { reader, .. } => reader.as_mut().unwrap(),
434 CollectionSource::Parser(_) => {
435 panic!("cannot retrieve raw reader after setting parser")
436 }
437 }
438 }
439
440 fn set_parser_with_ts(
441 &mut self,
442 ts_uid: &str,
443 ) -> Result<&mut LazyDataSetReader<DynStatefulDecoder<S>>> {
444 match self {
445 CollectionSource::Raw {
446 reader: src,
447 ts_index,
448 odd_length,
449 charset_override,
450 } => {
451 let src = src.take().unwrap();
452
453 let ts = ts_index
455 .get(ts_uid)
456 .context(UnrecognizedTransferSyntaxSnafu {
457 ts_uid: ts_uid.to_string(),
458 })?;
459
460 let mut options = LazyDataSetReaderOptions::default();
461 options.odd_length = *odd_length;
462 options.charset_override = *charset_override;
463 *self = CollectionSource::Parser(
464 LazyDataSetReader::new_with_ts_options(src, ts, options)
465 .context(CreateParserSnafu)?,
466 );
467 let CollectionSource::Parser(parser) = self else {
468 unreachable!();
469 };
470 Ok(parser)
471 }
472 CollectionSource::Parser(decoder) => Ok(decoder),
473 }
474 }
475
476 fn parser(&mut self) -> &mut LazyDataSetReader<DynStatefulDecoder<S>> {
477 match self {
478 CollectionSource::Raw { .. } => panic!("parser transfer syntax not set"),
479 CollectionSource::Parser(parser) => parser,
480 }
481 }
482}
483
484pub struct DicomCollector<S, D = StandardDataDictionary, R = TransferSyntaxRegistry> {
488 source: CollectionSource<S, R>,
490 dictionary: D,
492 ts_hint: Option<Cow<'static, str>>,
494 file_meta: Option<FileMetaTable>,
496 read_preamble: ReadPreamble,
499 state: CollectorState,
501}
502
503#[derive(Debug, Default, Copy, Clone, PartialEq)]
505enum CollectorState {
506 #[default]
508 Start,
509 Preamble,
512 FileMeta,
517 InDataset,
519 InPixelData,
521}
522
523impl<S, D, R> fmt::Debug for DicomCollector<S, D, R>
524where
525 D: fmt::Debug,
526 R: fmt::Debug,
527{
528 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
529 f.debug_struct("DicomCollector")
530 .field("source", &self.source)
531 .field("dictionary", &self.dictionary)
532 .field("ts_hint", &self.ts_hint)
533 .field(
534 "file_meta",
535 if self.file_meta.is_some() {
536 &"Some(...)"
537 } else {
538 &"None"
539 },
540 )
541 .field("read_preamble", &self.read_preamble)
542 .field("state", &self.state)
543 .finish()
544 }
545}
546
547impl<S> DicomCollector<BufReader<S>>
548where
549 S: Read + Seek,
550{
551 pub fn new(reader: BufReader<S>) -> Self {
557 DicomCollector {
558 source: CollectionSource::new(
559 reader,
560 TransferSyntaxRegistry,
561 Default::default(),
562 Default::default(),
563 ),
564 dictionary: StandardDataDictionary,
565 ts_hint: None,
566 file_meta: None,
567 read_preamble: Default::default(),
568 state: Default::default(),
569 }
570 }
571
572 pub fn new_with_ts(
578 reader: BufReader<S>,
579 transfer_syntax: impl Into<Cow<'static, str>>,
580 ) -> Self {
581 DicomCollector {
582 source: CollectionSource::new(
583 reader,
584 TransferSyntaxRegistry,
585 Default::default(),
586 Default::default(),
587 ),
588 dictionary: StandardDataDictionary,
589 ts_hint: Some(transfer_syntax.into()),
590 file_meta: None,
591 read_preamble: Default::default(),
592 state: Default::default(),
593 }
594 }
595}
596
597impl DicomCollector<BufReader<File>> {
598 pub fn open_file(filename: impl AsRef<Path>) -> Result<Self> {
604 Self::open_file_with_dict(filename, StandardDataDictionary)
605 }
606}
607
608impl<D> DicomCollector<BufReader<File>, D>
609where
610 D: DataDictionary + Clone,
611{
612 pub fn open_file_with_dict(filename: impl AsRef<Path>, dict: D) -> Result<Self> {
620 let filename = filename.as_ref();
621 let reader = BufReader::new(File::open(filename).context(OpenFileSnafu { filename })?);
622 Ok(Self::new_with_dict(reader, dict))
623 }
624}
625
626impl<S, D> DicomCollector<BufReader<S>, D>
627where
628 D: DataDictionary + Clone,
629 S: Read + Seek,
630{
631 fn new_with_dict(reader: BufReader<S>, dictionary: D) -> Self {
638 DicomCollector {
639 source: CollectionSource::new(
640 reader,
641 TransferSyntaxRegistry,
642 Default::default(),
643 Default::default(),
644 ),
645 dictionary,
646 ts_hint: None,
647 file_meta: None,
648 read_preamble: Default::default(),
649 state: Default::default(),
650 }
651 }
652}
653
654impl<S, D, R> DicomCollector<BufReader<S>, D, R>
655where
656 D: DataDictionary + Clone,
657 S: Read + Seek,
658 R: TransferSyntaxIndex,
659{
660 pub fn read_preamble(&mut self) -> Result<Option<[u8; 128]>> {
666 ensure!(self.state == CollectorState::Start, IllegalStateStartSnafu);
667
668 if self.read_preamble == ReadPreamble::Never {
669 self.state = CollectorState::Preamble;
670 return Ok(None);
671 }
672
673 let reader = self.source.raw_reader_mut();
674 let preamble = {
675 if self.read_preamble == ReadPreamble::Always {
676 let mut buf = [0; 128];
678 reader
679 .read_exact(&mut buf)
680 .context(ReadPreambleBytesSnafu)?;
681 Some(buf)
682 } else {
683 let buf = reader.fill_buf().context(ReadPreambleBytesSnafu)?;
685 if buf.len() < 4 {
686 return PrematureEndSnafu.fail().map_err(From::from);
687 }
688
689 if buf.len() >= 128 + 4 && &buf[128..132] == b"DICM" {
690 let out: [u8; 128] = std::convert::TryInto::try_into(&buf[0..128])
691 .expect("128 byte slice into array");
692 reader.consume(128);
693 Some(out)
694 } else if &buf[0..4] == b"DICM" {
695 None
697 } else {
698 let mut out = [0; 128];
700 reader
701 .read_exact(&mut out)
702 .context(ReadPreambleBytesSnafu)?;
703 Some(out)
704 }
705 }
706 };
707 self.state = CollectorState::Preamble;
708 Ok(preamble)
709 }
710
711 pub fn read_file_meta(&mut self) -> Result<&FileMetaTable> {
722 if self.state == CollectorState::Start {
726 self.read_preamble()?;
728 }
729
730 if self.state == CollectorState::Preamble {
731 let reader = self.source.raw_reader_mut();
732 self.file_meta = Some(FileMetaTable::from_reader(reader).context(BuildMetaTableSnafu)?);
733
734 self.state = CollectorState::FileMeta;
735 }
736
737 self.file_meta
738 .as_ref()
739 .context(IllegalStateMetaSnafu)
740 .map_err(From::from)
741 }
742
743 #[inline]
777 pub fn take_file_meta(&mut self) -> Option<FileMetaTable> {
778 self.file_meta.take()
779 }
780
781 pub fn read_dataset_to_end(&mut self, to: &mut InMemDicomObject<D>) -> Result<()> {
784 let parser = if !self.source.has_parser() {
785 let ts = {
786 if self.ts_hint.is_none() {
787 self.populate_ts_hint();
788 }
789 self.ts_hint.as_deref()
790 }
791 .context(GuessTransferSyntaxSnafu)?;
792 self.source.set_parser_with_ts(ts)?
793 } else {
794 self.source.parser()
795 };
796
797 Self::collect_to_object(&mut self.state, parser, false, None, to, &self.dictionary)
798 }
799
800 pub fn read_dataset_up_to(
804 &mut self,
805 stop_tag: Tag,
806 to: &mut InMemDicomObject<D>,
807 ) -> Result<()> {
808 let parser = if !self.source.has_parser() {
809 let ts = {
810 if self.ts_hint.is_none() {
811 self.populate_ts_hint();
812 }
813 self.ts_hint.as_deref()
814 }
815 .context(GuessTransferSyntaxSnafu)?;
816 self.source.set_parser_with_ts(ts)?
817 } else {
818 self.source.parser()
819 };
820
821 Self::collect_to_object(
822 &mut self.state,
823 parser,
824 false,
825 Some(stop_tag),
826 to,
827 &self.dictionary,
828 )
829 }
830
831 #[inline]
835 pub fn read_dataset_up_to_pixeldata(&mut self, to: &mut InMemDicomObject<D>) -> Result<()> {
836 self.read_dataset_up_to(dicom_dictionary_std::tags::PIXEL_DATA, to)
837 }
838
839 pub fn read_next_fragment(&mut self, to: &mut Vec<u8>) -> Result<Option<u32>> {
858 if self.state == CollectorState::Start || self.state == CollectorState::Preamble {
859 self.read_file_meta()?;
861 }
862
863 if !self.source.has_parser() {
865 let ts = {
866 if self.ts_hint.is_none() {
867 self.populate_ts_hint();
868 }
869 self.ts_hint.as_deref()
870 }
871 .context(GuessTransferSyntaxSnafu)?;
872 self.source.set_parser_with_ts(ts)?;
873 } else {
874 self.source.parser();
875 }
876
877 if self.state != CollectorState::InPixelData {
878 self.skip_until(|token| {
881 match token {
882 LazyDataToken::ElementHeader(header)
884 if header.tag == tags::PIXEL_DATA && header.length().is_defined() =>
885 {
886 true
887 }
888 LazyDataToken::PixelSequenceStart => true,
890 _ => false,
891 }
892 })?;
893
894 self.state = CollectorState::InPixelData;
895 }
896
897 let parser = if !self.source.has_parser() {
898 let ts = {
899 if self.ts_hint.is_none() {
900 self.populate_ts_hint();
901 }
902 self.ts_hint.as_deref()
903 }
904 .context(GuessTransferSyntaxSnafu)?;
905 self.source.set_parser_with_ts(ts)?
906 } else {
907 self.source.parser()
908 };
909
910 while let Some(token) = parser.advance() {
913 match token.context(ReadTokenSnafu)? {
914 LazyDataToken::LazyValue { header, decoder } => {
916 debug_assert!(header.length().is_defined());
917 let len = header.length().0;
918 decoder.read_to_vec(len, to).context(ReadItemSnafu)?;
919 return Ok(Some(len));
920 }
921 LazyDataToken::LazyItemValue { len, decoder } => {
923 decoder.read_to_vec(len, to).context(ReadItemSnafu)?;
924 return Ok(Some(len));
925 }
926 LazyDataToken::ItemStart { len: Length(0) } => return Ok(Some(0)),
929 _ => {
930 }
932 }
933 }
934
935 Ok(None)
936 }
937
938 pub fn read_basic_offset_table(&mut self, to: &mut Vec<u32>) -> Result<Option<u32>> {
950 if self.state == CollectorState::InPixelData {
951 return IllegalStateInPixelSnafu.fail().map_err(From::from);
952 }
953
954 if self.state == CollectorState::Start || self.state == CollectorState::Preamble {
955 self.read_file_meta()?;
957 }
958
959 if !self.source.has_parser() {
961 let ts = {
962 if self.ts_hint.is_none() {
963 self.populate_ts_hint();
964 }
965 self.ts_hint.as_deref()
966 }
967 .context(GuessTransferSyntaxSnafu)?;
968 self.source.set_parser_with_ts(ts)?;
969 } else {
970 self.source.parser();
971 }
972
973 if self.state != CollectorState::InPixelData {
974 self.skip_until(|token| {
977 match token {
978 LazyDataToken::ElementHeader(header)
980 if header.tag == tags::PIXEL_DATA && header.length().is_defined() =>
981 {
982 true
983 }
984 LazyDataToken::PixelSequenceStart => true,
986 _ => false,
987 }
988 })?;
989
990 self.state = CollectorState::InPixelData;
991 }
992
993 let parser = if !self.source.has_parser() {
994 let ts = {
995 if self.ts_hint.is_none() {
996 self.populate_ts_hint();
997 }
998 self.ts_hint.as_deref()
999 }
1000 .context(GuessTransferSyntaxSnafu)?;
1001 self.source.set_parser_with_ts(ts)?
1002 } else {
1003 self.source.parser()
1004 };
1005
1006 while let Some(token) = parser.advance() {
1009 match token.context(ReadTokenSnafu)? {
1010 LazyDataToken::LazyValue { .. } => {
1012 return Ok(None);
1013 }
1014 LazyDataToken::LazyItemValue { len, decoder } => {
1016 decoder.read_u32_to_vec(len, to).context(ReadItemSnafu)?;
1017 return Ok(Some(len));
1018 }
1019 LazyDataToken::ItemStart { len: Length(0) } => return Ok(Some(0)),
1022 _ => {
1023 }
1025 }
1026 }
1027
1028 Ok(None)
1029 }
1030
1031 #[inline]
1034 fn populate_ts_hint(&mut self) {
1035 if let Some(meta) = self.file_meta.as_ref() {
1036 self.ts_hint = Some(Cow::Owned(meta.transfer_syntax().to_string()));
1037 }
1038 }
1039
1040 fn skip_until(
1041 &mut self,
1042 mut pred: impl FnMut(
1043 &LazyDataToken<
1044 &mut StatefulDecoder<Box<dyn DecodeFrom<BufReader<S>> + 'static>, BufReader<S>>,
1045 >,
1046 ) -> bool,
1047 ) -> Result<bool> {
1048 let parser = self.source.parser();
1049 while let Some(token) = parser.advance() {
1050 let token = token.context(ReadTokenSnafu)?;
1051 if pred(&token) {
1052 return Ok(true);
1053 }
1054 token.skip().context(ReadItemSnafu)?;
1056 self.state = CollectorState::InDataset;
1057 }
1059
1060 Ok(false)
1061 }
1062
1063 fn collect_to_object(
1067 state: &mut CollectorState,
1068 token_src: &mut LazyDataSetReader<DynStatefulDecoder<BufReader<S>>>,
1069 in_item: bool,
1070 read_until: Option<Tag>,
1071 to: &mut InMemDicomObject<D>,
1072 dict: &D,
1073 ) -> Result<()> {
1074 let mut elements = Vec::new();
1075 Self::collect_elements(state, token_src, in_item, read_until, &mut elements, dict)?;
1076 to.extend(elements);
1077 Ok(())
1078 }
1079
1080 fn collect_elements(
1082 state: &mut CollectorState,
1083 token_src: &mut LazyDataSetReader<DynStatefulDecoder<BufReader<S>>>,
1084 in_item: bool,
1085 read_until: Option<Tag>,
1086 to: &mut Vec<DataElement<InMemDicomObject<D>>>,
1087 dict: &D,
1088 ) -> Result<()> {
1089 while let Some(token) = token_src.peek().context(ReadTokenSnafu)? {
1091 let token = token.clone();
1092 let elem = match token {
1093 DataToken::PixelSequenceStart => {
1094 if read_until
1096 .map(|t| t <= Tag(0x7fe0, 0x0010))
1097 .unwrap_or(false)
1098 {
1099 break;
1100 }
1101 *state = CollectorState::InPixelData;
1102 token_src.advance();
1103 let value = Self::build_encapsulated_data(&mut *token_src)?;
1104 DataElement::new(Tag(0x7fe0, 0x0010), VR::OB, value)
1105 }
1106 DataToken::ElementHeader(header) => {
1107 if read_until.map(|t| t <= header.tag).unwrap_or(false) {
1109 break;
1110 }
1111
1112 drop(token);
1113
1114 *state = CollectorState::InDataset;
1115 token_src.advance();
1116
1117 let next_token = token_src.advance().context(MissingElementValueSnafu)?;
1119 match next_token.context(ReadTokenSnafu)? {
1120 token @ LazyDataToken::LazyValue { .. }
1121 | token @ LazyDataToken::LazyItemValue { .. } => {
1122 InMemElement::new_with_len(
1123 header.tag,
1124 header.vr,
1125 header.len,
1126 token
1127 .into_value()
1128 .context(CollectDataValueSnafu { tag: header.tag })?,
1129 )
1130 }
1131 token => {
1132 return UnexpectedTokenSnafu { token }.fail().map_err(From::from);
1133 }
1134 }
1135 }
1136 DataToken::SequenceStart { tag, len } => {
1137 if read_until.map(|t| t <= tag).unwrap_or(false) {
1139 break;
1140 }
1141 *state = CollectorState::InDataset;
1142
1143 token_src.advance();
1144
1145 let mut items = C::new();
1147 Self::collect_sequence(
1148 &mut *state,
1149 tag,
1150 len,
1151 &mut *token_src,
1152 dict,
1153 &mut items,
1154 )?;
1155 DataElement::new_with_len(
1156 tag,
1157 VR::SQ,
1158 len,
1159 DicomValue::new_sequence(items, len),
1160 )
1161 }
1162 DataToken::ItemEnd if in_item => {
1163 token_src.advance();
1165 return Ok(());
1166 }
1167 token => {
1168 return UnexpectedDataTokenSnafu {
1169 token: token.clone(),
1170 }
1171 .fail()
1172 .map_err(From::from)
1173 }
1174 };
1175 to.push(elem);
1176 }
1177
1178 Ok(())
1179 }
1180
1181 fn build_encapsulated_data(
1184 dataset: &mut LazyDataSetReader<DynStatefulDecoder<BufReader<S>>>,
1185 ) -> Result<DicomValue<InMemDicomObject<D>, InMemFragment>> {
1186 let mut offset_table = None;
1191
1192 let mut fragments = C::new();
1193
1194 let mut first = true;
1197
1198 while let Some(token) = dataset.advance() {
1199 let token = token.context(ReadTokenSnafu)?;
1200 match token {
1201 LazyDataToken::LazyItemValue { decoder, len } => {
1202 if first {
1203 let mut table = Vec::new();
1204 decoder
1205 .read_u32_to_vec(len, &mut table)
1206 .context(ReadItemSnafu)?;
1207 first = false;
1208 } else {
1209 let mut data = Vec::new();
1210 decoder.read_to_vec(len, &mut data).context(ReadItemSnafu)?;
1211 fragments.push(data);
1212 }
1213 }
1214 LazyDataToken::ItemEnd => {
1215 if offset_table.is_none() {
1219 offset_table = Some(Vec::new())
1220 }
1221 }
1222 LazyDataToken::ItemStart { len: _ } => { }
1223 LazyDataToken::SequenceEnd => {
1224 break;
1226 }
1227 token @ LazyDataToken::ElementHeader(_)
1229 | token @ LazyDataToken::PixelSequenceStart
1230 | token @ LazyDataToken::SequenceStart { .. }
1231 | token @ LazyDataToken::LazyValue { .. }
1232 | token => {
1233 return UnexpectedTokenSnafu { token }.fail().map_err(From::from);
1234 }
1235 }
1236 }
1237
1238 Ok(DicomValue::from(PixelFragmentSequence::new(
1239 offset_table.unwrap_or_default(),
1240 fragments,
1241 )))
1242 }
1243
1244 fn collect_sequence(
1246 state: &mut CollectorState,
1247 _tag: Tag,
1248 _len: Length,
1249 token_src: &mut LazyDataSetReader<DynStatefulDecoder<BufReader<S>>>,
1250 dict: &D,
1251 items: &mut C<InMemDicomObject<D>>,
1252 ) -> Result<()> {
1253 while let Some(token) = token_src.advance() {
1254 match token.context(ReadTokenSnafu)? {
1255 LazyDataToken::ItemStart { len: _ } => {
1256 let mut obj = InMemDicomObject::new_empty_with_dict(dict.clone());
1257 Self::collect_to_object(state, token_src, true, None, &mut obj, dict)?;
1258 items.push(obj);
1259 }
1260 LazyDataToken::SequenceEnd => {
1261 return Ok(());
1262 }
1263 token => return UnexpectedTokenSnafu { token }.fail().map_err(From::from),
1264 };
1265 }
1266
1267 PrematureEndSnafu.fail().map_err(From::from)
1269 }
1270}
1271
1272#[cfg(test)]
1273mod tests {
1274 use std::io::{BufReader, Write};
1275
1276 use dicom_core::{prelude::*, value::DataSetSequence, PrimitiveValue};
1277 use dicom_dictionary_std::{tags, uids, StandardDataDictionary};
1278 use dicom_encoding::TransferSyntaxIndex;
1279 use dicom_parser::dataset::read::OddLengthStrategy;
1280 use dicom_transfer_syntax_registry::TransferSyntaxRegistry;
1281
1282 use crate::{
1283 file::ReadPreamble, DicomCollectorOptions, FileMetaTable, FileMetaTableBuilder,
1284 InMemDicomObject,
1285 };
1286
1287 use super::DicomCollector;
1288
1289 #[test]
1292 fn test_read_dataset_to_end_set_ts() {
1293 let dataset1 = InMemDicomObject::<StandardDataDictionary>::from_element_iter([
1294 DataElement::new(
1295 tags::SOP_INSTANCE_UID,
1296 VR::UI,
1297 "2.25.51008724832548260562721775118239811861\0",
1298 ),
1299 DataElement::new(
1300 tags::SOP_CLASS_UID,
1301 VR::UI,
1302 uids::NUCLEAR_MEDICINE_IMAGE_STORAGE,
1303 ),
1304 DataElement::new(tags::PATIENT_NAME, VR::PN, "Doe^John"),
1305 DataElement::new(tags::STUDY_DESCRIPTION, VR::LO, "Test study"),
1306 DataElement::new(tags::ROWS, VR::US, PrimitiveValue::from(64_u16)),
1307 DataElement::new(tags::COLUMNS, VR::US, PrimitiveValue::from(64_u16)),
1308 DataElement::new(tags::BITS_ALLOCATED, VR::US, PrimitiveValue::from(8_u16)),
1309 DataElement::new(tags::BITS_STORED, VR::US, PrimitiveValue::from(8_u16)),
1310 DataElement::new(tags::HIGH_BIT, VR::US, PrimitiveValue::from(7_u16)),
1311 DataElement::new(
1312 tags::PIXEL_DATA,
1313 VR::OB,
1314 PrimitiveValue::from(vec![0x55u8; 64 * 64]),
1315 ),
1316 ]);
1317
1318 let ts_expl_vr_le = TransferSyntaxRegistry
1319 .get(uids::EXPLICIT_VR_LITTLE_ENDIAN)
1320 .unwrap();
1321
1322 let mut encoded = Vec::new();
1323 dataset1
1324 .write_dataset_with_ts(&mut encoded, ts_expl_vr_le)
1325 .unwrap();
1326
1327 let reader = BufReader::new(std::io::Cursor::new(&encoded));
1328 let mut collector = DicomCollector::new_with_ts(reader, uids::EXPLICIT_VR_LITTLE_ENDIAN);
1329
1330 let mut dset = InMemDicomObject::new_empty();
1331 collector.read_dataset_to_end(&mut dset).unwrap();
1332
1333 assert_eq!(dset, dataset1);
1334 }
1335
1336 #[test]
1339 fn test_read_dataset_to_end_infer_from_meta() {
1340 let dataset1 = InMemDicomObject::<StandardDataDictionary>::from_element_iter([
1341 DataElement::new(
1342 tags::SOP_INSTANCE_UID,
1343 VR::UI,
1344 "2.25.245029432991021387484564600987886994494",
1345 ),
1346 DataElement::new(
1347 tags::SOP_CLASS_UID,
1348 VR::UI,
1349 uids::NUCLEAR_MEDICINE_IMAGE_STORAGE,
1350 ),
1351 DataElement::new(tags::PATIENT_NAME, VR::PN, "Doe^John"),
1352 DataElement::new(tags::STUDY_DESCRIPTION, VR::LO, "Test study"),
1353 DataElement::new(tags::ROWS, VR::US, PrimitiveValue::from(128_u16)),
1354 DataElement::new(tags::COLUMNS, VR::US, PrimitiveValue::from(128_u16)),
1355 DataElement::new(tags::BITS_ALLOCATED, VR::US, PrimitiveValue::from(16_u16)),
1356 DataElement::new(tags::BITS_STORED, VR::US, PrimitiveValue::from(16_u16)),
1357 DataElement::new(tags::HIGH_BIT, VR::US, PrimitiveValue::from(15_u16)),
1358 DataElement::new(
1359 tags::PIXEL_DATA,
1360 VR::OB,
1361 PrimitiveValue::from(vec![0x55u8; 128 * 128 * 2]),
1362 ),
1363 ]);
1364
1365 let file_dataset1 = dataset1
1366 .clone()
1367 .with_meta(FileMetaTableBuilder::new().transfer_syntax(uids::EXPLICIT_VR_LITTLE_ENDIAN))
1368 .unwrap();
1369
1370 let mut encoded = Vec::new();
1372 encoded.write_all(b"DICM").unwrap();
1373 file_dataset1.meta().write(&mut encoded).unwrap();
1374 file_dataset1
1375 .write_dataset_with_ts(
1376 &mut encoded,
1377 TransferSyntaxRegistry
1378 .get(uids::EXPLICIT_VR_LITTLE_ENDIAN)
1379 .unwrap(),
1380 )
1381 .unwrap();
1382
1383 let reader = BufReader::new(std::io::Cursor::new(&encoded));
1384 let mut collector = DicomCollector::new(reader);
1385
1386 let mut dset = InMemDicomObject::new_empty();
1387 let file_meta = collector.read_file_meta().unwrap();
1388 assert_eq!(file_meta.transfer_syntax(), uids::EXPLICIT_VR_LITTLE_ENDIAN,);
1389 collector.read_dataset_to_end(&mut dset).unwrap();
1390
1391 assert_eq!(dset, dataset1);
1392 }
1393
1394 #[test]
1396 fn test_take_file_meta() {
1397 let dataset1 = InMemDicomObject::<StandardDataDictionary>::from_element_iter([
1398 DataElement::new(
1399 tags::SOP_INSTANCE_UID,
1400 VR::UI,
1401 "2.25.248821220596756482508841578490676982546",
1402 ),
1403 DataElement::new(
1404 tags::SOP_CLASS_UID,
1405 VR::UI,
1406 uids::NUCLEAR_MEDICINE_IMAGE_STORAGE,
1407 ),
1408 DataElement::new(tags::PATIENT_NAME, VR::PN, "Doe^John"),
1409 DataElement::new(tags::STUDY_DESCRIPTION, VR::LO, "Test study"),
1410 DataElement::new(tags::ROWS, VR::US, PrimitiveValue::from(64_u16)),
1411 DataElement::new(tags::COLUMNS, VR::US, PrimitiveValue::from(64_u16)),
1412 DataElement::new(tags::SAMPLES_PER_PIXEL, VR::US, PrimitiveValue::from(1_u16)),
1413 DataElement::new(tags::BITS_ALLOCATED, VR::US, PrimitiveValue::from(8_u16)),
1414 DataElement::new(tags::BITS_STORED, VR::US, PrimitiveValue::from(8_u16)),
1415 DataElement::new(tags::HIGH_BIT, VR::US, PrimitiveValue::from(7_u16)),
1416 DataElement::new(
1417 tags::PIXEL_DATA,
1418 VR::OB,
1419 PrimitiveValue::from(vec![0x55u8; 64 * 64]),
1420 ),
1421 ]);
1422
1423 let file_dataset1 = dataset1
1424 .clone()
1425 .with_meta(FileMetaTableBuilder::new().transfer_syntax(uids::EXPLICIT_VR_LITTLE_ENDIAN))
1426 .unwrap();
1427
1428 let mut encoded = Vec::new();
1430 encoded.write_all(b"DICM").unwrap();
1431 file_dataset1.meta().write(&mut encoded).unwrap();
1432 file_dataset1
1433 .write_dataset_with_ts(
1434 &mut encoded,
1435 TransferSyntaxRegistry
1436 .get(uids::EXPLICIT_VR_LITTLE_ENDIAN)
1437 .unwrap(),
1438 )
1439 .unwrap();
1440
1441 let reader = BufReader::new(std::io::Cursor::new(&encoded));
1442 let mut collector = DicomCollector::new(reader);
1443
1444 let _: &FileMetaTable = collector.read_file_meta().unwrap();
1446 let mut main_dataset = InMemDicomObject::new_empty();
1448 collector
1449 .read_dataset_up_to_pixeldata(&mut main_dataset)
1450 .unwrap();
1451
1452 let file_meta: FileMetaTable = collector
1454 .take_file_meta()
1455 .expect("should have file meta info");
1456 assert_eq!(
1457 file_meta.media_storage_sop_instance_uid(),
1458 "2.25.248821220596756482508841578490676982546"
1459 );
1460
1461 let mut fragment_data = Vec::new();
1463 let bytes_read = collector.read_next_fragment(&mut fragment_data).unwrap();
1464 assert_eq!(bytes_read, Some(64 * 64));
1465 assert_eq!(fragment_data.len(), bytes_read.unwrap() as usize);
1466 }
1467
1468 #[test]
1470 fn test_read_dataset_nested() {
1471 let dataset1 = InMemDicomObject::<StandardDataDictionary>::from_element_iter([
1472 DataElement::new(
1473 tags::SOP_INSTANCE_UID,
1474 VR::UI,
1475 "2.25.245029432991021387484564600987886994494",
1476 ),
1477 DataElement::new(
1478 tags::SOP_CLASS_UID,
1479 VR::UI,
1480 uids::NUCLEAR_MEDICINE_IMAGE_STORAGE,
1481 ),
1482 DataElement::new(tags::PATIENT_NAME, VR::PN, "Doe^John"),
1483 DataElement::new(tags::STUDY_DESCRIPTION, VR::LO, "Test study"),
1484 DataElement::new(
1485 tags::ANATOMIC_REGION_SEQUENCE,
1486 VR::SQ,
1487 DataSetSequence::from(vec![InMemDicomObject::from_element_iter([
1488 DataElement::new(tags::CODE_VALUE, VR::SH, "51185008"),
1489 DataElement::new(tags::CODING_SCHEME_DESIGNATOR, VR::SH, "SCT"),
1490 DataElement::new(tags::CODE_MEANING, VR::LO, "chest"),
1491 DataElement::new(
1492 tags::ANATOMIC_REGION_MODIFIER_SEQUENCE,
1493 VR::SQ,
1494 DataSetSequence::from(vec![InMemDicomObject::from_element_iter([
1495 DataElement::new(tags::CODE_VALUE, VR::SH, "302551006"),
1496 DataElement::new(tags::CODING_SCHEME_DESIGNATOR, VR::SH, "SCT"),
1497 DataElement::new(tags::CODE_MEANING, VR::LO, "entire thorax "),
1498 ])]),
1499 ),
1500 ])]),
1501 ),
1502 DataElement::new(tags::ROWS, VR::US, PrimitiveValue::from(128_u16)),
1503 DataElement::new(tags::COLUMNS, VR::US, PrimitiveValue::from(128_u16)),
1504 DataElement::new(tags::BITS_ALLOCATED, VR::US, PrimitiveValue::from(16_u16)),
1505 DataElement::new(tags::BITS_STORED, VR::US, PrimitiveValue::from(16_u16)),
1506 DataElement::new(tags::HIGH_BIT, VR::US, PrimitiveValue::from(7_u16)),
1507 DataElement::new(
1508 tags::PIXEL_DATA,
1509 VR::OB,
1510 PrimitiveValue::from(vec![0x55_u8; 128 * 128]),
1511 ),
1512 ]);
1513
1514 let ts_expl_vr_le = TransferSyntaxRegistry
1515 .get(uids::EXPLICIT_VR_LITTLE_ENDIAN)
1516 .unwrap();
1517
1518 let mut encoded = Vec::new();
1519 dataset1
1520 .write_dataset_with_ts(&mut encoded, ts_expl_vr_le)
1521 .unwrap();
1522
1523 let reader = BufReader::new(std::io::Cursor::new(&encoded));
1524
1525 let mut collector = DicomCollector::new_with_ts(reader, uids::EXPLICIT_VR_LITTLE_ENDIAN);
1526
1527 let mut dset = InMemDicomObject::new_empty();
1528 collector.read_dataset_to_end(&mut dset).unwrap();
1529
1530 let v = dset
1532 .value_at((tags::ANATOMIC_REGION_SEQUENCE, tags::CODE_VALUE))
1533 .unwrap()
1534 .to_str()
1535 .unwrap();
1536 assert_eq!(v, "51185008");
1537
1538 let v = dset
1539 .value_at((
1540 tags::ANATOMIC_REGION_SEQUENCE,
1541 tags::ANATOMIC_REGION_MODIFIER_SEQUENCE,
1542 tags::CODE_MEANING,
1543 ))
1544 .unwrap()
1545 .to_str()
1546 .unwrap();
1547 assert_eq!(v, "entire thorax");
1548 }
1549
1550 #[test]
1552 fn test_read_dataset_two_parts() {
1553 let dataset1 = InMemDicomObject::<StandardDataDictionary>::from_element_iter([
1554 DataElement::new(
1555 tags::SOP_INSTANCE_UID,
1556 VR::UI,
1557 "2.25.245029432991021387484564600987886994494",
1558 ),
1559 DataElement::new(
1560 tags::SOP_CLASS_UID,
1561 VR::UI,
1562 uids::NUCLEAR_MEDICINE_IMAGE_STORAGE,
1563 ),
1564 DataElement::new(tags::PATIENT_NAME, VR::PN, "Doe^John"),
1565 DataElement::new(tags::STUDY_DESCRIPTION, VR::LO, "Test study"),
1566 DataElement::new(tags::ROWS, VR::US, PrimitiveValue::from(128_u16)),
1567 DataElement::new(tags::COLUMNS, VR::US, PrimitiveValue::from(128_u16)),
1568 DataElement::new(tags::BITS_ALLOCATED, VR::US, PrimitiveValue::from(16_u16)),
1569 DataElement::new(tags::BITS_STORED, VR::US, PrimitiveValue::from(16_u16)),
1570 DataElement::new(tags::HIGH_BIT, VR::US, PrimitiveValue::from(7_u16)),
1571 DataElement::new(
1572 tags::PIXEL_DATA,
1573 VR::OB,
1574 PrimitiveValue::from(vec![0x55_u8; 128 * 128]),
1575 ),
1576 ]);
1577
1578 let ts_expl_vr_le = TransferSyntaxRegistry
1579 .get(uids::EXPLICIT_VR_LITTLE_ENDIAN)
1580 .unwrap();
1581
1582 let mut encoded = Vec::new();
1583 dataset1
1584 .write_dataset_with_ts(&mut encoded, ts_expl_vr_le)
1585 .unwrap();
1586
1587 let reader = BufReader::new(std::io::Cursor::new(&encoded));
1588
1589 let mut collector = DicomCollectorOptions::new()
1590 .expected_ts(uids::EXPLICIT_VR_LITTLE_ENDIAN)
1591 .read_preamble(ReadPreamble::Never)
1592 .odd_length_strategy(OddLengthStrategy::Fail)
1593 .from_reader(reader);
1594
1595 let mut dset1 = InMemDicomObject::new_empty();
1597
1598 collector
1599 .read_dataset_up_to(tags::ROWS, &mut dset1)
1600 .unwrap();
1601 assert_eq!(
1603 dset1.get(tags::PATIENT_NAME).unwrap().to_str().unwrap(),
1604 "Doe^John"
1605 );
1606 assert_eq!(
1607 dset1
1608 .get(tags::STUDY_DESCRIPTION)
1609 .unwrap()
1610 .to_str()
1611 .unwrap(),
1612 "Test study"
1613 );
1614 assert!(dset1.get(tags::ROWS).is_none());
1616 assert!(dset1.get(tags::PIXEL_DATA).is_none());
1617
1618 let mut dset2 = InMemDicomObject::new_empty();
1620
1621 collector.read_dataset_to_end(&mut dset2).unwrap();
1622
1623 assert_eq!(dset2.get(tags::ROWS).unwrap().to_int::<u16>().unwrap(), 128);
1625 assert_eq!(
1626 dset2.get(tags::COLUMNS).unwrap().to_int::<u16>().unwrap(),
1627 128
1628 );
1629 assert_eq!(
1630 &*dset2.get(tags::PIXEL_DATA).unwrap().to_bytes().unwrap(),
1631 &[0x55_u8; 128 * 128]
1632 );
1633
1634 assert!(dset2.get(tags::SOP_INSTANCE_UID).is_none());
1636 assert!(dset2.get(tags::PATIENT_NAME).is_none());
1637 assert!(dset2.get(tags::STUDY_DESCRIPTION).is_none());
1638 }
1639
1640 #[test]
1642 fn test_read_fragments() {
1643 let filename = dicom_test_files::path("WG04/JPLY/SC1_JPLY").unwrap();
1644
1645 let mut collector = DicomCollector::open_file(filename).unwrap();
1646
1647 let fmi = collector.read_file_meta().unwrap();
1648
1649 assert_eq!(fmi.transfer_syntax(), uids::JPEG_EXTENDED12_BIT);
1650
1651 let mut bot = Vec::new();
1654 let len = collector
1655 .read_next_fragment(&mut bot)
1656 .expect("should read basic offset table successfully")
1657 .expect("should have basic offset table fragment");
1658 assert_eq!(len, 0);
1659 assert!(bot.is_empty());
1660
1661 let mut fragment = Vec::with_capacity(131_072);
1664
1665 let len = collector
1666 .read_next_fragment(&mut fragment)
1667 .expect("should read fragment successfully")
1668 .expect("should have fragment #0");
1669 assert_eq!(len, 65_536);
1670
1671 assert_eq!(&fragment[0..4], &[0xFF, 0xD8, 0xFF, 0xC1]);
1673
1674 let len = collector
1677 .read_next_fragment(&mut fragment)
1678 .expect("should read fragment successfully")
1679 .expect("should have fragment #1");
1680 assert_eq!(len, 65_536);
1681
1682 assert_eq!(fragment.len(), 131_072);
1684
1685 assert_eq!(&fragment[0..4], &[0xFF, 0xD8, 0xFF, 0xC1]);
1687 assert_eq!(&fragment[65_536..65_540], &[0x04, 0x6C, 0x3B, 0x60]);
1688
1689 let mut remaining: i32 = 10; fragment.clear();
1693
1694 while let Some(_len) = collector
1695 .read_next_fragment(&mut fragment)
1696 .expect("should have read fragment successfully")
1697 {
1698 remaining -= 1;
1699 assert!(!fragment.is_empty());
1700 fragment.clear();
1701 }
1702
1703 assert_eq!(remaining, 0);
1704 }
1705
1706 #[test]
1708 fn test_read_bot_and_fragments() {
1709 let filename = dicom_test_files::path("pydicom/SC_rgb_rle_2frame.dcm").unwrap();
1710
1711 let mut collector = DicomCollector::open_file(filename).unwrap();
1712
1713 let fmi = collector.read_file_meta().unwrap();
1714
1715 assert_eq!(fmi.transfer_syntax(), uids::RLE_LOSSLESS);
1716
1717 let mut bot = Vec::new();
1719 let len = collector
1720 .read_basic_offset_table(&mut bot)
1721 .expect("should read basic offset table successfully")
1722 .expect("should have basic offset table fragment");
1723 assert_eq!(len, 8);
1724 assert_eq!(&bot, &[0x0000, 0x02A0]);
1725
1726 assert!(matches!(
1728 collector.read_basic_offset_table(&mut bot),
1729 Err(super::Error(super::InnerError::IllegalStateInPixel { .. })),
1730 ));
1731
1732 let mut fragment = Vec::with_capacity(2048);
1735
1736 let len = collector
1737 .read_next_fragment(&mut fragment)
1738 .expect("should read fragment successfully")
1739 .expect("should have fragment #0");
1740 assert_eq!(len, 664);
1741
1742 assert_eq!(&fragment[0..5], &[0x03, 0x00, 0x00, 0x00, 0x40]);
1744
1745 let len = collector
1748 .read_next_fragment(&mut fragment)
1749 .expect("should read fragment successfully")
1750 .expect("should have fragment #1");
1751 assert_eq!(len, 664);
1752
1753 assert_eq!(fragment.len(), 664 + 664);
1755
1756 assert_eq!(&fragment[0..5], &[0x03, 0x00, 0x00, 0x00, 0x40]);
1758 assert_eq!(&fragment[664 + 659..], &[0x00, 0x9D, 0x00, 0x9D, 0x00]);
1759
1760 assert!(collector
1762 .read_next_fragment(&mut fragment)
1763 .expect("attempt to read the next fragment should not have failed")
1764 .is_none());
1765 }
1766}