dicom_encoding/transfer_syntax/mod.rs
1//! Module containing the DICOM Transfer Syntax data structure and related methods.
2//! Similar to the DcmCodec in DCMTK, the `TransferSyntax` contains all of the necessary
3//! algorithms for decoding and encoding DICOM data in a certain transfer syntax.
4//!
5//! This crate does not host specific transfer syntaxes. Instead, they are created in
6//! other crates and registered in the global transfer syntax registry,
7//! which implements [`TransferSyntaxIndex`].
8//! For more information, please see the [`dicom-transfer-syntax-registry`] crate,
9//! which provides built-in implementations.
10//!
11//! This module allows you to register your own transfer syntaxes.
12//! With the `inventory-registry` Cargo feature,
13//! you can use the macro [`submit_transfer_syntax`](crate::submit_transfer_syntax)
14//! or [`submit_ele_transfer_syntax`](crate::submit_ele_transfer_syntax)
15//! to instruct the compiler to include your implementation in the registry.
16//! Without the `inventory`-based registry
17//! (in case your environment does not support it),
18//! you can still roll your own [transfer syntax index][1].
19//!
20//! [1]: TransferSyntaxIndex
21//! [`dicom-transfer-syntax-registry`]: https://docs.rs/dicom-transfer-syntax-registry
22
23use crate::adapters::{
24 DynPixelDataReader, DynPixelDataWriter, NeverPixelAdapter, PixelDataReader, PixelDataWriter,
25};
26use crate::decode::{
27 basic::BasicDecoder, explicit_be::ExplicitVRBigEndianDecoder,
28 explicit_le::ExplicitVRLittleEndianDecoder, implicit_le::ImplicitVRLittleEndianDecoder,
29 DecodeFrom,
30};
31use crate::encode::{
32 explicit_be::ExplicitVRBigEndianEncoder, explicit_le::ExplicitVRLittleEndianEncoder,
33 implicit_le::ImplicitVRLittleEndianEncoder, EncodeTo, EncoderFor,
34};
35use std::io::{Read, Write};
36
37pub use byteordered::Endianness;
38
39/// A decoder with its type erased.
40pub type DynDecoder<S> = Box<dyn DecodeFrom<S>>;
41
42/// An encoder with its type erased.
43pub type DynEncoder<'w, W> = Box<dyn EncodeTo<W> + 'w>;
44
45/// A DICOM transfer syntax specifier.
46///
47/// Custom encoding and decoding capabilities
48/// are defined via the parameter types `D` and `P`,
49/// The type parameter `D` specifies
50/// an adapter for reading and writing data sets,
51/// whereas `P` specifies the encoder and decoder of encapsulated pixel data.
52///
53/// This type is usually consumed in its "type erased" form,
54/// with its default parameter types.
55/// On the other hand, implementers of `TransferSyntax` will typically specify
56/// concrete types for `D` and `P`,
57/// which are type-erased before registration.
58/// If the transfer syntax requires no data set codec,
59/// `D` can be assigned to the utility type [`NeverAdapter`].
60/// If pixel data encoding/decoding is not needed or not supported,
61/// you can assign `P` to [`NeverPixelAdapter`].
62#[derive(Debug)]
63pub struct TransferSyntax<D = DynDataRWAdapter, R = DynPixelDataReader, W = DynPixelDataWriter> {
64 /// The unique identifier of the transfer syntax.
65 uid: &'static str,
66 /// The name of the transfer syntax.
67 name: &'static str,
68 /// The byte order of data.
69 byte_order: Endianness,
70 /// Whether the transfer syntax mandates an explicit value representation,
71 /// or the VR is implicit.
72 explicit_vr: bool,
73 /// The transfer syntax' requirements and implemented capabilities.
74 codec: Codec<D, R, W>,
75}
76
77/// Wrapper type for a provider of transfer syntax descriptors.
78///
79/// This is a piece of the plugin interface for
80/// registering and collecting transfer syntaxes.
81/// Implementers and consumers of transfer syntaxes
82/// will usually not interact with it directly.
83/// In order to register a new transfer syntax,
84/// see the macro [`submit_transfer_syntax`](crate::submit_transfer_syntax).
85#[derive(Debug, Copy, Clone, PartialEq)]
86pub struct TransferSyntaxFactory(pub fn() -> TransferSyntax);
87
88#[cfg(feature = "inventory-registry")]
89// Collect transfer syntax specifiers from other crates.
90inventory::collect!(TransferSyntaxFactory);
91
92/// Trait for a container/repository of transfer syntax specifiers.
93///
94/// Types implementing this trait are held responsible for populating
95/// themselves with a set of transfer syntaxes, which can be fully supported,
96/// partially supported, or not supported. Usually, only one implementation
97/// of this trait is used for the entire program,
98/// the most common one being the `TransferSyntaxRegistry` type
99/// from [`transfer-syntax-registry`].
100///
101/// [`transfer-syntax-registry`]: https://docs.rs/dicom-transfer-syntax-registry
102pub trait TransferSyntaxIndex {
103 /// Obtain a DICOM transfer syntax by its respective UID.
104 ///
105 /// Implementations of this method should be robust to the possible
106 /// presence of trailing null characters (`\0`) in `uid`.
107 fn get(&self, uid: &str) -> Option<&TransferSyntax>;
108}
109
110impl<T: ?Sized> TransferSyntaxIndex for &T
111where
112 T: TransferSyntaxIndex,
113{
114 fn get(&self, uid: &str) -> Option<&TransferSyntax> {
115 (**self).get(uid)
116 }
117}
118
119#[cfg(feature = "inventory-registry")]
120#[macro_export]
121/// Submit a transfer syntax specifier to be supported by the
122/// program's runtime. This is to be used by crates wishing to provide
123/// additional support for a certain transfer syntax using the
124/// main transfer syntax registry.
125///
126/// This macro does not actually "run" anything, so place it outside of a
127/// function body at the root of the crate.
128/// The expression is evaluated when the transfer syntax registry is populated
129/// upon the first request,
130/// and must resolve to a value of type [`TransferSyntax<D, P>`],
131/// for valid definitions of the parameter types `D` and `P`.
132/// The macro will type-erase these parameters automatically.
133///
134/// # Example
135///
136/// One common use case is wanting to read data sets
137/// of DICOM objects in a private transfer syntax,
138/// even when a decoder for that pixel data is not available.
139/// By writing a simple stub at your project's root,
140/// the rest of the ecosystem will know
141/// how to read and write data sets in that transfer syntax.
142///
143/// ```
144/// use dicom_encoding::{
145/// submit_transfer_syntax, AdapterFreeTransferSyntax, Codec, Endianness,
146/// };
147///
148/// submit_transfer_syntax!(AdapterFreeTransferSyntax::new(
149/// // Transfer Syntax UID
150/// "1.3.46.670589.33.1.4.1",
151/// // Name/alias
152/// "CT Private ELE",
153/// // Data set byte order
154/// Endianness::Little,
155/// // Explicit VR (true) or Implicit VR (false)
156/// true,
157/// Codec::EncapsulatedPixelData(None, None), // pixel data codec
158/// ));
159/// ```
160///
161/// With [`Codec::EncapsulatedPixelData(None, None)`][1],
162/// we are indicating that the transfer syntax uses encapsulated pixel data.
163/// albeit without the means to decode or encode it.
164/// See the [`adapters`](crate::adapters) module
165/// to know how to write pixel data encoders and decoders.
166///
167/// [1]: Codec::EncapsulatedPixelData
168macro_rules! submit_transfer_syntax {
169 ($ts: expr) => {
170 $crate::inventory::submit! {
171 $crate::transfer_syntax::TransferSyntaxFactory(|| ($ts).erased())
172 }
173 };
174}
175
176#[cfg(not(feature = "inventory-registry"))]
177#[macro_export]
178/// Submit a transfer syntax specifier to be supported by the
179/// program's runtime. This is to be used by crates wishing to provide
180/// additional support for a certain transfer syntax using the
181/// main transfer syntax registry.
182///
183/// This macro does actually "run" anything, so place it outside of a
184/// function body at the root of the crate.
185///
186/// Without the `inventory-registry` feature, this request is ignored.
187macro_rules! submit_transfer_syntax {
188 ($ts: expr) => {
189 // ignore request
190 };
191}
192
193#[cfg(feature = "inventory-registry")]
194#[macro_export]
195/// Submit an explicit VR little endian transfer syntax specifier
196/// to be supported by the program's runtime.
197///
198/// This macro is equivalent in behavior as [`submit_transfer_syntax`](crate::submit_transfer_syntax),
199/// but it is easier to use when
200/// writing support for compressed pixel data formats,
201/// which are usually in explicit VR little endian.
202///
203/// This macro does not actually "run" anything, so place it outside of a
204/// function body at the root of the crate.
205/// The expression is evaluated when the transfer syntax registry is populated
206/// upon the first request,
207/// and must resolve to a value of type [`Codec<D, R, W>`],
208/// for valid definitions of the parameter types `D`, `R`, and `W`.
209/// The macro will type-erase these parameters automatically.
210///
211/// # Example
212///
213/// One common use case is wanting to read data sets
214/// of DICOM objects in a private transfer syntax,
215/// even when a decoder for that pixel data is not available.
216/// By writing a simple stub at your project's root,
217/// the rest of the ecosystem will know
218/// how to read and write data sets in that transfer syntax.
219///
220/// ```
221/// use dicom_encoding::{submit_ele_transfer_syntax, Codec};
222///
223/// submit_ele_transfer_syntax!(
224/// // Transfer Syntax UID
225/// "1.3.46.670589.33.1.4.1",
226/// // Name/alias
227/// "CT Private ELE",
228/// // pixel data codec
229/// Codec::EncapsulatedPixelData(None, None)
230/// );
231/// ```
232///
233/// With [`Codec::EncapsulatedPixelData(None, None)`][1],
234/// we are indicating that the transfer syntax uses encapsulated pixel data.
235/// albeit without the means to decode or encode it.
236/// See the [`adapters`](crate::adapters) module
237/// to know how to write pixel data encoders and decoders.
238///
239/// [1]: Codec::EncapsulatedPixelData
240macro_rules! submit_ele_transfer_syntax {
241 ($uid: expr, $name: expr, $codec: expr) => {
242 $crate::submit_transfer_syntax! {
243 $crate::AdapterFreeTransferSyntax::new_ele(
244 $uid,
245 $name,
246 $codec
247 )
248 }
249 };
250}
251
252#[cfg(not(feature = "inventory-registry"))]
253#[macro_export]
254/// Submit an explicit VR little endian transfer syntax specifier
255/// to be supported by the program's runtime.
256///
257/// This macro is equivalent in behavior as [`submit_transfer_syntax`],
258/// but it is easier to use when
259/// writing support for compressed pixel data formats,
260/// which are usually in explicit VR little endian.
261///
262/// This macro does actually "run" anything, so place it outside of a
263/// function body at the root of the crate.
264///
265/// Without the `inventory-registry` feature, this request is ignored.
266macro_rules! submit_ele_transfer_syntax {
267 ($uid: literal, $name: literal, $codec: expr) => {
268 // ignore request
269 };
270}
271
272/// A description and possible implementation regarding
273/// the encoding and decoding requirements of a transfer syntax.
274/// This is also used as a means to describe whether pixel data is encapsulated
275/// and whether this implementation supports decoding and/or encoding it.
276///
277/// ### Type parameters
278///
279/// - `D` should implement [`DataRWAdapter`]
280/// and defines how one should read and write DICOM data sets,
281/// such as in the case for deflated data.
282/// When no special considerations for data set reading and writing
283/// are necessary, this can be set to [`NeverAdapter`].
284/// - `R` should implement [`PixelDataReader`],
285/// and enables programs to convert encapsulated pixel data fragments
286/// into native pixel data.
287/// - `W` should implement [`PixelDataWriter`],
288/// and enables programs to convert native pixel data
289/// into encapsulated pixel data.
290///
291#[derive(Debug, Clone, PartialEq)]
292pub enum Codec<D, R, W> {
293 /// No codec is required for this transfer syntax.
294 ///
295 /// Pixel data, if any, should be in its _native_, unencapsulated format.
296 None,
297 /// Pixel data for this transfer syntax is encapsulated
298 /// and likely subjected to a specific encoding process.
299 /// The first part of the tuple struct contains the pixel data decoder,
300 /// whereas the second item is for the pixel data encoder.
301 ///
302 /// Decoding of the pixel data is not supported
303 /// if the decoder is `None`.
304 /// In this case, the program should still be able to
305 /// parse DICOM data sets
306 /// and fetch the pixel data in its encapsulated form.
307 EncapsulatedPixelData(Option<R>, Option<W>),
308 /// A custom data set codec is required for reading and writing data sets.
309 ///
310 /// If the item in the tuple struct is `None`,
311 /// then no reading and writing whatsoever is supported.
312 /// This could be used by a stub of
313 /// _Deflated Explicit VR Little Endian_, for example.
314 Dataset(Option<D>),
315}
316
317/// An alias for a transfer syntax specifier with no pixel data encapsulation
318/// nor data set deflating.
319pub type AdapterFreeTransferSyntax =
320 TransferSyntax<NeverAdapter, NeverPixelAdapter, NeverPixelAdapter>;
321
322/// An adapter of byte read and write streams.
323pub trait DataRWAdapter<R, W> {
324 /// The type of the adapted reader.
325 type Reader: Read;
326 /// The type of the adapted writer.
327 type Writer: Write;
328
329 /// Adapt a byte reader.
330 fn adapt_reader(&self, reader: R) -> Self::Reader
331 where
332 R: Read;
333
334 /// Adapt a byte writer.
335 fn adapt_writer(&self, writer: W) -> Self::Writer
336 where
337 W: Write;
338}
339
340/// Alias type for a dynamically dispatched data adapter.
341pub type DynDataRWAdapter = Box<
342 dyn DataRWAdapter<
343 Box<dyn Read>,
344 Box<dyn Write>,
345 Reader = Box<dyn Read>,
346 Writer = Box<dyn Write>,
347 > + Send
348 + Sync,
349>;
350
351impl<'a, T, R, W> DataRWAdapter<R, W> for &'a T
352where
353 T: DataRWAdapter<R, W>,
354 R: Read,
355 W: Write,
356{
357 type Reader = <T as DataRWAdapter<R, W>>::Reader;
358 type Writer = <T as DataRWAdapter<R, W>>::Writer;
359
360 /// Adapt a byte reader.
361 fn adapt_reader(&self, reader: R) -> Self::Reader
362 where
363 R: Read,
364 {
365 (**self).adapt_reader(reader)
366 }
367
368 /// Adapt a byte writer.
369 fn adapt_writer(&self, writer: W) -> Self::Writer
370 where
371 W: Write,
372 {
373 (**self).adapt_writer(writer)
374 }
375}
376
377/// An immaterial type representing a data set adapter which is never required,
378/// and as such is never instantiated.
379/// Most transfer syntaxes use this,
380/// as they do not have to adapt readers and writers
381/// for encoding and decoding data sets.
382/// The main exception is in the family of
383/// _Deflated Explicit VR Little Endian_ transfer syntaxes.
384#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
385pub enum NeverAdapter {}
386
387impl<R, W> DataRWAdapter<R, W> for NeverAdapter {
388 type Reader = Box<dyn Read>;
389 type Writer = Box<dyn Write>;
390
391 fn adapt_reader(&self, _reader: R) -> Self::Reader
392 where
393 R: Read,
394 {
395 unreachable!()
396 }
397
398 fn adapt_writer(&self, _writer: W) -> Self::Writer
399 where
400 W: Write,
401 {
402 unreachable!()
403 }
404}
405
406impl<D, R, W> TransferSyntax<D, R, W> {
407 /// Create a new transfer syntax descriptor.
408 ///
409 /// Note that only transfer syntax implementers are expected to
410 /// construct TS descriptors from scratch.
411 /// For a practical usage of transfer syntaxes,
412 /// one should look up an existing transfer syntax registry by UID.
413 ///
414 /// # Example
415 ///
416 /// To register a private transfer syntax in your program,
417 /// use [`submit_transfer_syntax`](crate::submit_transfer_syntax)
418 /// outside of a function body:
419 ///
420 /// ```no_run
421 /// # use dicom_encoding::{
422 /// # submit_transfer_syntax, Codec, Endianness,
423 /// # NeverAdapter, NeverPixelAdapter, TransferSyntax,
424 /// # };
425 /// submit_transfer_syntax! {
426 /// TransferSyntax::<NeverAdapter, NeverPixelAdapter, NeverPixelAdapter>::new(
427 /// "1.3.46.670589.33.1.4.1",
428 /// "CT-Private-ELE",
429 /// Endianness::Little,
430 /// true,
431 /// Codec::EncapsulatedPixelData(None, None),
432 /// )
433 /// }
434 /// ```
435 pub const fn new(
436 uid: &'static str,
437 name: &'static str,
438 byte_order: Endianness,
439 explicit_vr: bool,
440 codec: Codec<D, R, W>,
441 ) -> Self {
442 TransferSyntax {
443 uid,
444 name,
445 byte_order,
446 explicit_vr,
447 codec,
448 }
449 }
450
451 /// Create a new descriptor
452 /// for a transfer syntax in explicit VR little endian.
453 ///
454 /// Note that only transfer syntax implementers are expected to
455 /// construct TS descriptors from scratch.
456 /// For a practical usage of transfer syntaxes,
457 /// one should look up an existing transfer syntax registry by UID.
458 ///
459 /// # Example
460 ///
461 /// To register a private transfer syntax in your program,
462 /// use [`submit_transfer_syntax`](crate::submit_transfer_syntax)
463 /// outside of a function body:
464 ///
465 /// ```no_run
466 /// # use dicom_encoding::{
467 /// # submit_transfer_syntax, Codec,
468 /// # NeverAdapter, NeverPixelAdapter, TransferSyntax,
469 /// # };
470 /// submit_transfer_syntax! {
471 /// TransferSyntax::<NeverAdapter, NeverPixelAdapter, NeverPixelAdapter>::new_ele(
472 /// "1.3.46.670589.33.1.4.1",
473 /// "CT-Private-ELE",
474 /// Codec::EncapsulatedPixelData(None, None),
475 /// )
476 /// }
477 /// ```
478 ///
479 /// See [`submit_ele_transfer_syntax`](crate::submit_ele_transfer_syntax)
480 /// for an alternative.
481 pub const fn new_ele(uid: &'static str, name: &'static str, codec: Codec<D, R, W>) -> Self {
482 TransferSyntax {
483 uid,
484 name,
485 byte_order: Endianness::Little,
486 explicit_vr: true,
487 codec,
488 }
489 }
490
491 /// Obtain this transfer syntax' unique identifier.
492 pub const fn uid(&self) -> &'static str {
493 self.uid
494 }
495
496 /// Obtain the name of this transfer syntax.
497 pub const fn name(&self) -> &'static str {
498 self.name
499 }
500
501 /// Obtain this transfer syntax' expected endianness.
502 pub const fn endianness(&self) -> Endianness {
503 self.byte_order
504 }
505
506 /// Obtain this transfer syntax' codec specification.
507 pub fn codec(&self) -> &Codec<D, R, W> {
508 &self.codec
509 }
510
511 /// Check whether this transfer syntax specifier provides a complete
512 /// implementation,
513 /// meaning that it can both decode and encode in this transfer syntax.
514 pub fn is_fully_supported(&self) -> bool {
515 matches!(
516 self.codec,
517 Codec::None | Codec::Dataset(Some(_)) | Codec::EncapsulatedPixelData(Some(_), Some(_)),
518 )
519 }
520
521 /// Check whether no codecs are required for this transfer syntax,
522 /// meaning that a complete implementation is available
523 /// and no pixel data conversion is required.
524 pub fn is_codec_free(&self) -> bool {
525 matches!(self.codec, Codec::None)
526 }
527
528 /// Check whether neither reading nor writing of data sets is supported.
529 /// If this is `true`, encoding and decoding will not be available.
530 pub fn is_unsupported(&self) -> bool {
531 matches!(self.codec, Codec::Dataset(None))
532 }
533
534 /// Check whether reading and writing the pixel data is unsupported.
535 /// If this is `true`, encoding and decoding of the data set may still
536 /// be possible, but the pixel data will only be available in its
537 /// encapsulated form.
538 pub fn is_unsupported_pixel_encapsulation(&self) -> bool {
539 matches!(
540 self.codec,
541 Codec::Dataset(None) | Codec::EncapsulatedPixelData(None, None)
542 )
543 }
544
545 /// Check whether this codec can fully decode
546 /// both data sets and pixel data.
547 pub fn can_decode_all(&self) -> bool {
548 matches!(
549 self.codec,
550 Codec::None | Codec::Dataset(Some(_)) | Codec::EncapsulatedPixelData(Some(_), _)
551 )
552 }
553
554 /// Check whether this codec can decode the data set.
555 pub fn can_decode_dataset(&self) -> bool {
556 matches!(
557 self.codec,
558 Codec::None | Codec::Dataset(Some(_)) | Codec::EncapsulatedPixelData(..)
559 )
560 }
561
562 /// Retrieve the appropriate data element decoder for this transfer syntax.
563 /// Can yield none if decoding is not supported.
564 ///
565 /// The resulting decoder does not consider pixel data encapsulation or
566 /// data set compression rules. This means that the consumer of this method
567 /// needs to adapt the reader before using the decoder.
568 pub fn decoder<'s>(&self) -> Option<DynDecoder<dyn Read + 's>> {
569 self.decoder_for()
570 }
571
572 /// Retrieve the appropriate data element decoder for this transfer syntax
573 /// and given reader type (this method is not object safe).
574 /// Can yield none if decoding is not supported.
575 ///
576 /// The resulting decoder does not consider pixel data encapsulation or
577 /// data set compression rules. This means that the consumer of this method
578 /// needs to adapt the reader before using the decoder.
579 pub fn decoder_for<S>(&self) -> Option<DynDecoder<S>>
580 where
581 Self: Sized,
582 S: ?Sized + Read,
583 {
584 match (self.byte_order, self.explicit_vr) {
585 (Endianness::Little, false) => Some(Box::<ImplicitVRLittleEndianDecoder<_>>::default()),
586 (Endianness::Little, true) => Some(Box::<ExplicitVRLittleEndianDecoder>::default()),
587 (Endianness::Big, true) => Some(Box::<ExplicitVRBigEndianDecoder>::default()),
588 _ => None,
589 }
590 }
591
592 /// Retrieve the appropriate data element encoder for this transfer syntax.
593 /// Can yield none if encoding is not supported. The resulting encoder does not
594 /// consider pixel data encapsulation or data set compression rules.
595 pub fn encoder<'w>(&self) -> Option<DynEncoder<'w, dyn Write + 'w>> {
596 self.encoder_for()
597 }
598
599 /// Retrieve the appropriate data element encoder for this transfer syntax
600 /// and the given writer type (this method is not object safe).
601 /// Can yield none if encoding is not supported. The resulting encoder does not
602 /// consider pixel data encapsulation or data set compression rules.
603 pub fn encoder_for<'w, T>(&self) -> Option<DynEncoder<'w, T>>
604 where
605 Self: Sized,
606 T: ?Sized + Write + 'w,
607 {
608 match (self.byte_order, self.explicit_vr) {
609 (Endianness::Little, false) => Some(Box::new(EncoderFor::new(
610 ImplicitVRLittleEndianEncoder::default(),
611 ))),
612 (Endianness::Little, true) => Some(Box::new(EncoderFor::new(
613 ExplicitVRLittleEndianEncoder::default(),
614 ))),
615 (Endianness::Big, true) => Some(Box::new(EncoderFor::new(
616 ExplicitVRBigEndianEncoder::default(),
617 ))),
618 _ => None,
619 }
620 }
621
622 /// Obtain a dynamic basic decoder, based on this transfer syntax' expected endianness.
623 pub fn basic_decoder(&self) -> BasicDecoder {
624 BasicDecoder::from(self.endianness())
625 }
626
627 /// Type-erase the pixel data or data set codec.
628 pub fn erased(self) -> TransferSyntax
629 where
630 D: Send + Sync + 'static,
631 D: DataRWAdapter<
632 Box<dyn Read>,
633 Box<dyn Write>,
634 Reader = Box<dyn Read>,
635 Writer = Box<dyn Write>,
636 >,
637 R: Send + Sync + 'static,
638 R: PixelDataReader,
639 W: Send + Sync + 'static,
640 W: PixelDataWriter,
641 {
642 let codec = match self.codec {
643 Codec::Dataset(d) => Codec::Dataset(d.map(|d| Box::new(d) as _)),
644 Codec::EncapsulatedPixelData(r, w) => Codec::EncapsulatedPixelData(
645 r.map(|r| Box::new(r) as _),
646 w.map(|w| Box::new(w) as _),
647 ),
648 Codec::None => Codec::None,
649 };
650
651 TransferSyntax {
652 uid: self.uid,
653 name: self.name,
654 byte_order: self.byte_order,
655 explicit_vr: self.explicit_vr,
656 codec,
657 }
658 }
659}