Skip to content

Commit

Permalink
return lengthcode from emHeaders for PL_CDR2
Browse files Browse the repository at this point in the history
  • Loading branch information
snosenzo committed Nov 14, 2023
1 parent bd8039f commit d6cd8a5
Show file tree
Hide file tree
Showing 4 changed files with 134 additions and 39 deletions.
33 changes: 32 additions & 1 deletion src/CdrReader.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -277,19 +277,50 @@ Object {
[true, 63, 9],
[false, 127, 0xffffffff],
])(
"round trips EMHEADER values with mustUnderstand: %d, id: %d, and size: %d",
"round trips EMHEADER values with mustUnderstand: %d, id: %d, and size: %d without lengthCode",
(mustUnderstand: boolean, id: number, objectSize: number) => {
const writer = new CdrWriter({ kind: EncapsulationKind.PL_CDR2_LE });

writer.emHeader(mustUnderstand, id, objectSize);

const reader = new CdrReader(writer.data);
// don't want to test default assignment of length code here
const { lengthCode, ...headerNoLengthCode } = reader.emHeader();

expect(headerNoLengthCode).toEqual({
objectSize,
id,
mustUnderstand,
});
// should be defined because of CDR2
expect(lengthCode).toBeDefined();
},
);
it.each([
[true, 100, 1, 0], // LC 0, 1 byte
[false, 200, 2, 1], // LC 1, 2 bytes
[false, 1028, 4, 2], // LC 2, 4 bytes
[false, 65, 8, 3], // LC 3, 8 bytes
[true, 63, 9, 4], // LC 4, any size
[false, 127, 0xffffffff, 5], // LC
[false, 65, 12, 6], // LC 6, multiple of 4 bytes
[false, 65, 32, 7], // LC 7, multiple of 8 bytes
[false, 127, 0xffffffff, 5],
])(
"round trips EMHEADER values with mustUnderstand: %d, id: %d, size: %d, and lengthCode: %d",
(mustUnderstand: boolean, id: number, objectSize: number, lengthCode: number) => {
const writer = new CdrWriter({ kind: EncapsulationKind.PL_CDR2_LE });

writer.emHeader(mustUnderstand, id, objectSize, lengthCode);

const reader = new CdrReader(writer.data);
const header = reader.emHeader();

expect(header).toEqual({
objectSize,
id,
mustUnderstand,
lengthCode,
});
},
);
Expand Down
26 changes: 17 additions & 9 deletions src/CdrReader.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { EncapsulationKind } from "./EncapsulationKind";
import { getEncapsulationKindInfo } from "./getEncapsulationKindInfo";
import { isBigEndian } from "./isBigEndian";
import { lengthCodeToObjectSizes } from "./lengthCodes";
import { EXTENDED_PID, SENTINEL_PID } from "./reservedPIDs";

