-
Notifications
You must be signed in to change notification settings - Fork 4
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Expose read/write lengthcode for CDR2 emheaders #20
Changes from 3 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
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 = { | ||
|
@@ -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); | ||
} | ||
|
||
|
@@ -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}`); | ||
|
@@ -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); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not clear to me how someone would decide whether to pass in a lengthCode or use this default fallback. I guess it depends on what they are planning to serialize immediately after the header? Maybe this info should be documented in the doc comments There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. My thoughts here were that people who don't know how to use lengthCodes can use the default we determine that won't modify serialization, or if they understand lengthCodes they can pass their own, and would hopefully understand the affects on serialization for higher numbers. I will update the comment to be more clear on this. |
||
|
||
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)); | ||
snosenzo marked this conversation as resolved.
Show resolved
Hide resolved
|
||
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)); | ||
snosenzo marked this conversation as resolved.
Show resolved
Hide resolved
|
||
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}`); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Did we ever hit this error in the past? Or if not does that mean we are just adding support for this in the writer so that we can use it in our unit tests (we won't actually be using lengthCodes > 4 in production anywhere)? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We really only use this for unit tests, so we haven't ever hit it before. I don't know about downstream users of this writer though. I'm adding this capability to the writer only for unit tests. |
||
} | ||
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; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
export function getLengthCodeForObjectSize(objectSize: number): number { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could be nice if LengthCode was a type? Since it is not actually a freeform number but a specific known set of codes? |
||
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 = { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. could :just: do There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. True, but I like having the const type here to reflect the pairing in the spec |
||
0: 1, | ||
1: 2, | ||
2: 4, | ||
3: 8, | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
😵💫