26constexpr uint16_t ITEM_GROUP = 0xFFFE;
27constexpr uint16_t ITEM_TAG_ELEMENT = 0xE000;
28constexpr uint16_t ITEM_DELIM_ELEMENT = 0xE00D;
29constexpr uint16_t SEQ_DELIM_ELEMENT = 0xE0DD;
31constexpr uint32_t UNDEFINED_LENGTH = 0xFFFFFFFF;
33constexpr bool is_sequence_delimiter(core::dicom_tag tag) {
34 return tag.group() == ITEM_GROUP && tag.element() == SEQ_DELIM_ELEMENT;
37constexpr bool is_item_delimiter(core::dicom_tag tag) {
38 return tag.group() == ITEM_GROUP && tag.element() == ITEM_DELIM_ELEMENT;
41constexpr bool is_item_tag(core::dicom_tag tag) {
42 return tag.group() == ITEM_GROUP && tag.element() == ITEM_TAG_ELEMENT;
45constexpr bool is_special_tag(core::dicom_tag tag) {
46 return tag.group() == ITEM_GROUP;
58constexpr bool requires_byte_swap(vr_type
vr) {
89constexpr size_t swap_unit_size(vr_type
vr) {
133 vr_type vr, std::span<const uint8_t> data) {
134 if (!requires_byte_swap(
vr) || data.empty()) {
135 return {data.begin(), data.end()};
138 switch (swap_unit_size(
vr)) {
146 return {data.begin(), data.end()};
151 vr_type vr, std::span<const uint8_t> data) {
162 std::vector<uint8_t> buffer;
163 buffer.reserve(4096);
165 for (
const auto& [tag, element] : dataset) {
167 buffer.insert(buffer.end(), encoded.begin(), encoded.end());
175 std::vector<uint8_t> buffer;
183 buffer.push_back(
static_cast<uint8_t
>(vr_str[0]));
184 buffer.push_back(
static_cast<uint8_t
>(vr_str[1]));
196 uint32_t length =
static_cast<uint32_t
>(be_data.size());
205 write_be16(buffer,
static_cast<uint16_t
>(length));
209 buffer.insert(buffer.end(), be_data.begin(), be_data.end());
215 std::vector<uint8_t>& buffer,
225 for (
const auto& item : items) {
236 std::vector<uint8_t>& buffer,
243 auto item_content =
encode(item);
246 write_be32(buffer,
static_cast<uint32_t
>(item_content.size()));
249 buffer.insert(buffer.end(), item_content.begin(), item_content.end());
260 while (!data.empty()) {
262 if (data.size() >= 4) {
264 uint16_t elem =
read_be16(data.data() + 2);
268 if (is_sequence_delimiter(tag)) {
272 if (is_item_delimiter(tag)) {
293 if (data.size() < 8) {
294 return make_codec_error<core::dicom_element>(
296 "Insufficient data to decode element");
301 uint16_t elem =
read_be16(data.data() + 2);
305 if (is_special_tag(tag)) {
306 data = data.subspan(8);
314 vr_chars[0] =
static_cast<char>(data[4]);
315 vr_chars[1] =
static_cast<char>(data[5]);
316 std::string_view vr_str(vr_chars, 2);
320 return make_codec_error<core::dicom_element>(
332 if (data.size() < 12) {
333 return make_codec_error<core::dicom_element>(
335 "Insufficient data for extended VR format");
346 data = data.subspan(header_size);
349 if (length == UNDEFINED_LENGTH) {
354 if (data.size() < length) {
355 return make_codec_error<core::dicom_element>(
357 "Insufficient data for element value");
361 auto value_data = data.subspan(0, length);
362 data = data.subspan(length);
373 std::span<const uint8_t>& data) {
378 while (!data.empty()) {
380 if (data.size() < 8) {
381 return make_codec_error<core::dicom_element>(
383 "Insufficient data for sequence delimiter");
386 uint16_t item_group =
read_be16(data.data());
387 uint16_t item_elem =
read_be16(data.data() + 2);
391 if (is_sequence_delimiter(item_tag)) {
393 data = data.subspan(8);
398 if (!is_item_tag(item_tag)) {
399 return make_codec_error<core::dicom_element>(
401 "Expected Item tag in sequence");
406 if (!item_result.is_ok()) {
407 return make_codec_error<core::dicom_element>(
408 item_result.error().code,
409 item_result.error().message);
412 seq_element.
sequence_items().push_back(std::move(item_result.value()));
420 std::vector<uint8_t> accumulated_data;
422 while (!data.empty()) {
423 if (data.size() < 8) {
424 return make_codec_error<core::dicom_element>(
426 "Insufficient data for encapsulated data");
429 uint16_t item_group =
read_be16(data.data());
430 uint16_t item_elem =
read_be16(data.data() + 2);
434 if (is_sequence_delimiter(item_tag)) {
435 data = data.subspan(8);
440 if (is_item_tag(item_tag)) {
441 uint32_t item_length =
read_be32(data.data() + 4);
442 data = data.subspan(8);
444 if (item_length != UNDEFINED_LENGTH && data.size() >= item_length) {
446 auto item_data = data.subspan(0, item_length);
448 accumulated_data.insert(accumulated_data.end(),
449 le_item.begin(), le_item.end());
450 data = data.subspan(item_length);
453 return make_codec_error<core::dicom_element>(
455 "Invalid tag in encapsulated data");
464 std::span<const uint8_t>& data) {
466 if (data.size() < 8) {
467 return make_codec_error<core::dicom_dataset>(
469 "Insufficient data for sequence item");
473 uint16_t elem =
read_be16(data.data() + 2);
476 if (!is_item_tag(tag)) {
477 return make_codec_error<core::dicom_dataset>(
479 "Expected Item tag for sequence item");
482 uint32_t item_length =
read_be32(data.data() + 4);
483 data = data.subspan(8);
485 if (item_length == UNDEFINED_LENGTH) {
489 while (!data.empty()) {
490 if (data.size() < 4) {
491 return make_codec_error<core::dicom_dataset>(
493 "Insufficient data for item delimiter check");
496 uint16_t elem_group =
read_be16(data.data());
497 uint16_t elem_elem =
read_be16(data.data() + 2);
500 if (is_item_delimiter(elem_tag)) {
502 data = data.subspan(8);
507 if (!elem_result.is_ok()) {
508 return make_codec_error<core::dicom_dataset>(
509 elem_result.error().code,
510 elem_result.error().message);
513 item.insert(std::move(elem_result.value()));
520 if (data.size() < item_length) {
521 return make_codec_error<core::dicom_dataset>(
523 "Insufficient data for item content");
526 auto item_data = data.subspan(0, item_length);
527 data = data.subspan(item_length);
Byte swapping utilities for endianness conversion.
void insert(dicom_element element)
Insert or replace an element in the dataset.
auto is_sequence() const noexcept -> bool
Check if this element is a sequence.
constexpr auto tag() const noexcept -> dicom_tag
Get the element's tag.
auto raw_data() const noexcept -> std::span< const uint8_t >
Get the raw data bytes.
constexpr auto vr() const noexcept -> encoding::vr_type
Get the element's VR.
auto sequence_items() -> std::vector< dicom_dataset > &
Get mutable access to sequence items.
static std::vector< uint8_t > from_big_endian(vr_type vr, std::span< const uint8_t > data)
Convert element value from big-endian to little-endian.
static void encode_sequence_item(std::vector< uint8_t > &buffer, const core::dicom_dataset &item)
static std::vector< uint8_t > to_big_endian(vr_type vr, std::span< const uint8_t > data)
Convert element value from little-endian to big-endian.
static result< core::dicom_dataset > decode(std::span< const uint8_t > data)
Decode bytes to a dataset using Explicit VR Big Endian.
static result< core::dicom_element > decode_undefined_length(core::dicom_tag tag, vr_type vr, std::span< const uint8_t > &data)
static result< core::dicom_element > decode_element(std::span< const uint8_t > &data)
Decode a single element from bytes.
static std::vector< uint8_t > encode_element(const core::dicom_element &element)
Encode a single element to bytes.
static void encode_sequence(std::vector< uint8_t > &buffer, const core::dicom_element &element)
static std::vector< uint8_t > encode(const core::dicom_dataset &dataset)
Encode a dataset to bytes using Explicit VR Big Endian.
static result< core::dicom_dataset > decode_sequence_item(std::span< const uint8_t > &data)
Encoder/decoder for Explicit VR Big Endian transfer syntax.
constexpr uint32_t read_be32(const uint8_t *data) noexcept
Reads a 32-bit value from big-endian bytes.
std::vector< uint8_t > swap_od_bytes(std::span< const uint8_t > data)
Swaps bytes in-place for OD (Other Double) data.
vr_type
DICOM Value Representation (VR) types.
@ UN
Unknown (variable length)
@ SQ
Sequence of Items (undefined length)
@ UL
Unsigned Long (4 bytes)
@ SL
Signed Long (4 bytes)
@ US
Unsigned Short (2 bytes)
@ OD
Other Double (variable length)
@ OF
Other Float (variable length)
@ OW
Other Word (variable length)
@ FD
Floating Point Double (8 bytes)
@ FL
Floating Point Single (4 bytes)
@ OL
Other Long (variable length)
@ SS
Signed Short (2 bytes)
@ AT
Attribute Tag (4 bytes)
std::vector< uint8_t > swap_ow_bytes(std::span< const uint8_t > data)
Swaps bytes in-place for OW (Other Word) data.
constexpr std::optional< vr_type > from_string(std::string_view str) noexcept
Parses a two-character string to a vr_type.
std::vector< uint8_t > pad_to_even(vr_type vr, std::span< const uint8_t > data)
Pads data to even length as required by DICOM.
void write_be32(std::vector< uint8_t > &buffer, uint32_t value)
Writes a 32-bit value in big-endian byte order.
void write_be16(std::vector< uint8_t > &buffer, uint16_t value)
Writes a 16-bit value in big-endian byte order.
constexpr uint16_t read_be16(const uint8_t *data) noexcept
Reads a 16-bit value from big-endian bytes.
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.
std::vector< uint8_t > swap_ol_bytes(std::span< const uint8_t > data)
Swaps bytes in-place for OL (Other Long) data.
constexpr int insufficient_data
constexpr int invalid_sequence
Result< T > pacs_error(int code, const std::string &message, const std::string &details="")
Create a PACS error result with module context.