Skip to content

Commit e22ad40

Browse files
committed
[object] Convert DICOM objects into tokens
- add `tokens` module, InMemObjectTokens turns objects into tokens - rename `from_element_iter` and `from_iter_with_dict` - to `from_element_source` and `from_element_source_with_dict` - replace former names with non-fallible versions
1 parent 461005d commit e22ad40

File tree

3 files changed

+243
-86
lines changed

3 files changed

+243
-86
lines changed

object/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ pub mod loader;
3535
pub mod mem;
3636
pub mod meta;
3737
pub mod pixeldata;
38+
pub mod tokens;
3839

3940
mod util;
4041

object/src/mem.rs

Lines changed: 143 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -99,10 +99,18 @@ impl InMemDicomObject<StandardDataDictionary> {
9999
}
100100
}
101101

102-
/// Construct a DICOM object from an iterator of structured elements.
103-
pub fn from_element_iter<I>(iter: I) -> Result<Self>
102+
/// Construct a DICOM object from a fallible source of structured elements.
103+
pub fn from_element_source<I>(iter: I) -> Result<Self>
104104
where
105105
I: IntoIterator<Item = Result<InMemElement<StandardDataDictionary>>>,
106+
{
107+
Self::from_element_source_with_dict(iter, StandardDataDictionary)
108+
}
109+
110+
/// Construct a DICOM object from a non-fallible source of structured elements.
111+
pub fn from_element_iter<I>(iter: I) -> Self
112+
where
113+
I: IntoIterator<Item = InMemElement<StandardDataDictionary>>,
106114
{
107115
Self::from_iter_with_dict(iter, StandardDataDictionary)
108116
}
@@ -231,7 +239,7 @@ where
231239
}
232240

233241
/// Construct a DICOM object from an iterator of structured elements.
234-
pub fn from_iter_with_dict<I>(iter: I, dict: D) -> Result<Self>
242+
pub fn from_element_source_with_dict<I>(iter: I, dict: D) -> Result<Self>
235243
where
236244
I: IntoIterator<Item = Result<InMemElement<D>>>,
237245
{
@@ -243,6 +251,19 @@ where
243251
})
244252
}
245253

254+
/// Construct a DICOM object from a non-fallible iterator of structured elements.
255+
pub fn from_iter_with_dict<I>(iter: I, dict: D) -> Self
256+
where
257+
I: IntoIterator<Item = InMemElement<D>>,
258+
{
259+
let entries = iter.into_iter().map(|e| (e.tag(), e)).collect();
260+
InMemDicomObject {
261+
entries,
262+
dict,
263+
len: Length::UNDEFINED,
264+
}
265+
}
266+
246267
// Standard methods follow. They are not placed as a trait implementation
247268
// because they may require outputs to reference the lifetime of self,
248269
// which is not possible without GATs.
@@ -372,88 +393,6 @@ impl<D> IntoIterator for InMemDicomObject<D> {
372393
}
373394
}
374395

