Skip to content
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

fs: allow read/write to accept an ArrayBuffer #11641

Closed
wants to merge 1 commit into from

Conversation

trevnorris
Copy link
Contributor

@trevnorris trevnorris commented Mar 1, 2017

Checklist

  • make -j4 test (UNIX), or vcbuild test (Windows) passes
  • tests and/or benchmarks are included
  • documentation is changed or added
  • commit message follows commit guidelines

Affected core subsystem(s):
fs

fs.read() and fs.write() now accept an ArrayBuffer.

Typed Arrays are limited in size by a max index of Smi (0x7fffffff on
x64 and 0x3fffffff on ia32). While, in V8, ArrayBuffers are limited to
max size_t. Though for practical reasons it would be limited to
Number.MAX_SAFE_INTEGER (or approximately 8PB).

So now the maximum file size that can be read/written is only limited by
the amount of available memory. This presents a problem to fully test
because it requires that at least 4GB of memory be available.

To implement this, one shortcoming of uv_buf_init() was addressed. The
len argument is an unsigned int. Whereas the len property of a
uv_buf_t is a size_t. So len needs to be set manually. Otherwise it
could be truncated.

CI: https://ci.nodejs.org/job/node-test-commit/8191/

@trevnorris trevnorris added the fs Issues and PRs related to the fs subsystem / file system. label Mar 1, 2017
@trevnorris trevnorris requested a review from bnoordhuis Mar 1, 2017
@nodejs-github-bot nodejs-github-bot added c++ Issues and PRs that require attention from people who are familiar with C++. fs Issues and PRs related to the fs subsystem / file system. labels Mar 1, 2017
@trevnorris
Copy link
Contributor Author

trevnorris commented Mar 1, 2017

Pause on this. It won't work on Windows (ref libuv/libuv#1236 (comment)). So it appears that len on Windows is an unsigned long. So internally it'll need to manually write large buffers in smaller chunks.

@mscdex mscdex added the wip Issues and PRs that are still a work in progress. label Mar 2, 2017
@BridgeAR
Copy link
Member

BridgeAR commented Aug 26, 2017

@trevnorris are you going to follow up on this?

@trevnorris
Copy link
Contributor Author

trevnorris commented Aug 30, 2017

@BridgeAR Forgot I had started this. It stalled b/c I wanted to see about changing uv_buf_init(char*, unsigned int) to uv_buf_init(char*, size_t), but that's not ABI compatible and won't land in node. Thus len will need to be changed manually.

I'll try to get this rebased and fixing again in the near future. Unless you are asking b/c you want to pick this up and run with it. :-)

@BridgeAR
Copy link
Member

BridgeAR commented Sep 12, 2017

@trevnorris as time is limited for all of us, I was not planning on following up on this currently. So it would be nice if you find the time to rebase this. In that case this could be properly reviewed.

@trevnorris
Copy link
Contributor Author

trevnorris commented Sep 14, 2017

@BridgeAR Will do.

fs.read() and fs.write() now accept an ArrayBuffer.

Typed Arrays are limited in size by a max index of Smi (0x7fffffff on
x64 and 0x3fffffff on ia32). While, in V8, ArrayBuffers are limited to
max size_t. Though for practical reasons it would be limited to
Number.MAX_SAFE_INTEGER (or approximately 8PB).

So now the maximum file size that can be read/written is only limited by
the amount of available memory. This presents a problem to fully test
because it requires that at least 4GB of memory be available.

To implement this, one shortcoming of uv_buf_init() was addressed. The
"len" argument is an unsigned int. Whereas the "len" property of a
uv_buf_t is a size_t. So len needs to be set manually. Otherwise it
could be truncated.

PR-URL: nodejs#11641
@trevnorris
Copy link
Contributor Author

trevnorris commented Sep 14, 2017

@BridgeAR Has been rebased and running new CI

CI: https://ci.nodejs.org/job/node-test-pull-request/10084/

@jasnell
Copy link
Member

jasnell commented Sep 14, 2017

