Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions index.d.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
declare module 'dicom-parser' {

export type ByteArray = Uint8Array | Buffer;

export interface Fragment {
Expand Down Expand Up @@ -100,19 +100,21 @@ declare module 'dicom-parser' {
byteArrayParser: ByteArrayParser;
position: number;
warnings: string[];

constructor(byteArrayParser: ByteArrayParser, byteArray: ByteArray, position: number);
seek: (offset: number) => void;
readByteStream: (numBytes: number) => ByteStream;
readUint16: () => number;
readUint32: () => number;
readBigUint64: () => BigInt;
readFixedString: (length: number) => string;
}

export interface ByteArrayParser {
readUint16: (byteArray: ByteArray, position: number) => number;
readInt16: (byteArray: ByteArray, position: number) => number;
readUint32: (byteArray: ByteArray, position: number) => number;
readBigUint64: (byteArray: ByteArray, position: number) => BigInt;
readInt32: (byteArray: ByteArray, position: number) => number;
readFloat: (byteArray: ByteArray, position: number) => number;
readDouble: (byteArray: ByteArray, position: number) => number;
Expand Down Expand Up @@ -173,5 +175,5 @@ declare module 'dicom-parser' {
export function readSequenceItemsImplicit(byteStream: ByteStream, element: Element, vrCallback?: vrCallback): void
export function readSequenceItem(byteStream: ByteStream): Pick<Element, 'tag' | 'length' | 'dataOffset'>
export function readTag(byteStream: ByteStream): string

}
30 changes: 30 additions & 0 deletions src/bigEndianByteArrayParser.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
/* global BigInt */

/**
* Internal helper functions for parsing different types from a big-endian byte array
*/
Expand Down Expand Up @@ -77,6 +79,34 @@ export default {
return uint32;
},

/**
* Parses an unsigned int 64 from a big-endian byte array
*
* @param byteArray the byte array to read from
* @param position the position in the byte array to read from
* @returns {*} the parsed unsigned int 64
* @throws error if buffer overread would occur
* @access private
*/
readBigUint64 (byteArray, position) {
if (position < 0) {
throw 'bigEndianByteArrayParser.readBigUint64: position cannot be less than 0';
}

if (position + 8 > byteArray.length) {
throw 'bigEndianByteArrayParser.readBigUint64: attempt to read past end of buffer';
}

var uint64 = 0n;

for (let i = 0; i < 8; i++) {
uint64 <<= 8n;
uint64 |= BigInt(byteArray[position + i]);
}

return uint64;
},

/**
* Parses a signed int 32 from a big-endian byte array
*
Expand Down
15 changes: 15 additions & 0 deletions src/byteStream.js
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,21 @@ export default class ByteStream {
return result;
}

/**
* Parses an unsigned int 64 as BigInt from a byte array and advances
* the position by 8 bytes
*
* @returns {*} the parsed unsigned int 64 (BigInt)
* @throws error if buffer overread would occur
*/
readBigUint64 () {
var result = this.byteArrayParser.readBigUint64(this.byteArray, this.position);

this.position += 8;

return result;
}

/**
* Reads a string of 8-bit characters from an array of bytes and advances
* the position by length bytes. A null terminator will end the string
Expand Down
19 changes: 18 additions & 1 deletion src/dataSet.js
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,23 @@ export default class DataSet {
return undefined;
}

/**
* Finds the element for tag and returns an unsigned int 64 (as a BigInt) if it exists and has data
* @param tag The DICOM tag in the format xGGGGEEEE
* @param index the index of the value in a multivalued element. Default is index 0 if not supplied
* @returns {*} unsigned int 64 (BigInt) or undefined if the attribute is not present or has data of length 0
*/
uint64 (tag, index) {
var element = this.elements[tag];

index = (index !== undefined) ? index : 0;
if (element && element.length !== 0) {
return getByteArrayParser(element, this.byteArrayParser).readBigUint64(this.byteArray, element.dataOffset + (index * 8));
}

return undefined;
}

/**
* Finds the element for tag and returns an signed int 32 if it exists and has data
* @param tag The DICOM tag in the format xGGGGEEEE
Expand Down Expand Up @@ -178,7 +195,7 @@ export default class DataSet {
var element = this.elements[tag];

if( element && element.Value ) return element.Value;

if (element && element.length > 0) {
var fixedString = readFixedString(this.byteArray, element.dataOffset, element.length);

Expand Down
30 changes: 30 additions & 0 deletions src/littleEndianByteArrayParser.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
/* global BigInt */

/**
* Internal helper functions for parsing different types from a little-endian byte array
*/
Expand Down Expand Up @@ -79,6 +81,34 @@ export default {
(byteArray[position + 3] * 256 * 256 * 256));
},

/**
* Parses an unsigned int 64 from a little-endian byte array
*
* @param byteArray the byte array to read from
* @param position the position in the byte array to read from
* @returns {*} the parsed unsigned int 64
* @throws error if buffer overread would occur
* @access private
*/
readBigUint64 (byteArray, position) {
if (position < 0) {
throw 'bigEndianByteArrayParser.readBigUint64: position cannot be less than 0';
}

if (position + 8 > byteArray.length) {
throw 'bigEndianByteArrayParser.readBigUint64: attempt to read past end of buffer';
}

var uint64 = 0n;

for (let i = 7; i >= 0; i--) {
uint64 <<= 8n;
uint64 |= BigInt(byteArray[position + i]);
}

return uint64;
},

/**
* Parses a signed int 32 from a little-endian byte array
*
Expand Down
3 changes: 2 additions & 1 deletion src/readDicomElementExplicit.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ const getDataLengthSizeInBytesForVR = (vr) => {
vr === 'UC' ||
vr === 'UR' ||
vr === 'UT' ||
vr === 'UN') {
vr === 'UN' ||
vr === 'UV') {
return 4;
}

Expand Down
2 changes: 2 additions & 0 deletions src/util/elementToString.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ export default function explicitElementToString (dataSet, element) {
textResult = multiElementToString(element.length / 4, dataSet.uint32);
} else if (vr === 'SL') {
textResult = multiElementToString(element.length / 4, dataSet.int32);
} else if (vr === 'UV') {
textResult = multiElementToString(element.length / 8, dataSet.uint64);
} else if (vr === 'FD') {
textResult = multiElementToString(element.length / 8, dataSet.double);
} else if (vr === 'FL') {
Expand Down
62 changes: 62 additions & 0 deletions test/bigEndianByteArrayParser_test.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
/* global BigInt */

import { expect } from 'chai';
import bigEndianByteArrayParser from '../src/bigEndianByteArrayParser';

Expand Down Expand Up @@ -125,6 +127,66 @@ describe('bigEndianByteArrayParser', () => {

});

describe('#readBigUint64', () => {

it('should return the expected value', () => {
// Arrange
const byteArray = new Uint8Array(32);
byteArray[0] = 0x11;
byteArray[1] = 0x22;
byteArray[2] = 0x33;
byteArray[3] = 0x44;
byteArray[4] = 0x55;
byteArray[5] = 0x66;
byteArray[6] = 0x77;
byteArray[7] = 0x88;

// Act
const uint64 = bigEndianByteArrayParser.readBigUint64(byteArray, 0);

// Assert
expect(uint64).to.equal(BigInt('0x1122334455667788'));
});

it('should return the expected value', () => {
// Arrange
const byteArray = new Uint8Array(8);
byteArray[0] = 0xFF;
byteArray[1] = 0xFF;
byteArray[2] = 0xFF;
byteArray[3] = 0xFF;
byteArray[4] = 0xFF;
byteArray[5] = 0xFF;
byteArray[6] = 0xFF;
byteArray[7] = 0xFF;

// Act
const uint64 = bigEndianByteArrayParser.readBigUint64(byteArray, 0);

// Assert
expect(uint64).to.equal(BigInt('0xFFFFFFFFFFFFFFFF'));
});

it('should throw an exception on buffer overread', () => {
// Arrange
const byteArray = new Uint8Array(32);
const invoker = () => bigEndianByteArrayParser.readBigUint64(byteArray, 25);

// Act / Assert
expect(invoker).to.throw();
});

it('should throw an exception on position < 0', () => {
// Arrange
const byteArray = new Uint8Array(32);
const invoker = () => bigEndianByteArrayParser.readBigUint64(byteArray, -1);

// Act / Assert
expect(invoker).to.throw();
});

});

describe('#readInt32', () => {

it('should return the expected value when reading a null terminated string', () => {
Expand Down
81 changes: 79 additions & 2 deletions test/byteStream_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -309,13 +309,90 @@ describe('ByteStream', () => {

});

describe('#readBigUint64', () => {

it('should return the expected value', () => {
// Arrange
const byteArray = new Uint8Array(32);
byteArray[0] = 0x11;
byteArray[1] = 0x22;
byteArray[2] = 0x33;
byteArray[3] = 0x44;
byteArray[4] = 0x55;
byteArray[5] = 0x66;
byteArray[6] = 0x77;
byteArray[7] = 0x88;
const byteStream = new ByteStream(littleEndianByteArrayParser, byteArray);

// Act
const uint64 = byteStream.readBigUint64();

// Assert
expect(uint64).to.equal(0x8877665544332211n);
});

it('should work with different parser', () => {
// Arrange
const byteArray = new Uint8Array(32);
byteArray[0] = 0x11;
byteArray[1] = 0x22;
byteArray[2] = 0x33;
byteArray[3] = 0x44;
byteArray[4] = 0x55;
byteArray[5] = 0x66;
byteArray[6] = 0x77;
byteArray[7] = 0x88;
const byteStream = new ByteStream(bigEndianByteArrayParser, byteArray);

// Act
const uint64 = byteStream.readBigUint64();

// Assert
expect(uint64).to.equal(0x1122334455667788n);
});

it('should read at end of buffer', () => {
// Arrange
const byteArray = new Uint8Array(8);
byteArray[0] = 0x11;
byteArray[1] = 0x22;
byteArray[2] = 0x33;
byteArray[3] = 0x44;
byteArray[4] = 0x55;
byteArray[5] = 0x66;
byteArray[6] = 0x77;
byteArray[7] = 0x88;
const byteStream = new ByteStream(littleEndianByteArrayParser, byteArray);

// Act
const uint64 = byteStream.readBigUint64();

// Assert
expect(uint64).to.equal(0x8877665544332211n);
});


it('should throw an exception on buffer overread', () => {
// Arrange
const byteArray = new Uint8Array(32);
const byteStream = new ByteStream(littleEndianByteArrayParser, byteArray);
const invoker = () => byteStream.readBigUint64();

byteStream.seek(31);

// Act / Assert
expect(invoker).to.throw();
});

});

describe('#readFixedString', () => {

it('should return the expected value', () => {
// Arrange
const byteArray = new Uint8Array(32);
const str = 'Hello';

for (let i = 0; i < str.length; i++) {
byteArray[i] = str.charCodeAt(i);
}
Expand Down Expand Up @@ -353,7 +430,7 @@ describe('ByteStream', () => {
// Arrange
const byteArray = new Uint8Array(5);
const str = 'Hello';

for (let i = 0; i < str.length; i++) {
byteArray[i] = str.charCodeAt(i);
}
Expand Down
Loading