/
githubmirror
/
node
Обзор
Документация
Войти
/
githubmirror
/
node
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/node_file_utils.cc
248 строк
7 KB
Joyee Cheung
src: consolidate C++ ReadFileSync/WriteFileSync utilities
07 фев 2026, 05:38
07 фев 2026, 05:38
3ab9dd8
Код
Авторство
О чём код?
#include "node_file_utils.h" #include <climits> #include <cstdio> #include <cstring> #include <functional> #include <string> #include <vector> #include "util-inl.h" #ifdef _WIN32 #include <io.h> // _S_IREAD _S_IWRITE #ifndef S_IRUSR #define S_IRUSR _S_IREAD #endif // S_IRUSR #ifndef S_IWUSR #define S_IWUSR _S_IWRITE #endif // S_IWUSR #endif namespace node { int WriteFileSync(const char* path, uv_buf_t buf) { return WriteFileSync(path, &buf, 1); } constexpr int64_t kCurrentFileOffset = -1; int WriteFileSync(const char* path, uv_buf_t* bufs, size_t buf_count) { uv_fs_t req; int fd = uv_fs_open(nullptr, &req, path, O_WRONLY | O_CREAT | O_TRUNC, S_IWUSR | S_IRUSR, nullptr); uv_fs_req_cleanup(&req); if (fd < 0) { return fd; } // Handle potential partial writes by looping until all data is written. std::vector<uv_buf_t> iovs(bufs, bufs + buf_count); size_t idx = 0; while (idx < iovs.size()) { // Skip empty buffers. if (iovs[idx].len == 0) { idx++; continue; } uv_fs_write(nullptr, &req, fd, iovs.data() + idx, iovs.size() - idx, kCurrentFileOffset, nullptr); if (req.result <= 0) { // Error during write. // UV_EIO should not happen unless the file system is full. int err = req.result < 0 ? req.result : UV_EIO; uv_fs_req_cleanup(&req); uv_fs_close(nullptr, &req, fd, nullptr); uv_fs_req_cleanup(&req); return err; } size_t written = req.result; uv_fs_req_cleanup(&req); // Consume written bytes from buffers. while (written > 0 && idx < iovs.size()) { if (written >= iovs[idx].len) { written -= iovs[idx].len; idx++; } else { iovs[idx].base += written; iovs[idx].len -= written; written = 0; } } } int err = uv_fs_close(nullptr, &req, fd, nullptr); uv_fs_req_cleanup(&req); return err; } int WriteFileSync(v8::Isolate* isolate, const char* path, v8::Local<v8::String> string) { node::Utf8Value utf8(isolate, string); uv_buf_t buf = uv_buf_init(utf8.out(), utf8.length()); return WriteFileSync(path, buf); } // Default size used if fstat reports a file size of 0 for special files. static constexpr size_t kDefaultReadSize = 8192; // The resize_buffer callback is called with the file size after fstat, and must // return a pointer to a buffer of at least that size. If the file grows during // reading, resize_buffer may be called again with a larger size; the callback // must preserve existing content and release old storage if needed. // After reading completes, resize_buffer may be called with the actual bytes // read. template <typename ResizeBuffer> int ReadFileSyncImpl(const char* path, ResizeBuffer resize_buffer) { uv_fs_t req; uv_file file = uv_fs_open(nullptr, &req, path, O_RDONLY, 0, nullptr); if (req.result < 0) { int err = req.result; uv_fs_req_cleanup(&req); return err; } uv_fs_req_cleanup(&req); // Get the file size first, which should be cheap enough on an already opened // files, and saves us from repeated reallocations/reads. int err = uv_fs_fstat(nullptr, &req, file, nullptr); if (err < 0) { uv_fs_req_cleanup(&req); uv_fs_close(nullptr, &req, file, nullptr); uv_fs_req_cleanup(&req); return err; } // SIZE_MAX is ~18 exabytes on 64-bit and ~4 GB on 32-bit systems. // In both cases, the process is unlikely to have that much memory // to hold the file content, so we just error with UV_EFBIG. if (req.statbuf.st_size > static_cast<uint64_t>(SIZE_MAX)) { uv_fs_req_cleanup(&req); uv_fs_close(nullptr, &req, file, nullptr); uv_fs_req_cleanup(&req); return UV_EFBIG; } size_t size = static_cast<size_t>(req.statbuf.st_size); uv_fs_req_cleanup(&req); // If the file is reported as 0 bytes for special files, use a default // size to start reading. if (size == 0) { size = kDefaultReadSize; } char* buffer = resize_buffer(size); if (buffer == nullptr) { uv_fs_close(nullptr, &req, file, nullptr); uv_fs_req_cleanup(&req); return UV_ENOMEM; } size_t total_read = 0; while (true) { size_t remaining = size - total_read; // On Windows, uv_buf_t uses ULONG which may truncate the // length for large buffers. Limit the individual read request size to // INT_MAX to be safe. The loop will issue multiple reads for larger files. if (remaining > INT_MAX) { remaining = INT_MAX; } uv_buf_t buf = uv_buf_init(buffer + total_read, remaining); uv_fs_read( nullptr, &req, file, &buf, 1 /* nbufs */, kCurrentFileOffset, nullptr); ssize_t bytes_read = req.result; uv_fs_req_cleanup(&req); if (bytes_read < 0) { uv_fs_close(nullptr, &req, file, nullptr); uv_fs_req_cleanup(&req); return bytes_read; } if (bytes_read == 0) { // EOF, stop reading. break; } total_read += bytes_read; if (total_read == size) { // Buffer is full, the file may have grown during reading. // Try increasing the buffer size and reading more. if (size == SIZE_MAX) { uv_fs_close(nullptr, &req, file, nullptr); uv_fs_req_cleanup(&req); return UV_EFBIG; } if (size > SIZE_MAX / 2) { size = SIZE_MAX; } else { size *= 2; } buffer = resize_buffer(size); if (buffer == nullptr) { uv_fs_close(nullptr, &req, file, nullptr); uv_fs_req_cleanup(&req); return UV_ENOMEM; } } } int close_err = uv_fs_close(nullptr, &req, file, nullptr); uv_fs_req_cleanup(&req); if (close_err < 0) { return close_err; } // Truncate the actual size read if necessary. if (total_read != size) { buffer = resize_buffer(total_read); if (buffer == nullptr && total_read != 0) { return UV_ENOMEM; } } return 0; } int ReadFileSync(const char* path, std::string* result) { return ReadFileSyncImpl(path, [result](size_t size) { result->resize(size); return result->data(); }); } // Legacy interface. TODO(joyeecheung): update the callers to pass path first, // output parameters second. int ReadFileSync(std::string* result, const char* path) { return ReadFileSync(path, result); } int ReadFileSync(const char* path, std::vector<uint8_t>* result) { return ReadFileSyncImpl(path, [result](size_t size) { result->resize(size); return reinterpret_cast<char*>(result->data()); }); } std::vector<char> ReadFileSync(FILE* fp) { CHECK_EQ(ftell(fp), 0); int err = fseek(fp, 0, SEEK_END); CHECK_EQ(err, 0); size_t size = ftell(fp); CHECK_NE(size, static_cast<size_t>(-1L)); err = fseek(fp, 0, SEEK_SET); CHECK_EQ(err, 0); std::vector<char> contents(size); size_t num_read = fread(contents.data(), size, 1, fp); CHECK_EQ(num_read, 1); return contents; } } // namespace node