dicom_dictionary_std/
data_element.rs

1//! Data element dictionary implementation
2
3use crate::tags::ENTRIES;
4use dicom_core::dictionary::{DataDictionary, DataDictionaryEntryRef, TagRange::*, VirtualVr};
5use dicom_core::header::Tag;
6use dicom_core::VR;
7use once_cell::sync::Lazy;
8use std::collections::{HashMap, HashSet};
9use std::fmt;
10use std::fmt::{Display, Formatter};
11
12static DICT: Lazy<StandardDataDictionaryRegistry> = Lazy::new(init_dictionary);
13
14/// Retrieve a singleton instance of the standard dictionary registry.
15///
16/// Note that one does not generally have to call this
17/// unless when retrieving the underlying registry is important.
18/// The unit type [`StandardDataDictionary`]
19/// already provides a lazy loaded singleton implementing the necessary traits.
20#[inline]
21pub fn registry() -> &'static StandardDataDictionaryRegistry {
22    &DICT
23}
24
25/// The data struct actually containing the standard dictionary.
26///
27/// This structure is made opaque via the unit type [`StandardDataDictionary`],
28/// which provides a lazy loaded singleton.
29#[derive(Debug)]
30pub struct StandardDataDictionaryRegistry {
31    /// mapping: name → entry
32    by_name: HashMap<&'static str, &'static DataDictionaryEntryRef<'static>>,
33    /// mapping: tag → entry
34    by_tag: HashMap<Tag, &'static DataDictionaryEntryRef<'static>>,
35    /// repeating elements of the form (ggxx, eeee). The `xx` portion is zeroed.
36    repeating_ggxx: HashSet<Tag>,
37    /// repeating elements of the form (gggg, eexx). The `xx` portion is zeroed.
38    repeating_eexx: HashSet<Tag>,
39}
40
41impl StandardDataDictionaryRegistry {
42    fn new() -> StandardDataDictionaryRegistry {
43        StandardDataDictionaryRegistry {
44            by_name: HashMap::with_capacity(5000),
45            by_tag: HashMap::with_capacity(5000),
46            repeating_ggxx: HashSet::with_capacity(75),
47            repeating_eexx: HashSet::new(),
48        }
49    }
50
51    /// record the given dictionary entry reference
52    fn index(&mut self, entry: &'static DataDictionaryEntryRef<'static>) -> &mut Self {
53        self.by_name.insert(entry.alias, entry);
54        self.by_tag.insert(entry.tag.inner(), entry);
55        match entry.tag {
56            Group100(tag) => {
57                self.repeating_ggxx.insert(tag);
58            }
59            Element100(tag) => {
60                self.repeating_eexx.insert(tag);
61            }
62            _ => {}
63        }
64        self
65    }
66}
67
68/// Generic Group Length dictionary entry.
69static GROUP_LENGTH_ENTRY: DataDictionaryEntryRef<'static> = DataDictionaryEntryRef {
70    tag: GroupLength,
71    alias: "GenericGroupLength",
72    vr: VirtualVr::Exact(VR::UL),
73};
74
75/// Generic Private Creator dictionary entry.
76static PRIVATE_CREATOR_ENTRY: DataDictionaryEntryRef<'static> = DataDictionaryEntryRef {
77    tag: PrivateCreator,
78    alias: "PrivateCreator",
79    vr: VirtualVr::Exact(VR::LO),
80};
81
82/// A data element dictionary which consults
83/// the library's global DICOM attribute registry.
84///
85/// This is the type which would generally be used
86/// whenever a data element dictionary is needed,
87/// such as when reading DICOM objects.
88///
89/// The dictionary index is automatically initialized upon the first use.
90#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
91pub struct StandardDataDictionary;
92
93impl StandardDataDictionary {
94    fn indexed_tag(tag: Tag) -> Option<&'static DataDictionaryEntryRef<'static>> {
95        let r = registry();
96
97        r.by_tag
98            .get(&tag)
99            .or_else(|| {
100                // check tags repeating in different groups
101                let group_trimmed = Tag(tag.0 & 0xFF00, tag.1);
102                if r.repeating_ggxx.contains(&group_trimmed) {
103                    return r.by_tag.get(&group_trimmed);
104                }
105                // check tags repeating in different elements
106                let elem_trimmed = Tag(tag.0, tag.1 & 0xFF00);
107                if r.repeating_eexx.contains(&elem_trimmed) {
108                    return r.by_tag.get(&elem_trimmed);
109                }
110
111                None
112            })
113            .cloned()
114            .or_else(|| {
115                // check for private creator
116                if tag.0 & 1 == 1 && (0x0010..=0x00FF).contains(&tag.1) {
117                    return Some(&PRIVATE_CREATOR_ENTRY);
118                }
119                // check for group length
120                if tag.element() == 0x0000 {
121                    return Some(&GROUP_LENGTH_ENTRY);
122                }
123
124                None
125            })
126    }
127}
128
129impl DataDictionary for StandardDataDictionary {
130    type Entry = DataDictionaryEntryRef<'static>;
131
132    fn by_name(&self, name: &str) -> Option<&Self::Entry> {
133        registry().by_name.get(name).cloned()
134    }
135
136    fn by_tag(&self, tag: Tag) -> Option<&Self::Entry> {
137        StandardDataDictionary::indexed_tag(tag)
138    }
139}
140
141impl<'a> DataDictionary for &'a StandardDataDictionary {
142    type Entry = DataDictionaryEntryRef<'static>;
143
144    fn by_name(&self, name: &str) -> Option<&'static DataDictionaryEntryRef<'static>> {
145        registry().by_name.get(name).cloned()
146    }
147
148    fn by_tag(&self, tag: Tag) -> Option<&'static DataDictionaryEntryRef<'static>> {
149        StandardDataDictionary::indexed_tag(tag)
150    }
151}
152
153impl Display for StandardDataDictionary {
154    fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
155        f.write_str("Standard DICOM Data Dictionary")
156    }
157}
158
159fn init_dictionary() -> StandardDataDictionaryRegistry {
160    let mut d = StandardDataDictionaryRegistry::new();
161    for entry in ENTRIES {
162        d.index(entry);
163    }
164    // generic group length is not a generated entry,
165    // inserting it manually
166    d.by_name.insert("GenericGroupLength", &GROUP_LENGTH_ENTRY);
167    d
168}
169
170#[cfg(test)]
171mod tests {
172    use crate::tags;
173
174    use super::StandardDataDictionary;
175    use dicom_core::dictionary::{DataDictionary, DataDictionaryEntryRef, TagRange::*, VirtualVr};
176    use dicom_core::header::{Tag, VR};
177    use dicom_core::ops::AttributeSelector;
178
179    // tests for just a few attributes to make sure that the entries
180    // were well installed into the crate
181    #[test]
182    fn smoke_test() {
183        let dict = StandardDataDictionary::default();
184
185        assert_eq!(
186            dict.by_name("PatientName"),
187            Some(&DataDictionaryEntryRef {
188                tag: Single(Tag(0x0010, 0x0010)),
189                alias: "PatientName",
190                vr: VR::PN.into(),
191            })
192        );
193
194        assert_eq!(
195            dict.by_name("Modality"),
196            Some(&DataDictionaryEntryRef {
197                tag: Single(Tag(0x0008, 0x0060)),
198                alias: "Modality",
199                vr: VR::CS.into(),
200            })
201        );
202
203        let pixel_data = dict
204            .by_tag(Tag(0x7FE0, 0x0010))
205            .expect("Pixel Data attribute should exist");
206        eprintln!("{:X?}", pixel_data.tag);
207        assert_eq!(pixel_data.tag, Single(Tag(0x7FE0, 0x0010)));
208        assert_eq!(pixel_data.alias, "PixelData");
209        assert!(pixel_data.vr == VirtualVr::Px);
210
211        let overlay_data = dict
212            .by_tag(Tag(0x6000, 0x3000))
213            .expect("Overlay Data attribute should exist");
214        assert_eq!(overlay_data.tag, Group100(Tag(0x6000, 0x3000)));
215        assert_eq!(overlay_data.alias, "OverlayData");
216        assert!(overlay_data.vr == VirtualVr::Ox);
217
218        // repeated overlay data
219        let overlay_data = dict
220            .by_tag(Tag(0x60EE, 0x3000))
221            .expect("Repeated Overlay Data attribute should exist");
222        assert_eq!(overlay_data.tag, Group100(Tag(0x6000, 0x3000)));
223        assert_eq!(overlay_data.alias, "OverlayData");
224        assert!(overlay_data.vr == VirtualVr::Ox);
225    }
226
227    #[test]
228    fn can_parse_tags() {
229        let dict = StandardDataDictionary;
230
231        assert_eq!(dict.parse_tag("(7FE0,0010)"), Some(crate::tags::PIXEL_DATA));
232        assert_eq!(dict.parse_tag("0010,21C0"), Some(Tag(0x0010, 0x21C0)));
233        assert_eq!(
234            dict.parse_tag("OperatorsName"),
235            Some(crate::tags::OPERATORS_NAME)
236        );
237
238        // can't parse these
239        assert_eq!(dict.parse_tag(""), None);
240        assert_eq!(dict.parse_tag("1111,2222,3333"), None);
241        assert_eq!(dict.parse_tag("OperatorNickname"), None);
242    }
243
244    #[test]
245    fn can_query_by_expression() {
246        let dict = StandardDataDictionary;
247
248        assert_eq!(
249            dict.by_expr("(0010,0010)"),
250            Some(&DataDictionaryEntryRef {
251                tag: Single(crate::tags::PATIENT_NAME),
252                alias: "PatientName",
253                vr: VR::PN.into(),
254            })
255        );
256
257        assert_eq!(
258            dict.by_expr("0008,0060"),
259            Some(&DataDictionaryEntryRef {
260                tag: Single(crate::tags::MODALITY),
261                alias: "Modality",
262                vr: VR::CS.into(),
263            })
264        );
265
266        assert_eq!(
267            dict.by_expr("OperatorsName"),
268            Some(&DataDictionaryEntryRef {
269                tag: Single(crate::tags::OPERATORS_NAME),
270                alias: "OperatorsName",
271                vr: VR::PN.into(),
272            })
273        );
274
275        // can't handle these
276        assert_eq!(dict.parse_tag("0080 0010"), None);
277        assert_eq!(dict.parse_tag("(0000.0600)"), None);
278        assert_eq!(dict.parse_tag("OPERATORSNAME"), None);
279    }
280
281    #[test]
282    fn has_group_length_tags() {
283        use crate::tags::*;
284        assert_eq!(COMMAND_GROUP_LENGTH, Tag(0x0000, 0x0000));
285        assert_eq!(FILE_META_INFORMATION_GROUP_LENGTH, Tag(0x0002, 0x0000));
286
287        let dict = StandardDataDictionary::default();
288
289        assert_eq!(
290            dict.by_tag(FILE_META_INFORMATION_GROUP_LENGTH),
291            Some(&DataDictionaryEntryRef {
292                tag: Single(FILE_META_INFORMATION_GROUP_LENGTH),
293                alias: "FileMetaInformationGroupLength",
294                vr: VR::UL.into(),
295            }),
296        );
297
298        assert_eq!(
299            dict.by_tag(COMMAND_GROUP_LENGTH),
300            Some(&DataDictionaryEntryRef {
301                tag: Single(COMMAND_GROUP_LENGTH),
302                alias: "CommandGroupLength",
303                vr: VR::UL.into(),
304            }),
305        );
306
307        // generic group length
308
309        assert_eq!(
310            dict.by_tag(Tag(0x7FE0, 0x0000)),
311            Some(&DataDictionaryEntryRef {
312                tag: GroupLength,
313                alias: "GenericGroupLength",
314                vr: VR::UL.into(),
315            }),
316        );
317
318        assert_eq!(
319            dict.by_name("GenericGroupLength"),
320            Some(&DataDictionaryEntryRef {
321                tag: GroupLength,
322                alias: "GenericGroupLength",
323                vr: VR::UL.into(),
324            }),
325        );
326    }
327
328    #[test]
329    fn has_private_creator() {
330        let dict = StandardDataDictionary::default();
331
332        let private_creator = DataDictionaryEntryRef {
333            tag: PrivateCreator,
334            alias: "PrivateCreator",
335            vr: VR::LO.into(),
336        };
337
338        assert_eq!(dict.by_tag(Tag(0x0009, 0x0010)), Some(&private_creator));
339        assert_eq!(dict.by_tag(Tag(0x0009, 0x0011)), Some(&private_creator));
340        assert_eq!(dict.by_tag(Tag(0x000B, 0x0010)), Some(&private_creator));
341        assert_eq!(dict.by_tag(Tag(0x00ED, 0x00FF)), Some(&private_creator));
342    }
343
344    #[test]
345    fn can_parse_selectors() {
346        let dict = StandardDataDictionary;
347        // - `(0002,0010)`:
348        //   _Transfer Syntax UID_
349        let selector: AttributeSelector = dict.parse_selector("(0002,0010)").unwrap();
350        assert_eq!(selector, AttributeSelector::from(tags::TRANSFER_SYNTAX_UID));
351
352        // - `00101010`:
353        //   _Patient Age_
354        let selector: AttributeSelector = dict.parse_selector("00101010").unwrap();
355        assert_eq!(selector, AttributeSelector::from(tags::PATIENT_AGE));
356
357        // - `0040A168[0].CodeValue`:
358        //   _Code Value_ in first item of _Concept Code Sequence_
359        let selector: AttributeSelector = dict.parse_selector("0040A168[0].CodeValue").unwrap();
360        assert_eq!(
361            selector,
362            AttributeSelector::from((tags::CONCEPT_CODE_SEQUENCE, 0, tags::CODE_VALUE)),
363        );
364        // - `0040,A730[1].ContentSequence`:
365        //   _Content Sequence_ in second item of _Content Sequence_
366        let selector: AttributeSelector =
367            dict.parse_selector("0040,A730[1].ContentSequence").unwrap();
368        assert_eq!(
369            selector,
370            AttributeSelector::from((tags::CONTENT_SEQUENCE, 1, tags::CONTENT_SEQUENCE,)),
371        );
372
373        // - `SequenceOfUltrasoundRegions.RegionSpatialFormat`:
374        //   _Region Spatial Format_ in first item of _Sequence of Ultrasound Regions_
375        let selector: AttributeSelector = dict
376            .parse_selector("SequenceOfUltrasoundRegions.RegionSpatialFormat")
377            .unwrap();
378        assert_eq!(
379            selector,
380            AttributeSelector::from((
381                tags::SEQUENCE_OF_ULTRASOUND_REGIONS,
382                0,
383                tags::REGION_SPATIAL_FORMAT
384            )),
385        );
386    }
387
388    /// Can go to is text form and back without losing info
389    #[test]
390    fn print_and_parse_selectors() {
391        let selectors = [
392            AttributeSelector::from((tags::CONTENT_SEQUENCE, 1, tags::CONTENT_SEQUENCE)),
393            AttributeSelector::new([
394                (tags::CONTENT_SEQUENCE, 1).into(),
395                (tags::CONTENT_SEQUENCE, 3).into(),
396                (tags::CONTENT_SEQUENCE, 5).into(),
397                (tags::CONCEPT_NAME_CODE_SEQUENCE, 0).into(),
398                tags::CODE_VALUE.into(),
399            ])
400            .unwrap(),
401        ];
402
403        for selector in selectors {
404            let selector2: AttributeSelector = StandardDataDictionary
405                .parse_selector(&selector.to_string())
406                .unwrap();
407            assert_eq!(selector, selector2);
408        }
409    }
410}