Browse Source

Deprecate string interface for fs.write()

This patch makes buffers the preferred input for fs.write() and
fs.writeSync(). The old string interface is still supported by
converting strings to buffers dynamically. This allows to remove the
C++ code for string handling which is also part of this patch.
v0.7.4-release
Felix Geisendörfer 15 years ago
committed by Ryan Dahl
parent
commit
c46cbe0de4
  1. 12
      doc/api.markdown
  2. 29
      lib/fs.js
  3. 97
      src/node_file.cc
  4. 32
      test/simple/test-fs-write-buffer.js
  5. 12
      test/simple/test-fs-write-sync.js

12
doc/api.markdown

@ -1392,11 +1392,15 @@ or 'a+'. The callback gets two arguments `(err, fd)`.
Synchronous open(2). Synchronous open(2).
### fs.write(fd, data, position, encoding, callback) ### fs.write(fd, buffer, offset, length, position, callback)
Write data to the file specified by `fd`. `position` refers to the offset Write `buffer` to the file specified by `fd`.
from the beginning of the file where this data should be written. If
`position` is `null`, the data will be written at the current position. `offset` and `length` determine the part of the buffer to be written.
`position` refers to the offset from the beginning of the file where this data
should be written. If `position` is `null`, the data will be written at the
current position.
See pwrite(2). See pwrite(2).
The callback will be given two arguments `(err, written)` where `written` The callback will be given two arguments `(err, written)` where `written`

29
lib/fs.js

