Skip to content
Merged
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
10 changes: 9 additions & 1 deletion src/logger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,15 @@ function processData(data: Uint8Array | string, encoding?: BufferEncoding) {
let decodedData;
try {
if (data instanceof Uint8Array) {
decodedData = Buffer.from(data.buffer).toString();
// Decode only the bytes the view actually spans. A Uint8Array (e.g. a
// Buffer carved from Node's shared pool) is often a view into a larger
// ArrayBuffer, so reading `data.buffer` directly would include unrelated
// bytes outside the view's byteOffset/byteLength range.
decodedData = Buffer.from(
data.buffer,
data.byteOffset,
data.byteLength,
).toString();
} else {
decodedData = Buffer.from(data, encoding).toString();
}
Expand Down
12 changes: 12 additions & 0 deletions test/logger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,18 @@ describe('getModifiedData', () => {
assert.equal(modifiedData, expectedTextOutput);
});

it('uint8Array that is a view into a larger (pooled) buffer', () => {
// Node.js allocates most Buffers from a shared internal pool, so a Buffer
// written to stdout/stderr is frequently a view with a non-zero byteOffset
// into a much larger ArrayBuffer. The logger must honor the view's
// byteOffset/byteLength rather than decoding the entire backing buffer.
const pool = Buffer.alloc(64, 0x2e /* '.' */);
const view = pool.subarray(8, 8 + sampleText.length);
view.write(sampleText);
const modifiedData = getModifiedData(view);
assert.equal(modifiedData, expectedTextOutput);
});

it('simple text with error', () => {
const modifiedData = getModifiedData(sampleText, undefined, true);
const expectedOutput =
Expand Down
Loading