[njs] Adding support for Buffer objects in "fs" methods.

Dmitry Volyntsev xeioex at nginx.com
Mon Sep 28 17:46:32 UTC 2020


details:   https://hg.nginx.org/njs/rev/f1ed239edc7d
branches:  
changeset: 1530:f1ed239edc7d
user:      Dmitry Volyntsev <xeioex at nginx.com>
date:      Mon Sep 28 17:45:44 2020 +0000
description:
Adding support for Buffer objects in "fs" methods.

fs.writeFile(), fs.appendFile() and friends may accept an instance of
Buffer as an argument.

Also, fs.readFile() and friends now return an instance of Buffer instead of
Byte-string when encoding is not provided.

Added Buffer encoding for fs.readdir(), fs.realpath() and friends.

diffstat:

 src/njs_fs.c                 |  511 +++++++++++++++++++-----------------------
 src/test/njs_unit_test.c     |  175 +++++++-------
 test/js/fs_appendFile.js     |   61 +++++
 test/js/fs_appendFileSync.js |   59 ++++
 test/js/fs_promises_001.js   |    5 -
 test/js/fs_promises_007.js   |   16 +-
 test/js/fs_readFile.js       |   40 +++
 test/js/fs_readFileSync.js   |   43 +++
 test/js/fs_writeFile.js      |   57 ++++
 test/js/fs_writeFileSync.js  |   54 ++++
 test/njs_expect_test.exp     |  284 +++++++----------------
 11 files changed, 736 insertions(+), 569 deletions(-)

diffs (truncated from 1810 to 1000 lines):

diff -r 366aa456dc90 -r f1ed239edc7d src/njs_fs.c
--- a/src/njs_fs.c	Mon Sep 28 16:59:35 2020 +0000
+++ b/src/njs_fs.c	Mon Sep 28 17:45:44 2020 +0000
@@ -50,13 +50,6 @@ typedef enum {
 } njs_fs_writemode_t;
 
 