@ -153,14 +153,31 @@ fs.readSync = function (fd, length, position, encoding) {
return binding.read(fd, length, position, encoding); return binding.read(fd, length, position, encoding);
}; };
fs.write = function (fd, data, position, encoding, callback) { fs.write = function (fd, buffer, offset, length, position, callback) {
encoding = encoding || "binary"; if (!(buffer instanceof Buffer)) {
binding.write(fd, data, position, encoding, callback || noop); // legacy string interface (fd, data, position, encoding, callback)
callback = arguments[4];
position = arguments[2];
buffer = new Buffer(''+arguments[1], arguments[3]);
offset = 0;
length = buffer.length;
}
binding.write(fd, buffer, offset, length, position, callback || noop);
}; };
fs.writeSync = function (fd, data, position, encoding) { fs.writeSync = function (fd, buffer, offset, length, position) {
encoding = encoding || "binary"; if (!(buffer instanceof Buffer)) {
return binding.write(fd, data, position, encoding); // legacy string interface (fd, data, position, encoding)
position = arguments[2];
buffer = new Buffer(''+arguments[1], arguments[3]);
offset = 0;
length = buffer.length;
}
binding.write(fd, buffer, offset, length, position);
}; };
fs.rename = function (oldPath, newPath, callback) { fs.rename = function (oldPath, newPath, callback) {

97
src/node_file.cc

@ -150,11 +150,6 @@ static int After(eio_req *req) {
} }
} }
if (req->type == EIO_WRITE && req->int3 == 1) {
assert(req->ptr2);
delete [] reinterpret_cast<char*>(req->ptr2);
}
TryCatch try_catch; TryCatch try_catch;
(*callback)->Call(Context::GetCurrent()->Global(), argc, argv); (*callback)->Call(Context::GetCurrent()->Global(), argc, argv);
@ -536,14 +531,6 @@ static Handle<Value> Open(const Arguments& args) {
// 3 length how much to write // 3 length how much to write
// 4 position if integer, position to write at in the file. // 4 position if integer, position to write at in the file.
// if null, write from the current position // if null, write from the current position
//
// - OR -
//
// 0 fd integer. file descriptor
// 1 string the data to write
// 2 position if integer, position to write at in the file.
// if null, write from the current position
// 3 encoding
static Handle<Value> Write(const Arguments& args) { static Handle<Value> Write(const Arguments& args) {
HandleScope scope; HandleScope scope;
@ -553,24 +540,10 @@ static Handle<Value> Write(const Arguments& args) {
int fd = args[0]->Int32Value(); int fd = args[0]->Int32Value();
off_t pos; if (!Buffer::HasInstance(args[1])) {
ssize_t len; return ThrowException(Exception::Error(
char * buf; String::New("Second argument needs to be a buffer")));
ssize_t written; }
Local<Value> cb;
bool legacy;
if (Buffer::HasInstance(args[1])) {
legacy = false;
// buffer
//
// 0 fd integer. file descriptor
// 1 buffer the data to write
// 2 offset where in the buffer to start from
// 3 length how much to write
// 4 position if integer, position to write at in the file.
// if null, write from the current position
Buffer * buffer = ObjectWrap::Unwrap<Buffer>(args[1]->ToObject()); Buffer * buffer = ObjectWrap::Unwrap<Buffer>(args[1]->ToObject());
@ -580,71 +553,21 @@ static Handle<Value> Write(const Arguments& args) {
String::New("Offset is out of bounds"))); String::New("Offset is out of bounds")));
} }
len = args[3]->Int32Value(); ssize_t len = args[3]->Int32Value();
if (off + len > buffer->length()) { if (off + len > buffer->length()) {
return ThrowException(Exception::Error( return ThrowException(Exception::Error(
String::New("Length is extends beyond buffer"))); String::New("Length is extends beyond buffer")));
} }
pos = GET_OFFSET(args[4]); off_t pos = GET_OFFSET(args[4]);
buf = (char*)buffer->data() + off;
cb = args[5];
} else {
legacy = true;
// legacy interface.. args[1] is a string
pos = GET_OFFSET(args[2]);
enum encoding enc = ParseEncoding(args[3]);
len = DecodeBytes(args[1], enc);
if (len < 0) {
Local<Value> exception = Exception::TypeError(String::New("Bad argument"));
return ThrowException(exception);
}
buf = new char[len];
written = DecodeWrite(buf, len, args[1], enc);
assert(written == len);
cb = args[4]; char * buf = (char*)buffer->data() + off;
} Local<Value> cb = args[5];
if (cb->IsFunction()) { if (cb->IsFunction()) {
// WARNING: HACK AHEAD, PROCEED WITH CAUTION ASYNC_CALL(write, cb, fd, buf, len, pos)
// Normally here I would do
// ASYNC_CALL(write, cb, fd, buf, len, pos)
// however, I'm trying to support a legacy interface; where in the
// String version a chunk of memory is allocated for the string to be
// decoded into. In the After() function it is freed.
//
// However in the Buffer version, we increase the reference count
// to the Buffer while we're in the thread pool.
//
// We have to let After() know which version is being done so it can
// know if it needs to free 'buf' or not. We do that by using
// req->int3.
//
// req->int3 == 1 legacy, String version. Need to free `buf`.
// req->int3 == 0 Buffer version. Don't do anything.
//
eio_req *req = eio_write(fd, buf, len, pos,
EIO_PRI_DEFAULT,
After,
cb_persist(cb));
assert(req);
req->int3 = legacy ? 1 : 0;
ev_ref(EV_DEFAULT_UC);
return Undefined();
} else { } else {
written = pos < 0 ? write(fd, buf, len) : pwrite(fd, buf, len, pos); ssize_t written = pos < 0 ? write(fd, buf, len) : pwrite(fd, buf, len, pos);
if (legacy) {
delete [] reinterpret_cast<char*>(buf);
}
if (written < 0) return ThrowException(ErrnoException(errno)); if (written < 0) return ThrowException(ErrnoException(errno));
return scope.Close(Integer::New(written)); return scope.Close(Integer::New(written));
} }

32
test/simple/test-fs-write-buffer.js

@ -0,0 +1,32 @@
require('../common');
var path = require('path')
, Buffer = require('buffer').Buffer
, fs = require('fs')
, fn = path.join(fixturesDir, 'write.txt')
, expected = new Buffer('hello')
, openCalled = 0
, writeCalled = 0;
fs.open(fn, 'w', 0644, function (err, fd) {
openCalled++;
if (err) throw err;
fs.write(fd, expected, 0, expected.length, null, function(err, written) {
writeCalled++;
if (err) throw err;
assert.equal(expected.length, written);
fs.closeSync(fd);
var found = fs.readFileSync(fn);
assert.deepEqual(found, expected.toString());
fs.unlinkSync(fn);
});
});
process.addListener("exit", function () {
assert.equal(openCalled, 1);
assert.equal(writeCalled, 1);
});

12
test/simple/test-fs-write-sync.js

@ -0,0 +1,12 @@
require('../common');
var path = require('path')
, Buffer = require('buffer').Buffer
, fs = require('fs')
, fn = path.join(fixturesDir, 'write.txt');
var fd = fs.openSync(fn, 'w');
fs.writeSync(fd, 'foo');
fs.writeSync(fd, new Buffer('bar'), 0, 3);
fs.closeSync(fd);
assert.equal(fs.readFileSync(fn), 'foobar');
Loading…
Cancel
Save