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;
93 std::vector<uint8_t> buffer;
96 for (
const auto& [tag, element] : dataset) {
98 buffer.insert(buffer.end(), encoded.begin(), encoded.end());
106 std::vector<uint8_t> buffer;
109 write_le16(buffer, element.
tag().group());
110 write_le16(buffer, element.
tag().element());
123 write_le32(buffer,
static_cast<uint32_t
>(padded_data.size()));
126 buffer.insert(buffer.end(), padded_data.begin(), padded_data.end());
134 write_le32(buffer, UNDEFINED_LENGTH);
138 for (
const auto& item : items) {
143 write_le16(buffer, ITEM_GROUP);
144 write_le16(buffer, SEQ_DELIM_ELEMENT);
145 write_le32(buffer, 0);
151 write_le16(buffer, ITEM_GROUP);
152 write_le16(buffer, ITEM_TAG_ELEMENT);
155 auto item_content =
encode(item);
158 write_le32(buffer,
static_cast<uint32_t
>(item_content.size()));
161 buffer.insert(buffer.end(), item_content.begin(), item_content.end());
169 std::span<const uint8_t> data) {
172 while (!data.empty()) {
174 if (data.size() >= 4) {
175 uint16_t group = read_le16(data.data());
176 uint16_t elem = read_le16(data.data() + 2);
180 if (is_sequence_delimiter(tag)) {
184 if (is_item_delimiter(tag)) {
203 std::span<const uint8_t>& data) {
205 if (data.size() < 8) {
206 return make_codec_error<core::dicom_element>(
208 "Insufficient data to decode element");
212 uint16_t group = read_le16(data.data());
213 uint16_t elem = read_le16(data.data() + 2);
217 uint32_t length = read_le32(data.data() + 4);
218 data = data.subspan(8);
222 auto tag_info = dict.find(tag);
230 if (tag.group() == ITEM_GROUP) {
237 if (length == UNDEFINED_LENGTH) {
242 if (data.size() < length) {
243 return make_codec_error<core::dicom_element>(
245 "Insufficient data for element value");
249 auto value_data = data.subspan(0, length);
250 data = data.subspan(length);
257 std::span<const uint8_t>& data) {
262 while (!data.empty()) {
264 if (data.size() < 8) {
265 return make_codec_error<core::dicom_element>(
267 "Insufficient data for sequence delimiter");
270 uint16_t item_group = read_le16(data.data());
271 uint16_t item_elem = read_le16(data.data() + 2);
275 if (is_sequence_delimiter(item_tag)) {
277 data = data.subspan(8);
282 if (!is_item_tag(item_tag)) {
283 return make_codec_error<core::dicom_element>(
285 "Expected Item tag in sequence");
290 if (!item_result.is_ok()) {
291 return make_codec_error<core::dicom_element>(
292 item_result.error().code,
293 item_result.error().message);
296 seq_element.
sequence_items().push_back(std::move(item_result.value()));
304 std::vector<uint8_t> accumulated_data;
306 while (!data.empty()) {
307 if (data.size() < 8) {
308 return make_codec_error<core::dicom_element>(
310 "Insufficient data for encapsulated data");
313 uint16_t item_group = read_le16(data.data());
314 uint16_t item_elem = read_le16(data.data() + 2);
318 if (is_sequence_delimiter(item_tag)) {
319 data = data.subspan(8);
324 if (is_item_tag(item_tag)) {
325 uint32_t item_length = read_le32(data.data() + 4);
326 data = data.subspan(8);
328 if (item_length != UNDEFINED_LENGTH && data.size() >= item_length) {
329 accumulated_data.insert(accumulated_data.end(),
330 data.begin(), data.begin() + item_length);
331 data = data.subspan(item_length);
334 return make_codec_error<core::dicom_element>(
336 "Invalid tag in encapsulated data");
344 std::span<const uint8_t>& data) {
346 if (data.size() < 8) {
347 return make_codec_error<core::dicom_dataset>(
349 "Insufficient data for sequence item");
352 uint16_t group = read_le16(data.data());
353 uint16_t elem = read_le16(data.data() + 2);
356 if (!is_item_tag(tag)) {
357 return make_codec_error<core::dicom_dataset>(
359 "Expected Item tag for sequence item");
362 uint32_t item_length = read_le32(data.data() + 4);
363 data = data.subspan(8);
365 if (item_length == UNDEFINED_LENGTH) {
369 while (!data.empty()) {
370 if (data.size() < 4) {
371 return make_codec_error<core::dicom_dataset>(
373 "Insufficient data for item delimiter check");
376 uint16_t elem_group = read_le16(data.data());
377 uint16_t elem_elem = read_le16(data.data() + 2);
380 if (is_item_delimiter(elem_tag)) {
382 data = data.subspan(8);
387 if (!elem_result.is_ok()) {
388 return make_codec_error<core::dicom_dataset>(
389 elem_result.error().code,
390 elem_result.error().message);
393 item.insert(std::move(elem_result.value()));
400 if (data.size() < item_length) {
401 return make_codec_error<core::dicom_dataset>(
403 "Insufficient data for item content");
406 auto item_data = data.subspan(0, item_length);
407 data = data.subspan(item_length);
void insert(dicom_element element)
Insert or replace an element in the dataset.
static auto instance() -> dicom_dictionary &
Get the singleton instance.
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_element(std::span< const uint8_t > &data)
Decode a single element from bytes.
static result< core::dicom_dataset > decode_sequence_item(std::span< const uint8_t > &data)
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 std::vector< uint8_t > encode_element(const core::dicom_element &element)
Encode a single element to bytes.
static result< core::dicom_dataset > decode(std::span< const uint8_t > data)
Decode bytes to a dataset using Implicit VR Little Endian.
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 Implicit VR Little Endian.
DICOM Data Dictionary for tag metadata lookup.
Encoder/decoder for Implicit VR Little Endian transfer syntax.
vr_type
DICOM Value Representation (VR) types.
@ UN
Unknown (variable length)
@ SQ
Sequence of Items (undefined length)
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 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.