-typedef enum {
-    NJS_FS_ENC_INVALID,
-    NJS_FS_ENC_NONE,
-    NJS_FS_ENC_UTF8,
-} njs_fs_encoding_t;
-
-
 typedef struct {
     njs_str_t   name;
     int         value;
@@ -92,19 +85,19 @@ static njs_int_t njs_fs_error(njs_vm_t *
 static njs_int_t njs_fs_result(njs_vm_t *vm, njs_value_t *result,
     njs_index_t calltype, const njs_value_t* callback, njs_uint_t nargs);
 
-static njs_int_t
-njs_file_tree_walk(const char *path, njs_file_tree_walk_cb_t cb, int fd_limit,
-    njs_ftw_flags_t flags);
+static njs_int_t njs_file_tree_walk(const char *path,
+    njs_file_tree_walk_cb_t cb, int fd_limit, njs_ftw_flags_t flags);
 
 static njs_int_t njs_fs_make_path(njs_vm_t *vm, const char *path, mode_t md,
     njs_bool_t recursive, njs_value_t *retval);
 static njs_int_t njs_fs_rmtree(njs_vm_t *vm, const char *path,
     njs_bool_t recursive, njs_value_t *retval);
 
+static njs_int_t njs_fs_path(njs_vm_t *vm, const char **dst,
+    const njs_value_t* src, const njs_str_t *prop_name);
 static int njs_fs_flags(njs_vm_t *vm, njs_value_t *value, int default_flags);
 static mode_t njs_fs_mode(njs_vm_t *vm, njs_value_t *value,
     mode_t default_mode);
-static njs_fs_encoding_t njs_fs_encoding(njs_vm_t *vm, njs_value_t *value);
 
 static njs_int_t njs_fs_add_event(njs_vm_t *vm, const njs_value_t *callback,
     const njs_value_t *args, njs_uint_t nargs);
@@ -135,44 +128,28 @@ static njs_fs_entry_t njs_flags_table[] 
 };
 
 
-njs_inline njs_int_t
-njs_fs_path_arg(njs_vm_t *vm, const char **dst, const njs_value_t* src,
-    const njs_str_t *prop_name)
-{
-    if (njs_slow_path(!njs_is_string(src))) {
-        njs_type_error(vm, "\"%V\" must be a string", prop_name);
-        return NJS_ERROR;
-    }
-
-    *dst = njs_string_to_c_string(vm, njs_value_arg(src));
-    if (njs_slow_path(*dst == NULL)) {
-        return NJS_ERROR;
-    }
-
-    return NJS_OK;
-}
+static const njs_value_t  string_flag = njs_string("flag");
+static const njs_value_t  string_mode = njs_string("mode");
+static const njs_value_t  string_buffer = njs_string("buffer");
+static const njs_value_t  string_encoding = njs_string("encoding");
+static const njs_value_t  string_recursive = njs_string("recursive");
 
 
 static njs_int_t
 njs_fs_read_file(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
     njs_index_t calltype)
 {
-    int                fd, flags;
-    u_char             *start;
-    size_t             size;
-    ssize_t            length;
-    njs_str_t          data;
-    njs_int_t          ret;
-    const char         *file_path;
-    njs_value_t        flag, encoding, retval, *callback, *options, *path;
-    struct stat        sb;
-    njs_fs_encoding_t  enc;
-
-    static const njs_value_t  string_flag = njs_string("flag");
-    static const njs_value_t  string_encoding = njs_string("encoding");
+    int                          fd, flags;
+    njs_str_t                    data;
+    njs_int_t                    ret;
+    const char                   *file_path;
+    njs_value_t                  flag, encode, retval, *callback, *options,
+                                 *path;
+    struct stat                  sb;
+    const njs_buffer_encoding_t  *encoding;
 
     path = njs_arg(args, nargs, 1);
-    ret = njs_fs_path_arg(vm, &file_path, path, &njs_str_value("path"));
+    ret = njs_fs_path(vm, &file_path, path, &njs_str_value("path"));
     if (njs_slow_path(ret != NJS_OK)) {
         return ret;
     }
@@ -194,11 +171,11 @@ njs_fs_read_file(njs_vm_t *vm, njs_value
     }
 
     njs_set_undefined(&flag);
-    njs_set_undefined(&encoding);
+    njs_set_undefined(&encode);
 
     switch (options->type) {
     case NJS_STRING:
-        encoding = *options;
+        encode = *options;
         break;
 
     case NJS_UNDEFINED:
@@ -219,7 +196,7 @@ njs_fs_read_file(njs_vm_t *vm, njs_value
         }
 
         ret = njs_value_property(vm, options, njs_value_arg(&string_encoding),
-                                 &encoding);
+                                 &encode);
         if (njs_slow_path(ret == NJS_ERROR)) {
             return ret;
         }
@@ -230,9 +207,12 @@ njs_fs_read_file(njs_vm_t *vm, njs_value
         return NJS_ERROR;
     }
 
-    enc = njs_fs_encoding(vm, &encoding);
-    if (njs_slow_path(enc == NJS_FS_ENC_INVALID)) {
-        return NJS_ERROR;
+    encoding = NULL;
+    if (njs_is_defined(&encode)) {
+        encoding = njs_buffer_encoding(vm, &encode);
+        if (njs_slow_path(encoding == NULL)) {
+            return NJS_ERROR;
+        }
     }
 
     fd = open(file_path, flags);
@@ -252,88 +232,25 @@ njs_fs_read_file(njs_vm_t *vm, njs_value
         goto done;
     }
 
-    if (enc == NJS_FS_ENC_UTF8) {
-        length = sb.st_size;
-
-        if (length > NJS_STRING_MAP_STRIDE) {
-            /*
-             * At this point length is not known, in order to set it to
-             * the correct value after file is read, we need to ensure that
-             * offset_map is allocated by njs_string_alloc(). This can be
-             * achieved by making length != size.
-             */
-            length += 1;
+    data.start = NULL;
+    data.length = sb.st_size;
+
+    ret = njs_fs_fd_read(vm, fd, &data);
+    if (njs_slow_path(ret != NJS_OK)) {
+        if (ret == NJS_DECLINED) {
+            ret = njs_fs_error(vm, "read", strerror(errno), path, errno,
+                               &retval);
         }
 
+        goto done;
+    }
+
+    if (encoding == NULL) {
+        ret = njs_buffer_set(vm, &retval, data.start, data.length);
+
     } else {
-        length = 0;
-    }
-
-    size = sb.st_size;
-
-    if (njs_fast_path(size != 0)) {
-        start = njs_string_alloc(vm, &retval, size, length);
-        if (njs_slow_path(start == NULL)) {
-            ret = NJS_ERROR;
-            goto done;
-        }
-
-        data.start = start;
-        data.length = size;
-
-        ret = njs_fs_fd_read(vm, fd, &data);
-        if (njs_slow_path(ret != NJS_OK)) {
-            if (ret == NJS_DECLINED) {
-                ret = njs_fs_error(vm, "read", strerror(errno), path, errno,
-                                   &retval);
-            }
-
-            goto done;
-        }
-
-        if (njs_slow_path(data.length < size)) {
-            /* Pseudo-files may return less data than declared by st_size. */
-            njs_string_truncate(&retval, data.length, length);
-        }
-
-        size = data.length;
-        start = data.start;
-
-    } else {
-        /* size of the file is not known in advance. */
-
-        data.length = 0;
-
-        ret = njs_fs_fd_read(vm, fd, &data);
-        if (njs_slow_path(ret != NJS_OK)) {
-            if (ret == NJS_DECLINED) {
-                ret = njs_fs_error(vm, "read", strerror(errno), path, errno,
-                                   &retval);
-            }
-
-            goto done;
-        }
-
-        size = data.length;
-        start = data.start;
-
-        ret = njs_string_new(vm, &retval, start, size, length);
-        if (njs_slow_path(ret != NJS_OK)) {
-            goto done;
-        }
-    }
-
-    if (enc == NJS_FS_ENC_UTF8) {
-        length = njs_utf8_length(start, size);
-
-        if (length >= 0) {
-            njs_string_length_set(&retval, length);
-
-        } else {
-            ret = njs_fs_error(vm, NULL, "Non-UTF8 file, convertion "
-                               "is not implemented", path, 0, &retval);
-            goto done;
-        }
+        ret = encoding->encode(vm, &retval, &data);
+        njs_mp_free(vm->mem_pool, data.start);
     }
 
 done:
@@ -354,34 +271,26 @@ static njs_int_t
 njs_fs_write_file(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
     njs_index_t magic)
 {
-    int                fd, flags;
-    u_char             *p, *end;
-    mode_t             md;
-    ssize_t            n;
-    njs_str_t          content;
-    njs_int_t          ret;
-    const char         *file_path;
-    njs_value_t        flag, mode, encoding, retval,
-                       *path, *data, *callback, *options;
-    njs_fs_encoding_t  enc;
-    njs_fs_calltype_t  calltype;
-
-    static const njs_value_t  string_flag = njs_string("flag");
-    static const njs_value_t  string_mode = njs_string("mode");
-    static const njs_value_t  string_encoding = njs_string("encoding");
+    int                          fd, flags;
+    u_char                       *p, *end;
+    mode_t                       md;
+    ssize_t                      n;
+    njs_str_t                    content;
+    njs_int_t                    ret;
+    const char                   *file_path;
+    njs_value_t                  flag, mode, encode, retval, *path, *data,
+                                 *callback, *options;
+    njs_typed_array_t            *array;
+    njs_fs_calltype_t            calltype;
+    njs_array_buffer_t           *buffer;
+    const njs_buffer_encoding_t  *encoding;
 
     path = njs_arg(args, nargs, 1);
-    ret = njs_fs_path_arg(vm, &file_path, path, &njs_str_value("path"));
+    ret = njs_fs_path(vm, &file_path, path, &njs_str_value("path"));
     if (njs_slow_path(ret != NJS_OK)) {
         return ret;
     }
 
-    data = njs_arg(args, nargs, 2);
-    if (njs_slow_path(!njs_is_string(data))) {
-        njs_type_error(vm, "\"data\" must be a string");
-        return NJS_ERROR;
-    }
-
     callback = NULL;
     calltype = magic & 3;
     options = njs_arg(args, nargs, 3);
@@ -400,11 +309,11 @@ njs_fs_write_file(njs_vm_t *vm, njs_valu
 
     njs_set_undefined(&flag);
     njs_set_undefined(&mode);
-    njs_set_undefined(&encoding);
+    njs_set_undefined(&encode);
 
     switch (options->type) {
     case NJS_STRING:
-        encoding = *options;
+        encode = *options;
         break;
 
     case NJS_UNDEFINED:
@@ -431,12 +340,49 @@ njs_fs_write_file(njs_vm_t *vm, njs_valu
         }
 
         ret = njs_value_property(vm, options, njs_value_arg(&string_encoding),
-                                 &encoding);
+                                 &encode);
         if (njs_slow_path(ret == NJS_ERROR)) {
             return ret;
         }
     }
 
+    data = njs_arg(args, nargs, 2);
+
+    switch (data->type) {
+    case NJS_TYPED_ARRAY:
+    case NJS_DATA_VIEW:
+        array = njs_typed_array(data);
+        buffer = array->buffer;
+        if (njs_slow_path(njs_is_detached_buffer(buffer))) {
+            njs_type_error(vm, "detached buffer");
+            return NJS_ERROR;
+        }
+
+        content.start = &buffer->u.u8[array->offset];
+        content.length = array->byte_length;
+        break;
+
+    case NJS_STRING:
+    default:
+        encoding = njs_buffer_encoding(vm, &encode);
+        if (njs_slow_path(encoding == NULL)) {
+            return NJS_ERROR;
+        }
+
+        ret = njs_value_to_string(vm, &retval, data);
+        if (njs_slow_path(ret != NJS_OK)) {
+            return NJS_ERROR;
+        }
+
+        ret = njs_buffer_decode_string(vm, &retval, &retval, encoding);
+        if (njs_slow_path(ret != NJS_OK)) {
+            return NJS_ERROR;
+        }
+
+        njs_string_get(&retval, &content);
+        break;
+    }
+
     flags = njs_fs_flags(vm, &flag, O_CREAT | O_WRONLY);
     if (njs_slow_path(flags == -1)) {
         return NJS_ERROR;
@@ -449,19 +395,12 @@ njs_fs_write_file(njs_vm_t *vm, njs_valu
         return NJS_ERROR;
     }
 
-    enc = njs_fs_encoding(vm, &encoding);
-    if (njs_slow_path(enc == NJS_FS_ENC_INVALID)) {
-        return NJS_ERROR;
-    }
-
     fd = open(file_path, flags, md);
     if (njs_slow_path(fd < 0)) {
         ret = njs_fs_error(vm, "open", strerror(errno), path, errno, &retval);
         goto done;
     }
 
-    njs_string_get(data, &content);
-
     p = content.start;
     end = p + content.length;
 
@@ -515,13 +454,13 @@ njs_fs_rename(njs_vm_t *vm, njs_value_t 
         }
     }
 
-    ret = njs_fs_path_arg(vm, &old_path, njs_arg(args, nargs, 1),
+    ret = njs_fs_path(vm, &old_path, njs_arg(args, nargs, 1),
                           &njs_str_value("oldPath"));
     if (njs_slow_path(ret != NJS_OK)) {
         return ret;
     }
 
-    ret = njs_fs_path_arg(vm, &new_path, njs_arg(args, nargs, 2),
+    ret = njs_fs_path(vm, &new_path, njs_arg(args, nargs, 2),
                           &njs_str_value("newPath"));
     if (njs_slow_path(ret != NJS_OK)) {
         return ret;
@@ -552,7 +491,7 @@ njs_fs_access(njs_vm_t *vm, njs_value_t 
     njs_value_t  retval, *path, *callback, *mode;
 
     path = njs_arg(args, nargs, 1);
-    ret = njs_fs_path_arg(vm, &file_path, path, &njs_str_value("path"));
+    ret = njs_fs_path(vm, &file_path, path, &njs_str_value("path"));
     if (njs_slow_path(ret != NJS_OK)) {
         return ret;
     }
@@ -610,13 +549,13 @@ njs_fs_symlink(njs_vm_t *vm, njs_value_t
     njs_value_t  retval, *target, *path, *callback, *type;
 
     target = njs_arg(args, nargs, 1);
-    ret = njs_fs_path_arg(vm, &target_path, target, &njs_str_value("target"));
+    ret = njs_fs_path(vm, &target_path, target, &njs_str_value("target"));
     if (njs_slow_path(ret != NJS_OK)) {
         return ret;
     }
 
     path = njs_arg(args, nargs, 2);
-    ret = njs_fs_path_arg(vm, &file_path, path, &njs_str_value("path"));
+    ret = njs_fs_path(vm, &file_path, path, &njs_str_value("path"));
     if (njs_slow_path(ret != NJS_OK)) {
         return ret;
     }
@@ -666,7 +605,7 @@ njs_fs_unlink(njs_vm_t *vm, njs_value_t 
     njs_value_t  retval, *path, *callback;
 
     path = njs_arg(args, nargs, 1);
-    ret = njs_fs_path_arg(vm, &file_path, path, &njs_str_value("path"));
+    ret = njs_fs_path(vm, &file_path, path, &njs_str_value("path"));
     if (njs_slow_path(ret != NJS_OK)) {
         return ret;
     }
@@ -700,19 +639,15 @@ static njs_int_t
 njs_fs_realpath(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
     njs_index_t calltype)
 {
-    u_char             *resolved_path;
-    size_t             size;
-    ssize_t            length;
-    njs_int_t          ret;
-    const char         *file_path;
-    njs_value_t        encoding, retval, *path, *callback, *options;
-    njs_fs_encoding_t  enc;
-    char               path_buf[MAXPATHLEN];
-
-    static const njs_value_t  string_encoding = njs_string("encoding");
+    njs_int_t                    ret;
+    njs_str_t                    s;
+    const char                   *file_path;
+    njs_value_t                  encode, retval, *path, *callback, *options;
+    const njs_buffer_encoding_t  *encoding;
+    char                         path_buf[MAXPATHLEN];
 
     path = njs_arg(args, nargs, 1);
-    ret = njs_fs_path_arg(vm, &file_path, path, &njs_str_value("path"));
+    ret = njs_fs_path(vm, &file_path, path, &njs_str_value("path"));
     if (njs_slow_path(ret != NJS_OK)) {
         return ret;
     }
@@ -732,11 +667,11 @@ njs_fs_realpath(njs_vm_t *vm, njs_value_
         }
     }
 
-    njs_set_undefined(&encoding);
+    njs_set_undefined(&encode);
 
     switch (options->type) {
     case NJS_STRING:
-        encoding = *options;
+        encode = *options;
         break;
 
     case NJS_UNDEFINED:
@@ -751,33 +686,34 @@ njs_fs_realpath(njs_vm_t *vm, njs_value_
         }
 
         ret = njs_value_property(vm, options, njs_value_arg(&string_encoding),
-                                 &encoding);
+                                 &encode);
         if (njs_slow_path(ret == NJS_ERROR)) {
             return ret;
         }
     }
 
-    enc = njs_fs_encoding(vm, &encoding);
-    if (njs_slow_path(enc == NJS_FS_ENC_INVALID)) {
-        return NJS_ERROR;
+    encoding = NULL;
+    if (!njs_is_string(&encode) || !njs_string_eq(&encode, &string_buffer)) {
+        encoding = njs_buffer_encoding(vm, &encode);
+        if (njs_slow_path(encoding == NULL)) {
+            return NJS_ERROR;
+        }
     }
 
-    resolved_path = (u_char *) realpath(file_path, path_buf);
-    if (njs_slow_path(resolved_path == NULL)) {
+    s.start = (u_char *) realpath(file_path, path_buf);
+    if (njs_slow_path(s.start == NULL)) {
         ret = njs_fs_error(vm, "realpath", strerror(errno), path, errno,
                            &retval);
         goto done;
     }
 
-    size = njs_strlen(resolved_path);
-    length = njs_utf8_length(resolved_path, size);
-    if (njs_slow_path(length < 0)) {
-        length = 0;
-    }
-
-    ret = njs_string_new(vm, &retval, resolved_path, size, length);
-    if (njs_slow_path(ret != NJS_OK)) {
-        return NJS_ERROR;
+    s.length = njs_strlen(s.start);
+
+    if (encoding == NULL) {
+        ret = njs_buffer_new(vm, &retval, s.start, s.length);
+
+    } else {
+        ret = encoding->encode(vm, &retval, &s);
     }
 
 done:
@@ -799,11 +735,8 @@ njs_fs_mkdir(njs_vm_t *vm, njs_value_t *
     const char   *file_path;
     njs_value_t  mode, recursive, retval, *path, *callback, *options;
 
-    static const njs_value_t  string_mode = njs_string("mode");
-    static const njs_value_t  string_recursive = njs_string("recursive");
-
     path = njs_arg(args, nargs, 1);
-    ret = njs_fs_path_arg(vm, &file_path, path, &njs_str_value("path"));
+    ret = njs_fs_path(vm, &file_path, path, &njs_str_value("path"));
     if (njs_slow_path(ret != NJS_OK)) {
         return ret;
     }
@@ -878,10 +811,8 @@ njs_fs_rmdir(njs_vm_t *vm, njs_value_t *
     const char   *file_path;
     njs_value_t  recursive, retval, *path, *callback, *options;
 
-    static const njs_value_t  string_recursive = njs_string("recursive");
-
     path = njs_arg(args, nargs, 1);
-    ret = njs_fs_path_arg(vm, &file_path, path, &njs_str_value("path"));
+    ret = njs_fs_path(vm, &file_path, path, &njs_str_value("path"));
     if (njs_slow_path(ret != NJS_OK)) {
         return ret;
     }
@@ -935,23 +866,20 @@ static njs_int_t
 njs_fs_readdir(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
     njs_index_t calltype)
 {
-    DIR                *dir;
-    u_char             *d_name;
-    size_t             size;
-    ssize_t            length;
-    njs_int_t          ret;
-    const char         *dir_path;
-    njs_value_t        encoding, types, ename, etype, retval, *path, *callback,
-                       *options, *value;
-    njs_array_t        *results;
-    struct dirent      *entry;
-    njs_fs_encoding_t  enc;
-
-    static const njs_value_t  string_encoding = njs_string("encoding");
+    DIR                          *dir;
+    njs_str_t                    s;
+    njs_int_t                    ret;
+    const char                   *dir_path;
+    njs_value_t                  encode, types, ename, etype, retval, *path,
+                                 *callback, *options, *value;
+    njs_array_t                  *results;
+    struct dirent                *entry;
+    const njs_buffer_encoding_t  *encoding;
+
     static const njs_value_t  string_types = njs_string("withFileTypes");
 
     path = njs_arg(args, nargs, 1);
-    ret = njs_fs_path_arg(vm, &dir_path, path, &njs_str_value("path"));
+    ret = njs_fs_path(vm, &dir_path, path, &njs_str_value("path"));
     if (njs_slow_path(ret != NJS_OK)) {
         return ret;
     }
@@ -971,11 +899,11 @@ njs_fs_readdir(njs_vm_t *vm, njs_value_t
     }
 
     njs_set_false(&types);
-    njs_set_undefined(&encoding);
+    njs_set_undefined(&encode);
 
     switch (options->type) {
     case NJS_STRING:
-        encoding = *options;
+        encode = *options;
         break;
 
     case NJS_UNDEFINED:
@@ -990,7 +918,7 @@ njs_fs_readdir(njs_vm_t *vm, njs_value_t
         }
 
         ret = njs_value_property(vm, options, njs_value_arg(&string_encoding),
-                                 &encoding);
+                                 &encode);
         if (njs_slow_path(ret == NJS_ERROR)) {
             return ret;
         }
@@ -1002,9 +930,12 @@ njs_fs_readdir(njs_vm_t *vm, njs_value_t
         }
     }
 
-    enc = njs_fs_encoding(vm, &encoding);
-    if (njs_slow_path(enc == NJS_FS_ENC_INVALID)) {
-        return NJS_ERROR;
+    encoding = NULL;
+    if (!njs_is_string(&encode) || !njs_string_eq(&encode, &string_buffer)) {
+        encoding = njs_buffer_encoding(vm, &encode);
+        if (njs_slow_path(encoding == NULL)) {
+            return NJS_ERROR;
+        }
     }
 
     results = njs_array_alloc(vm, 1, 0, NJS_ARRAY_SPARE);
@@ -1033,41 +964,38 @@ njs_fs_readdir(njs_vm_t *vm, njs_value_t
             goto done;
         }
 
-        d_name = (u_char *) entry->d_name;
-
-        size = njs_strlen(d_name);
-        length = njs_utf8_length(d_name, size);
-        if (njs_slow_path(length < 0)) {
-            length = 0;
-        }
-
-        if ((length == 1 && d_name[0] == '.')
-            || (length == 2 && (d_name[0] == '.' && d_name[1] == '.')))
+        s.start = (u_char *) entry->d_name;
+        s.length = njs_strlen(s.start);
+
+        if ((s.length == 1 && s.start[0] == '.')
+            || (s.length == 2 && (s.start[0] == '.' && s.start[1] == '.')))
         {
             continue;
         }
 
-        if (njs_fast_path(!njs_is_true(&types))) {
-            ret = njs_array_string_add(vm, results, d_name, size, length);
-            if (njs_slow_path(ret != NJS_OK)) {
-                goto done;
-            }
-
-            continue;
-        }
-
-        ret = njs_string_new(vm, &ename, d_name, size, length);
-        if (njs_slow_path(ret != NJS_OK)) {
-            goto done;
-        }
-
-        njs_set_number(&etype, njs_dentry_type(entry));
-
         value = njs_array_push(vm, results);
         if (njs_slow_path(value == NULL)) {
             goto done;
         }
 
+        if (encoding == NULL) {
+            ret = njs_buffer_set(vm, &ename, s.start, s.length);
+
+        } else {
+            ret = encoding->encode(vm, &ename, &s);
+        }
+
+        if (njs_slow_path(ret != NJS_OK)) {
+            goto done;
+        }
+
+        if (njs_fast_path(!njs_is_true(&types))) {
+            *value = ename;
+            continue;
+        }
+
+        njs_set_number(&etype, njs_dentry_type(entry));
+
         ret = njs_fs_dirent_create(vm, &ename, &etype, value);
         if (njs_slow_path(ret != NJS_OK)) {
             goto done;
@@ -1099,12 +1027,12 @@ njs_fs_fd_read(njs_vm_t *vm, int fd, njs
 
     if (size == 0) {
         size = 4096;
-
-        data->start = njs_mp_alloc(vm->mem_pool, size);
-        if (data->start == NULL) {
-            njs_memory_error(vm);
-            return NJS_ERROR;
-        }
+    }
+
+    data->start = njs_mp_alloc(vm->mem_pool, size);
+    if (data->start == NULL) {
+        njs_memory_error(vm);
+        return NJS_ERROR;
     }
 
     p = data->start;
@@ -1474,6 +1402,61 @@ njs_fs_rmtree(njs_vm_t *vm, const char *
 }
 
 
+static njs_int_t
+njs_fs_path(njs_vm_t *vm, const char **dst, const njs_value_t* src,
+    const njs_str_t *prop_name)
+{
+    u_char              *data, *p, *start;
+    njs_typed_array_t   *array;
+    njs_array_buffer_t  *buffer;
+
+    switch (src->type) {
+    case NJS_STRING:
+        *dst = njs_string_to_c_string(vm, njs_value_arg(src));
+        if (njs_slow_path(*dst == NULL)) {
+            return NJS_ERROR;
+        }
+
+        break;
+
+    case NJS_TYPED_ARRAY:
+    case NJS_DATA_VIEW:
+        array = njs_typed_array(src);
+        buffer = array->buffer;
+        if (njs_slow_path(njs_is_detached_buffer(buffer))) {
+            njs_type_error(vm, "detached buffer");
+            return NJS_ERROR;
+        }
+
+        start = &buffer->u.u8[array->offset];
+
+        if (njs_slow_path(memchr(start, '\0', array->byte_length) != 0)) {
+            njs_type_error(vm, "\"%V\" must be a Buffer without null bytes",
+                           prop_name);
+            return NJS_ERROR;
+        }
+
+        data = njs_mp_alloc(vm->mem_pool, array->byte_length + 1);
+        if (njs_slow_path(data == NULL)) {
+            njs_memory_error(vm);
+            return NJS_ERROR;
+        }
+
+        p = njs_cpymem(data, start, array->byte_length);
+        *p++ = '\0';
+
+        *dst = (char *) data;
+        break;
+
+    default:
+        njs_type_error(vm, "\"%V\" must be a string or Buffer", prop_name);
+        return NJS_ERROR;
+    }
+
+    return NJS_OK;
+}
+
+
 static int
 njs_fs_flags(njs_vm_t *vm, njs_value_t *value, int default_flags)
 {
@@ -1526,32 +1509,6 @@ njs_fs_mode(njs_vm_t *vm, njs_value_t *v
 }
 
 
-static njs_fs_encoding_t
-njs_fs_encoding(njs_vm_t *vm, njs_value_t *value)
-{
-    njs_str_t  enc;
-    njs_int_t  ret;
-
-    if (njs_is_undefined(value)) {
-        return NJS_FS_ENC_NONE;
-    }
-
-    ret = njs_value_to_string(vm, value, value);
-    if (njs_slow_path(ret != NJS_OK)) {
-        return NJS_FS_ENC_INVALID;
-    }
-
-    njs_string_get(value, &enc);
-
-    if (enc.length != 4 || memcmp(enc.start, "utf8", 4) != 0) {
-        njs_type_error(vm, "Unknown encoding: \"%V\"", &enc);
-        return NJS_FS_ENC_INVALID;
-    }
-
-    return NJS_FS_ENC_UTF8;
-}
-
-
 static njs_int_t
 njs_fs_error(njs_vm_t *vm, const char *syscall, const char *description,
     njs_value_t *path, int errn, njs_value_t *retval)
diff -r 366aa456dc90 -r f1ed239edc7d src/test/njs_unit_test.c
--- a/src/test/njs_unit_test.c	Mon Sep 28 16:59:35 2020 +0000
+++ b/src/test/njs_unit_test.c	Mon Sep 28 17:45:44 2020 +0000
@@ -17013,145 +17013,146 @@ static njs_unit_test_t  njs_test[] =
     /* require('fs').readFile() */
 
     { njs_str("var fs = require('fs');"
-                 "fs.readFile()"),
-      njs_str("TypeError: \"path\" must be a string") },
-
-    { njs_str("var fs = require('fs');"
-                 "fs.readFile('/njs_unknown_path')"),
+              "fs.readFile()"),
+      njs_str("TypeError: \"path\" must be a string or Buffer") },
+
+    { njs_str("var fs = require('fs');"
+              "var path = Buffer.from('/broken'); path[3] = 0;"
+              "fs.readFile(path)"),
+      njs_str("TypeError: \"path\" must be a Buffer without null bytes") },
+
+    { njs_str("var fs = require('fs');"
+              "fs.readFile('/njs_unknown_path')"),
       njs_str("TypeError: \"callback\" must be a function") },
 
     { njs_str("var fs = require('fs');"
-                 "fs.readFile('/njs_unknown_path', 'utf8')"),
-      njs_str("TypeError: \"callback\" must be a function") },
-
-    { njs_str("var fs = require('fs');"
-                 "fs.readFile('/njs_unknown_path', {flag:'xx'})"),
+              "fs.readFile('/njs_unknown_path', 'utf8')"),
       njs_str("TypeError: \"callback\" must be a function") },
 
     { njs_str("var fs = require('fs');"
-                 "fs.readFile('/njs_unknown_path', {flag:'xx'}, 1)"),
+              "fs.readFile('/njs_unknown_path', {flag:'xx'})"),
       njs_str("TypeError: \"callback\" must be a function") },
 
     { njs_str("var fs = require('fs');"
-                 "fs.readFile('/njs_unknown_path', {flag:'xx'}, function () {})"),
+              "fs.readFile('/njs_unknown_path', {flag:'xx'}, 1)"),
+      njs_str("TypeError: \"callback\" must be a function") },
+
+    { njs_str("var fs = require('fs');"
+              "fs.readFile('/njs_unknown_path', {flag:'xx'}, function () {})"),
       njs_str("TypeError: Unknown file open flags: \"xx\"") },
 
     { njs_str("var fs = require('fs');"
-                 "fs.readFile('/njs_unknown_path', {encoding:'ascii'}, function () {})"),
-      njs_str("TypeError: Unknown encoding: \"ascii\"") },
-
-    { njs_str("var fs = require('fs');"
-                 "fs.readFile('/njs_unknown_path', 'ascii', function () {})"),
-      njs_str("TypeError: Unknown encoding: \"ascii\"") },
+              "fs.readFile('/njs_unknown_path', {encoding:'ascii'}, function () {})"),
+      njs_str("TypeError: \"ascii\" encoding is not supported") },
+
+    { njs_str("var fs = require('fs');"
+              "fs.readFile('/njs_unknown_path', 'ascii', function () {})"),
+      njs_str("TypeError: \"ascii\" encoding is not supported") },
 
     /* require('fs').readFileSync() */
 
     { njs_str("var fs = require('fs');"
-                 "fs.readFileSync()"),
-      njs_str("TypeError: \"path\" must be a string") },
-
-    { njs_str("var fs = require('fs');"
-                 "fs.readFileSync({})"),
-      njs_str("TypeError: \"path\" must be a string") },
-
-    { njs_str("var fs = require('fs');"
-                 "fs.readFileSync('/njs_unknown_path', {flag:'xx'})"),
+              "fs.readFileSync()"),
+      njs_str("TypeError: \"path\" must be a string or Buffer") },
+
+    { njs_str("var fs = require('fs');"
+              "fs.readFileSync({})"),
+      njs_str("TypeError: \"path\" must be a string or Buffer") },
+
+    { njs_str("var fs = require('fs');"
+              "fs.readFileSync('/njs_unknown_path', {flag:'xx'})"),
       njs_str("TypeError: Unknown file open flags: \"xx\"") },
 
     { njs_str("var fs = require('fs');"
-                 "fs.readFileSync('/njs_unknown_path', {encoding:'ascii'})"),
-      njs_str("TypeError: Unknown encoding: \"ascii\"") },
-
-    { njs_str("var fs = require('fs');"
-                 "fs.readFileSync('/njs_unknown_path', 'ascii')"),
-      njs_str("TypeError: Unknown encoding: \"ascii\"") },
-
-    { njs_str("var fs = require('fs');"
-                 "fs.readFileSync('/njs_unknown_path', true)"),
+              "fs.readFileSync(Buffer.from('/njs_unknown_path'), {encoding:'ascii'})"),
+      njs_str("TypeError: \"ascii\" encoding is not supported") },
+
+    { njs_str("var fs = require('fs');"
+              "fs.readFileSync('/njs_unknown_path', 'ascii')"),
+      njs_str("TypeError: \"ascii\" encoding is not supported") },
+
+    { njs_str("var fs = require('fs');"
+              "fs.readFileSync('/njs_unknown_path', true)"),
       njs_str("TypeError: Unknown options type: \"boolean\" (a string or object required)") },
 
 
     /* require('fs').writeFile() */
 
     { njs_str("var fs = require('fs');"
-                 "fs.writeFile()"),
-      njs_str("TypeError: \"path\" must be a string") },
-
-    { njs_str("var fs = require('fs');"
-                 "fs.writeFile({}, '', function () {})"),
-      njs_str("TypeError: \"path\" must be a string") },
-
-    { njs_str("var fs = require('fs');"
-                 "fs.writeFile('/njs_unknown_path')"),
-      njs_str("TypeError: \"data\" must be a string") },
-
-    { njs_str("var fs = require('fs');"
-                 "fs.writeFile('/njs_unknown_path', '')"),
+              "fs.writeFile()"),
+      njs_str("TypeError: \"path\" must be a string or Buffer") },
+
+    { njs_str("var fs = require('fs');"
+              "fs.writeFile({}, '', function () {})"),
+      njs_str("TypeError: \"path\" must be a string or Buffer") },
+
+    { njs_str("var fs = require('fs');"
+              "fs.writeFile('/njs_unknown_path')"),
       njs_str("TypeError: \"callback\" must be a function") },
 
     { njs_str("var fs = require('fs');"
-                 "fs.writeFile('/njs_unknown_path', '', undefined)"),
+              "fs.writeFile('/njs_unknown_path', '')"),
+      njs_str("TypeError: \"callback\" must be a function") },
+
+    { njs_str("var fs = require('fs');"
+              "fs.writeFile('/njs_unknown_path', '', undefined)"),
       njs_str("TypeError: \"callback\" must be a function") },
 
     { njs_str("var fs = require('fs');"
-                 "fs.writeFile('/njs_unknown_path', '', 'utf8')"),
+              "fs.writeFile('/njs_unknown_path', '', 'utf8')"),
       njs_str("TypeError: \"callback\" must be a function") },
 
     { njs_str("var fs = require('fs');"
-                 "fs.writeFile('/njs_unknown_path', '', {flag:'xx'}, function () {})"),
+              "fs.writeFile('/njs_unknown_path', '', {flag:'xx'}, function () {})"),
       njs_str("TypeError: Unknown file open flags: \"xx\"") },
 
     { njs_str("var fs = require('fs');"
-                 "fs.writeFile('/njs_unknown_path', '', {encoding:'ascii'}, function () {})"),
-      njs_str("TypeError: Unknown encoding: \"ascii\"") },
-
-    { njs_str("var fs = require('fs');"
-                 "fs.writeFile('/njs_unknown_path', '', 'ascii', function () {})"),
-      njs_str("TypeError: Unknown encoding: \"ascii\"") },
-
-    { njs_str("var fs = require('fs');"
-                 "fs.writeFile('/njs_unknown_path', '', true, function () {})"),
+              "fs.writeFile('/njs_unknown_path', '', {encoding:'ascii'}, function () {})"),
+      njs_str("TypeError: \"ascii\" encoding is not supported") },
+
+    { njs_str("var fs = require('fs');"
+              "fs.writeFile('/njs_unknown_path', '', 'ascii', function () {})"),
+      njs_str("TypeError: \"ascii\" encoding is not supported") },
+
+    { njs_str("var fs = require('fs');"
+              "fs.writeFile('/njs_unknown_path', '', true, function () {})"),
       njs_str("TypeError: Unknown options type: \"boolean\" (a string or object required)") },
 
     /* require('fs').writeFileSync() */
 
     { njs_str("var fs = require('fs');"
-                 "fs.writeFileSync()"),
-      njs_str("TypeError: \"path\" must be a string") },
-
-    { njs_str("var fs = require('fs');"
-                 "fs.writeFileSync('/njs_unknown_path')"),
-      njs_str("TypeError: \"data\" must be a string") },
-
-    { njs_str("var fs = require('fs');"
-                 "fs.writeFileSync({}, '')"),
-      njs_str("TypeError: \"path\" must be a string") },
-
-    { njs_str("var fs = require('fs');"
-                 "fs.writeFileSync('/njs_unknown_path', '', {flag:'xx'})"),
+              "fs.writeFileSync()"),
+      njs_str("TypeError: \"path\" must be a string or Buffer") },
+
+    { njs_str("var fs = require('fs');"
+              "fs.writeFileSync({}, '')"),
+      njs_str("TypeError: \"path\" must be a string or Buffer") },
+
+    { njs_str("var fs = require('fs');"
+              "fs.writeFileSync('/njs_unknown_path', '', {flag:'xx'})"),
       njs_str("TypeError: Unknown file open flags: \"xx\"") },
 
     { njs_str("var fs = require('fs');"
-                 "fs.writeFileSync('/njs_unknown_path', '', {encoding:'ascii'})"),
-      njs_str("TypeError: Unknown encoding: \"ascii\"") },


More information about the nginx-devel mailing list