Skip to content
This repository was archived by the owner on May 27, 2026. It is now read-only.

Commit ce10a8e

Browse files
committed
STYLE: Replace fixed-width Scanco field magic numbers with constexpr
The on-disk Scanco header has fixed-width text fields (16, 40, 64 bytes for Version/PatientName/CalibrationData and 16 for RescaleUnits; 8 bytes for the encoded VMS date) and the in-memory buffers are sized two bytes wider so a NUL terminator always fits. Until now those widths appeared as bare numeric literals throughout the read/write/manipulate paths, with the disk-vs-buffer distinction only implicit in pairings like 16/18, 40/42, 64/66. Introduce a ScancoHeaderField namespace in itkScancoDataManipulation.h holding constexpr widths for each field (DiskWidth + BufferSize pairs plus EncodedDateDiskWidth and DateStringBufferSize), and use them at all the read/write call sites in itkScancoImageIO, itkISQHeaderIO, itkAIMHeaderIO, and itkScancoDataManipulation. No semantic change.
1 parent 41444cc commit ce10a8e

4 files changed

Lines changed: 61 additions & 32 deletions

File tree

include/itkScancoDataManipulation.h

Lines changed: 31 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -30,14 +30,37 @@ struct itkScancoPixelData
3030
int m_PixelType;
3131
};
3232

33+
// Scanco header fixed-width fields. The on-disk format stores each text field
34+
// at a fixed binary width (no NUL terminator); the in-memory buffer is two
35+
// bytes wider so that decoded strings are always NUL-terminated.
36+
namespace ScancoHeaderField
37+
{
38+
inline constexpr std::size_t VersionDiskWidth = 16;
39+
inline constexpr std::size_t VersionBufferSize = VersionDiskWidth + 2;
40+
41+
inline constexpr std::size_t PatientNameDiskWidth = 40;
42+
inline constexpr std::size_t PatientNameBufferSize = PatientNameDiskWidth + 2;
43+
44+
inline constexpr std::size_t RescaleUnitsDiskWidth = 16;
45+
inline constexpr std::size_t RescaleUnitsBufferSize = RescaleUnitsDiskWidth + 2;
46+
47+
inline constexpr std::size_t CalibrationDataDiskWidth = 64;
48+
inline constexpr std::size_t CalibrationDataBufferSize = CalibrationDataDiskWidth + 2;
49+
50+
// ISQ stores creation/modification timestamps as an 8-byte binary date; in
51+
// memory both ends decode to a 32-byte human-readable date string.
52+
inline constexpr std::size_t EncodedDateDiskWidth = 8;
53+
inline constexpr std::size_t DateStringBufferSize = 32;
54+
} // namespace ScancoHeaderField
55+
3356
struct itkScancoHeaderData
3457
{
35-
char m_Version[18]; // Version string, e.g., "AIMDATA_V020 "
36-
char m_PatientName[42];
58+
char m_Version[ScancoHeaderField::VersionBufferSize]; // e.g., "AIMDATA_V020 "
59+
char m_PatientName[ScancoHeaderField::PatientNameBufferSize];
3760
int m_PatientIndex;
3861
int m_ScannerID;
39-
char m_CreationDate[32];
40-
char m_ModificationDate[32];
62+
char m_CreationDate[ScancoHeaderField::DateStringBufferSize];
63+
char m_ModificationDate[ScancoHeaderField::DateStringBufferSize];
4164
int m_ScanDimensionsPixels[3];
4265
double m_ScanDimensionsPhysical[3];
4366
double m_SliceThickness; // Slice thickness in mm
@@ -59,8 +82,8 @@ struct itkScancoHeaderData
5982
double m_Energy;
6083
double m_Intensity;
6184
int m_RescaleType;
62-
char m_RescaleUnits[18];
63-
char m_CalibrationData[66];
85+
char m_RescaleUnits[ScancoHeaderField::RescaleUnitsBufferSize];
86+
char m_CalibrationData[ScancoHeaderField::CalibrationDataBufferSize];
6487
double m_RescaleSlope;
6588
double m_RescaleIntercept;
6689
double m_MuWater;
@@ -96,7 +119,7 @@ constexpr int ScancoHeaderBlockSize = 512;
96119
* 2 if AIM 020, 3 if AIM 030.
97120
*/
98121
int
99-
CheckVersion(const char header[16]);
122+
CheckVersion(const char header[ScancoHeaderField::VersionDiskWidth]);
100123

101124
/** Convert char data to 32-bit int (little-endian).
102125
*
@@ -203,7 +226,7 @@ EncodeCurrentDate(void * target);
203226
* \param dateString A string in the format "YYYY-MM-DD HH:MM:SS.mmm"
204227
*/
205228
void
206-
EncodeDateFromString(void * target, const char dateString[32]);
229+
EncodeDateFromString(void * target, const char dateString[ScancoHeaderField::DateStringBufferSize]);
207230

208231
/** Strip a string by removing trailing whitespace.
209232
*

src/itkAIMHeaderIO.cxx

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ AIMHeaderIO::ReadHeader(std::ifstream & infile)
130130
case static_cast<int>(ScancoFileVersions::AIM_030):
131131
this->m_IntSize = 8; // AIM v030 uses 64-bit [8 byte] integers
132132
strcpy(this->m_HeaderData->m_Version, AIM030String);
133-
bytesRead += 16; // Skip the version string
133+
bytesRead += ScancoHeaderField::VersionDiskWidth; // Skip the version string
134134
break;
135135

136136
default:
@@ -153,10 +153,12 @@ AIMHeaderIO::ReadHeader(std::ifstream & infile)
153153
// Allocate more space for the header and read the rest into the raw header
154154
delete[] headerBytes;
155155
delete[] this->m_HeaderData->m_RawHeader;
156-
headerBytes = new char[headerSize + (versionType == static_cast<int>(ScancoFileVersions::AIM_030) ? 16 : 0)];
156+
const size_t v030Extra =
157+
(versionType == static_cast<int>(ScancoFileVersions::AIM_030)) ? ScancoHeaderField::VersionDiskWidth : 0;
158+
headerBytes = new char[headerSize + v030Extra];
157159
// headerSize does not include the version string from v030
158160
infile.seekg(0, std::ios::beg);
159-
infile.read(headerBytes, headerSize + (versionType == static_cast<int>(ScancoFileVersions::AIM_030) ? 16 : 0));
161+
infile.read(headerBytes, headerSize + v030Extra);
160162
}
161163
// we have read the full header, save to our data structure
162164
this->m_HeaderData->m_RawHeader = headerBytes;

src/itkISQHeaderIO.cxx

Lines changed: 21 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -30,13 +30,13 @@ typedef EncodedByte EncodedDouble[8];
3030

3131
struct ISQEncodedPreHeader
3232
{
33-
EncodedByte m_Version[16];
33+
EncodedByte m_Version[ScancoHeaderField::VersionDiskWidth];
3434
EncodedInt m_DataType;
3535
EncodedInt m_ImageSizeBytes;
3636
EncodedInt m_ImageSizeBlocks;
3737
EncodedInt m_PatientIndex;
3838
EncodedInt m_ScannerID;
39-
EncodedByte m_CreationDate[8];
39+
EncodedByte m_CreationDate[ScancoHeaderField::EncodedDateDiskWidth];
4040
EncodedIntTuple m_PixelDimensions;
4141
EncodedIntTuple m_PhysicalDimensions;
4242
};
@@ -59,7 +59,7 @@ struct ISQEncodedHeaderBlock
5959
EncodedInt m_Site;
6060
EncodedInt m_ReferenceLine;
6161
EncodedInt m_ReconstructionAlg;
62-
EncodedByte m_PatientName[40] = { 0 };
62+
EncodedByte m_PatientName[ScancoHeaderField::PatientNameDiskWidth] = { 0 };
6363
EncodedInt m_Energy = { 0 }; /* V */
6464
EncodedInt m_Intensity = { 0 }; /* uA */
6565
EncodedByte m_Fill[83 * 4] = { 0 };
@@ -73,7 +73,7 @@ struct RADEncodedHeaderBlock
7373
EncodedInt m_DataMin;
7474
EncodedInt m_DataMax;
7575
EncodedInt m_MuScaling;
76-
EncodedByte m_PatientName[40] = { 0 };
76+
EncodedByte m_PatientName[ScancoHeaderField::PatientNameDiskWidth] = { 0 };
7777
EncodedInt m_ZPosition;
7878
EncodedByte m_UnknownFill[4];
7979
EncodedInt m_SampleTime;
@@ -89,18 +89,18 @@ struct RADEncodedHeaderBlock
8989
struct ISQCalibrationHeaderBlock
9090
{
9191
EncodedByte m_Fill1[136];
92-
EncodedByte m_Title[16];
92+
EncodedByte m_Title[ScancoHeaderField::VersionDiskWidth];
9393
EncodedInt m_CalHeaderSize; // size of calibration header in blocks
9494
EncodedByte m_Fill2[356];
9595

9696
EncodedByte m_Fill3[28];
97-
EncodedByte m_CalibrationData[64];
97+
EncodedByte m_CalibrationData[ScancoHeaderField::CalibrationDataDiskWidth];
9898
EncodedByte m_Fill4[420];
9999

100100
EncodedByte m_Fill5[120];
101101
EncodedInt m_RescaleType;
102102
EncodedByte m_Fill6[12];
103-
EncodedByte m_RescaleUnits[16];
103+
EncodedByte m_RescaleUnits[ScancoHeaderField::RescaleUnitsDiskWidth];
104104
EncodedDouble m_RescaleSlope;
105105
EncodedDouble m_RescaleIntercept;
106106
EncodedByte m_Fill7[8];
@@ -133,8 +133,8 @@ ISQHeaderIO::ReadHeader(std::ifstream & infile)
133133
throw std::runtime_error("ISQHeaderIO: cannot read file, is not an ISQ.");
134134
}
135135

136-
memcpy(this->m_HeaderData->m_Version, imageInfo->m_Version, 16);
137-
this->m_HeaderData->m_Version[16] = '\0'; // ensure null-terminated string
136+
memcpy(this->m_HeaderData->m_Version, imageInfo->m_Version, ScancoHeaderField::VersionDiskWidth);
137+
this->m_HeaderData->m_Version[ScancoHeaderField::VersionDiskWidth] = '\0'; // ensure null-terminated string
138138
this->m_HeaderData->m_PixelData.m_ComponentType = DecodeInt(imageInfo->m_DataType);
139139
// Ignore image blocks and bytes size information
140140
// This will be re-populated on write
@@ -199,7 +199,7 @@ ISQHeaderIO::WriteHeader(std::ofstream & outfile, unsigned long imageSize)
199199

200200
ISQEncodedHeaderBlock header = { 0 };
201201

202-
PadString(header.m_PreHeader.m_Version, this->m_HeaderData->m_Version, 16);
202+
PadString(header.m_PreHeader.m_Version, this->m_HeaderData->m_Version, ScancoHeaderField::VersionDiskWidth);
203203
EncodeInt(3, header.m_PreHeader.m_DataType);
204204
EncodeInt(imageSize, header.m_PreHeader.m_ImageSizeBytes);
205205
EncodeInt(imageSize / ScancoHeaderBlockSize, header.m_PreHeader.m_ImageSizeBlocks);
@@ -234,7 +234,7 @@ ISQHeaderIO::WriteHeader(std::ofstream & outfile, unsigned long imageSize)
234234
EncodeInt((int)(this->m_HeaderData->m_Site), header.m_Site);
235235
EncodeInt((int)(this->m_HeaderData->m_ReferenceLine * 1e3), header.m_ReferenceLine);
236236
EncodeInt((int)(this->m_HeaderData->m_ReconstructionAlg), header.m_ReconstructionAlg);
237-
PadString(header.m_PatientName, this->m_HeaderData->m_PatientName, 40);
237+
PadString(header.m_PatientName, this->m_HeaderData->m_PatientName, ScancoHeaderField::PatientNameDiskWidth);
238238
EncodeInt((int)(this->m_HeaderData->m_Energy * 1e3), header.m_Energy);
239239
EncodeInt((int)(this->m_HeaderData->m_Intensity * 1e3), header.m_Intensity);
240240
const std::size_t fillSize = 83 * 4;
@@ -318,7 +318,7 @@ ISQHeaderIO::ReadRADHeader(RADEncodedHeaderBlock * headerData)
318318
this->m_HeaderData->m_DataRange[0] = DecodeInt(headerData->m_DataMin);
319319
this->m_HeaderData->m_DataRange[1] = DecodeInt(headerData->m_DataMax);
320320
this->m_HeaderData->m_MuScaling = DecodeInt(headerData->m_MuScaling);
321-
StripString(this->m_HeaderData->m_PatientName, headerData->m_PatientName, 40);
321+
StripString(this->m_HeaderData->m_PatientName, headerData->m_PatientName, ScancoHeaderField::PatientNameDiskWidth);
322322
this->m_HeaderData->m_ZPosition = DecodeInt(headerData->m_ZPosition) * 1e-3;
323323
this->m_HeaderData->m_SampleTime = DecodeInt(headerData->m_SampleTime) * 1e-3;
324324
this->m_HeaderData->m_Energy = DecodeInt(headerData->m_Energy) * 1e-3;
@@ -353,7 +353,7 @@ ISQHeaderIO::ReadISQHeader(ISQEncodedHeaderBlock * headerData)
353353
this->m_HeaderData->m_Site = DecodeInt(headerData->m_Site);
354354
this->m_HeaderData->m_ReferenceLine = DecodeInt(headerData->m_ReferenceLine) * 1e-3;
355355
this->m_HeaderData->m_ReconstructionAlg = DecodeInt(headerData->m_ReconstructionAlg);
356-
StripString(this->m_HeaderData->m_PatientName, headerData->m_PatientName, 40);
356+
StripString(this->m_HeaderData->m_PatientName, headerData->m_PatientName, ScancoHeaderField::PatientNameDiskWidth);
357357
this->m_HeaderData->m_Energy = DecodeInt(headerData->m_Energy) * 1e-3;
358358
this->m_HeaderData->m_Intensity = DecodeInt(headerData->m_Intensity) * 1e-3;
359359
this->m_HeaderSize = (DecodeInt(headerData->m_DataOffset) + 1) * 512;
@@ -413,11 +413,14 @@ ISQHeaderIO::ReadExtendedHeader(const char * buffer, unsigned long length)
413413
if (calHeader && calHeaderSize >= 1024)
414414
{
415415
// Read Calibration data from header
416-
StripString(this->m_HeaderData->m_CalibrationData, calHeader->m_CalibrationData, 64);
416+
StripString(this->m_HeaderData->m_CalibrationData,
417+
calHeader->m_CalibrationData,
418+
ScancoHeaderField::CalibrationDataDiskWidth);
417419
// std::string calFile(h + 112, 256);
418420
// std::string s3(h + 376, 256);
419421
this->m_HeaderData->m_RescaleType = DecodeInt(calHeader->m_RescaleType);
420-
StripString(this->m_HeaderData->m_RescaleUnits, calHeader->m_RescaleUnits, 16);
422+
StripString(
423+
this->m_HeaderData->m_RescaleUnits, calHeader->m_RescaleUnits, ScancoHeaderField::RescaleUnitsDiskWidth);
421424
// std::string s5(h + 700, 16);
422425
// std::string calFilter(h + 772, 16);
423426
this->m_HeaderData->m_RescaleSlope = DecodeDouble(calHeader->m_RescaleSlope);
@@ -443,12 +446,13 @@ ISQHeaderIO::WriteExtendedHeader(std::ofstream & outfile)
443446

444447
// Next block adds calibration data
445448
memcpy(calHeader.m_Title, "Calibration ", 16);
446-
PadString(calHeader.m_CalibrationData, this->m_HeaderData->m_CalibrationData, 64);
449+
PadString(
450+
calHeader.m_CalibrationData, this->m_HeaderData->m_CalibrationData, ScancoHeaderField::CalibrationDataDiskWidth);
447451
EncodeInt(2, calHeader.m_CalHeaderSize);
448452

449453
// Last block adds density scaling data:
450454
EncodeInt((int)this->m_HeaderData->m_RescaleType, calHeader.m_RescaleType);
451-
PadString(calHeader.m_RescaleUnits, this->m_HeaderData->m_RescaleUnits, 16);
455+
PadString(calHeader.m_RescaleUnits, this->m_HeaderData->m_RescaleUnits, ScancoHeaderField::RescaleUnitsDiskWidth);
452456
EncodeDouble(this->m_HeaderData->m_RescaleSlope, calHeader.m_RescaleSlope);
453457
EncodeDouble(this->m_HeaderData->m_RescaleIntercept, calHeader.m_RescaleIntercept);
454458
EncodeDouble(this->m_HeaderData->m_MuWater, calHeader.m_MuWater);

src/itkScancoDataManipulation.cxx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,11 +44,11 @@ static constexpr const char * monthStrings[] = { "XXX", "JAN", "FEB", "MAR", "AP
4444
"JUL", "AUG", "SEP", "OCT", "NOV", "DEC" };
4545

4646
int
47-
CheckVersion(const char header[16])
47+
CheckVersion(const char header[ScancoHeaderField::VersionDiskWidth])
4848
{
4949
int fileType = 0;
5050

51-
if (strncmp(header, "CTDATA-HEADER_V1", 16) == 0)
51+
if (strncmp(header, "CTDATA-HEADER_V1", ScancoHeaderField::VersionDiskWidth) == 0)
5252
{
5353
fileType = 1;
5454
}
@@ -266,7 +266,7 @@ GetCurrentDateString(void * target)
266266
void
267267
EncodeCurrentDate(void * target)
268268
{
269-
char * dateString = new char[32];
269+
char * dateString = new char[ScancoHeaderField::DateStringBufferSize];
270270
GetCurrentDateString(dateString);
271271
EncodeDateFromString(target, dateString);
272272
delete[] dateString;
@@ -295,7 +295,7 @@ julianDayFromDate(int year, int month, int day)
295295
}
296296

297297
void
298-
EncodeDateFromString(void * target, const char dateString[32])
298+
EncodeDateFromString(void * target, const char dateString[ScancoHeaderField::DateStringBufferSize])
299299
{
300300
int year, day, hour, minute, second, millis = 0;
301301
char monthStr[4];

0 commit comments

Comments
 (0)