Skip to content

Commit 62d3580

Browse files
authored
Merge pull request #52 from jcupitt/fix-vm-zero
Fix vm zero
2 parents 0bec5e6 + bb6c1e7 commit 62d3580

File tree

6 files changed

+122
-15
lines changed

6 files changed

+122
-15
lines changed

meson.build

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ project(
88
],
99
license : 'MIT',
1010
meson_version : '>=0.50',
11-
version : '0.2.1',
11+
version : '0.3.0'
1212
)
1313
if not meson.is_subproject()
1414
meson.add_dist_script(

src/dicom-data.c

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -216,13 +216,13 @@ void dcm_element_destroy(DcmElement *element)
216216

217217
uint16_t dcm_element_get_group_number(const DcmElement *element)
218218
{
219-
return (uint16_t)(element->tag >> 16);
219+
return element->tag >> 16;
220220
}
221221

222222

223223
uint16_t dcm_element_get_element_number(const DcmElement *element)
224224
{
225-
return (uint16_t)(element->tag);
225+
return element->tag & 0xffff;
226226
}
227227

228228

@@ -1141,6 +1141,8 @@ void dcm_element_print(const DcmElement *element, int indentation)
11411141
keyword,
11421142
dcm_dict_str_from_vr(element->vr));
11431143
} else {
1144+
// private tag, or unknown public tag
1145+
// in any case, we can't display the keyword
11441146
printf("%*.*s (%04X,%04X) | %s",
11451147
num_indent,
11461148
num_indent,

src/dicom-dict.c

Lines changed: 27 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -304,6 +304,7 @@ static const struct _DcmAttribute attribute_table[] = {
304304
{0X00041512, DCM_VR_TAG_UI, "ReferencedTransferSyntaxUIDInFile"},
305305
{0X0004151A, DCM_VR_TAG_UI, "ReferencedRelatedGeneralSOPClassUIDInFile"},
306306
{0X00041600, DCM_VR_TAG_UL, "NumberOfReferences"},
307+
{0X00080000, DCM_VR_TAG_UL, "GenericGroupLength"},
307308
{0X00080001, DCM_VR_TAG_UL, "LengthToEnd"},
308309
{0X00080005, DCM_VR_TAG_CS, "SpecificCharacterSet"},
309310
{0X00080006, DCM_VR_TAG_SQ, "LanguageCodeSequence"},
@@ -523,6 +524,7 @@ static const struct _DcmAttribute attribute_table[] = {
523524
{0X00089458, DCM_VR_TAG_SQ, "FrameDisplaySequence"},
524525
{0X00089459, DCM_VR_TAG_FL, "RecommendedDisplayFrameRateInFloat"},
525526
{0X00089460, DCM_VR_TAG_CS, "SkipFrameRangeFlag"},
527+
{0X00090010, DCM_VR_TAG_LO, "PrivateCreator"},
526528
{0X00100010, DCM_VR_TAG_PN, "PatientName"},
527529
{0X00100020, DCM_VR_TAG_LO, "PatientID"},
528530
{0X00100021, DCM_VR_TAG_LO, "IssuerOfPatientID"},
@@ -5280,12 +5282,20 @@ static const struct _DcmAttribute *attribute_from_tag(uint32_t tag)
52805282

52815283
dcm_init();
52825284

5285+
// tags with element number 0 are generic group length tags ... map all of
5286+
// these (except 0000,0000) to tag 0008,0000 (GenericGroupLength)
5287+
if (tag != 0 &&
5288+
(tag & 0xffff) == 0) {
5289+
tag = 0x00080000;
5290+
}
5291+
52835292
HASH_FIND_INT(attribute_from_tag_dict, &tag, entry);
52845293

52855294
return (const struct _DcmAttribute *)entry;
52865295
}
52875296

52885297

5298+
// this will also fail for unknown or retired public tags
52895299
bool dcm_is_public_tag(uint32_t tag)
52905300
{
52915301
return attribute_from_tag(tag) != NULL;
@@ -5311,25 +5321,32 @@ DcmVR dcm_vr_from_tag(uint32_t tag)
53115321
{
53125322
const struct _DcmAttribute *attribute;
53135323

5314-
if ((attribute = attribute_from_tag(tag)) &&
5315-
dcm_dict_str_from_vr((DcmVR) attribute->vr_tag)) {
5316-
return (DcmVR) attribute->vr_tag;
5324+
if (!(attribute = attribute_from_tag(tag))) {
5325+
return DCM_VR_ERROR;
53175326
}
53185327

5319-
return DCM_VR_ERROR;
5328+
return (DcmVR) attribute->vr_tag;
53205329
}
53215330

53225331

53235332
bool dcm_is_valid_vr_for_tag(DcmVR vr, uint32_t tag)
53245333
{
5325-
const struct _DcmAttribute *attribute = attribute_from_tag(tag);
5334+
// always fail for illegal VRs
5335+
if (vr < 0 || vr >= DCM_VR_LAST) {
5336+
return false;
5337+
}
53265338

5339+
// private tags are unknown to us and can have any legal VR
5340+
if (dcm_is_private_tag(tag)) {
5341+
return true;
5342+
}
5343+
5344+
const struct _DcmAttribute *attribute = attribute_from_tag(tag);
53275345
if (attribute == NULL) {
5328-
// unknown tag
5329-
return false;
5330-
} else if (vr < 0 || vr >= DCM_VR_LAST) {
5331-
// not a VR
5332-
return false;
5346+
// unknown public tag ... we don't include retired tags in our
5347+
// dictionary, so we can't check them, but we don't want to fail
5348+
// for them either
5349+
return true;
53335350
} else if (vr == (DcmVR) attribute->vr_tag) {
53345351
// trivially equal
53355352
return true;

src/dicom-file.c

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -353,12 +353,15 @@ static DcmElement *read_element_header(DcmError **error,
353353

354354
DcmVR vr;
355355
if (implicit) {
356+
// this can be an ambiguious VR, eg. pixeldata is allowed in implicit
357+
// mode and has to be disambiguated later from other tags
356358
vr = dcm_vr_from_tag(tag);
357359
if (vr == DCM_VR_ERROR) {
358360
dcm_error_set(error, DCM_ERROR_CODE_PARSE,
359361
"Reading of Data Element header failed",
360362
"Tag %08X not allowed in implicit mode", tag);
361363
}
364+
362365
if (!read_uint32(error, filehandle, length, position)) {
363366
return NULL;
364367
}
@@ -374,7 +377,7 @@ static DcmElement *read_element_header(DcmError **error,
374377
if (!dcm_is_valid_vr_for_tag(vr, tag)) {
375378
dcm_error_set(error, DCM_ERROR_CODE_PARSE,
376379
"Reading of Data Element header failed",
377-
"Unknown or mismatched VR %s", vr_str);
380+
"Tag %08X cannot have VR '%s'", tag, vr_str);
378381
return NULL;
379382
}
380383

src/dicom.c

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,18 @@
2626

2727
void *dcm_calloc(DcmError **error, size_t n, size_t size)
2828
{
29-
void *result = calloc(n, size);
29+
/* malloc(0) behaviour depends on the platform heap implementation. It can
30+
* return either a valid pointer that can't be dereferenced, or NULL.
31+
*
32+
* We need to be able to support dcm_calloc(0), since VM == 0 is allowed,
33+
* but we can't return NULL in this case, since that's how we detect
34+
* out of memory.
35+
*
36+
* Instead, force n == 0 to n == 1. This means we will always get a valid
37+
* pointer from calloc, a NULL return always means out of memory, and we
38+
* can always free the result.
39+
*/
40+
void *result = calloc(n == 0 ? 1 : n, size);
3041
if (!result) {
3142
dcm_error_set(error, DCM_ERROR_CODE_NOMEM,
3243
"Out of memory",

tests/check_dicom.c

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,27 @@ START_TEST(test_element_CS_multivalue)
294294
END_TEST
295295

296296

297+
START_TEST(test_element_CS_multivalue_empty)
298+
{
299+
uint32_t tag = 0x00080008;
300+
uint32_t vm = 0;
301+
302+
char **values = malloc(vm * sizeof(char *));
303+
304+
DcmElement *element = dcm_element_create(NULL, tag, DCM_VR_CS);
305+
(void) dcm_element_set_value_string_multi(NULL, element, values, vm, true);
306+
307+
ck_assert_int_eq(dcm_element_get_tag(element), tag);
308+
ck_assert_int_eq(dcm_element_get_vr(element), DCM_VR_CS);
309+
ck_assert_int_eq(dcm_element_get_length(element),
310+
compute_length_of_string_value_multi(values, vm));
311+
ck_assert_int_eq(dcm_element_is_multivalued(element), false);
312+
313+
dcm_element_destroy(element);
314+
}
315+
END_TEST
316+
317+
297318
START_TEST(test_element_DS)
298319
{
299320
uint32_t tag = 0x0040072A;
@@ -474,6 +495,56 @@ START_TEST(test_element_US)
474495
END_TEST
475496

476497

498+
START_TEST(test_element_US_multivalue)
499+
{
500+
uint32_t tag = 0x00280010;
501+
uint16_t value[] = {512, 513, 514, 515};
502+
uint32_t vm = sizeof(value) / sizeof(value[0]);
503+
504+
DcmElement *element = dcm_element_create(NULL, tag, DCM_VR_US);
505+
(void) dcm_element_set_value_numeric_multi(NULL,
506+
element, (int*) value, vm, false);
507+
508+
ck_assert_int_eq(dcm_element_get_tag(element), tag);
509+
ck_assert_int_eq(dcm_element_get_vr(element), DCM_VR_US);
510+
ck_assert_int_eq(dcm_element_get_length(element), sizeof(value));
511+
ck_assert_int_eq(dcm_element_is_multivalued(element), true);
512+
513+
for (uint32_t i = 0; i < vm; i++) {
514+
int64_t integer;
515+
(void) dcm_element_get_value_integer(NULL, element, i, &integer);
516+
ck_assert_int_eq(value[i], integer);
517+
}
518+
519+
dcm_element_print(element, 0);
520+
521+
dcm_element_destroy(element);
522+
}
523+
END_TEST
524+
525+
526+
START_TEST(test_element_US_multivalue_empty)
527+
{
528+
uint32_t tag = 0x00280010;
529+
uint16_t value[] = {};
530+
uint32_t vm = sizeof(value) / sizeof(value[0]);
531+
532+
DcmElement *element = dcm_element_create(NULL, tag, DCM_VR_US);
533+
(void) dcm_element_set_value_numeric_multi(NULL,
534+
element, (int*) &value, vm, false);
535+
536+
ck_assert_int_eq(dcm_element_get_tag(element), tag);
537+
ck_assert_int_eq(dcm_element_get_vr(element), DCM_VR_US);
538+
ck_assert_int_eq(dcm_element_get_length(element), sizeof(value));
539+
ck_assert_int_eq(dcm_element_is_multivalued(element), false);
540+
541+
dcm_element_print(element, 0);
542+
543+
dcm_element_destroy(element);
544+
}
545+
END_TEST
546+
547+
477548
START_TEST(test_sequence)
478549
{
479550
DcmElement *element;
@@ -738,13 +809,16 @@ static Suite *create_data_suite(void)
738809
tcase_add_test(element_case, test_element_AE);
739810
tcase_add_test(element_case, test_element_AS);
740811
tcase_add_test(element_case, test_element_CS_multivalue);
812+
tcase_add_test(element_case, test_element_CS_multivalue_empty);
741813
tcase_add_test(element_case, test_element_DS);
742814
tcase_add_test(element_case, test_element_IS);
743815
tcase_add_test(element_case, test_element_ST);
744816
tcase_add_test(element_case, test_element_SQ);
745817
tcase_add_test(element_case, test_element_SQ_empty);
746818
tcase_add_test(element_case, test_element_UI);
747819
tcase_add_test(element_case, test_element_US);
820+
tcase_add_test(element_case, test_element_US_multivalue);
821+
tcase_add_test(element_case, test_element_US_multivalue_empty);
748822
suite_add_tcase(suite, element_case);
749823

750824
TCase *dataset_case = tcase_create("dataset");

0 commit comments

Comments
 (0)