interface Indexable {
Expand Down Expand Up @@ -185,9 +186,10 @@ export class CdrReader {
}

/**
* Reads the member header (EMHEADER) and returns the member ID, mustUnderstand flag, and object size
* Reads the member header (EMHEADER) and returns the member ID, mustUnderstand flag, and object size with optional length code
* The length code is only present in CDR2 and should prompt objectSize to be used in place of sequence length if applicable.
*/
emHeader(): { mustUnderstand: boolean; id: number; objectSize: number } {
emHeader(): { mustUnderstand: boolean; id: number; objectSize: number; lengthCode?: number } {
if (this.isCDR2) {
return this.memberHeaderV2();
} else {
Expand All @@ -196,7 +198,11 @@ export class CdrReader {
}

/** XCDR1 PL_CDR encapsulation parameter header*/
private memberHeaderV1(): { id: number; objectSize: number; mustUnderstand: boolean } {
private memberHeaderV1(): {
id: number;
objectSize: number;
mustUnderstand: boolean;
} {
// 4-byte header with two 16-bit fields
this.align(4);
const idHeader = this.uint16();
Expand Down Expand Up @@ -260,7 +266,12 @@ export class CdrReader {
}
}

private memberHeaderV2(): { id: number; objectSize: number; mustUnderstand: boolean } {
private memberHeaderV2(): {
id: number;
objectSize: number;
mustUnderstand: boolean;
lengthCode: number;
} {
const header = this.uint32();
// EMHEADER = (M_FLAG<<31) + (LC<<28) + M.id
// M is the member of a structure
Expand All @@ -272,7 +283,7 @@ export class CdrReader {

const objectSize = this.emHeaderObjectSize(lengthCode);

return { mustUnderstand, id, objectSize };
return { mustUnderstand, id, objectSize, lengthCode };
}

/** Uses the length code to derive the member object size in
Expand All @@ -282,13 +293,10 @@ export class CdrReader {
// 7.4.3.4.2 Member Header (EMHEADER), Length Code (LC) and NEXTINT
switch (lengthCode) {
case 0:
return 1;
case 1:
return 2;
case 2:
return 4;
case 3:
return 8;
return lengthCodeToObjectSizes[lengthCode];
// LC > 3 -> NEXTINT exists after header
case 4:
case 5:
Expand Down
78 changes: 49 additions & 29 deletions src/CdrWriter.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { EncapsulationKind } from "./EncapsulationKind";
import { getEncapsulationKindInfo } from "./getEncapsulationKindInfo";
import { isBigEndian } from "./isBigEndian";
import { getLengthCodeForObjectSize, lengthCodeToObjectSizes } from "./lengthCodes";
import { EXTENDED_PID, SENTINEL_PID } from "./reservedPIDs";

export type CdrWriterOpts = {
Expand Down Expand Up @@ -184,12 +185,17 @@ export class CdrWriter {
}

/**
* Writes the member header (EMHEADER): mustUnderstand flag, the member ID, and object size
* Writes the member header (EMHEADER): mustUnderstand flag, the member ID, object size, and optional length code for CDR2 emHeaders
* Accomodates for PL_CDR and PL_CDR2 based on the CdrWriter constructor options
*/
emHeader(mustUnderstand: boolean, id: number, objectSize: number): CdrWriter {
emHeader(
mustUnderstand: boolean,
id: number,
objectSize: number,
lengthCode?: number,
): CdrWriter {
return this.isCDR2
? this.memberHeaderV2(mustUnderstand, id, objectSize)
? this.memberHeaderV2(mustUnderstand, id, objectSize, lengthCode)
: this.memberHeaderV1(mustUnderstand, id, objectSize);
}

Expand Down Expand Up @@ -231,7 +237,12 @@ export class CdrWriter {
return this;
}

private memberHeaderV2(mustUnderstand: boolean, id: number, objectSize: number): CdrWriter {
private memberHeaderV2(
mustUnderstand: boolean,
id: number,
objectSize: number,
lengthCode?: number,
): CdrWriter {
if (id > 0x0fffffff) {
// first byte is used for M_FLAG and LC
throw Error(`Member ID ${id} is too large. Max value is ${0x0fffffff}`);
Expand All @@ -241,37 +252,46 @@ export class CdrWriter {
// M_FLAG is the value of the Must Understand option for the member
const mustUnderstandFlag = mustUnderstand ? 1 << 31 : 0;
// LC is the value of the Length Code for the member.
let lengthCode: number | undefined;
switch (objectSize) {
const finalLengthCode = lengthCode ?? getLengthCodeForObjectSize(objectSize);

const header = mustUnderstandFlag | (finalLengthCode << 28) | id;

this.uint32(header);

switch (finalLengthCode) {
case 0:
case 1:
lengthCode = 0;
break;
case 2:
lengthCode = 1;
case 3: {
const shouldBeSize = lengthCodeToObjectSizes[finalLengthCode];
if (objectSize !== shouldBeSize) {
throw new Error(
`Cannot write a length code ${finalLengthCode} header with an object size not equal to ${shouldBeSize}`,
);
}
break;
}
// When the length code is > 3 the header is 8 bytes because of the NEXTINT value storing the object size
case 4:
lengthCode = 2;
case 5:
this.uint32(objectSize);
break;
case 8:
lengthCode = 3;
case 6:
if (objectSize % 4 !== 0) {
throw new Error(
"Cannot write a length code 6 header with an object size that is not a multiple of 4",
);
}
this.uint32(Math.floor(objectSize / 4));
break;
case 7:
if (objectSize % 8 !== 0) {
throw new Error(
"Cannot write a length code 7 header with an object size that is not a multiple of 8",
);
}
this.uint32(Math.floor(objectSize / 8));
break;
}

if (lengthCode == undefined) {
// Not currently supporting writing of lengthCodes > 4
if (objectSize > 0xffffffff) {
throw Error(`Object size ${objectSize} for EMHEADER too large. Max size is ${0xfffffffff}`);
}
lengthCode = 4;
}

const header = mustUnderstandFlag | (lengthCode << 28) | id;

this.uint32(header);

// When the length code is > 3 the header is 8 bytes because of the NEXTINT value storing the object size
if (lengthCode >= 4) {
this.uint32(objectSize);
}

return this;
Expand Down
36 changes: 36 additions & 0 deletions src/lengthCodes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
export function getLengthCodeForObjectSize(objectSize: number): number {
let defaultLengthCode;

switch (objectSize) {
case 1:
defaultLengthCode = 0;
break;
case 2:
defaultLengthCode = 1;
break;
case 4:
defaultLengthCode = 2;
break;
case 8:
defaultLengthCode = 3;
break;
}

if (defaultLengthCode == undefined) {
// Not currently supporting writing of lengthCodes > 4
if (objectSize > 0xffffffff) {
throw Error(
`Object size ${objectSize} for EMHEADER too large without specifying length code. Max size is ${0xffffffff}`,
);
}
defaultLengthCode = 4;
}
return defaultLengthCode;
}

export const lengthCodeToObjectSizes = {
0: 1,
1: 2,
2: 4,
3: 8,
};

0 comments on commit d6cd8a5

Please sign in to comment.