PACS System 0.1.0
PACS DICOM system library
Loading...
Searching...
No Matches
dicom_element.h
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
18#pragma once
19
20#include "dicom_tag.h"
21#include "result.h"
22
24
25#include <cstdint>
26#include <cstring>
27#include <span>
28#include <stdexcept>
29#include <string>
30#include <string_view>
31#include <type_traits>
32#include <vector>
33
34namespace kcenon::pacs::core {
35
36// Forward declaration to break circular dependency
37class dicom_dataset;
38
68public:
75
83 std::span<const uint8_t> data);
84
89
94
98 auto operator=(const dicom_element&) -> dicom_element&;
99
103 auto operator=(dicom_element&&) noexcept -> dicom_element&;
104
109
110 // ========================================================================
111 // Factory Methods
112 // ========================================================================
113
121 [[nodiscard]] static auto from_string(dicom_tag tag, encoding::vr_type vr,
122 std::string_view value) -> dicom_element;
123
132 template <typename T>
133 requires std::is_arithmetic_v<T>
134 [[nodiscard]] static auto from_numeric(dicom_tag tag, encoding::vr_type vr,
135 T value) -> dicom_element;
136
145 template <typename T>
146 requires std::is_arithmetic_v<T>
147 [[nodiscard]] static auto from_numeric_list(dicom_tag tag, encoding::vr_type vr,
148 std::span<const T> values) -> dicom_element;
149
150 // ========================================================================
151 // Accessors
152 // ========================================================================
153
158 [[nodiscard]] constexpr auto tag() const noexcept -> dicom_tag { return tag_; }
159
164 [[nodiscard]] constexpr auto vr() const noexcept -> encoding::vr_type { return vr_; }
165
170 [[nodiscard]] auto length() const noexcept -> uint32_t {
171 return static_cast<uint32_t>(data_.size());
172 }
173
178 [[nodiscard]] auto raw_data() const noexcept -> std::span<const uint8_t> {
179 return data_;
180 }
181
186 [[nodiscard]] auto is_empty() const noexcept -> bool { return data_.empty(); }
187
188 // ========================================================================
189 // String Value Access
190 // ========================================================================
191
200 [[nodiscard]] auto as_string() const -> kcenon::pacs::Result<std::string>;
201
209 [[nodiscard]] auto as_string_list() const
210 -> kcenon::pacs::Result<std::vector<std::string>>;
211
212 // ========================================================================
213 // Numeric Value Access
214 // ========================================================================
215
221 template <typename T>
222 requires std::is_arithmetic_v<T>
223 [[nodiscard]] auto as_numeric() const -> kcenon::pacs::Result<T>;
224
230 template <typename T>
231 requires std::is_arithmetic_v<T>
232 [[nodiscard]] auto as_numeric_list() const -> kcenon::pacs::Result<std::vector<T>>;
233
234 // ========================================================================
235 // Sequence Access
236 // ========================================================================
237
242 [[nodiscard]] auto is_sequence() const noexcept -> bool {
243 return vr_ == encoding::vr_type::SQ;
244 }
245
250 [[nodiscard]] auto sequence_item_count() const noexcept -> std::size_t;
251
259 [[nodiscard]] auto sequence_item(std::size_t index) const
260 -> const dicom_dataset&;
261
269 [[nodiscard]] auto sequence_item(std::size_t index) -> dicom_dataset&;
270
276 [[nodiscard]] auto sequence_items() -> std::vector<dicom_dataset>&;
277
283 [[nodiscard]] auto sequence_items() const -> const std::vector<dicom_dataset>&;
284
291
292 // ========================================================================
293 // Modification
294 // ========================================================================
295
300 void set_value(std::span<const uint8_t> data);
301
306 void set_string(std::string_view value);
307
313 template <typename T>
314 requires std::is_arithmetic_v<T>
315 void set_numeric(T value);
316
317private:
319 encoding::vr_type vr_;
320 std::vector<uint8_t> data_;
321 std::vector<dicom_dataset> sequence_items_; // Used only when VR == SQ
322
328 [[nodiscard]] auto apply_padding(std::string_view str) const -> std::string;
329
335 [[nodiscard]] static auto remove_padding(std::string_view str,
336 encoding::vr_type vr) -> std::string;
337};
338
339// ============================================================================
340// Template Implementations
341// ============================================================================
342
343template <typename T>
344 requires std::is_arithmetic_v<T>
345auto dicom_element::from_numeric(dicom_tag tag, encoding::vr_type vr,
346 T value) -> dicom_element {
347 dicom_element elem{tag, vr};
348 elem.set_numeric(value);
349 return elem;
350}
351
352template <typename T>
353 requires std::is_arithmetic_v<T>
355 std::span<const T> values) -> dicom_element {
356 dicom_element elem{tag, vr};
357 std::vector<uint8_t> data(values.size() * sizeof(T));
358
359 for (size_t i = 0; i < values.size(); ++i) {
360 std::memcpy(data.data() + i * sizeof(T), &values[i], sizeof(T));
361 }
362
363 elem.set_value(data);
364 return elem;
365}
366
367template <typename T>
368 requires std::is_arithmetic_v<T>
370 if (data_.size() < sizeof(T)) {
373 "Insufficient data for numeric conversion: expected " +
374 std::to_string(sizeof(T)) + " bytes, got " +
375 std::to_string(data_.size()));
376 }
377
378 T result{};
379 std::memcpy(&result, data_.data(), sizeof(T));
380 return kcenon::pacs::ok(result);
381}
382
383template <typename T>
384 requires std::is_arithmetic_v<T>
385auto dicom_element::as_numeric_list() const -> kcenon::pacs::Result<std::vector<T>> {
386 if (data_.size() % sizeof(T) != 0) {
387 return kcenon::pacs::pacs_error<std::vector<T>>(
389 "Data size not aligned for numeric type: " +
390 std::to_string(data_.size()) + " bytes is not divisible by " +
391 std::to_string(sizeof(T)));
392 }
393
394 const size_t count = data_.size() / sizeof(T);
395 std::vector<T> result(count);
396
397 for (size_t i = 0; i < count; ++i) {
398 std::memcpy(&result[i], data_.data() + i * sizeof(T), sizeof(T));
399 }
400
401 return kcenon::pacs::ok(result);
402}
403
404template <typename T>
405 requires std::is_arithmetic_v<T>
407 data_.resize(sizeof(T));
408 std::memcpy(data_.data(), &value, sizeof(T));
409}
410
411} // namespace kcenon::pacs::core
412
auto is_sequence() const noexcept -> bool
Check if this element is a sequence.
auto apply_padding(std::string_view str) const -> std::string
Apply DICOM padding to ensure even length.
auto length() const noexcept -> uint32_t
Get the value length in bytes.
constexpr auto tag() const noexcept -> dicom_tag
Get the element's tag.
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.
static auto from_numeric_list(dicom_tag tag, encoding::vr_type vr, std::span< const T > values) -> dicom_element
Create an element from multiple numeric values.
auto raw_data() const noexcept -> std::span< const uint8_t >
Get the raw data bytes.
std::vector< dicom_dataset > sequence_items_
dicom_element(dicom_element &&) noexcept
Move constructor.
auto as_numeric_list() const -> kcenon::pacs::Result< std::vector< T > >
Get multi-valued numeric data as a list.
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.
constexpr auto vr() const noexcept -> encoding::vr_type
Get the element's VR.
void set_numeric(T value)
Set the value from a numeric value.
dicom_element(const dicom_element &)
Copy constructor.
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.
static auto from_numeric(dicom_tag tag, encoding::vr_type vr, T value) -> dicom_element
Create an element from a numeric value.
auto sequence_items() -> std::vector< dicom_dataset > &
Get mutable access to sequence items.
auto is_empty() const noexcept -> bool
Check if the element has no value.
DICOM Tag representation (Group, Element pairs)
vr_type
DICOM Value Representation (VR) types.
Definition vr_type.h:29
@ SQ
Sequence of Items (undefined length)
constexpr int data_size_mismatch
Definition result.h:73
Result< T > pacs_error(int code, const std::string &message, const std::string &details="")
Create a PACS error result with module context.
Definition result.h:234
Result<T> type aliases and helpers for PACS system.