From 5220d58a6565769c3025ee25d917ab4c9da77aae Mon Sep 17 00:00:00 2001 From: Alexis Svinartchouk Date: Thu, 9 Mar 2017 17:50:04 +0100 Subject: [PATCH] Implement writeBuffers using writev and pwritev syscalls. Change-Type: patch --- lib/binding.js | 19 ++++++++++++++++--- lib/syscall.js | 4 +++- src/bindings.cc | 1 + src/node_lkl.cc | 16 ++++++++++++++++ src/node_lkl.h | 1 + test/index.js | 26 ++++++++++++++++++++++++++ 6 files changed, 63 insertions(+), 4 deletions(-) diff --git a/lib/binding.js b/lib/binding.js index c75cc95..341bd54 100644 --- a/lib/binding.js +++ b/lib/binding.js @@ -15,7 +15,7 @@ function getCallback(req) { } function addNullTerminationIfNeeded(s) { - // If the s is a Buffer, we need to add a NULL termination byte. + // If s is a Buffer, we need to add a NULL termination byte. if (Buffer.isBuffer(s)) { const nullTerminated = Buffer.allocUnsafe(s.length + 1); s.copy(nullTerminated); @@ -193,8 +193,21 @@ exports.writeBuffer = function(fd, buffer, offset, length, position, req) { } } -exports.writeBuffers = function() { - throw new Error('Unimplemented'); +exports.writeBuffers = function(fd, buffers, position, req) { + const callback = getCallback(req); + const iovecs = bindings.buffersToIoVecs(buffers); + if (typeof position === 'number' && position !== -1) { + return syscall.pwritev( + fd, + iovecs, + buffers.length, + position & 0xffffffff, // 32 lower bits + Math.floor(position / Math.pow(2, 32)), // high bits + callback + ); + } else { + return syscall.writev(fd, iovecs, buffers.length, callback); + } } exports.writeString = function() { diff --git a/lib/syscall.js b/lib/syscall.js index 72f9f07..b6ac194 100644 --- a/lib/syscall.js +++ b/lib/syscall.js @@ -47,6 +47,7 @@ module.exports = { openat: syscall.bind(null, constants.SYS_openat), pread64: syscall.bind(null, constants.SYS_pread64), pwrite64: syscall.bind(null, constants.SYS_pwrite64), + pwritev: syscall.bind(null, constants.SYS_pwritev), read: syscall.bind(null, constants.SYS_read), readlinkat: syscall.bind(null, constants.SYS_readlinkat), removexattr: syscall.bind(null, constants.SYS_removexattr), @@ -56,7 +57,8 @@ module.exports = { sync: syscall.bind(null, constants.SYS_sync), truncate: syscall.bind(null, constants.SYS_truncate), unlinkat: syscall.bind(null, constants.SYS_unlinkat), - write: syscall.bind(null, constants.SYS_write) + write: syscall.bind(null, constants.SYS_write), + writev: syscall.bind(null, constants.SYS_writev) }; // Unimplemented syscalls diff --git a/src/bindings.cc b/src/bindings.cc index cef1a58..510439d 100644 --- a/src/bindings.cc +++ b/src/bindings.cc @@ -13,6 +13,7 @@ NAN_MODULE_INIT(InitAll) { NAN_EXPORT(target, sizeOfStructLklStat); NAN_EXPORT(target, parseLklStat); NAN_EXPORT(target, millisecondsToTimespec); + NAN_EXPORT(target, buffersToIoVecs); } NODE_MODULE(bindings, InitAll) diff --git a/src/node_lkl.cc b/src/node_lkl.cc index ae72a64..3f4b507 100644 --- a/src/node_lkl.cc +++ b/src/node_lkl.cc @@ -208,3 +208,19 @@ NAN_METHOD(millisecondsToTimespec) { ).ToLocalChecked(); info.GetReturnValue().Set(result); } + +NAN_METHOD(buffersToIoVecs) { + auto arr = info[0].As(); + auto len = arr->Length(); + auto *iovecs = new struct iovec[len]; + for (unsigned int i = 0; i < len; i++) { + auto buf = arr->Get(i); + iovecs[i].iov_base = reinterpret_cast(node::Buffer::Data(buf)); + iovecs[i].iov_len = node::Buffer::Length(buf); + } + auto result = Nan::NewBuffer( + reinterpret_cast(iovecs), + sizeof *iovecs + ).ToLocalChecked(); + info.GetReturnValue().Set(result); +} diff --git a/src/node_lkl.h b/src/node_lkl.h index 83c9c98..6e95499 100644 --- a/src/node_lkl.h +++ b/src/node_lkl.h @@ -7,3 +7,4 @@ NAN_METHOD(parseDirent64); NAN_METHOD(sizeOfStructLklStat); NAN_METHOD(parseLklStat); NAN_METHOD(millisecondsToTimespec); +NAN_METHOD(buffersToIoVecs); diff --git a/test/index.js b/test/index.js index 6498a69..751b5f6 100644 --- a/test/index.js +++ b/test/index.js @@ -432,5 +432,31 @@ describe('node-lkl', function() { .then(done); }); }); + + describe('writev', function() { + it('should write all buffers to file', function(done) { + const fpath = path.join(this.mountpoint, 'filename'); + const stream = lkl.fs.WriteStream(fpath); + const chunks = [ + {chunk: Buffer.from('1')}, + {chunk: Buffer.from('2')}, + {chunk: Buffer.from('3')}, + ] + // testing writev syscall + stream._writev(chunks, function(err) { + // testing pwritev syscall + stream.pos = 1; + stream._writev(chunks, function(err) { + stream.close(function(err){ + lkl.fs.readFileAsync(fpath, 'utf8') + .then(function(content) { + assert.strictEqual(content, '1123', 'should read what it has written'); + done(); + }) + }) + }); + }); + }); + }); }); });