PACS System 0.1.0
PACS DICOM system library
Loading...
Searching...
No Matches
dicom_element.cpp
Go to the documentation of this file.
1// BSD 3-Clause License
2// Copyright (c) 2021-2025, 🍀☀🌕🌥 🌊
3// See the LICENSE file in the project root for full license information.
4
13
14#include <algorithm>
15#include <charconv>
16#include <sstream>
17
18namespace kcenon::pacs::core {
19
20// ============================================================================
21// Constructors
22// ============================================================================
23
25 : tag_{tag}, vr_{vr}, data_{}, sequence_items_{} {}
26
28 std::span<const uint8_t> data)
29 : tag_{tag}, vr_{vr}, data_(data.begin(), data.end()), sequence_items_{} {}
30
33auto dicom_element::operator=(const dicom_element&) -> dicom_element& = default;
34auto dicom_element::operator=(dicom_element&&) noexcept -> dicom_element& = default;
35dicom_element::~dicom_element() = default;
36
37// ============================================================================
38// Factory Methods
39// ============================================================================
40
41auto dicom_element::from_string(dicom_tag tag, encoding::vr_type vr,
42 std::string_view value) -> dicom_element {
43 dicom_element elem{tag, vr};
44 elem.set_string(value);
45 return elem;
46}
47
48// ============================================================================
49// String Value Access
50// ============================================================================
51
52auto dicom_element::as_string() const -> kcenon::pacs::Result<std::string> {
53 if (data_.empty()) {
54 return kcenon::pacs::ok(std::string{});
55 }
56
57 // For string VRs, convert bytes to string and remove padding
59 std::string_view raw{reinterpret_cast<const char*>(data_.data()),
60 data_.size()};
61 return kcenon::pacs::ok(remove_padding(raw, vr_));
62 }
63
64 // For numeric VRs, convert to string representation
66 switch (vr_) {
68 if (data_.size() >= 2) {
69 if (auto val = as_numeric<uint16_t>(); val.is_ok())
70 return kcenon::pacs::ok(std::to_string(val.value()));
71 }
72 break;
74 if (data_.size() >= 2) {
75 if (auto val = as_numeric<int16_t>(); val.is_ok())
76 return kcenon::pacs::ok(std::to_string(val.value()));
77 }
78 break;
80 if (data_.size() >= 4) {
81 if (auto val = as_numeric<uint32_t>(); val.is_ok())
82 return kcenon::pacs::ok(std::to_string(val.value()));
83 }
84 break;
86 if (data_.size() >= 4) {
87 if (auto val = as_numeric<int32_t>(); val.is_ok())
88 return kcenon::pacs::ok(std::to_string(val.value()));
89 }
90 break;
92 if (data_.size() >= 8) {
93 if (auto val = as_numeric<uint64_t>(); val.is_ok())
94 return kcenon::pacs::ok(std::to_string(val.value()));
95 }
96 break;
98 if (data_.size() >= 8) {
99 if (auto val = as_numeric<int64_t>(); val.is_ok())
100 return kcenon::pacs::ok(std::to_string(val.value()));
101 }
102 break;
104 if (data_.size() >= 4) {
105 if (auto val = as_numeric<float>(); val.is_ok())
106 return kcenon::pacs::ok(std::to_string(val.value()));
107 }
108 break;
110 if (data_.size() >= 8) {
111 if (auto val = as_numeric<double>(); val.is_ok())
112 return kcenon::pacs::ok(std::to_string(val.value()));
113 }
114 break;
115 default:
116 break;
117 }
118 }
119
120 // For binary VRs or unknown, return raw bytes as string
121 return kcenon::pacs::ok(
122 std::string{reinterpret_cast<const char*>(data_.data()), data_.size()});
123}
124
126 -> kcenon::pacs::Result<std::vector<std::string>> {
127 auto str_result = as_string();
128 if (str_result.is_err()) {
129 return kcenon::pacs::pacs_error<std::vector<std::string>>(
130 str_result.error().code, str_result.error().message);
131 }
132 const std::string str = str_result.value();
133
134 std::vector<std::string> result;
135 if (str.empty()) {
136 return kcenon::pacs::ok(result);
137 }
138
139 // Split by backslash (DICOM VM delimiter)
140 std::string::size_type start = 0;
141 std::string::size_type pos = 0;
142
143 while ((pos = str.find('\\', start)) != std::string::npos) {
144 result.push_back(str.substr(start, pos - start));
145 start = pos + 1;
146 }
147
148 // Add the last segment
149 result.push_back(str.substr(start));
150
151 return kcenon::pacs::ok(result);
152}
153
154// ============================================================================
155// Sequence Access
156// ============================================================================
157
158auto dicom_element::sequence_item_count() const noexcept -> std::size_t {
159 return sequence_items_.size();
160}
161
162auto dicom_element::sequence_item(std::size_t index) const
163 -> const dicom_dataset& {
164 return sequence_items_.at(index);
165}
166
167auto dicom_element::sequence_item(std::size_t index) -> dicom_dataset& {
168 return sequence_items_.at(index);
169}
170
171auto dicom_element::sequence_items() -> std::vector<dicom_dataset>& {
172 return sequence_items_;
173}
174
175auto dicom_element::sequence_items() const -> const std::vector<dicom_dataset>& {
176 return sequence_items_;
177}
178
180 sequence_items_.push_back(std::move(item));
181}
182
183// ============================================================================
184// Modification
185// ============================================================================
186
187void dicom_element::set_value(std::span<const uint8_t> data) {
188 data_.assign(data.begin(), data.end());
189}
190
191void dicom_element::set_string(std::string_view value) {
192 // Apply padding for string VRs
193 std::string padded = apply_padding(value);
194 data_.assign(padded.begin(), padded.end());
195}
196
197// ============================================================================
198// Private Helpers
199// ============================================================================
200
201auto dicom_element::apply_padding(std::string_view str) const -> std::string {
202 std::string result{str};
203
204 // DICOM requires even length for all values
205 if (result.length() % 2 != 0) {
206 result += encoding::padding_char(vr_);
207 }
208
209 return result;
210}
211
212auto dicom_element::remove_padding(std::string_view str,
213 encoding::vr_type vr) -> std::string {
214 if (str.empty()) {
215 return {};
216 }
217
218 std::string result{str};
219
220 // Determine padding character based on VR
221 const char pad = encoding::padding_char(vr);
222
223 // Remove trailing padding characters
224 while (!result.empty() && result.back() == pad) {
225 result.pop_back();
226 }
227
228 // For string VRs (except UI), also trim trailing spaces
230 while (!result.empty() && result.back() == ' ') {
231 result.pop_back();
232 }
233 }
234
235 return result;
236}
237
238} // namespace kcenon::pacs::core
auto apply_padding(std::string_view str) const -> std::string
Apply DICOM padding to ensure even length.
auto sequence_item(std::size_t index) const -> const dicom_dataset &
Get a specific sequence item by index.
auto as_numeric() const -> kcenon::pacs::Result< T >
Get the value as a numeric type.
std::vector< dicom_dataset > sequence_items_
void add_sequence_item(dicom_dataset item)
Add a new item to the sequence.
void set_value(std::span< const uint8_t > data)
Set the raw value data.
dicom_element(dicom_tag tag, encoding::vr_type vr) noexcept
Construct an empty element with given tag and VR.
auto as_string() const -> kcenon::pacs::Result< std::string >
Get the value as a string.
static auto remove_padding(std::string_view str, encoding::vr_type vr) -> std::string
Remove DICOM padding from a string value.
auto sequence_item_count() const noexcept -> std::size_t
Get the number of items in the sequence.
void set_string(std::string_view value)
Set the value from a string.
auto as_string_list() const -> kcenon::pacs::Result< std::vector< std::string > >
Get multi-valued string as a list.
auto sequence_items() -> std::vector< dicom_dataset > &
Get mutable access to sequence items.
DICOM Dataset - ordered collection of Data Elements.
DICOM Data Element representation (Tag, VR, Value)
const uint8_t * data_
constexpr char padding_char(vr_type vr) noexcept
Gets the padding character for a VR.
Definition vr_type.h:292
constexpr bool is_numeric_vr(vr_type vr) noexcept
Checks if a VR is a numeric type.
Definition vr_type.h:214
vr_type
DICOM Value Representation (VR) types.
Definition vr_type.h:29
@ UL
Unsigned Long (4 bytes)
@ UI
Unique Identifier (64 chars max)
@ SL
Signed Long (4 bytes)
@ US
Unsigned Short (2 bytes)
@ FD
Floating Point Double (8 bytes)
@ FL
Floating Point Single (4 bytes)
@ SV
Signed 64-bit Very Long (8 bytes)
@ SS
Signed Short (2 bytes)
@ UV
Unsigned 64-bit Very Long (8 bytes)
constexpr bool is_string_vr(vr_type vr) noexcept
Checks if a VR is a string type.
Definition vr_type.h:175
Result<T> type aliases and helpers for PACS system.
std::string_view code
vr_encoding vr