I think it may make sense to expand this at some point to support any ArrayBufferView (TypedArray or DataView). But for now, this LGTM

@addaleax
Copy link
Member

addaleax commented Sep 14, 2017

@jasnell This is about allowing ArrayBuffers, not ArrayBufferViews; the latter would actually be a lot easier to do.

I don’t think the added complexity here justifies the feature (esp. given that it’s perfectly reasonably implementable in userland and not a common use case), but then again it’s not a huge problem either.

@jasnell
Copy link
Member

jasnell commented Sep 14, 2017

Yes, I know.

@trevnorris
Copy link
Contributor Author

trevnorris commented Sep 15, 2017

@addaleax

I don’t think the added complexity here justifies the feature (esp. given that it’s perfectly reasonably implementable in userland and not a common use case), but then again it’s not a huge problem either.

I'd like to have full ArrayBufferView support, but doing so would have the same amount of complexity. Consider the fact that a Float64Array supports 8 GB, the additional read/write loops are necessary. This is meant to be a step in full ArrayBufferView support.

EDIT: I agree that the added complexity sucks, but it's not that much, and if you see a way to simplify it I'll happily update the PR.

@addaleax
Copy link
Member

addaleax commented Sep 15, 2017

@trevnorris what do we need for full ArrayBufferView support beyond looser typechecking?

@trevnorris
Copy link
Contributor Author

trevnorris commented Sep 21, 2017

@addaleax read/write is currently broken. Example writing:

const fs = require('fs');
const file_path = '/tmp/buf_file.blah';

const buf = Buffer.alloc(require('buffer').kMaxLength, 1);
const fd = fs.openSync(file_path, 'w+');
const written = fs.writeSync(fd, buf, 0, buf.byteLength, 0);

console.log(fs.statSync(file_path).size, buf.byteLength);
// output: 2147479552 2147483647

now for reading. first create the file:

$ fallocate -l 2147483647 /tmp/buf_file.blah

now reading the file:

const fs = require('fs');
const file_path = '/tmp/buf_file.blah';
const buf = Buffer.allocUnsafe(fs.statSync(file_path).size);

const fd = fs.openSync(file_path, 'r+');
const bytes = fs.readSync(fd, buf, 0, buf.byteLength, 0);
console.log(bytes, buf.byteLength);
// output: 2147479552 2147483647

We've been ignoring a documented kernel limitation:

On Linux, write() (and similar system calls) will transfer at most
0x7ffff000 (2,147,479,552) bytes, returning the number of bytes
actually transferred. (This is true on both 32-bit and 64-bit
systems.)

This PR fixes that and adds new functionality.

EDIT: @addaleax I was taking the opinion that it should always write all the data to the file unless there was an error because I'd expect the same of doing a TCP write, but guess they don't work that way. node is just so cozy and handles all that hard stuff for me so often that I became lazy.

@trevnorris
Copy link
Contributor Author

trevnorris commented Sep 21, 2017

@santigimeno mentioned the libuv bug libuv/libuv#1501 which we'll need to work around. if the libuv fix doesn't land soon i'll change the PR so the maximum data written at a single time is 2GB.

@trevnorris
Copy link
Contributor Author

trevnorris commented Sep 21, 2017

Closing this after having a discussion on IRC. To summarize: I had expected fs.read() and fs.write() to handle writing out all the data for me. Instead it's actually the users responsibility to make sure to check the number of bytes read/written and loop until the operation is complete. Reason I thought fs.read() and fs.write() would handle it for me is because other operations like fs.readFile(), fs.writeFile() or net.Socket#write() handle sending everything when too much data is sent.

So, instead we'll need a PR for updating the docs with language to clarify that it's the user's responsibility to check the return value and make sure all the data has been read/written.

@trevnorris trevnorris closed this Sep 21, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
c++ Issues and PRs that require attention from people who are familiar with C++. fs Issues and PRs related to the fs subsystem / file system. wip Issues and PRs that are still a work in progress.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

6 participants