375-
376-
#[derive(Debug)]
377-
pub struct InMemTokens<M, I, D> {
378-
main_iters: Vec<M>,
379-
item_iters: Vec<I>,
380-
pending_elem: Option<DataElement<D>>,
381-
382-
}
383-
384-
impl<A, I, D> InMemTokens<A, I, D>
385-
where
386-
A: Iterator,
387-
I: Iterator,
388-
{
389-
pub fn new<T>(obj: T) -> Self
390-
where
391-
T: IntoIterator<Item = A::Item, IntoIter = A>,
392-
{
393-
InMemTokens {
394-
main_iters: vec![obj.into_iter()],
395-
item_iters: vec![],
396-
pending_elem: None,
397-
}
398-
}
399-
}
400-
401-
impl<P, A, D> Iterator for InMemTokens<P, A, D>
402-
where
403-
P: Iterator<Item = InMemElement<D>>,
404-
A: Iterator<Item = InMemDicomObject<D>>,
405-
D: DataDictionary,
406-
{
407-
type Item = dicom_parser::dataset::DataToken;
408-
409-
fn next(&mut self) -> Option<Self::Item> {
410-
// if no more iterators, return None
411-
let iter = match self.main_iters.last_mut() {
412-
None => return None,
413-
Some(iter) => iter,
414-
};
415-
416-
// if no more tokens on the last iterator,
417-
// return sequence/item end
418-
let elem = match iter.next() {
419-
None => {
420-
self.main_iters.pop();
421-
match self.main_iters.len() {
422-
// object end
423-
0 => return None,
424-
// item end
425-
n if n % 2 == 0 => {
426-
return Some(DataToken::ItemEnd);
427-
},
428-
// sequence end
429-
_ => {
430-
return Some(DataToken::SequenceEnd);
431-
},
432-
}
433-
}
434-
Some(elem) => elem,
435-
};
436-
437-
match elem.vr() {
438-
VR::SQ => {
439-
}
440-
_ => {
441-
442-
}
443-
}
444-
445-
todo!()
446-
}
447-
448-
fn size_hint(&self) -> (usize, Option<usize>) {
449-
// make a slightly better estimation for the minimum
450-
// number of tokens that follow
451-
let min_tokens: usize = self.main_iters.iter().map(|iter| iter.size_hint().0).sum::<usize>()
452-
+ self.item_iters.iter().map(|iter| iter.size_hint().0).sum::<usize>();
453-
(min_tokens, None)
454-
}
455-
}
456-
457396
/// Base iterator type for an in-memory DICOM object.
458397
#[derive(Debug)]
459398
pub struct Iter<D> {
@@ -480,8 +419,9 @@ impl<D> Iterator for Iter<D> {
480419
mod tests {
481420

482421
use super::*;
422+
use dicom_core::header::{DataElementHeader, Length, VR};
483423
use dicom_core::value::PrimitiveValue;
484-
use dicom_core::VR;
424+
use dicom_parser::dataset::IntoTokens;
485425

486426
#[test]
487427
fn inmem_object_write() {
@@ -520,4 +460,121 @@ mod tests {
520460
let elem1 = (&obj).element_by_name("PatientName").unwrap();
521461
assert_eq!(elem1, &another_patient_name);
522462
}
463+
464+
#[test]
465+
fn inmem_empty_object_into_tokens() {
466+
let obj = InMemDicomObject::create_empty();
467+
let tokens = obj.into_tokens();
468+
assert_eq!(tokens.count(), 0);
469+
}
470+
471+
#[test]
472+
fn inmem_shallow_object_into_tokens() {
473+
let patient_name = DataElement::new(
474+
Tag(0x0010, 0x0010),
475+
VR::PN,
476+
PrimitiveValue::Str("Doe^John".to_string()).into(),
477+
);
478+
let modality = DataElement::new(
479+
Tag(0x0008, 0x0060),
480+
VR::CS,
481+
PrimitiveValue::Str("MG".to_string()).into(),
482+
);
483+
let mut obj = InMemDicomObject::create_empty();
484+
obj.put(patient_name);
485+
obj.put(modality);
486+
487+
let tokens: Vec<_> = obj.into_tokens().collect();
488+
489+
assert_eq!(
490+
tokens,
491+
vec![
492+
DataToken::ElementHeader(DataElementHeader {
493+
tag: Tag(0x0008, 0x0060),
494+
vr: VR::CS,
495+
len: Length(2),
496+
}),
497+
DataToken::PrimitiveValue(PrimitiveValue::Str("MG".to_owned())),
498+
DataToken::ElementHeader(DataElementHeader {
499+
tag: Tag(0x0010, 0x0010),
500+
vr: VR::PN,
501+
len: Length(8),
502+
}),
503+
DataToken::PrimitiveValue(PrimitiveValue::Str("Doe^John".to_owned())),
504+
]
505+
);
506+
}
507+
508+
#[test]
509+
fn inmem_deep_object_into_tokens() {
510+
use smallvec::smallvec;
511+
512+
let obj_1 = InMemDicomObject::from_element_iter(vec![
513+
DataElement::new(Tag(0x0018, 0x6012), VR::US, Value::Primitive(1_u16.into())),
514+
DataElement::new(Tag(0x0018, 0x6014), VR::US, Value::Primitive(2_u16.into())),
515+
]);
516+
517+
let obj_2 = InMemDicomObject::from_element_iter(vec![DataElement::new(
518+
Tag(0x0018, 0x6012),
519+
VR::US,
520+
Value::Primitive(4_u16.into()),
521+
)]);
522+
523+
let main_obj = InMemDicomObject::from_element_iter(vec![
524+
DataElement::new(
525+
Tag(0x0018, 0x6011),
526+
VR::SQ,
527+
Value::Sequence {
528+
items: smallvec![obj_1, obj_2],
529+
size: Length::UNDEFINED,
530+
},
531+
),
532+
DataElement::new(Tag(0x0020, 0x4000), VR::LT, Value::Primitive("TEST".into())),
533+
]);
534+
535+
let tokens: Vec<_> = main_obj.into_tokens().collect();
536+
537+
assert_eq!(
538+
tokens,
539+
vec![
540+
DataToken::SequenceStart {
541+
tag: Tag(0x0018, 0x6011),
542+
len: Length::UNDEFINED,
543+
},
544+
DataToken::ItemStart {
545+
len: Length::UNDEFINED,
546+
},
547+
DataToken::ElementHeader(DataElementHeader {
548+
tag: Tag(0x0018, 0x6012),
549+
vr: VR::US,
550+
len: Length(2),
551+
}),
552+
DataToken::PrimitiveValue(PrimitiveValue::U16([1].as_ref().into())),
553+
DataToken::ElementHeader(DataElementHeader {
554+
tag: Tag(0x0018, 0x6014),
555+
vr: VR::US,
556+
len: Length(2),
557+
}),
558+
DataToken::PrimitiveValue(PrimitiveValue::U16([2].as_ref().into())),
559+
DataToken::ItemEnd,
560+
DataToken::ItemStart {
561+
len: Length::UNDEFINED,
562+
},
563+
DataToken::ElementHeader(DataElementHeader {
564+
tag: Tag(0x0018, 0x6012),
565+
vr: VR::US,
566+
len: Length(2),
567+
}),
568+
DataToken::PrimitiveValue(PrimitiveValue::U16([4].as_ref().into())),
569+
DataToken::ItemEnd,
570+
DataToken::SequenceEnd,
571+
DataToken::ElementHeader(DataElementHeader {
572+
tag: Tag(0x0020, 0x4000),
573+
vr: VR::LT,
574+
len: Length(4),
575+
}),
576+
DataToken::PrimitiveValue(PrimitiveValue::Str("TEST".into())),
577+
]
578+
);
579+
}
523580
}

object/src/tokens.rs

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
//! Convertion of DICOM objects into tokens.
2+
use crate::mem::{InMemDicomObject};
3+
use dicom_core::value::{PrimitiveValue};
4+
use dicom_core::{DataElement, Length};
5+
use dicom_parser::dataset::{DataToken, IntoTokens};
6+
use std::collections::VecDeque;
7+
8+
/// A stream of tokens from a DICOM object.
9+
pub struct InMemObjectTokens<E> {
10+
/// iterators of tokens in order of priority.
11+
tokens_pending: VecDeque<DataToken>,
12+
/// the iterator of data elements in order.
13+
elem_iter: E,
14+
/// whenever a primitive data element is yet to be processed
15+
elem_pending: Option<PrimitiveValue>,
16+
/// whether the tokens are done
17+
fused: bool,
18+
}
19+
20+
impl<E> InMemObjectTokens<E>
21+
where
22+
E: Iterator,
23+
{
24+
pub fn new<T>(obj: T) -> Self
25+
where
26+
T: IntoIterator<IntoIter = E, Item = E::Item>,
27+
{
28+
InMemObjectTokens {
29+
tokens_pending: Default::default(),
30+
elem_iter: obj.into_iter(),
31+
elem_pending: None,
32+
fused: false,
33+
}
34+
}
35+
}
36+
37+
impl<E> InMemObjectTokens<E>
38+
where
39+
E: Iterator,
40+
E::Item: IntoTokens,
41+
{
42+
fn next_token(&mut self) -> Option<DataToken> {
43+
if let Some(token) = self.tokens_pending.pop_front() {
44+
return Some(token);
45+
}
46+
47+
// otherwise, expand next element, recurse
48+
if let Some(elem) = self.elem_iter.next() {
49+
// TODO eventually optimize this to be less eager
50+
self.tokens_pending = elem.into_tokens().collect();
51+
52+
self.next_token()
53+
} else {
54+
// no more elements
55+
None
56+
}
57+
}
58+
}
59+
60+
impl<E, I> Iterator for InMemObjectTokens<E>
61+
where
62+
E: Iterator<Item = DataElement<I>>,
63+
E::Item: IntoTokens,
64+
{
65+
type Item = DataToken;
66+
67+
fn next(&mut self) -> Option<Self::Item> {
68+
if self.fused {
69+
return None;
70+
}
71+
// if a data element is pending, return a value token
72+
if let Some(val) = self.elem_pending.take() {
73+
return Some(DataToken::PrimitiveValue(val));
74+
}
75+
76+
// otherwise, consume pending tokens
77+
if let Some(token) = self.next_token() {
78+
return Some(token);
79+
};
80+
81+
None
82+
}
83+
84+
fn size_hint(&self) -> (usize, Option<usize>) {
85+
// make a slightly better estimation for the minimum
86+
// number of tokens that follow: 2 tokens per element left
87+
(self.elem_iter.size_hint().0 * 2, None)
88+
}
89+
}
90+
91+
impl<D> IntoTokens for InMemDicomObject<D> {
92+
type Iter =
93+
InMemObjectTokens<<InMemDicomObject<D> as IntoIterator>::IntoIter>;
94+
95+
fn into_tokens(self) -> Self::Iter {
96+
InMemObjectTokens::new(self)
97+
}
98+
}
99+

0 commit comments

Comments
 (0)