26constexpr uint32_t kUndefinedLength = 0xFFFFFFFF;
29constexpr uint16_t kItemTagGroup = 0xFFFE;
30constexpr uint16_t kItemTagElement = 0xE000;
33constexpr uint16_t kItemDelimitationElement = 0xE00D;
36constexpr uint16_t kSequenceDelimitationElement = 0xE0DD;
41[[nodiscard]]
auto read_uint16_le(std::span<const uint8_t> data) -> uint16_t {
42 return static_cast<uint16_t
>(data[0]) |
43 (
static_cast<uint16_t
>(data[1]) << 8);
49[[nodiscard]]
auto read_uint32_le(std::span<const uint8_t> data) -> uint32_t {
50 return static_cast<uint32_t
>(data[0]) |
51 (
static_cast<uint32_t
>(data[1]) << 8) |
52 (
static_cast<uint32_t
>(data[2]) << 16) |
53 (
static_cast<uint32_t
>(data[3]) << 24);
59[[nodiscard]]
auto read_uint16_be(std::span<const uint8_t> data) -> uint16_t {
60 return static_cast<uint16_t
>(data[1]) |
61 (
static_cast<uint16_t
>(data[0]) << 8);
67[[nodiscard]]
auto read_uint32_be(std::span<const uint8_t> data) -> uint32_t {
68 return static_cast<uint32_t
>(data[3]) |
69 (
static_cast<uint32_t
>(data[2]) << 8) |
70 (
static_cast<uint32_t
>(data[1]) << 16) |
71 (
static_cast<uint32_t
>(data[0]) << 24);
77void write_uint16_le(std::vector<uint8_t>& buffer, uint16_t value) {
78 buffer.push_back(
static_cast<uint8_t
>(value & 0xFF));
79 buffer.push_back(
static_cast<uint8_t
>((value >> 8) & 0xFF));
85void write_uint32_le(std::vector<uint8_t>& buffer, uint32_t value) {
86 buffer.push_back(
static_cast<uint8_t
>(value & 0xFF));
87 buffer.push_back(
static_cast<uint8_t
>((value >> 8) & 0xFF));
88 buffer.push_back(
static_cast<uint8_t
>((value >> 16) & 0xFF));
89 buffer.push_back(
static_cast<uint8_t
>((value >> 24) & 0xFF));
95void write_uint16_be(std::vector<uint8_t>& buffer, uint16_t value) {
96 buffer.push_back(
static_cast<uint8_t
>((value >> 8) & 0xFF));
97 buffer.push_back(
static_cast<uint8_t
>(value & 0xFF));
103void write_uint32_be(std::vector<uint8_t>& buffer, uint32_t value) {
104 buffer.push_back(
static_cast<uint8_t
>((value >> 24) & 0xFF));
105 buffer.push_back(
static_cast<uint8_t
>((value >> 16) & 0xFF));
106 buffer.push_back(
static_cast<uint8_t
>((value >> 8) & 0xFF));
107 buffer.push_back(
static_cast<uint8_t
>(value & 0xFF));
113[[nodiscard]]
auto read_file_contents(
const std::filesystem::path& path)
115 std::ifstream file(path, std::ios::binary | std::ios::ate);
117 return kcenon::pacs::pacs_error<std::vector<uint8_t>>(
119 "File not found: " + path.string());
122 const auto size = file.tellg();
123 file.seekg(0, std::ios::beg);
125 std::vector<uint8_t> buffer(
static_cast<size_t>(size));
126 if (!file.read(
reinterpret_cast<char*
>(buffer.data()),
127 static_cast<std::streamsize
>(size))) {
128 return kcenon::pacs::pacs_error<std::vector<uint8_t>>(
130 "Failed to read file: " + path.string());
143 : meta_info_(std::move(meta_info)), dataset_(std::move(main_dataset)) {}
153 if (mmap_result.is_ok()) {
154 return from_bytes(mmap_result.value().as_span());
158 auto contents = read_file_contents(path);
159 if (contents.is_err()) {
163 return from_bytes(contents.value());
169 if (data.size() < kPreambleSize + 4) {
172 "File too small to be valid DICOM Part 10 file");
176 const auto prefix = data.subspan(kPreambleSize, 4);
177 if (std::memcmp(prefix.data(), kDicmPrefix, 4) != 0) {
180 "Missing DICM prefix at offset 128");
184 const auto meta_start = data.subspan(kPreambleSize + 4);
185 size_t meta_bytes_read = 0;
187 auto meta_result = parse_meta_information(meta_start, meta_bytes_read);
188 if (meta_result.is_err()) {
194 if (ts_elem ==
nullptr) {
197 "Transfer Syntax UID not found in meta information");
200 auto ts_uid_result = ts_elem->as_string();
201 if (ts_uid_result.is_err()) {
204 "Failed to read Transfer Syntax UID");
206 const auto ts_uid = ts_uid_result.value();
209 if (!
ts.is_valid()) {
212 "Unsupported Transfer Syntax: " + ts_uid);
216 const auto dataset_start = meta_start.subspan(meta_bytes_read);
217 size_t dataset_bytes_read = 0;
219 auto dataset_result = decode_dataset(dataset_start,
ts, dataset_bytes_read);
220 if (dataset_result.is_err()) {
225 dicom_file{std::move(meta_result.value()), std::move(dataset_result.value())});
235 auto meta_info = generate_meta_information(dataset,
ts);
236 return dicom_file{std::move(meta_info), std::move(dataset)};
244 -> kcenon::pacs::VoidResult {
245 auto bytes = to_bytes();
247 std::ofstream file(path, std::ios::binary);
251 "Failed to open file for writing: " + path.string());
254 if (!file.write(
reinterpret_cast<const char*
>(bytes.data()),
255 static_cast<std::streamsize
>(bytes.size()))) {
258 "Failed to write to file: " + path.string());
261 return kcenon::pacs::ok();
265 std::vector<uint8_t> result;
278 result.insert(result.end(), meta_bytes.begin(), meta_bytes.end());
283 result.insert(result.end(), dataset_bytes.begin(), dataset_bytes.end());
339 while (offset + 8 <= data.size()) {
341 const uint16_t group = read_uint16_le(data.subspan(offset, 2));
342 const uint16_t element = read_uint16_le(data.subspan(offset + 2, 2));
346 if (group != 0x0002) {
351 if (offset + 6 > data.size()) {
354 "Unexpected end of data while reading VR");
357 const char vr_chars[3] = {
358 static_cast<char>(data[offset + 4]),
359 static_cast<char>(data[offset + 5]),
366 "Invalid VR: " + std::string(vr_chars, 2));
368 const auto vr = *vr_opt;
372 size_t header_size = 0;
376 if (offset + 12 > data.size()) {
379 "Unexpected end of data while reading length field");
381 length = read_uint32_le(data.subspan(offset + 8, 4));
385 if (offset + 8 > data.size()) {
388 "Unexpected end of data while reading length field");
390 length = read_uint16_le(data.subspan(offset + 6, 2));
395 if (offset + header_size + length > data.size()) {
398 "Value length exceeds available data");
401 const auto value_data = data.subspan(offset + header_size, length);
403 meta_info.
insert(std::move(elem));
405 offset += header_size + length;
418 const uint8_t version_bytes[] = {0x00, 0x01};
422 std::span<const uint8_t>(version_bytes, 2)
445 std::string(
ts.uid())
452 std::string(kImplementationClassUid)
459 std::string(kImplementationVersionName)
466 -> std::vector<uint8_t> {
467 std::vector<uint8_t> result;
468 result.reserve(65536);
470 for (
const auto& [tag, element] : dataset) {
472 write_uint16_le(result, tag.group());
473 write_uint16_le(result, tag.element());
477 result.push_back(
static_cast<uint8_t
>(vr_str[0]));
478 result.push_back(
static_cast<uint8_t
>(vr_str[1]));
480 const auto& raw_data = element.raw_data();
481 const auto length =
static_cast<uint32_t
>(raw_data.size());
485 result.push_back(0x00);
486 result.push_back(0x00);
487 write_uint32_le(result, length);
490 write_uint16_le(result,
static_cast<uint16_t
>(length));
494 result.insert(result.end(), raw_data.begin(), raw_data.end());
506 while (offset + 8 <= data.size()) {
508 const uint16_t group = read_uint16_le(data.subspan(offset, 2));
509 const uint16_t element = read_uint16_le(data.subspan(offset + 2, 2));
513 if (group == 0xFFFE) {
518 if (offset + 6 > data.size()) {
522 const char vr_chars[3] = {
523 static_cast<char>(data[offset + 4]),
524 static_cast<char>(data[offset + 5]),
533 size_t header_size = 0;
536 if (offset + 12 > data.size()) {
539 length = read_uint32_le(data.subspan(offset + 8, 4));
542 if (offset + 8 > data.size()) {
545 length = read_uint16_le(data.subspan(offset + 6, 2));
550 if (length == kUndefinedLength) {
553 size_t seq_bytes_read = 0;
554 auto seq_result = parse_undefined_length_sequence(
555 data.subspan(offset + header_size),
556 seq_bytes_read,
true,
false);
558 if (seq_result.is_ok()) {
561 for (
auto& item : seq_result.value()) {
562 seq_elem.add_sequence_item(std::move(item));
564 dataset.
insert(std::move(seq_elem));
566 offset += header_size + seq_bytes_read;
571 size_t pixel_start = offset + header_size;
572 size_t scan_offset = pixel_start;
575 while (scan_offset + 8 <= data.size()) {
576 uint16_t g = read_uint16_le(data.subspan(scan_offset, 2));
577 uint16_t e = read_uint16_le(data.subspan(scan_offset + 2, 2));
579 if (g == kItemTagGroup && e == kSequenceDelimitationElement) {
585 if (g == kItemTagGroup && e == kItemTagElement) {
587 if (scan_offset + 4 > data.size())
break;
588 uint32_t frag_len = read_uint32_le(data.subspan(scan_offset, 4));
589 scan_offset += 4 + frag_len;
596 size_t encap_length = scan_offset - pixel_start;
597 auto pixel_span = data.subspan(pixel_start, encap_length);
599 dataset.
insert(std::move(pixel_elem));
601 offset = scan_offset;
611 if (offset + header_size + length > data.size()) {
616 const auto value_data = data.subspan(offset + header_size, length);
618 dataset.
insert(std::move(elem));
620 offset += header_size + length;
634 while (offset + 8 <= data.size()) {
636 const uint16_t group = read_uint16_le(data.subspan(offset, 2));
637 const uint16_t element = read_uint16_le(data.subspan(offset + 2, 2));
641 if (group == kItemTagGroup) {
642 if (element == kSequenceDelimitationElement ||
643 element == kItemDelimitationElement) {
649 if (offset + 8 > data.size()) {
652 uint32_t length = read_uint32_le(data.subspan(offset + 4, 4));
669 auto block_num = tag.private_block_number();
671 uint8_t elem_offset =
static_cast<uint8_t
>(
672 tag.element() & 0x00FF);
674 *creator, elem_offset);
683 if (tag.is_private_creator()) {
687 constexpr size_t kImplicitHeaderSize = 8;
690 if (length == kUndefinedLength) {
693 size_t seq_bytes_read = 0;
694 auto seq_result = parse_undefined_length_sequence(
695 data.subspan(offset + kImplicitHeaderSize),
696 seq_bytes_read,
false,
false);
698 if (seq_result.is_ok()) {
701 for (
auto& item : seq_result.value()) {
702 seq_elem.add_sequence_item(std::move(item));
704 dataset.
insert(std::move(seq_elem));
706 offset += kImplicitHeaderSize + seq_bytes_read;
711 size_t pixel_start = offset + kImplicitHeaderSize;
712 size_t scan_offset = pixel_start;
715 while (scan_offset + 8 <= data.size()) {
716 uint16_t g = read_uint16_le(data.subspan(scan_offset, 2));
717 uint16_t e = read_uint16_le(data.subspan(scan_offset + 2, 2));
719 if (g == kItemTagGroup && e == kSequenceDelimitationElement) {
724 if (g == kItemTagGroup && e == kItemTagElement) {
726 if (scan_offset + 4 > data.size())
break;
727 uint32_t frag_len = read_uint32_le(data.subspan(scan_offset, 4));
728 scan_offset += 4 + frag_len;
734 size_t encap_length = scan_offset - pixel_start;
735 auto pixel_span = data.subspan(pixel_start, encap_length);
737 dataset.
insert(std::move(pixel_elem));
739 offset = scan_offset;
749 if (offset + kImplicitHeaderSize + length > data.size()) {
754 const auto value_data = data.subspan(offset + kImplicitHeaderSize, length);
756 dataset.
insert(std::move(elem));
758 offset += kImplicitHeaderSize + length;
771 while (offset + 8 <= data.size()) {
773 const uint16_t group = read_uint16_be(data.subspan(offset, 2));
774 const uint16_t element = read_uint16_be(data.subspan(offset + 2, 2));
778 if (group == kItemTagGroup) {
783 if (offset + 6 > data.size()) {
787 const char vr_chars[3] = {
788 static_cast<char>(data[offset + 4]),
789 static_cast<char>(data[offset + 5]),
798 size_t header_size = 0;
801 if (offset + 12 > data.size()) {
805 length = read_uint32_be(data.subspan(offset + 8, 4));
808 if (offset + 8 > data.size()) {
811 length = read_uint16_be(data.subspan(offset + 6, 2));
816 if (length == kUndefinedLength) {
819 size_t seq_bytes_read = 0;
820 auto seq_result = parse_undefined_length_sequence(
821 data.subspan(offset + header_size),
822 seq_bytes_read,
true,
true);
824 if (seq_result.is_ok()) {
827 for (
auto& item : seq_result.value()) {
828 seq_elem.add_sequence_item(std::move(item));
830 dataset.
insert(std::move(seq_elem));
832 offset += header_size + seq_bytes_read;
837 size_t pixel_start = offset + header_size;
838 size_t scan_offset = pixel_start;
840 while (scan_offset + 8 <= data.size()) {
841 uint16_t g = read_uint16_be(data.subspan(scan_offset, 2));
842 uint16_t e = read_uint16_be(data.subspan(scan_offset + 2, 2));
844 if (g == kItemTagGroup && e == kSequenceDelimitationElement) {
849 if (g == kItemTagGroup && e == kItemTagElement) {
851 if (scan_offset + 4 > data.size())
break;
852 uint32_t frag_len = read_uint32_be(data.subspan(scan_offset, 4));
853 scan_offset += 4 + frag_len;
859 size_t encap_length = scan_offset - pixel_start;
860 auto pixel_span = data.subspan(pixel_start, encap_length);
862 dataset.
insert(std::move(pixel_elem));
864 offset = scan_offset;
873 if (offset + header_size + length > data.size()) {
878 const auto value_data = data.subspan(offset + header_size, length);
879 std::vector<uint8_t> swapped_data(value_data.begin(), value_data.end());
884 if (element_size == 2) {
885 for (
size_t i = 0; i + 1 < swapped_data.size(); i += 2) {
886 std::swap(swapped_data[i], swapped_data[i + 1]);
888 }
else if (element_size == 4) {
889 for (
size_t i = 0; i + 3 < swapped_data.size(); i += 4) {
890 std::swap(swapped_data[i], swapped_data[i + 3]);
891 std::swap(swapped_data[i + 1], swapped_data[i + 2]);
893 }
else if (element_size == 8) {
894 for (
size_t i = 0; i + 7 < swapped_data.size(); i += 8) {
895 std::swap(swapped_data[i], swapped_data[i + 7]);
896 std::swap(swapped_data[i + 1], swapped_data[i + 6]);
897 std::swap(swapped_data[i + 2], swapped_data[i + 5]);
898 std::swap(swapped_data[i + 3], swapped_data[i + 4]);
904 dataset.
insert(std::move(elem));
906 offset += header_size + length;
914 -> std::vector<uint8_t> {
915 std::vector<uint8_t> result;
916 result.reserve(65536);
918 for (
const auto& [tag, element] : dataset) {
920 write_uint16_le(result, tag.group());
921 write_uint16_le(result, tag.element());
924 const auto& raw_data = element.raw_data();
925 write_uint32_le(result,
static_cast<uint32_t
>(raw_data.size()));
928 result.insert(result.end(), raw_data.begin(), raw_data.end());
935 -> std::vector<uint8_t> {
936 std::vector<uint8_t> result;
937 result.reserve(65536);
939 for (
const auto& [tag, element] : dataset) {
941 write_uint16_be(result, tag.group());
942 write_uint16_be(result, tag.element());
946 result.push_back(
static_cast<uint8_t
>(vr_str[0]));
947 result.push_back(
static_cast<uint8_t
>(vr_str[1]));
949 const auto& raw_data = element.raw_data();
950 const auto length =
static_cast<uint32_t
>(raw_data.size());
954 result.push_back(0x00);
955 result.push_back(0x00);
956 write_uint32_be(result, length);
959 write_uint16_be(result,
static_cast<uint16_t
>(length));
965 std::vector<uint8_t> swapped_data(raw_data.begin(), raw_data.end());
967 if (element_size == 2) {
968 for (
size_t i = 0; i + 1 < swapped_data.size(); i += 2) {
969 std::swap(swapped_data[i], swapped_data[i + 1]);
971 }
else if (element_size == 4) {
972 for (
size_t i = 0; i + 3 < swapped_data.size(); i += 4) {
973 std::swap(swapped_data[i], swapped_data[i + 3]);
974 std::swap(swapped_data[i + 1], swapped_data[i + 2]);
976 }
else if (element_size == 8) {
977 for (
size_t i = 0; i + 7 < swapped_data.size(); i += 8) {
978 std::swap(swapped_data[i], swapped_data[i + 7]);
979 std::swap(swapped_data[i + 1], swapped_data[i + 6]);
980 std::swap(swapped_data[i + 2], swapped_data[i + 5]);
981 std::swap(swapped_data[i + 3], swapped_data[i + 4]);
984 result.insert(result.end(), swapped_data.begin(), swapped_data.end());
986 result.insert(result.end(), raw_data.begin(), raw_data.end());
1001 return decode_implicit_vr_le(data, bytes_read);
1006 return decode_explicit_vr_be(data, bytes_read);
1012 return decode_explicit_vr_le(data, bytes_read);
1017 -> std::vector<uint8_t> {
1021 return encode_implicit_vr_le(dataset);
1025 return encode_explicit_vr_be(dataset);
1029 return encode_explicit_vr_le(dataset);
1033 std::span<const uint8_t> data,
size_t& bytes_read,
1034 bool explicit_vr,
bool big_endian)
1037 std::vector<dicom_dataset> items;
1041 auto read_u16 = big_endian ? read_uint16_be : read_uint16_le;
1042 auto read_u32 = big_endian ? read_uint32_be : read_uint32_le;
1044 while (offset + 8 <= data.size()) {
1046 const uint16_t group = read_u16(data.subspan(offset, 2));
1047 const uint16_t element = read_u16(data.subspan(offset + 2, 2));
1050 if (group == kItemTagGroup && element == kSequenceDelimitationElement) {
1057 if (group != kItemTagGroup || element != kItemTagElement) {
1062 uint32_t item_length = read_u32(data.subspan(offset + 4, 4));
1065 if (item_length == kUndefinedLength) {
1067 size_t item_end = offset;
1068 while (item_end + 8 <= data.size()) {
1069 uint16_t g = read_u16(data.subspan(item_end, 2));
1070 uint16_t e = read_u16(data.subspan(item_end + 2, 2));
1071 if (g == kItemTagGroup && e == kItemDelimitationElement) {
1077 if (item_end + 4 > data.size())
break;
1078 uint32_t len = read_u32(data.subspan(item_end, 4));
1080 if (len != kUndefinedLength) {
1085 size_t item_bytes_read = 0;
1086 auto item_data = data.subspan(offset, item_end - offset);
1088 ? (big_endian ? decode_explicit_vr_be(item_data, item_bytes_read)
1089 : decode_explicit_vr_le(item_data, item_bytes_read))
1090 : decode_implicit_vr_le(item_data, item_bytes_read);
1092 if (item_result.is_ok()) {
1093 items.push_back(std::move(item_result.value()));
1095 offset = item_end + 8;
1098 if (offset + item_length > data.size()) {
1101 size_t item_bytes_read = 0;
1102 auto item_data = data.subspan(offset, item_length);
1104 ? (big_endian ? decode_explicit_vr_be(item_data, item_bytes_read)
1105 : decode_explicit_vr_le(item_data, item_bytes_read))
1106 : decode_implicit_vr_le(item_data, item_bytes_read);
1108 if (item_result.is_ok()) {
1109 items.push_back(std::move(item_result.value()));
1111 offset += item_length;
1115 bytes_read = offset;
1120 -> std::vector<std::vector<uint8_t>> {
1122 std::vector<std::vector<uint8_t>> frames;
1126 if (offset + 8 <= data.size()) {
1127 const uint16_t group = read_uint16_le(data.subspan(offset, 2));
1128 const uint16_t element = read_uint16_le(data.subspan(offset + 2, 2));
1130 if (group == kItemTagGroup && element == kItemTagElement) {
1131 uint32_t bot_length = read_uint32_le(data.subspan(offset + 4, 4));
1132 offset += 8 + bot_length;
1137 while (offset + 8 <= data.size()) {
1138 const uint16_t group = read_uint16_le(data.subspan(offset, 2));
1139 const uint16_t element = read_uint16_le(data.subspan(offset + 2, 2));
1142 if (group == kItemTagGroup && element == kSequenceDelimitationElement) {
1147 if (group != kItemTagGroup || element != kItemTagElement) {
1151 uint32_t fragment_length = read_uint32_le(data.subspan(offset + 4, 4));
1154 if (offset + fragment_length > data.size()) {
1159 std::vector<uint8_t> fragment(
1160 data.begin() +
static_cast<std::ptrdiff_t
>(offset),
1161 data.begin() +
static_cast<std::ptrdiff_t
>(offset + fragment_length));
1162 frames.push_back(std::move(fragment));
1164 offset += fragment_length;
void insert(dicom_element element)
Insert or replace an element in the dataset.
void set_string(dicom_tag tag, encoding::vr_type vr, std::string_view value)
Set a string value for the given tag.
auto get_string(dicom_tag tag, std::string_view default_value="") const -> std::string
Get the string value of an element.
auto get_private_creator(dicom_tag private_data_tag) const -> std::optional< std::string >
Get the Private Creator identification string for a private data element.
static auto instance() -> dicom_dictionary &
Get the singleton instance.
auto save(const std::filesystem::path &path) const -> kcenon::pacs::VoidResult
Save the DICOM file to disk.
static auto decode_explicit_vr_be(std::span< const uint8_t > data, size_t &bytes_read) -> kcenon::pacs::Result< dicom_dataset >
Decode a dataset from Explicit VR Big Endian format.
auto dataset() const noexcept -> const dicom_dataset &
Get read-only access to the main dataset.
static auto open(const std::filesystem::path &path) -> kcenon::pacs::Result< dicom_file >
Open and read a DICOM file from disk.
static auto from_bytes(std::span< const uint8_t > data) -> kcenon::pacs::Result< dicom_file >
Parse a DICOM file from raw bytes.
static auto encode_dataset(const dicom_dataset &dataset, const encoding::transfer_syntax &ts) -> std::vector< uint8_t >
Encode a dataset based on its Transfer Syntax.
static auto encode_implicit_vr_le(const dicom_dataset &dataset) -> std::vector< uint8_t >
Encode a dataset using Implicit VR Little Endian.
static auto decode_implicit_vr_le(std::span< const uint8_t > data, size_t &bytes_read) -> kcenon::pacs::Result< dicom_dataset >
Decode a dataset from Implicit VR Little Endian format.
static auto encode_explicit_vr_le(const dicom_dataset &dataset) -> std::vector< uint8_t >
Encode a dataset using Explicit VR Little Endian.
static auto parse_encapsulated_frames(std::span< const uint8_t > data) -> std::vector< std::vector< uint8_t > >
Parse encapsulated pixel data frames.
static constexpr size_t kPreambleSize
Preamble size.
dicom_dataset meta_info_
File Meta Information (Group 0002)
auto sop_instance_uid() const -> std::string
Get the SOP Instance UID.
auto to_bytes() const -> std::vector< uint8_t >
Encode the DICOM file to raw bytes.
static auto decode_explicit_vr_le(std::span< const uint8_t > data, size_t &bytes_read) -> kcenon::pacs::Result< dicom_dataset >
Decode a dataset from Explicit VR Little Endian format.
auto transfer_syntax() const -> encoding::transfer_syntax
Get the Transfer Syntax of this file.
static auto encode_explicit_vr_be(const dicom_dataset &dataset) -> std::vector< uint8_t >
Encode a dataset using Explicit VR Big Endian.
auto sop_class_uid() const -> std::string
Get the SOP Class UID.
static auto decode_dataset(std::span< const uint8_t > data, const encoding::transfer_syntax &ts, size_t &bytes_read) -> kcenon::pacs::Result< dicom_dataset >
Decode a dataset based on its Transfer Syntax.
static auto parse_undefined_length_sequence(std::span< const uint8_t > data, size_t &bytes_read, bool explicit_vr, bool big_endian) -> kcenon::pacs::Result< std::vector< dicom_dataset > >
Parse a sequence with undefined length.
static constexpr uint8_t kDicmPrefix[4]
DICOM magic bytes.
static auto generate_meta_information(const dicom_dataset &dataset, const encoding::transfer_syntax &ts) -> dicom_dataset
Generate File Meta Information for a dataset.
dicom_file()=default
Default constructor - creates an empty file.
dicom_dataset dataset_
Main dataset (encoded per Transfer Syntax)
static auto create(dicom_dataset dataset, const encoding::transfer_syntax &ts) -> dicom_file
Create a new DICOM file from a dataset.
static auto parse_meta_information(std::span< const uint8_t > data, size_t &bytes_read) -> kcenon::pacs::Result< dicom_dataset >
Parse file meta information from raw data.
auto meta_information() const noexcept -> const dicom_dataset &
Get read-only access to the File Meta Information.
static auto open(const std::filesystem::path &path) -> kcenon::pacs::Result< memory_mapped_file >
Open and memory-map a file for reading.
static auto instance() -> private_tag_registry &
Get the singleton instance.
Represents a DICOM Transfer Syntax.
DICOM Data Dictionary for tag metadata lookup.
DICOM Part 10 file handling for reading/writing DICOM files.
RAII wrapper for memory-mapped file I/O.
constexpr std::size_t fixed_length(vr_type vr) noexcept
Gets the fixed size of a VR if applicable.
constexpr bool is_numeric_vr(vr_type vr) noexcept
Checks if a VR is a numeric type.
vr_type
DICOM Value Representation (VR) types.
@ OB
Other Byte (variable length)
@ UN
Unknown (variable length)
@ SQ
Sequence of Items (undefined length)
@ LO
Long String (64 chars max)
@ UI
Unique Identifier (64 chars max)
@ SH
Short String (16 chars max)
constexpr std::optional< vr_type > from_string(std::string_view str) noexcept
Parses a two-character string to a vr_type.
@ big_endian
Most significant byte first (legacy, rarely used)
@ implicit
VR determined from data dictionary lookup.
constexpr bool has_explicit_32bit_length(vr_type vr) noexcept
Checks if a VR requires 32-bit length field in Explicit VR encoding.
constexpr std::string_view to_string(vr_type vr) noexcept
Converts a vr_type to its two-character string representation.
constexpr int missing_dicm_prefix
constexpr int value_conversion_error
constexpr int file_read_error
constexpr int file_write_error
constexpr int invalid_dicom_file
constexpr int missing_transfer_syntax
constexpr int decode_error
constexpr int unsupported_transfer_syntax
constexpr int file_not_found
VoidResult pacs_void_error(int code, const std::string &message, const std::string &details="")
Create a PACS void error result.
Result< T > pacs_error(int code, const std::string &message, const std::string &details="")
Create a PACS error result with module context.
Registry of vendor-specific private tag definitions.
uint16_t vr
VR type as uint16_t (encoding::vr_type value)