25constexpr uint16_t read_le16(
const uint8_t* data) {
26 return static_cast<uint16_t
>(data[0]) |
27 (
static_cast<uint16_t
>(data[1]) << 8);
30constexpr uint32_t read_le32(
const uint8_t* data) {
31 return static_cast<uint32_t
>(data[0]) |
32 (
static_cast<uint32_t
>(data[1]) << 8) |
33 (
static_cast<uint32_t
>(data[2]) << 16) |
34 (
static_cast<uint32_t
>(data[3]) << 24);
37void write_le16(std::vector<uint8_t>& buffer, uint16_t value) {
38 buffer.push_back(
static_cast<uint8_t
>(value & 0xFF));
39 buffer.push_back(
static_cast<uint8_t
>((value >> 8) & 0xFF));
42void write_le32(std::vector<uint8_t>& buffer, uint32_t value) {
43 buffer.push_back(
static_cast<uint8_t
>(value & 0xFF));
44 buffer.push_back(
static_cast<uint8_t
>((value >> 8) & 0xFF));
45 buffer.push_back(
static_cast<uint8_t
>((value >> 16) & 0xFF));
46 buffer.push_back(
static_cast<uint8_t
>((value >> 24) & 0xFF));
53constexpr uint16_t ITEM_GROUP = 0xFFFE;
54constexpr uint16_t ITEM_TAG_ELEMENT = 0xE000;
55constexpr uint16_t ITEM_DELIM_ELEMENT = 0xE00D;
56constexpr uint16_t SEQ_DELIM_ELEMENT = 0xE0DD;
58constexpr uint32_t UNDEFINED_LENGTH = 0xFFFFFFFF;
60constexpr bool is_sequence_delimiter(core::dicom_tag tag) {
61 return tag.group() == ITEM_GROUP && tag.element() == SEQ_DELIM_ELEMENT;
64constexpr bool is_item_delimiter(core::dicom_tag tag) {
65 return tag.group() == ITEM_GROUP && tag.element() == ITEM_DELIM_ELEMENT;
68constexpr bool is_item_tag(core::dicom_tag tag) {
69 return tag.group() == ITEM_GROUP && tag.element() == ITEM_TAG_ELEMENT;
72constexpr bool is_special_tag(core::dicom_tag tag) {
73 return tag.group() == ITEM_GROUP;
97 std::vector<uint8_t> buffer;
100 for (
const auto& [tag, element] : dataset) {
102 buffer.insert(buffer.end(), encoded.begin(), encoded.end());
110 std::vector<uint8_t> buffer;
113 write_le16(buffer, element.
tag().group());
114 write_le16(buffer, element.
tag().element());
118 buffer.push_back(
static_cast<uint8_t
>(vr_str[0]));
119 buffer.push_back(
static_cast<uint8_t
>(vr_str[1]));
130 uint32_t length =
static_cast<uint32_t
>(padded_data.size());
135 write_le16(buffer, 0x0000);
136 write_le32(buffer, length);
139 write_le16(buffer,
static_cast<uint16_t
>(length));
143 buffer.insert(buffer.end(), padded_data.begin(), padded_data.end());
151 write_le16(buffer, 0x0000);
154 write_le32(buffer, UNDEFINED_LENGTH);
158 for (
const auto& item : items) {
163 write_le16(buffer, ITEM_GROUP);
164 write_le16(buffer, SEQ_DELIM_ELEMENT);
165 write_le32(buffer, 0);
171 write_le16(buffer, ITEM_GROUP);
172 write_le16(buffer, ITEM_TAG_ELEMENT);
175 auto item_content =
encode(item);
178 write_le32(buffer,
static_cast<uint32_t
>(item_content.size()));
181 buffer.insert(buffer.end(), item_content.begin(), item_content.end());
189 std::span<const uint8_t> data) {
192 while (!data.empty()) {
194 if (data.size() >= 4) {
195 uint16_t group = read_le16(data.data());
196 uint16_t elem = read_le16(data.data() + 2);
200 if (is_sequence_delimiter(tag)) {
204 if (is_item_delimiter(tag)) {
223 std::span<const uint8_t>& data) {
225 if (data.size() < 8) {
226 return make_codec_error<core::dicom_element>(
228 "Insufficient data to decode element");
232 uint16_t group = read_le16(data.data());
233 uint16_t elem = read_le16(data.data() + 2);
237 if (is_special_tag(tag)) {
238 data = data.subspan(8);
246 vr_chars[0] =
static_cast<char>(data[4]);
247 vr_chars[1] =
static_cast<char>(data[5]);
248 std::string_view vr_str(vr_chars, 2);
252 return make_codec_error<core::dicom_element>(
264 if (data.size() < 12) {
265 return make_codec_error<core::dicom_element>(
267 "Insufficient data for extended VR format");
270 length = read_le32(data.data() + 8);
274 length = read_le16(data.data() + 6);
278 data = data.subspan(header_size);
281 if (length == UNDEFINED_LENGTH) {
286 if (data.size() < length) {
287 return make_codec_error<core::dicom_element>(
289 "Insufficient data for element value");
293 auto value_data = data.subspan(0, length);
294 data = data.subspan(length);
301 std::span<const uint8_t>& data) {
306 while (!data.empty()) {
308 if (data.size() < 8) {
309 return make_codec_error<core::dicom_element>(
311 "Insufficient data for sequence delimiter");
314 uint16_t item_group = read_le16(data.data());
315 uint16_t item_elem = read_le16(data.data() + 2);
319 if (is_sequence_delimiter(item_tag)) {
321 data = data.subspan(8);
326 if (!is_item_tag(item_tag)) {
327 return make_codec_error<core::dicom_element>(
329 "Expected Item tag in sequence");
334 if (!item_result.is_ok()) {
335 return make_codec_error<core::dicom_element>(
336 item_result.error().code,
337 item_result.error().message);
340 seq_element.
sequence_items().push_back(std::move(item_result.value()));
348 std::vector<uint8_t> accumulated_data;
350 while (!data.empty()) {
351 if (data.size() < 8) {
352 return make_codec_error<core::dicom_element>(
354 "Insufficient data for encapsulated data");
357 uint16_t item_group = read_le16(data.data());
358 uint16_t item_elem = read_le16(data.data() + 2);
362 if (is_sequence_delimiter(item_tag)) {
363 data = data.subspan(8);
368 if (is_item_tag(item_tag)) {
369 uint32_t item_length = read_le32(data.data() + 4);
370 data = data.subspan(8);
372 if (item_length != UNDEFINED_LENGTH && data.size() >= item_length) {
373 accumulated_data.insert(accumulated_data.end(),
374 data.begin(), data.begin() + item_length);
375 data = data.subspan(item_length);
378 return make_codec_error<core::dicom_element>(
380 "Invalid tag in encapsulated data");
388 std::span<const uint8_t>& data) {
390 if (data.size() < 8) {
391 return make_codec_error<core::dicom_dataset>(
393 "Insufficient data for sequence item");
396 uint16_t group = read_le16(data.data());
397 uint16_t elem = read_le16(data.data() + 2);
400 if (!is_item_tag(tag)) {
401 return make_codec_error<core::dicom_dataset>(
403 "Expected Item tag for sequence item");
406 uint32_t item_length = read_le32(data.data() + 4);
407 data = data.subspan(8);
409 if (item_length == UNDEFINED_LENGTH) {
413 while (!data.empty()) {
414 if (data.size() < 4) {
415 return make_codec_error<core::dicom_dataset>(
417 "Insufficient data for item delimiter check");
420 uint16_t elem_group = read_le16(data.data());
421 uint16_t elem_elem = read_le16(data.data() + 2);
424 if (is_item_delimiter(elem_tag)) {
426 data = data.subspan(8);
431 if (!elem_result.is_ok()) {
432 return make_codec_error<core::dicom_dataset>(
433 elem_result.error().code,
434 elem_result.error().message);
437 item.insert(std::move(elem_result.value()));
444 if (data.size() < item_length) {
445 return make_codec_error<core::dicom_dataset>(
447 "Insufficient data for item content");
450 auto item_data = data.subspan(0, item_length);
451 data = data.subspan(item_length);
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 result< core::dicom_element > decode_undefined_length(core::dicom_tag tag, vr_type vr, std::span< const uint8_t > &data)
static void encode_sequence_item(std::vector< uint8_t > &buffer, const core::dicom_dataset &item)
static result< core::dicom_element > decode_element(std::span< const uint8_t > &data)
Decode a single element from bytes.
static void encode_sequence(std::vector< uint8_t > &buffer, const core::dicom_element &element)
static result< core::dicom_dataset > decode_sequence_item(std::span< const uint8_t > &data)
static result< core::dicom_dataset > decode(std::span< const uint8_t > data)
Decode bytes to a dataset using Explicit VR Little Endian.
static std::vector< uint8_t > encode_element(const core::dicom_element &element)
Encode a single element to bytes.
static std::vector< uint8_t > encode(const core::dicom_dataset &dataset)
Encode a dataset to bytes using Explicit VR Little Endian.
Encoder/decoder for Explicit VR Little Endian transfer syntax.
vr_type
DICOM Value Representation (VR) types.
@ UN
Unknown (variable length)
@ SQ
Sequence of Items (undefined length)
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.
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 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.