-
Notifications
You must be signed in to change notification settings - Fork 5.4k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix(ext/node): Add fs.readv, fs.readvSync (#23166)
Part of #18218. Implements `fs.readv` and `fs.readvSync` and enables the corresponding `node_compat` tests.
- Loading branch information
1 parent
9aef08b
commit 0a5e4cb
Showing
8 changed files
with
341 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,130 @@ | ||
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. | ||
|
||
// TODO(petamoriken): enable prefer-primordials for node polyfills | ||
// deno-lint-ignore-file prefer-primordials | ||
|
||
import { | ||
ERR_INVALID_ARG_TYPE, | ||
type ErrnoException, | ||
} from "ext:deno_node/internal/errors.ts"; | ||
import { | ||
getValidatedFd, | ||
validateBufferArray, | ||
} from "ext:deno_node/internal/fs/utils.mjs"; | ||
import { maybeCallback } from "ext:deno_node/_fs/_fs_common.ts"; | ||
import { validateInteger } from "ext:deno_node/internal/validators.mjs"; | ||
import * as io from "ext:deno_io/12_io.js"; | ||
import * as fs from "ext:deno_fs/30_fs.js"; | ||
|
||
type Callback = ( | ||
err: ErrnoException | null, | ||
bytesRead: number, | ||
buffers: readonly ArrayBufferView[], | ||
) => void; | ||
|
||
export function readv( | ||
fd: number, | ||
buffers: readonly ArrayBufferView[], | ||
callback: Callback, | ||
): void; | ||
export function readv( | ||
fd: number, | ||
buffers: readonly ArrayBufferView[], | ||
position: number | Callback, | ||
callback?: Callback, | ||
): void { | ||
if (typeof fd !== "number") { | ||
throw new ERR_INVALID_ARG_TYPE("fd", "number", fd); | ||
} | ||
fd = getValidatedFd(fd); | ||
validateBufferArray(buffers); | ||
const cb = maybeCallback(callback || position) as Callback; | ||
let pos: number | null = null; | ||
if (typeof position === "number") { | ||
validateInteger(position, "position", 0); | ||
pos = position; | ||
} | ||
|
||
if (buffers.length === 0) { | ||
process.nextTick(cb, null, 0, buffers); | ||
return; | ||
} | ||
|
||
const innerReadv = async ( | ||
fd: number, | ||
buffers: readonly ArrayBufferView[], | ||
position: number | null, | ||
) => { | ||
if (typeof position === "number") { | ||
await fs.seek(fd, position, io.SeekMode.Start); | ||
} | ||
|
||
let readTotal = 0; | ||
let readInBuf = 0; | ||
let bufIdx = 0; | ||
let buf = buffers[bufIdx]; | ||
while (bufIdx < buffers.length) { | ||
const nread = await io.read(fd, buf); | ||
if (nread === null) { | ||
break; | ||
} | ||
readInBuf += nread; | ||
if (readInBuf === buf.byteLength) { | ||
readTotal += readInBuf; | ||
readInBuf = 0; | ||
bufIdx += 1; | ||
buf = buffers[bufIdx]; | ||
} | ||
} | ||
readTotal += readInBuf; | ||
|
||
return readTotal; | ||
}; | ||
|
||
innerReadv(fd, buffers, pos).then( | ||
(numRead) => { | ||
cb(null, numRead, buffers); | ||
}, | ||
(err) => cb(err, -1, buffers), | ||
); | ||
} | ||
|
||
export function readvSync( | ||
fd: number, | ||
buffers: readonly ArrayBufferView[], | ||
position: number | null = null, | ||
): number { | ||
if (typeof fd !== "number") { | ||
throw new ERR_INVALID_ARG_TYPE("fd", "number", fd); | ||
} | ||
fd = getValidatedFd(fd); | ||
validateBufferArray(buffers); | ||
if (buffers.length === 0) { | ||
return 0; | ||
} | ||
if (typeof position === "number") { | ||
validateInteger(position, "position", 0); | ||
fs.seekSync(fd, position, io.SeekMode.Start); | ||
} | ||
|
||
let readTotal = 0; | ||
let readInBuf = 0; | ||
let bufIdx = 0; | ||
let buf = buffers[bufIdx]; | ||
while (bufIdx < buffers.length) { | ||
const nread = io.readSync(fd, buf); | ||
if (nread === null) { | ||
break; | ||
} | ||
readInBuf += nread; | ||
if (readInBuf === buf.byteLength) { | ||
readTotal += readInBuf; | ||
readInBuf = 0; | ||
bufIdx += 1; | ||
buf = buffers[bufIdx]; | ||
} | ||
} | ||
readTotal += readInBuf; | ||
|
||
return readTotal; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
// deno-fmt-ignore-file | ||
// deno-lint-ignore-file | ||
|
||
// Copyright Joyent and Node contributors. All rights reserved. MIT license. | ||
// Taken from Node 18.12.1 | ||
// This file is automatically generated by `tools/node_compat/setup.ts`. Do not modify this file manually. | ||
|
||
'use strict'; | ||
|
||
require('../common'); | ||
const assert = require('assert'); | ||
const fs = require('fs'); | ||
const path = require('path'); | ||
const tmpdir = require('../common/tmpdir'); | ||
|
||
tmpdir.refresh(); | ||
|
||
const expected = 'ümlaut. Лорем 運務ホソモ指及 आपको करने विकास 紙読決多密所 أضف'; | ||
|
||
const exptectedBuff = Buffer.from(expected); | ||
const expectedLength = exptectedBuff.length; | ||
|
||
const filename = path.join(tmpdir.path, 'readv_sync.txt'); | ||
fs.writeFileSync(filename, exptectedBuff); | ||
|
||
const allocateEmptyBuffers = (combinedLength) => { | ||
const bufferArr = []; | ||
// Allocate two buffers, each half the size of exptectedBuff | ||
bufferArr[0] = Buffer.alloc(Math.floor(combinedLength / 2)); | ||
bufferArr[1] = Buffer.alloc(combinedLength - bufferArr[0].length); | ||
|
||
return bufferArr; | ||
}; | ||
|
||
// fs.readvSync with array of buffers with all parameters | ||
{ | ||
const fd = fs.openSync(filename, 'r'); | ||
|
||
const bufferArr = allocateEmptyBuffers(exptectedBuff.length); | ||
|
||
let read = fs.readvSync(fd, [Buffer.from('')], 0); | ||
assert.strictEqual(read, 0); | ||
|
||
read = fs.readvSync(fd, bufferArr, 0); | ||
assert.strictEqual(read, expectedLength); | ||
|
||
fs.closeSync(fd); | ||
|
||
assert(Buffer.concat(bufferArr).equals(fs.readFileSync(filename))); | ||
} | ||
|
||
// fs.readvSync with array of buffers without position | ||
{ | ||
const fd = fs.openSync(filename, 'r'); | ||
|
||
const bufferArr = allocateEmptyBuffers(exptectedBuff.length); | ||
|
||
let read = fs.readvSync(fd, [Buffer.from('')]); | ||
assert.strictEqual(read, 0); | ||
|
||
read = fs.readvSync(fd, bufferArr); | ||
assert.strictEqual(read, expectedLength); | ||
|
||
fs.closeSync(fd); | ||
|
||
assert(Buffer.concat(bufferArr).equals(fs.readFileSync(filename))); | ||
} | ||
|
||
/** | ||
* Testing with incorrect arguments | ||
*/ | ||
const wrongInputs = [false, 'test', {}, [{}], ['sdf'], null, undefined]; | ||
|
||
{ | ||
const fd = fs.openSync(filename, 'r'); | ||
|
||
wrongInputs.forEach((wrongInput) => { | ||
assert.throws( | ||
() => fs.readvSync(fd, wrongInput, null), { | ||
code: 'ERR_INVALID_ARG_TYPE', | ||
name: 'TypeError' | ||
} | ||
); | ||
}); | ||
|
||
fs.closeSync(fd); | ||
} | ||
|
||
{ | ||
// fs.readv with wrong fd argument | ||
wrongInputs.forEach((wrongInput) => { | ||
assert.throws( | ||
() => fs.readvSync(wrongInput), | ||
{ | ||
code: 'ERR_INVALID_ARG_TYPE', | ||
name: 'TypeError' | ||
} | ||
); | ||
}); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,102 @@ | ||
// deno-fmt-ignore-file | ||
// deno-lint-ignore-file | ||
|
||
// Copyright Joyent and Node contributors. All rights reserved. MIT license. | ||
// Taken from Node 18.12.1 | ||
// This file is automatically generated by `tools/node_compat/setup.ts`. Do not modify this file manually. | ||
|
||
'use strict'; | ||
|
||
const common = require('../common'); | ||
const assert = require('assert'); | ||
const path = require('path'); | ||
const fs = require('fs'); | ||
const tmpdir = require('../common/tmpdir'); | ||
|
||
tmpdir.refresh(); | ||
|
||
const expected = 'ümlaut. Лорем 運務ホソモ指及 आपको करने विकास 紙読決多密所 أضف'; | ||
|
||
let cnt = 0; | ||
const getFileName = () => path.join(tmpdir.path, `readv_${++cnt}.txt`); | ||
const exptectedBuff = Buffer.from(expected); | ||
|
||
const allocateEmptyBuffers = (combinedLength) => { | ||
const bufferArr = []; | ||
// Allocate two buffers, each half the size of exptectedBuff | ||
bufferArr[0] = Buffer.alloc(Math.floor(combinedLength / 2)); | ||
bufferArr[1] = Buffer.alloc(combinedLength - bufferArr[0].length); | ||
|
||
return bufferArr; | ||
}; | ||
|
||
const getCallback = (fd, bufferArr) => { | ||
return common.mustSucceed((bytesRead, buffers) => { | ||
assert.deepStrictEqual(bufferArr, buffers); | ||
const expectedLength = exptectedBuff.length; | ||
assert.deepStrictEqual(bytesRead, expectedLength); | ||
fs.closeSync(fd); | ||
|
||
assert(Buffer.concat(bufferArr).equals(exptectedBuff)); | ||
}); | ||
}; | ||
|
||
// fs.readv with array of buffers with all parameters | ||
{ | ||
const filename = getFileName(); | ||
const fd = fs.openSync(filename, 'w+'); | ||
fs.writeSync(fd, exptectedBuff); | ||
|
||
const bufferArr = allocateEmptyBuffers(exptectedBuff.length); | ||
const callback = getCallback(fd, bufferArr); | ||
|
||
fs.readv(fd, bufferArr, 0, callback); | ||
} | ||
|
||
// fs.readv with array of buffers without position | ||
{ | ||
const filename = getFileName(); | ||
fs.writeFileSync(filename, exptectedBuff); | ||
const fd = fs.openSync(filename, 'r'); | ||
|
||
const bufferArr = allocateEmptyBuffers(exptectedBuff.length); | ||
const callback = getCallback(fd, bufferArr); | ||
|
||
fs.readv(fd, bufferArr, callback); | ||
} | ||
|
||
/** | ||
* Testing with incorrect arguments | ||
*/ | ||
const wrongInputs = [false, 'test', {}, [{}], ['sdf'], null, undefined]; | ||
|
||
{ | ||
const filename = getFileName(2); | ||
fs.writeFileSync(filename, exptectedBuff); | ||
const fd = fs.openSync(filename, 'r'); | ||
|
||
|
||
wrongInputs.forEach((wrongInput) => { | ||
assert.throws( | ||
() => fs.readv(fd, wrongInput, null, common.mustNotCall()), { | ||
code: 'ERR_INVALID_ARG_TYPE', | ||
name: 'TypeError' | ||
} | ||
); | ||
}); | ||
|
||
fs.closeSync(fd); | ||
} | ||
|
||
{ | ||
// fs.readv with wrong fd argument | ||
wrongInputs.forEach((wrongInput) => { | ||
assert.throws( | ||
() => fs.readv(wrongInput, common.mustNotCall()), | ||
{ | ||
code: 'ERR_INVALID_ARG_TYPE', | ||
name: 'TypeError' | ||
} | ||
); | ||
}); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters