summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJames M Snell <jasnell@gmail.com>2016-03-08 20:58:45 -0800
committerJames M Snell <jasnell@gmail.com>2016-03-25 14:21:27 -0700
commit060e5f0c0064e578c2150f13e3f91ac15fdeed92 (patch)
tree50783508a123991a024a9a85a2994d5ef2a83c5d /src
parent4d4f3535a9fd7486d7a94d825ac8e92a65bf9121 (diff)
downloadandroid-node-v8-060e5f0c0064e578c2150f13e3f91ac15fdeed92.tar.gz
android-node-v8-060e5f0c0064e578c2150f13e3f91ac15fdeed92.tar.bz2
android-node-v8-060e5f0c0064e578c2150f13e3f91ac15fdeed92.zip
fs: Buffer and encoding enhancements to fs API
This makes several changes: 1. Allow path/filename to be passed in as a Buffer on fs methods 2. Add `options.encoding` to fs.readdir, fs.readdirSync, fs.readlink, fs.readlinkSync and fs.watch. 3. Documentation updates For 1... it's now possible to do: ```js fs.open(Buffer('/fs/foo/bar'), 'w+', (err, fd) => { }); ``` For 2... ```js fs.readdir('/fs/foo/bar', {encoding:'hex'}, (err,list) => { }); fs.readdir('/fs/foo/bar', {encoding:'buffer'}, (err, list) => { }); ``` encoding can also be passed as a string ```js fs.readdir('/fs/foo/bar', 'hex', (err,list) => { }); ``` The default encoding is set to UTF8 so this addresses the discrepency that existed previously between fs.readdir and fs.watch handling filenames differently. Fixes: https://github.com/nodejs/node/issues/2088 Refs: https://github.com/nodejs/node/issues/3519 PR-URL: https://github.com/nodejs/node/pull/5616 Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl> Reviewed-By: Trevor Norris <trev.norris@gmail.com>
Diffstat (limited to 'src')
-rw-r--r--src/fs_event_wrap.cc27
-rw-r--r--src/node_file.cc295
-rw-r--r--src/string_bytes.cc31
-rw-r--r--src/string_bytes.h4
-rw-r--r--src/util.cc69
-rw-r--r--src/util.h19
6 files changed, 310 insertions, 135 deletions
diff --git a/src/fs_event_wrap.cc b/src/fs_event_wrap.cc
index 7768f94459..58f2716a6c 100644
--- a/src/fs_event_wrap.cc
+++ b/src/fs_event_wrap.cc
@@ -6,6 +6,7 @@
#include "util-inl.h"
#include "node.h"
#include "handle_wrap.h"
+#include "string_bytes.h"
#include <stdlib.h>
@@ -41,6 +42,7 @@ class FSEventWrap: public HandleWrap {
uv_fs_event_t handle_;
bool initialized_;
+ enum encoding encoding_;
};
@@ -86,16 +88,20 @@ void FSEventWrap::Start(const FunctionCallbackInfo<Value>& args) {
FSEventWrap* wrap = Unwrap<FSEventWrap>(args.Holder());
- if (args.Length() < 1 || !args[0]->IsString()) {
- return env->ThrowTypeError("filename must be a valid string");
- }
+ static const char kErrMsg[] = "filename must be a string or Buffer";
+ if (args.Length() < 1)
+ return env->ThrowTypeError(kErrMsg);
- node::Utf8Value path(env->isolate(), args[0]);
+ BufferValue path(env->isolate(), args[0]);
+ if (*path == nullptr)
+ return env->ThrowTypeError(kErrMsg);
unsigned int flags = 0;
if (args[2]->IsTrue())
flags |= UV_FS_EVENT_RECURSIVE;
+ wrap->encoding_ = ParseEncoding(env->isolate(), args[3], UTF8);
+
int err = uv_fs_event_init(wrap->env()->event_loop(), &wrap->handle_);
if (err == 0) {
wrap->initialized_ = true;
@@ -156,7 +162,18 @@ void FSEventWrap::OnEvent(uv_fs_event_t* handle, const char* filename,
};
if (filename != nullptr) {
- argv[2] = OneByteString(env->isolate(), filename);
+ Local<Value> fn = StringBytes::Encode(env->isolate(),
+ filename,
+ wrap->encoding_);
+ if (fn.IsEmpty()) {
+ argv[0] = Integer::New(env->isolate(), UV_EINVAL);
+ argv[2] = StringBytes::Encode(env->isolate(),
+ filename,
+ strlen(filename),
+ BUFFER);
+ } else {
+ argv[2] = fn;
+ }
}
wrap->MakeCallback(env->onchange_string(), ARRAY_SIZE(argv), argv);
diff --git a/src/node_file.cc b/src/node_file.cc
index 9c2bea4897..a669c0855f 100644
--- a/src/node_file.cc
+++ b/src/node_file.cc
@@ -34,6 +34,7 @@ using v8::FunctionCallbackInfo;
using v8::FunctionTemplate;
using v8::HandleScope;
using v8::Integer;
+using v8::Isolate;
using v8::Local;
using v8::Number;
using v8::Object;
@@ -56,6 +57,7 @@ class FSReqWrap: public ReqWrap<uv_fs_t> {
Local<Object> req,
const char* syscall,
const char* data = nullptr,
+ enum encoding encoding = UTF8,
Ownership ownership = COPY);
inline void Dispose();
@@ -69,6 +71,7 @@ class FSReqWrap: public ReqWrap<uv_fs_t> {
const char* syscall() const { return syscall_; }
const char* data() const { return data_; }
+ const enum encoding encoding_;
size_t self_size() const override { return sizeof(*this); }
@@ -76,8 +79,10 @@ class FSReqWrap: public ReqWrap<uv_fs_t> {
FSReqWrap(Environment* env,
Local<Object> req,
const char* syscall,
- const char* data)
+ const char* data,
+ enum encoding encoding)
: ReqWrap(env, req, AsyncWrap::PROVIDER_FSREQWRAP),
+ encoding_(encoding),
syscall_(syscall),
data_(data) {
Wrap(object(), this);
@@ -95,17 +100,21 @@ class FSReqWrap: public ReqWrap<uv_fs_t> {
DISALLOW_COPY_AND_ASSIGN(FSReqWrap);
};
+#define ASSERT_PATH(path) \
+ if (*path == nullptr) \
+ return TYPE_ERROR( #path " must be a string or Buffer");
FSReqWrap* FSReqWrap::New(Environment* env,
Local<Object> req,
const char* syscall,
const char* data,
+ enum encoding encoding,
Ownership ownership) {
const bool copy = (data != nullptr && ownership == COPY);
const size_t size = copy ? 1 + strlen(data) : 0;
FSReqWrap* that;
char* const storage = new char[sizeof(*that) + size];
- that = new(storage) FSReqWrap(env, req, syscall, data);
+ that = new(storage) FSReqWrap(env, req, syscall, data, encoding);
if (copy)
that->data_ = static_cast<char*>(memcpy(that->inline_data(), data, size));
return that;
@@ -127,7 +136,6 @@ static inline bool IsInt64(double x) {
return x == static_cast<double>(static_cast<int64_t>(x));
}
-
static void After(uv_fs_t *req) {
FSReqWrap* req_wrap = static_cast<FSReqWrap*>(req->data);
CHECK_EQ(&req_wrap->req_, req);
@@ -143,6 +151,7 @@ static void After(uv_fs_t *req) {
// Allocate space for two args. We may only use one depending on the case.
// (Feel free to increase this if you need more)
Local<Value> argv[2];
+ Local<Value> link;
if (req->result < 0) {
// An error happened.
@@ -201,13 +210,35 @@ static void After(uv_fs_t *req) {
break;
case UV_FS_MKDTEMP:
- argv[1] = String::NewFromUtf8(env->isolate(),
- static_cast<const char*>(req->path));
+ link = StringBytes::Encode(env->isolate(),
+ static_cast<const char*>(req->path),
+ req_wrap->encoding_);
+ if (link.IsEmpty()) {
+ argv[0] = UVException(env->isolate(),
+ UV_EINVAL,
+ req_wrap->syscall(),
+ "Invalid character encoding for filename",
+ req->path,
+ req_wrap->data());
+ } else {
+ argv[1] = link;
+ }
break;
case UV_FS_READLINK:
- argv[1] = String::NewFromUtf8(env->isolate(),
- static_cast<const char*>(req->ptr));
+ link = StringBytes::Encode(env->isolate(),
+ static_cast<const char*>(req->ptr),
+ req_wrap->encoding_);
+ if (link.IsEmpty()) {
+ argv[0] = UVException(env->isolate(),
+ UV_EINVAL,
+ req_wrap->syscall(),
+ "Invalid character encoding for link",
+ req->path,
+ req_wrap->data());
+ } else {
+ argv[1] = link;
+ }
break;
case UV_FS_READ:
@@ -237,8 +268,19 @@ static void After(uv_fs_t *req) {
break;
}
- name_argv[name_idx++] =
- String::NewFromUtf8(env->isolate(), ent.name);
+ Local<Value> filename = StringBytes::Encode(env->isolate(),
+ ent.name,
+ req_wrap->encoding_);
+ if (filename.IsEmpty()) {
+ argv[0] = UVException(env->isolate(),
+ UV_EINVAL,
+ req_wrap->syscall(),
+ "Invalid character encoding for filename",
+ req->path,
+ req_wrap->data());
+ break;
+ }
+ name_argv[name_idx++] = filename;
if (name_idx >= ARRAY_SIZE(name_argv)) {
fn->Call(env->context(), names, name_idx, name_argv)
@@ -277,10 +319,11 @@ struct fs_req_wrap {
};
-#define ASYNC_DEST_CALL(func, req, dest, ...) \
+#define ASYNC_DEST_CALL(func, req, dest, encoding, ...) \
Environment* env = Environment::GetCurrent(args); \
CHECK(req->IsObject()); \
- FSReqWrap* req_wrap = FSReqWrap::New(env, req.As<Object>(), #func, dest); \
+ FSReqWrap* req_wrap = FSReqWrap::New(env, req.As<Object>(), \
+ #func, dest, encoding); \
int err = uv_fs_ ## func(env->event_loop(), \
&req_wrap->req_, \
__VA_ARGS__, \
@@ -296,8 +339,8 @@ struct fs_req_wrap {
args.GetReturnValue().Set(req_wrap->persistent()); \
}
-#define ASYNC_CALL(func, req, ...) \
- ASYNC_DEST_CALL(func, req, nullptr, __VA_ARGS__) \
+#define ASYNC_CALL(func, req, encoding, ...) \
+ ASYNC_DEST_CALL(func, req, nullptr, encoding, __VA_ARGS__) \
#define SYNC_DEST_CALL(func, path, dest, ...) \
fs_req_wrap req_wrap; \
@@ -317,23 +360,22 @@ struct fs_req_wrap {
#define SYNC_RESULT err
-
static void Access(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args.GetIsolate());
HandleScope scope(env->isolate());
if (args.Length() < 2)
return TYPE_ERROR("path and mode are required");
- if (!args[0]->IsString())
- return TYPE_ERROR("path must be a string");
if (!args[1]->IsInt32())
return TYPE_ERROR("mode must be an integer");
- node::Utf8Value path(env->isolate(), args[0]);
+ BufferValue path(env->isolate(), args[0]);
+ ASSERT_PATH(path)
+
int mode = static_cast<int>(args[1]->Int32Value());
if (args[2]->IsObject()) {
- ASYNC_CALL(access, args[2], *path, mode);
+ ASYNC_CALL(access, args[2], UTF8, *path, mode);
} else {
SYNC_CALL(access, *path, *path, mode);
}
@@ -351,7 +393,7 @@ static void Close(const FunctionCallbackInfo<Value>& args) {
int fd = args[0]->Int32Value();
if (args[1]->IsObject()) {
- ASYNC_CALL(close, args[1], fd)
+ ASYNC_CALL(close, args[1], UTF8, fd)
} else {
SYNC_CALL(close, 0, fd)
}
@@ -544,13 +586,12 @@ static void Stat(const FunctionCallbackInfo<Value>& args) {
if (args.Length() < 1)
return TYPE_ERROR("path required");
- if (!args[0]->IsString())
- return TYPE_ERROR("path must be a string");
- node::Utf8Value path(env->isolate(), args[0]);
+ BufferValue path(env->isolate(), args[0]);
+ ASSERT_PATH(path)
if (args[1]->IsObject()) {
- ASYNC_CALL(stat, args[1], *path)
+ ASYNC_CALL(stat, args[1], UTF8, *path)
} else {
SYNC_CALL(stat, *path, *path)
args.GetReturnValue().Set(
@@ -563,13 +604,12 @@ static void LStat(const FunctionCallbackInfo<Value>& args) {
if (args.Length() < 1)
return TYPE_ERROR("path required");
- if (!args[0]->IsString())
- return TYPE_ERROR("path must be a string");
- node::Utf8Value path(env->isolate(), args[0]);
+ BufferValue path(env->isolate(), args[0]);
+ ASSERT_PATH(path)
if (args[1]->IsObject()) {
- ASYNC_CALL(lstat, args[1], *path)
+ ASYNC_CALL(lstat, args[1], UTF8, *path)
} else {
SYNC_CALL(lstat, *path, *path)
args.GetReturnValue().Set(
@@ -588,7 +628,7 @@ static void FStat(const FunctionCallbackInfo<Value>& args) {
int fd = args[0]->Int32Value();
if (args[1]->IsObject()) {
- ASYNC_CALL(fstat, args[1], fd)
+ ASYNC_CALL(fstat, args[1], UTF8, fd)
} else {
SYNC_CALL(fstat, 0, fd)
args.GetReturnValue().Set(
@@ -604,13 +644,12 @@ static void Symlink(const FunctionCallbackInfo<Value>& args) {
return TYPE_ERROR("target path required");
if (len < 2)
return TYPE_ERROR("src path required");
- if (!args[0]->IsString())
- return TYPE_ERROR("target path must be a string");
- if (!args[1]->IsString())
- return TYPE_ERROR("src path must be a string");
- node::Utf8Value target(env->isolate(), args[0]);
- node::Utf8Value path(env->isolate(), args[1]);
+ BufferValue target(env->isolate(), args[0]);
+ ASSERT_PATH(target)
+ BufferValue path(env->isolate(), args[1]);
+ ASSERT_PATH(path)
+
int flags = 0;
if (args[2]->IsString()) {
@@ -625,7 +664,7 @@ static void Symlink(const FunctionCallbackInfo<Value>& args) {
}
if (args[3]->IsObject()) {
- ASYNC_DEST_CALL(symlink, args[3], *path, *target, *path, flags)
+ ASYNC_DEST_CALL(symlink, args[3], *path, UTF8, *target, *path, flags)
} else {
SYNC_DEST_CALL(symlink, *target, *path, *target, *path, flags)
}
@@ -639,37 +678,51 @@ static void Link(const FunctionCallbackInfo<Value>& args) {
return TYPE_ERROR("src path required");
if (len < 2)
return TYPE_ERROR("dest path required");
- if (!args[0]->IsString())
- return TYPE_ERROR("src path must be a string");
- if (!args[1]->IsString())
- return TYPE_ERROR("dest path must be a string");
- node::Utf8Value orig_path(env->isolate(), args[0]);
- node::Utf8Value new_path(env->isolate(), args[1]);
+ BufferValue src(env->isolate(), args[0]);
+ ASSERT_PATH(src)
+
+ BufferValue dest(env->isolate(), args[1]);
+ ASSERT_PATH(dest)
if (args[2]->IsObject()) {
- ASYNC_DEST_CALL(link, args[2], *new_path, *orig_path, *new_path)
+ ASYNC_DEST_CALL(link, args[2], *dest, UTF8, *src, *dest)
} else {
- SYNC_DEST_CALL(link, *orig_path, *new_path, *orig_path, *new_path)
+ SYNC_DEST_CALL(link, *src, *dest, *src, *dest)
}
}
static void ReadLink(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);
- if (args.Length() < 1)
+ const int argc = args.Length();
+
+ if (argc < 1)
return TYPE_ERROR("path required");
- if (!args[0]->IsString())
- return TYPE_ERROR("path must be a string");
- node::Utf8Value path(env->isolate(), args[0]);
+ BufferValue path(env->isolate(), args[0]);
+ ASSERT_PATH(path)
- if (args[1]->IsObject()) {
- ASYNC_CALL(readlink, args[1], *path)
+ const enum encoding encoding = ParseEncoding(env->isolate(), args[1], UTF8);
+
+ Local<Value> callback = Null(env->isolate());
+ if (argc == 3)
+ callback = args[2];
+
+ if (callback->IsObject()) {
+ ASYNC_CALL(readlink, callback, encoding, *path)
} else {
SYNC_CALL(readlink, *path, *path)
const char* link_path = static_cast<const char*>(SYNC_REQ.ptr);
- Local<String> rc = String::NewFromUtf8(env->isolate(), link_path);
+ Local<Value> rc = StringBytes::Encode(env->isolate(),
+ link_path,
+ encoding);
+ if (rc.IsEmpty()) {
+ return env->ThrowUVException(UV_EINVAL,
+ "readlink",
+ "Invalid character encoding for link",
+ *path);
+ }
args.GetReturnValue().Set(rc);
}
}
@@ -682,16 +735,14 @@ static void Rename(const FunctionCallbackInfo<Value>& args) {
return TYPE_ERROR("old path required");
if (len < 2)
return TYPE_ERROR("new path required");
- if (!args[0]->IsString())
- return TYPE_ERROR("old path must be a string");
- if (!args[1]->IsString())
- return TYPE_ERROR("new path must be a string");
- node::Utf8Value old_path(env->isolate(), args[0]);
- node::Utf8Value new_path(env->isolate(), args[1]);
+ BufferValue old_path(env->isolate(), args[0]);
+ ASSERT_PATH(old_path)
+ BufferValue new_path(env->isolate(), args[1]);
+ ASSERT_PATH(new_path)
if (args[2]->IsObject()) {
- ASYNC_DEST_CALL(rename, args[2], *new_path, *old_path, *new_path)
+ ASYNC_DEST_CALL(rename, args[2], *new_path, UTF8, *old_path, *new_path)
} else {
SYNC_DEST_CALL(rename, *old_path, *new_path, *old_path, *new_path)
}
@@ -720,7 +771,7 @@ static void FTruncate(const FunctionCallbackInfo<Value>& args) {
const int64_t len = len_v->IntegerValue();
if (args[2]->IsObject()) {
- ASYNC_CALL(ftruncate, args[2], fd, len)
+ ASYNC_CALL(ftruncate, args[2], UTF8, fd, len)
} else {
SYNC_CALL(ftruncate, 0, fd, len)
}
@@ -737,7 +788,7 @@ static void Fdatasync(const FunctionCallbackInfo<Value>& args) {
int fd = args[0]->Int32Value();
if (args[1]->IsObject()) {
- ASYNC_CALL(fdatasync, args[1], fd)
+ ASYNC_CALL(fdatasync, args[1], UTF8, fd)
} else {
SYNC_CALL(fdatasync, 0, fd)
}
@@ -754,7 +805,7 @@ static void Fsync(const FunctionCallbackInfo<Value>& args) {
int fd = args[0]->Int32Value();
if (args[1]->IsObject()) {
- ASYNC_CALL(fsync, args[1], fd)
+ ASYNC_CALL(fsync, args[1], UTF8, fd)
} else {
SYNC_CALL(fsync, 0, fd)
}
@@ -765,13 +816,12 @@ static void Unlink(const FunctionCallbackInfo<Value>& args) {
if (args.Length() < 1)
return TYPE_ERROR("path required");
- if (!args[0]->IsString())
- return TYPE_ERROR("path must be a string");
- node::Utf8Value path(env->isolate(), args[0]);
+ BufferValue path(env->isolate(), args[0]);
+ ASSERT_PATH(path)
if (args[1]->IsObject()) {
- ASYNC_CALL(unlink, args[1], *path)
+ ASYNC_CALL(unlink, args[1], UTF8, *path)
} else {
SYNC_CALL(unlink, *path, *path)
}
@@ -782,13 +832,12 @@ static void RMDir(const FunctionCallbackInfo<Value>& args) {
if (args.Length() < 1)
return TYPE_ERROR("path required");
- if (!args[0]->IsString())
- return TYPE_ERROR("path must be a string");
- node::Utf8Value path(env->isolate(), args[0]);
+ BufferValue path(env->isolate(), args[0]);
+ ASSERT_PATH(path)
if (args[1]->IsObject()) {
- ASYNC_CALL(rmdir, args[1], *path)
+ ASYNC_CALL(rmdir, args[1], UTF8, *path)
} else {
SYNC_CALL(rmdir, *path, *path)
}
@@ -799,16 +848,16 @@ static void MKDir(const FunctionCallbackInfo<Value>& args) {
if (args.Length() < 2)
return TYPE_ERROR("path and mode are required");
- if (!args[0]->IsString())
- return TYPE_ERROR("path must be a string");
if (!args[1]->IsInt32())
return TYPE_ERROR("mode must be an integer");
- node::Utf8Value path(env->isolate(), args[0]);
+ BufferValue path(env->isolate(), args[0]);
+ ASSERT_PATH(path)
+
int mode = static_cast<int>(args[1]->Int32Value());
if (args[2]->IsObject()) {
- ASYNC_CALL(mkdir, args[2], *path, mode)
+ ASYNC_CALL(mkdir, args[2], UTF8, *path, mode)
} else {
SYNC_CALL(mkdir, *path, *path, mode)
}
@@ -817,15 +866,22 @@ static void MKDir(const FunctionCallbackInfo<Value>& args) {
static void ReadDir(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);
- if (args.Length() < 1)
+ const int argc = args.Length();
+
+ if (argc < 1)
return TYPE_ERROR("path required");
- if (!args[0]->IsString())
- return TYPE_ERROR("path must be a string");
- node::Utf8Value path(env->isolate(), args[0]);
+ BufferValue path(env->isolate(), args[0]);
+ ASSERT_PATH(path)
- if (args[1]->IsObject()) {
- ASYNC_CALL(scandir, args[1], *path, 0 /*flags*/)
+ const enum encoding encoding = ParseEncoding(env->isolate(), args[1], UTF8);
+
+ Local<Value> callback = Null(env->isolate());
+ if (argc == 3)
+ callback = args[2];
+
+ if (callback->IsObject()) {
+ ASYNC_CALL(scandir, callback, encoding, *path, 0 /*flags*/)
} else {
SYNC_CALL(scandir, *path, *path, 0 /*flags*/)
@@ -845,8 +901,17 @@ static void ReadDir(const FunctionCallbackInfo<Value>& args) {
if (r != 0)
return env->ThrowUVException(r, "readdir", "", *path);
+ Local<Value> filename = StringBytes::Encode(env->isolate(),
+ ent.name,
+ encoding);
+ if (filename.IsEmpty()) {
+ return env->ThrowUVException(UV_EINVAL,
+ "readdir",
+ "Invalid character encoding for filename",
+ *path);
+ }
- name_v[name_idx++] = String::NewFromUtf8(env->isolate(), ent.name);
+ name_v[name_idx++] = filename;
if (name_idx >= ARRAY_SIZE(name_v)) {
fn->Call(env->context(), names, name_idx, name_v)
@@ -873,19 +938,19 @@ static void Open(const FunctionCallbackInfo<Value>& args) {
return TYPE_ERROR("flags required");
if (len < 3)
return TYPE_ERROR("mode required");
- if (!args[0]->IsString())
- return TYPE_ERROR("path must be a string");
if (!args[1]->IsInt32())
return TYPE_ERROR("flags must be an int");
if (!args[2]->IsInt32())
return TYPE_ERROR("mode must be an int");
- node::Utf8Value path(env->isolate(), args[0]);
+ BufferValue path(env->isolate(), args[0]);
+ ASSERT_PATH(path)
+
int flags = args[1]->Int32Value();
int mode = static_cast<int>(args[2]->Int32Value());
if (args[3]->IsObject()) {
- ASYNC_CALL(open, args[3], *path, flags, mode)
+ ASYNC_CALL(open, args[3], UTF8, *path, flags, mode)
} else {
SYNC_CALL(open, *path, *path, flags, mode)
args.GetReturnValue().Set(SYNC_RESULT);
@@ -933,7 +998,7 @@ static void WriteBuffer(const FunctionCallbackInfo<Value>& args) {
uv_buf_t uvbuf = uv_buf_init(const_cast<char*>(buf), len);
if (req->IsObject()) {
- ASYNC_CALL(write, req, fd, &uvbuf, 1, pos)
+ ASYNC_CALL(write, req, UTF8, fd, &uvbuf, 1, pos)
return;
}
@@ -983,7 +1048,7 @@ static void WriteBuffers(const FunctionCallbackInfo<Value>& args) {
}
if (req->IsObject()) {
- ASYNC_CALL(write, req, fd, iovs, chunkCount, pos)
+ ASYNC_CALL(write, req, UTF8, fd, iovs, chunkCount, pos)
if (iovs != s_iovs)
delete[] iovs;
return;
@@ -1049,7 +1114,7 @@ static void WriteString(const FunctionCallbackInfo<Value>& args) {
}
FSReqWrap* req_wrap =
- FSReqWrap::New(env, req.As<Object>(), "write", buf, ownership);
+ FSReqWrap::New(env, req.As<Object>(), "write", buf, UTF8, ownership);
int err = uv_fs_write(env->event_loop(),
&req_wrap->req_,
fd,
@@ -1123,7 +1188,7 @@ static void Read(const FunctionCallbackInfo<Value>& args) {
req = args[5];
if (req->IsObject()) {
- ASYNC_CALL(read, req, fd, &uvbuf, 1, pos);
+ ASYNC_CALL(read, req, UTF8, fd, &uvbuf, 1, pos);
} else {
SYNC_CALL(read, 0, fd, &uvbuf, 1, pos)
args.GetReturnValue().Set(SYNC_RESULT);
@@ -1139,16 +1204,16 @@ static void Chmod(const FunctionCallbackInfo<Value>& args) {
if (args.Length() < 2)
return TYPE_ERROR("path and mode are required");
- if (!args[0]->IsString())
- return TYPE_ERROR("path must be a string");
if (!args[1]->IsInt32())
return TYPE_ERROR("mode must be an integer");
- node::Utf8Value path(env->isolate(), args[0]);
+ BufferValue path(env->isolate(), args[0]);
+ ASSERT_PATH(path)
+
int mode = static_cast<int>(args[1]->Int32Value());
if (args[2]->IsObject()) {
- ASYNC_CALL(chmod, args[2], *path, mode);
+ ASYNC_CALL(chmod, args[2], UTF8, *path, mode);
} else {
SYNC_CALL(chmod, *path, *path, mode);
}
@@ -1172,7 +1237,7 @@ static void FChmod(const FunctionCallbackInfo<Value>& args) {
int mode = static_cast<int>(args[1]->Int32Value());
if (args[2]->IsObject()) {
- ASYNC_CALL(fchmod, args[2], fd, mode);
+ ASYNC_CALL(fchmod, args[2], UTF8, fd, mode);
} else {
SYNC_CALL(fchmod, 0, fd, mode);
}
@@ -1192,19 +1257,19 @@ static void Chown(const FunctionCallbackInfo<Value>& args) {
return TYPE_ERROR("uid required");
if (len < 3)
return TYPE_ERROR("gid required");
- if (!args[0]->IsString())
- return TYPE_ERROR("path must be a string");
if (!args[1]->IsUint32())
return TYPE_ERROR("uid must be an unsigned int");
if (!args[2]->IsUint32())
return TYPE_ERROR("gid must be an unsigned int");
- node::Utf8Value path(env->isolate(), args[0]);
+ BufferValue path(env->isolate(), args[0]);
+ ASSERT_PATH(path)
+
uv_uid_t uid = static_cast<uv_uid_t>(args[1]->Uint32Value());
uv_gid_t gid = static_cast<uv_gid_t>(args[2]->Uint32Value());
if (args[3]->IsObject()) {
- ASYNC_CALL(chown, args[3], *path, uid, gid);
+ ASYNC_CALL(chown, args[3], UTF8, *path, uid, gid);
} else {
SYNC_CALL(chown, *path, *path, uid, gid);
}
@@ -1236,7 +1301,7 @@ static void FChown(const FunctionCallbackInfo<Value>& args) {
uv_gid_t gid = static_cast<uv_gid_t>(args[2]->Uint32Value());
if (args[3]->IsObject()) {
- ASYNC_CALL(fchown, args[3], fd, uid, gid);
+ ASYNC_CALL(fchown, args[3], UTF8, fd, uid, gid);
} else {
SYNC_CALL(fchown, 0, fd, uid, gid);
}
@@ -1253,19 +1318,19 @@ static void UTimes(const FunctionCallbackInfo<Value>& args) {
return TYPE_ERROR("atime required");
if (len < 3)
return TYPE_ERROR("mtime required");
- if (!args[0]->IsString())
- return TYPE_ERROR("path must be a string");
if (!args[1]->IsNumber())
return TYPE_ERROR("atime must be a number");
if (!args[2]->IsNumber())
return TYPE_ERROR("mtime must be a number");
- const node::Utf8Value path(env->isolate(), args[0]);
+ BufferValue path(env->isolate(), args[0]);
+ ASSERT_PATH(path)
+
const double atime = static_cast<double>(args[1]->NumberValue());
const double mtime = static_cast<double>(args[2]->NumberValue());
if (args[3]->IsObject()) {
- ASYNC_CALL(utime, args[3], *path, atime, mtime);
+ ASYNC_CALL(utime, args[3], UTF8, *path, atime, mtime);
} else {
SYNC_CALL(utime, *path, *path, atime, mtime);
}
@@ -1293,7 +1358,7 @@ static void FUTimes(const FunctionCallbackInfo<Value>& args) {
const double mtime = static_cast<double>(args[2]->NumberValue());
if (args[3]->IsObject()) {
- ASYNC_CALL(futime, args[3], fd, atime, mtime);
+ ASYNC_CALL(futime, args[3], UTF8, fd, atime, mtime);
} else {
SYNC_CALL(futime, 0, fd, atime, mtime);
}
@@ -1302,19 +1367,27 @@ static void FUTimes(const FunctionCallbackInfo<Value>& args) {
static void Mkdtemp(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);
- if (args.Length() < 1)
- return TYPE_ERROR("template is required");
- if (!args[0]->IsString())
- return TYPE_ERROR("template must be a string");
+ CHECK_GE(args.Length(), 2);
- node::Utf8Value tmpl(env->isolate(), args[0]);
+ BufferValue tmpl(env->isolate(), args[0]);
+ if (*tmpl == nullptr)
+ return TYPE_ERROR("template must be a string or Buffer");
- if (args[1]->IsObject()) {
- ASYNC_CALL(mkdtemp, args[1], *tmpl);
+ const enum encoding encoding = ParseEncoding(env->isolate(), args[1], UTF8);
+
+ if (args[2]->IsObject()) {
+ ASYNC_CALL(mkdtemp, args[2], encoding, *tmpl);
} else {
SYNC_CALL(mkdtemp, *tmpl, *tmpl);
- args.GetReturnValue().Set(String::NewFromUtf8(env->isolate(),
- SYNC_REQ.path));
+ const char* path = static_cast<const char*>(SYNC_REQ.path);
+ Local<Value> rc = StringBytes::Encode(env->isolate(), path, encoding);
+ if (rc.IsEmpty()) {
+ return env->ThrowUVException(UV_EINVAL,
+ "mkdtemp",
+ "Invalid character encoding for filename",
+ *tmpl);
+ }
+ args.GetReturnValue().Set(rc);
}
}
diff --git a/src/string_bytes.cc b/src/string_bytes.cc
index a2e4fe388a..b8d7d3b42f 100644
--- a/src/string_bytes.cc
+++ b/src/string_bytes.cc
@@ -24,7 +24,6 @@ using v8::String;
using v8::Value;
using v8::MaybeLocal;
-
template <typename ResourceType, typename TypeName>
class ExternString: public ResourceType {
public:
@@ -895,4 +894,34 @@ Local<Value> StringBytes::Encode(Isolate* isolate,
return val;
}
+Local<Value> StringBytes::Encode(Isolate* isolate,
+ const char* buf,
+ enum encoding encoding) {
+ const size_t len = strlen(buf);
+ Local<Value> ret;
+ if (encoding == UCS2) {
+ // In Node, UCS2 means utf16le. The data must be in little-endian
+ // order and must be aligned on 2-bytes. This returns an empty
+ // value if it's not aligned and ensures the appropriate byte order
+ // on big endian architectures.
+ const bool be = IsBigEndian();
+ if (len % 2 != 0)
+ return ret;
+ std::vector<uint16_t> vec(len / 2);
+ for (size_t i = 0, k = 0; i < len; i += 2, k += 1) {
+ const uint8_t hi = static_cast<uint8_t>(buf[i + 0]);
+ const uint8_t lo = static_cast<uint8_t>(buf[i + 1]);
+ vec[k] = be ?
+ static_cast<uint16_t>(hi) << 8 | lo
+ : static_cast<uint16_t>(lo) << 8 | hi;
+ }
+ ret = vec.empty() ?
+ static_cast< Local<Value> >(String::Empty(isolate))
+ : StringBytes::Encode(isolate, &vec[0], vec.size());
+ } else {
+ ret = StringBytes::Encode(isolate, buf, len, encoding);
+ }
+ return ret;
+}
+
} // namespace node
diff --git a/src/string_bytes.h b/src/string_bytes.h
index 79520d2470..7108862139 100644
--- a/src/string_bytes.h
+++ b/src/string_bytes.h
@@ -106,6 +106,10 @@ class StringBytes {
const uint16_t* buf,
size_t buflen);
+ static v8::Local<v8::Value> Encode(v8::Isolate* isolate,
+ const char* buf,
+ enum encoding encoding);
+
// Deprecated legacy interface
NODE_DEPRECATED("Use IsValidString(isolate, ...)",
diff --git a/src/util.cc b/src/util.cc
index 095e5582db..3b0278ceda 100644
--- a/src/util.cc
+++ b/src/util.cc
@@ -1,37 +1,48 @@
#include "util.h"
#include "string_bytes.h"
+#include "node_buffer.h"
+#include <stdio.h>
namespace node {
-Utf8Value::Utf8Value(v8::Isolate* isolate, v8::Local<v8::Value> value)
- : length_(0), str_(str_st_) {
- if (value.IsEmpty())
- return;
+using v8::Isolate;
+using v8::String;
+using v8::Local;
+using v8::Value;
- v8::Local<v8::String> string = value->ToString(isolate);
+static int MakeUtf8String(Isolate* isolate,
+ Local<Value> value,
+ char** dst,
+ const size_t size) {
+ Local<String> string = value->ToString(isolate);
if (string.IsEmpty())
- return;
-
- // Allocate enough space to include the null terminator
+ return 0;
size_t len = StringBytes::StorageSize(isolate, string, UTF8) + 1;
- if (len > sizeof(str_st_)) {
- str_ = static_cast<char*>(malloc(len));
- CHECK_NE(str_, nullptr);
+ if (len > size) {
+ *dst = static_cast<char*>(malloc(len));
+ CHECK_NE(*dst, nullptr);
}
-
const int flags =
- v8::String::NO_NULL_TERMINATION | v8::String::REPLACE_INVALID_UTF8;
- length_ = string->WriteUtf8(str_, len, 0, flags);
- str_[length_] = '\0';
+ String::NO_NULL_TERMINATION | String::REPLACE_INVALID_UTF8;
+ const int length = string->WriteUtf8(*dst, len, 0, flags);
+ (*dst)[length] = '\0';
+ return length;
+}
+
+Utf8Value::Utf8Value(Isolate* isolate, Local<Value> value)
+ : length_(0), str_(str_st_) {
+ if (value.IsEmpty())
+ return;
+ length_ = MakeUtf8String(isolate, value, &str_, sizeof(str_st_));
}
-TwoByteValue::TwoByteValue(v8::Isolate* isolate, v8::Local<v8::Value> value)
+TwoByteValue::TwoByteValue(Isolate* isolate, Local<Value> value)
: length_(0), str_(str_st_) {
if (value.IsEmpty())
return;
- v8::Local<v8::String> string = value->ToString(isolate);
+ Local<String> string = value->ToString(isolate);
if (string.IsEmpty())
return;
@@ -43,9 +54,31 @@ TwoByteValue::TwoByteValue(v8::Isolate* isolate, v8::Local<v8::Value> value)
}
const int flags =
- v8::String::NO_NULL_TERMINATION | v8::String::REPLACE_INVALID_UTF8;
+ String::NO_NULL_TERMINATION | String::REPLACE_INVALID_UTF8;
length_ = string->Write(str_, 0, len, flags);
str_[length_] = '\0';
}
+BufferValue::BufferValue(Isolate* isolate, Local<Value> value)
+ : str_(str_st_), fail_(true) {
+ // Slightly different take on Utf8Value. If value is a String,
+ // it will return a Utf8 encoded string. If value is a Buffer,
+ // it will copy the data out of the Buffer as is.
+ if (value.IsEmpty())
+ return;
+ if (value->IsString()) {
+ MakeUtf8String(isolate, value, &str_, sizeof(str_st_));
+ fail_ = false;
+ } else if (Buffer::HasInstance(value)) {
+ size_t len = Buffer::Length(value) + 1;
+ if (len > sizeof(str_st_)) {
+ str_ = static_cast<char*>(malloc(len));
+ CHECK_NE(str_, nullptr);
+ }
+ memcpy(str_, Buffer::Data(value), len);
+ str_[len - 1] = '\0';
+ fail_ = false;
+ }
+}
+
} // namespace node
diff --git a/src/util.h b/src/util.h
index 84d0b5a170..2c4d8c6286 100644
--- a/src/util.h
+++ b/src/util.h
@@ -232,6 +232,25 @@ class TwoByteValue {
uint16_t str_st_[1024];
};
+class BufferValue {
+ public:
+ explicit BufferValue(v8::Isolate* isolate, v8::Local<v8::Value> value);
+
+ ~BufferValue() {
+ if (str_ != str_st_)
+ free(str_);
+ }
+
+ const char* operator*() const {
+ return fail_ ? nullptr : str_;
+ };
+
+ private:
+ char* str_;
+ char str_st_[1024];
+ bool fail_;
+};
+
} // namespace node
#endif // SRC_UTIL_H_