dicom_transfer_syntax_registry/
lib.rs1#![deny(trivial_numeric_casts, unsafe_code, unstable_features)]
2#![warn(
3 missing_debug_implementations,
4 missing_docs,
5 unused_qualifications,
6 unused_import_braces
7)]
8use dicom_encoding::transfer_syntax::{
91 AdapterFreeTransferSyntax as Ts, Codec, TransferSyntaxIndex,
92};
93use lazy_static::lazy_static;
94use std::collections::hash_map::Entry;
95use std::collections::HashMap;
96use std::fmt;
97
98pub use dicom_encoding::TransferSyntax;
99pub mod entries;
100
101mod adapters;
102
103#[cfg(feature = "inventory-registry")]
104pub use dicom_encoding::inventory;
105
106pub struct TransferSyntaxRegistryImpl {
110 m: HashMap<&'static str, TransferSyntax>,
111}
112
113impl fmt::Debug for TransferSyntaxRegistryImpl {
114 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
115 let entries: HashMap<&str, &str> =
116 self.m.iter().map(|(uid, ts)| (*uid, ts.name())).collect();
117 f.debug_struct("TransferSyntaxRegistryImpl")
118 .field("m", &entries)
119 .finish()
120 }
121}
122
123impl TransferSyntaxRegistryImpl {
124 pub fn iter(&self) -> impl Iterator<Item = &TransferSyntax> {
126 self.m.values()
127 }
128
129 fn get<U: AsRef<str>>(&self, uid: U) -> Option<&TransferSyntax> {
131 let ts_uid = uid
132 .as_ref()
133 .trim_end_matches(|c: char| c.is_whitespace() || c == '\0');
134 self.m.get(ts_uid)
135 }
136
137 fn register(&mut self, ts: TransferSyntax) -> bool {
143 match self.m.entry(ts.uid()) {
144 Entry::Occupied(mut e) => {
145 let replace = match (&e.get().codec(), ts.codec()) {
146 (Codec::Dataset(None), Codec::Dataset(Some(_)))
147 | (
148 Codec::EncapsulatedPixelData(None, None),
149 Codec::EncapsulatedPixelData(..),
150 )
151 | (
152 Codec::EncapsulatedPixelData(Some(_), None),
153 Codec::EncapsulatedPixelData(Some(_), Some(_)),
154 )
155 | (
156 Codec::EncapsulatedPixelData(None, Some(_)),
157 Codec::EncapsulatedPixelData(Some(_), Some(_)),
158 ) => true,
159 (Codec::Dataset(None), Codec::EncapsulatedPixelData(_, _)) => {
163 tracing::warn!("Inconsistent requirements for transfer syntax {}: `Dataset` cannot be replaced by `EncapsulatedPixelData`", ts.uid());
164 false
165 }
166 (Codec::EncapsulatedPixelData(_, _), Codec::Dataset(None)) => {
169 tracing::warn!("Inconsistent requirements for transfer syntax {}: `EncapsulatedPixelData` cannot be replaced by `Dataset`", ts.uid());
170 false
171 }
172 _ => false,
174 };
175
176 if replace {
177 e.insert(ts);
178 true
179 } else {
180 false
181 }
182 }
183 Entry::Vacant(e) => {
184 e.insert(ts);
185 true
186 }
187 }
188 }
189}
190
191impl TransferSyntaxIndex for TransferSyntaxRegistryImpl {
192 #[inline]
193 fn get(&self, uid: &str) -> Option<&TransferSyntax> {
194 Self::get(self, uid)
195 }
196}
197
198impl TransferSyntaxRegistry {
199 #[inline]
201 pub fn iter(&self) -> impl Iterator<Item = &TransferSyntax> {
202 get_registry().iter()
203 }
204}
205
206#[derive(Debug, Default, Copy, Clone, Eq, PartialEq, Hash)]
208pub struct TransferSyntaxRegistry;
209
210impl TransferSyntaxIndex for TransferSyntaxRegistry {
211 #[inline]
212 fn get(&self, uid: &str) -> Option<&TransferSyntax> {
213 get_registry().get(uid)
214 }
215}
216
217lazy_static! {
218
219 static ref REGISTRY: TransferSyntaxRegistryImpl = {
220 let mut registry = TransferSyntaxRegistryImpl {
221 m: HashMap::with_capacity(32),
222 };
223
224 use self::entries::*;
225 let built_in_ts: [TransferSyntax; 37] = [
226 IMPLICIT_VR_LITTLE_ENDIAN.erased(),
227 EXPLICIT_VR_LITTLE_ENDIAN.erased(),
228 EXPLICIT_VR_BIG_ENDIAN.erased(),
229
230 ENCAPSULATED_UNCOMPRESSED_EXPLICIT_VR_LITTLE_ENDIAN.erased(),
231
232 DEFLATED_EXPLICIT_VR_LITTLE_ENDIAN.erased(),
233 JPIP_REFERENCED_DEFLATE.erased(),
234 JPEG_BASELINE.erased(),
235 JPEG_EXTENDED.erased(),
236 JPEG_LOSSLESS_NON_HIERARCHICAL.erased(),
237 JPEG_LOSSLESS_NON_HIERARCHICAL_FIRST_ORDER_PREDICTION.erased(),
238 JPEG_LS_LOSSLESS_IMAGE_COMPRESSION.erased(),
239 JPEG_LS_LOSSY_IMAGE_COMPRESSION.erased(),
240 JPEG_2000_IMAGE_COMPRESSION_LOSSLESS_ONLY.erased(),
241 JPEG_2000_IMAGE_COMPRESSION.erased(),
242 JPEG_2000_PART2_MULTI_COMPONENT_IMAGE_COMPRESSION_LOSSLESS_ONLY.erased(),
243 JPEG_2000_PART2_MULTI_COMPONENT_IMAGE_COMPRESSION.erased(),
244 JPIP_REFERENCED.erased(),
245 MPEG2_MAIN_PROFILE_MAIN_LEVEL.erased(),
246 FRAGMENTABLE_MPEG2_MAIN_PROFILE_MAIN_LEVEL.erased(),
247 MPEG2_MAIN_PROFILE_HIGH_LEVEL.erased(),
248 FRAGMENTABLE_MPEG2_MAIN_PROFILE_HIGH_LEVEL.erased(),
249 MPEG4_AVC_H264_HIGH_PROFILE.erased(),
250 FRAGMENTABLE_MPEG4_AVC_H264_HIGH_PROFILE.erased(),
251 MPEG4_AVC_H264_BD_COMPATIBLE_HIGH_PROFILE.erased(),
252 FRAGMENTABLE_MPEG4_AVC_H264_BD_COMPATIBLE_HIGH_PROFILE.erased(),
253 MPEG4_AVC_H264_HIGH_PROFILE_FOR_2D_VIDEO.erased(),
254 FRAGMENTABLE_MPEG4_AVC_H264_HIGH_PROFILE_FOR_2D_VIDEO.erased(),
255 MPEG4_AVC_H264_HIGH_PROFILE_FOR_3D_VIDEO.erased(),
256 FRAGMENTABLE_MPEG4_AVC_H264_HIGH_PROFILE_FOR_3D_VIDEO.erased(),
257 MPEG4_AVC_H264_STEREO_HIGH_PROFILE.erased(),
258 FRAGMENTABLE_MPEG4_AVC_H264_STEREO_HIGH_PROFILE.erased(),
259 HEVC_H265_MAIN_PROFILE.erased(),
260 HEVC_H265_MAIN_10_PROFILE.erased(),
261 RLE_LOSSLESS.erased(),
262 SMPTE_ST_2110_20_UNCOMPRESSED_PROGRESSIVE.erased(),
263 SMPTE_ST_2110_20_UNCOMPRESSED_INTERLACED.erased(),
264 SMPTE_ST_2110_30_PCM.erased(),
265 ];
266
267 for ts in built_in_ts {
269 registry.register(ts);
270 }
271 inventory_populate(&mut registry);
273
274 registry
275 };
276}
277
278#[cfg(feature = "inventory-registry")]
279#[inline]
280fn inventory_populate(registry: &mut TransferSyntaxRegistryImpl) {
281 use dicom_encoding::transfer_syntax::TransferSyntaxFactory;
282
283 for TransferSyntaxFactory(tsf) in inventory::iter::<TransferSyntaxFactory> {
284 let ts = tsf();
285 registry.register(ts);
286 }
287}
288
289#[cfg(not(feature = "inventory-registry"))]
290#[inline]
291fn inventory_populate(_: &mut TransferSyntaxRegistryImpl) {
292 }
294
295#[inline]
297pub(crate) fn get_registry() -> &'static TransferSyntaxRegistryImpl {
298 ®ISTRY
299}
300
301pub(crate) const fn create_ts_stub(uid: &'static str, name: &'static str) -> Ts {
303 TransferSyntax::new_ele(uid, name, Codec::EncapsulatedPixelData(None, None))
304}
305
306pub fn default() -> Ts {
308 entries::IMPLICIT_VR_LITTLE_ENDIAN
309}
310
311#[cfg(test)]
312mod tests {
313 use dicom_encoding::TransferSyntaxIndex;
314
315 use crate::TransferSyntaxRegistry;
316
317 #[test]
318 fn has_mandatory_tss() {
319 let implicit_vr_le = TransferSyntaxRegistry
320 .get("1.2.840.10008.1.2")
321 .expect("transfer syntax registry should provide Implicit VR Little Endian");
322 assert_eq!(implicit_vr_le.uid(), "1.2.840.10008.1.2");
323 assert!(implicit_vr_le.is_fully_supported());
324
325 let implicit_vr_le_2 = TransferSyntaxRegistry.get("1.2.840.10008.1.2\0").expect(
327 "transfer syntax registry should provide Implicit VR Little Endian with padded TS UID",
328 );
329
330 assert_eq!(implicit_vr_le_2.uid(), implicit_vr_le.uid());
331
332 let explicit_vr_le = TransferSyntaxRegistry
333 .get("1.2.840.10008.1.2.1")
334 .expect("transfer syntax registry should provide Explicit VR Little Endian");
335 assert_eq!(explicit_vr_le.uid(), "1.2.840.10008.1.2.1");
336 assert!(explicit_vr_le.is_fully_supported());
337
338 let explicit_vr_le_2 = TransferSyntaxRegistry.get("1.2.840.10008.1.2.1\0").expect(
340 "transfer syntax registry should provide Explicit VR Little Endian with padded TS UID",
341 );
342
343 assert_eq!(explicit_vr_le_2.uid(), explicit_vr_le.uid());
344 }
345
346 #[test]
347 fn provides_iter() {
348 let all_tss: Vec<_> = TransferSyntaxRegistry.iter().collect();
349
350 assert!(all_tss.len() >= 2);
351
352 assert!(all_tss.iter().any(|ts| ts.uid() == "1.2.840.10008.1.2"));
354 assert!(all_tss.iter().any(|ts| ts.uid() == "1.2.840.10008.1.2.1"));
355 }
356}