fs: add *timeNs properties to BigInt Stats objects
- Extend the aliased buffer for stats objects to contain the entire time spec (seconds and nanoseconds) for the time values instead of calculating the milliseconds in C++ and lose precision there. - Calculate the nanosecond-precision time values in JS and expose them in BigInt Stats objects as `*timeNs`. The millisecond-precision values are now calculated from the nanosecond-precision values. PR-URL: https://github.com/nodejs/node/pull/21387 Reviewed-By: Jeremiah Senkpiel <fishrock123@rocketmail.com> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Gus Caplan <me@gus.host>
This commit is contained in:
parent
4b1bcae681
commit
b245257b70
@ -512,7 +512,8 @@ A `fs.Stats` object provides information about a file.
|
||||
Objects returned from [`fs.stat()`][], [`fs.lstat()`][] and [`fs.fstat()`][] and
|
||||
their synchronous counterparts are of this type.
|
||||
If `bigint` in the `options` passed to those methods is true, the numeric values
|
||||
will be `bigint` instead of `number`.
|
||||
will be `bigint` instead of `number`, and the object will contain additional
|
||||
nanosecond-precision properties suffixed with `Ns`.
|
||||
|
||||
```console
|
||||
Stats {
|
||||
@ -539,7 +540,7 @@ Stats {
|
||||
`bigint` version:
|
||||
|
||||
```console
|
||||
Stats {
|
||||
BigIntStats {
|
||||
dev: 2114n,
|
||||
ino: 48064969n,
|
||||
mode: 33188n,
|
||||
@ -554,6 +555,10 @@ Stats {
|
||||
mtimeMs: 1318289051000n,
|
||||
ctimeMs: 1318289051000n,
|
||||
birthtimeMs: 1318289051000n,
|
||||
atimeNs: 1318289051000000000n,
|
||||
mtimeNs: 1318289051000000000n,
|
||||
ctimeNs: 1318289051000000000n,
|
||||
birthtimeNs: 1318289051000000000n,
|
||||
atime: Mon, 10 Oct 2011 23:24:11 GMT,
|
||||
mtime: Mon, 10 Oct 2011 23:24:11 GMT,
|
||||
ctime: Mon, 10 Oct 2011 23:24:11 GMT,
|
||||
@ -726,6 +731,54 @@ added: v8.1.0
|
||||
The timestamp indicating the creation time of this file expressed in
|
||||
milliseconds since the POSIX Epoch.
|
||||
|
||||
### stats.atimeNs
|
||||
<!-- YAML
|
||||
added: REPLACEME
|
||||
-->
|
||||
|
||||
* {bigint}
|
||||
|
||||
Only present when `bigint: true` is passed into the method that generates
|
||||
the object.
|
||||
The timestamp indicating the last time this file was accessed expressed in
|
||||
nanoseconds since the POSIX Epoch.
|
||||
|
||||
### stats.mtimeNs
|
||||
<!-- YAML
|
||||
added: REPLACEME
|
||||
-->
|
||||
|
||||
* {bigint}
|
||||
|
||||
Only present when `bigint: true` is passed into the method that generates
|
||||
the object.
|
||||
The timestamp indicating the last time this file was modified expressed in
|
||||
nanoseconds since the POSIX Epoch.
|
||||
|
||||
### stats.ctimeNs
|
||||
<!-- YAML
|
||||
added: REPLACEME
|
||||
-->
|
||||
|
||||
* {bigint}
|
||||
|
||||
Only present when `bigint: true` is passed into the method that generates
|
||||
the object.
|
||||
The timestamp indicating the last time the file status was changed expressed
|
||||
in nanoseconds since the POSIX Epoch.
|
||||
|
||||
### stats.birthtimeNs
|
||||
<!-- YAML
|
||||
added: REPLACEME
|
||||
-->
|
||||
|
||||
* {bigint}
|
||||
|
||||
Only present when `bigint: true` is passed into the method that generates
|
||||
the object.
|
||||
The timestamp indicating the creation time of this file expressed in
|
||||
nanoseconds since the POSIX Epoch.
|
||||
|
||||
### stats.atime
|
||||
<!-- YAML
|
||||
added: v0.11.13
|
||||
@ -765,8 +818,17 @@ The timestamp indicating the creation time of this file.
|
||||
### Stat Time Values
|
||||
|
||||
The `atimeMs`, `mtimeMs`, `ctimeMs`, `birthtimeMs` properties are
|
||||
[numbers][MDN-Number] that hold the corresponding times in milliseconds. Their
|
||||
precision is platform specific. `atime`, `mtime`, `ctime`, and `birthtime` are
|
||||
numeric values that hold the corresponding times in milliseconds. Their
|
||||
precision is platform specific. When `bigint: true` is passed into the
|
||||
method that generates the object, the properties will be [bigints][],
|
||||
otherwise they will be [numbers][MDN-Number].
|
||||
|
||||
The `atimeNs`, `mtimeNs`, `ctimeNs`, `birthtimeNs` properties are
|
||||
[bigints][] that hold the corresponding times in nanoseconds. They are
|
||||
only present when `bigint: true` is passed into the method that generates
|
||||
the object. Their precision is platform specific.
|
||||
|
||||
`atime`, `mtime`, `ctime`, and `birthtime` are
|
||||
[`Date`][MDN-Date] object alternate representations of the various times. The
|
||||
`Date` and number values are not connected. Assigning a new number value, or
|
||||
mutating the `Date` value, will not be reflected in the corresponding alternate
|
||||
@ -4976,6 +5038,7 @@ the file contents.
|
||||
[`net.Socket`]: net.html#net_class_net_socket
|
||||
[`stat()`]: fs.html#fs_fs_stat_path_options_callback
|
||||
[`util.promisify()`]: util.html#util_util_promisify_original
|
||||
[bigints]: https://tc39.github.io/proposal-bigint
|
||||
[Caveats]: #fs_caveats
|
||||
[Common System Errors]: errors.html#errors_common_system_errors
|
||||
[FS Constants]: #fs_fs_constants_1
|
||||
|
@ -1,6 +1,6 @@
|
||||
'use strict';
|
||||
|
||||
const { Reflect } = primordials;
|
||||
const { Object, Reflect } = primordials;
|
||||
|
||||
const { Buffer, kMaxLength } = require('buffer');
|
||||
const {
|
||||
@ -16,7 +16,8 @@ const {
|
||||
} = require('internal/errors');
|
||||
const {
|
||||
isUint8Array,
|
||||
isDate
|
||||
isDate,
|
||||
isBigUint64Array
|
||||
} = require('internal/util/types');
|
||||
const { once } = require('internal/util');
|
||||
const { toPathIfFileURL } = require('internal/url');
|
||||
@ -230,27 +231,9 @@ function preprocessSymlinkDestination(path, type, linkPath) {
|
||||
}
|
||||
}
|
||||
|
||||
function dateFromNumeric(num) {
|
||||
return new Date(Number(num) + 0.5);
|
||||
}
|
||||
|
||||
// Constructor for file stats.
|
||||
function Stats(
|
||||
dev,
|
||||
mode,
|
||||
nlink,
|
||||
uid,
|
||||
gid,
|
||||
rdev,
|
||||
blksize,
|
||||
ino,
|
||||
size,
|
||||
blocks,
|
||||
atim_msec,
|
||||
mtim_msec,
|
||||
ctim_msec,
|
||||
birthtim_msec
|
||||
) {
|
||||
function StatsBase(dev, mode, nlink, uid, gid, rdev, blksize,
|
||||
ino, size, blocks) {
|
||||
this.dev = dev;
|
||||
this.mode = mode;
|
||||
this.nlink = nlink;
|
||||
@ -261,63 +244,132 @@ function Stats(
|
||||
this.ino = ino;
|
||||
this.size = size;
|
||||
this.blocks = blocks;
|
||||
this.atimeMs = atim_msec;
|
||||
this.mtimeMs = mtim_msec;
|
||||
this.ctimeMs = ctim_msec;
|
||||
this.birthtimeMs = birthtim_msec;
|
||||
this.atime = dateFromNumeric(atim_msec);
|
||||
this.mtime = dateFromNumeric(mtim_msec);
|
||||
this.ctime = dateFromNumeric(ctim_msec);
|
||||
this.birthtime = dateFromNumeric(birthtim_msec);
|
||||
}
|
||||
|
||||
StatsBase.prototype.isDirectory = function() {
|
||||
return this._checkModeProperty(S_IFDIR);
|
||||
};
|
||||
|
||||
StatsBase.prototype.isFile = function() {
|
||||
return this._checkModeProperty(S_IFREG);
|
||||
};
|
||||
|
||||
StatsBase.prototype.isBlockDevice = function() {
|
||||
return this._checkModeProperty(S_IFBLK);
|
||||
};
|
||||
|
||||
StatsBase.prototype.isCharacterDevice = function() {
|
||||
return this._checkModeProperty(S_IFCHR);
|
||||
};
|
||||
|
||||
StatsBase.prototype.isSymbolicLink = function() {
|
||||
return this._checkModeProperty(S_IFLNK);
|
||||
};
|
||||
|
||||
StatsBase.prototype.isFIFO = function() {
|
||||
return this._checkModeProperty(S_IFIFO);
|
||||
};
|
||||
|
||||
StatsBase.prototype.isSocket = function() {
|
||||
return this._checkModeProperty(S_IFSOCK);
|
||||
};
|
||||
|
||||
const kNsPerMsBigInt = 10n ** 6n;
|
||||
const kNsPerSecBigInt = 10n ** 9n;
|
||||
const kMsPerSec = 10 ** 3;
|
||||
const kNsPerMs = 10 ** 6;
|
||||
function msFromTimeSpec(sec, nsec) {
|
||||
return sec * kMsPerSec + nsec / kNsPerMs;
|
||||
}
|
||||
|
||||
function nsFromTimeSpecBigInt(sec, nsec) {
|
||||
return sec * kNsPerSecBigInt + nsec;
|
||||
}
|
||||
|
||||
function dateFromMs(ms) {
|
||||
return new Date(Number(ms) + 0.5);
|
||||
}
|
||||
|
||||
function BigIntStats(dev, mode, nlink, uid, gid, rdev, blksize,
|
||||
ino, size, blocks,
|
||||
atimeNs, mtimeNs, ctimeNs, birthtimeNs) {
|
||||
StatsBase.call(this, dev, mode, nlink, uid, gid, rdev, blksize,
|
||||
ino, size, blocks);
|
||||
|
||||
this.atimeMs = atimeNs / kNsPerMsBigInt;
|
||||
this.mtimeMs = mtimeNs / kNsPerMsBigInt;
|
||||
this.ctimeMs = ctimeNs / kNsPerMsBigInt;
|
||||
this.birthtimeMs = birthtimeNs / kNsPerMsBigInt;
|
||||
this.atimeNs = atimeNs;
|
||||
this.mtimeNs = mtimeNs;
|
||||
this.ctimeNs = ctimeNs;
|
||||
this.birthtimeNs = birthtimeNs;
|
||||
this.atime = dateFromMs(this.atimeMs);
|
||||
this.mtime = dateFromMs(this.mtimeMs);
|
||||
this.ctime = dateFromMs(this.ctimeMs);
|
||||
this.birthtime = dateFromMs(this.birthtimeMs);
|
||||
}
|
||||
|
||||
Object.setPrototypeOf(BigIntStats.prototype, StatsBase.prototype);
|
||||
Object.setPrototypeOf(BigIntStats, StatsBase);
|
||||
|
||||
BigIntStats.prototype._checkModeProperty = function(property) {
|
||||
if (isWindows && (property === S_IFIFO || property === S_IFBLK ||
|
||||
property === S_IFSOCK)) {
|
||||
return false; // Some types are not available on Windows
|
||||
}
|
||||
return (this.mode & BigInt(S_IFMT)) === BigInt(property);
|
||||
};
|
||||
|
||||
function Stats(dev, mode, nlink, uid, gid, rdev, blksize,
|
||||
ino, size, blocks,
|
||||
atimeMs, mtimeMs, ctimeMs, birthtimeMs) {
|
||||
StatsBase.call(this, dev, mode, nlink, uid, gid, rdev, blksize,
|
||||
ino, size, blocks);
|
||||
this.atimeMs = atimeMs;
|
||||
this.mtimeMs = mtimeMs;
|
||||
this.ctimeMs = ctimeMs;
|
||||
this.birthtimeMs = birthtimeMs;
|
||||
this.atime = dateFromMs(atimeMs);
|
||||
this.mtime = dateFromMs(mtimeMs);
|
||||
this.ctime = dateFromMs(ctimeMs);
|
||||
this.birthtime = dateFromMs(birthtimeMs);
|
||||
}
|
||||
|
||||
Object.setPrototypeOf(Stats.prototype, StatsBase.prototype);
|
||||
Object.setPrototypeOf(Stats, StatsBase);
|
||||
|
||||
Stats.prototype._checkModeProperty = function(property) {
|
||||
if (isWindows && (property === S_IFIFO || property === S_IFBLK ||
|
||||
property === S_IFSOCK)) {
|
||||
return false; // Some types are not available on Windows
|
||||
}
|
||||
if (typeof this.mode === 'bigint') { // eslint-disable-line valid-typeof
|
||||
return (this.mode & BigInt(S_IFMT)) === BigInt(property);
|
||||
}
|
||||
return (this.mode & S_IFMT) === property;
|
||||
};
|
||||
|
||||
Stats.prototype.isDirectory = function() {
|
||||
return this._checkModeProperty(S_IFDIR);
|
||||
};
|
||||
|
||||
Stats.prototype.isFile = function() {
|
||||
return this._checkModeProperty(S_IFREG);
|
||||
};
|
||||
|
||||
Stats.prototype.isBlockDevice = function() {
|
||||
return this._checkModeProperty(S_IFBLK);
|
||||
};
|
||||
|
||||
Stats.prototype.isCharacterDevice = function() {
|
||||
return this._checkModeProperty(S_IFCHR);
|
||||
};
|
||||
|
||||
Stats.prototype.isSymbolicLink = function() {
|
||||
return this._checkModeProperty(S_IFLNK);
|
||||
};
|
||||
|
||||
Stats.prototype.isFIFO = function() {
|
||||
return this._checkModeProperty(S_IFIFO);
|
||||
};
|
||||
|
||||
Stats.prototype.isSocket = function() {
|
||||
return this._checkModeProperty(S_IFSOCK);
|
||||
};
|
||||
|
||||
function getStatsFromBinding(stats, offset = 0) {
|
||||
return new Stats(stats[0 + offset], stats[1 + offset], stats[2 + offset],
|
||||
stats[3 + offset], stats[4 + offset], stats[5 + offset],
|
||||
stats[6 + offset], // blksize
|
||||
stats[7 + offset], stats[8 + offset],
|
||||
stats[9 + offset], // blocks
|
||||
stats[10 + offset], stats[11 + offset],
|
||||
stats[12 + offset], stats[13 + offset]);
|
||||
if (isBigUint64Array(stats)) {
|
||||
return new BigIntStats(
|
||||
stats[0 + offset], stats[1 + offset], stats[2 + offset],
|
||||
stats[3 + offset], stats[4 + offset], stats[5 + offset],
|
||||
stats[6 + offset], stats[7 + offset], stats[8 + offset],
|
||||
stats[9 + offset],
|
||||
nsFromTimeSpecBigInt(stats[10 + offset], stats[11 + offset]),
|
||||
nsFromTimeSpecBigInt(stats[12 + offset], stats[13 + offset]),
|
||||
nsFromTimeSpecBigInt(stats[14 + offset], stats[15 + offset]),
|
||||
nsFromTimeSpecBigInt(stats[16 + offset], stats[17 + offset])
|
||||
);
|
||||
}
|
||||
return new Stats(
|
||||
stats[0 + offset], stats[1 + offset], stats[2 + offset],
|
||||
stats[3 + offset], stats[4 + offset], stats[5 + offset],
|
||||
stats[6 + offset], stats[7 + offset], stats[8 + offset],
|
||||
stats[9 + offset],
|
||||
msFromTimeSpec(stats[10 + offset], stats[11 + offset]),
|
||||
msFromTimeSpec(stats[12 + offset], stats[13 + offset]),
|
||||
msFromTimeSpec(stats[14 + offset], stats[15 + offset]),
|
||||
msFromTimeSpec(stats[16 + offset], stats[17 + offset])
|
||||
);
|
||||
}
|
||||
|
||||
function stringToFlags(flags) {
|
||||
@ -453,6 +505,7 @@ function warnOnNonPortableTemplate(template) {
|
||||
|
||||
module.exports = {
|
||||
assertEncoding,
|
||||
BigIntStats, // for testing
|
||||
copyObject,
|
||||
Dirent,
|
||||
getDirents,
|
||||
|
26
src/env.h
26
src/env.h
@ -102,10 +102,32 @@ struct PackageConfig {
|
||||
};
|
||||
} // namespace loader
|
||||
|
||||
enum class FsStatsOffset {
|
||||
kDev = 0,
|
||||
kMode,
|
||||
kNlink,
|
||||
kUid,
|
||||
kGid,
|
||||
kRdev,
|
||||
kBlkSize,
|
||||
kIno,
|
||||
kSize,
|
||||
kBlocks,
|
||||
kATimeSec,
|
||||
kATimeNsec,
|
||||
kMTimeSec,
|
||||
kMTimeNsec,
|
||||
kCTimeSec,
|
||||
kCTimeNsec,
|
||||
kBirthTimeSec,
|
||||
kBirthTimeNsec,
|
||||
kFsStatsFieldsNumber
|
||||
};
|
||||
|
||||
// Stat fields buffers contain twice the number of entries in an uv_stat_t
|
||||
// because `fs.StatWatcher` needs room to store 2 `fs.Stats` instances.
|
||||
constexpr size_t kFsStatsFieldsNumber = 14;
|
||||
constexpr size_t kFsStatsBufferLength = kFsStatsFieldsNumber * 2;
|
||||
constexpr size_t kFsStatsBufferLength =
|
||||
static_cast<size_t>(FsStatsOffset::kFsStatsFieldsNumber) * 2;
|
||||
|
||||
// PER_ISOLATE_* macros: We have a lot of per-isolate properties
|
||||
// and adding and maintaining their getters and setters by hand would be
|
||||
|
@ -2197,10 +2197,13 @@ void Initialize(Local<Object> target,
|
||||
|
||||
env->SetMethod(target, "mkdtemp", Mkdtemp);
|
||||
|
||||
target->Set(context,
|
||||
FIXED_ONE_BYTE_STRING(isolate, "kFsStatsFieldsNumber"),
|
||||
Integer::New(isolate, kFsStatsFieldsNumber))
|
||||
.Check();
|
||||
target
|
||||
->Set(context,
|
||||
FIXED_ONE_BYTE_STRING(isolate, "kFsStatsFieldsNumber"),
|
||||
Integer::New(
|
||||
isolate,
|
||||
static_cast<int32_t>(FsStatsOffset::kFsStatsFieldsNumber)))
|
||||
.Check();
|
||||
|
||||
target->Set(context,
|
||||
FIXED_ONE_BYTE_STRING(isolate, "statValues"),
|
||||
|
@ -6,6 +6,7 @@
|
||||
#include "node.h"
|
||||
#include "stream_base.h"
|
||||
#include "req_wrap-inl.h"
|
||||
#include <iostream>
|
||||
|
||||
namespace node {
|
||||
|
||||
@ -149,74 +150,48 @@ class FSReqCallback : public FSReqBase {
|
||||
FSReqCallback& operator=(const FSReqCallback&) = delete;
|
||||
};
|
||||
|
||||
// Wordaround a GCC4.9 bug that C++14 N3652 was not implemented
|
||||
// Refs: https://www.gnu.org/software/gcc/projects/cxx-status.html#cxx14
|
||||
// Refs: https://isocpp.org/files/papers/N3652.html
|
||||
#if __cpp_constexpr < 201304
|
||||
# define constexpr inline
|
||||
#endif
|
||||
|
||||
template <typename NativeT,
|
||||
// SFINAE limit NativeT to arithmetic types
|
||||
typename = std::enable_if<std::is_arithmetic<NativeT>::value>>
|
||||
constexpr NativeT ToNative(uv_timespec_t ts) {
|
||||
// This template has exactly two specializations below.
|
||||
static_assert(std::is_arithmetic<NativeT>::value == false, "Not implemented");
|
||||
return NativeT();
|
||||
}
|
||||
|
||||
template <>
|
||||
constexpr double ToNative(uv_timespec_t ts) {
|
||||
// We need to do a static_cast since the original FS values are ulong.
|
||||
/* NOLINTNEXTLINE(runtime/int) */
|
||||
const auto u_sec = static_cast<unsigned long>(ts.tv_sec);
|
||||
const double full_sec = u_sec * 1000.0;
|
||||
/* NOLINTNEXTLINE(runtime/int) */
|
||||
const auto u_nsec = static_cast<unsigned long>(ts.tv_nsec);
|
||||
const double full_nsec = u_nsec / 1000'000.0;
|
||||
return full_sec + full_nsec;
|
||||
}
|
||||
|
||||
template <>
|
||||
constexpr uint64_t ToNative(uv_timespec_t ts) {
|
||||
// We need to do a static_cast since the original FS values are ulong.
|
||||
/* NOLINTNEXTLINE(runtime/int) */
|
||||
const auto u_sec = static_cast<unsigned long>(ts.tv_sec);
|
||||
const auto full_sec = static_cast<uint64_t>(u_sec) * 1000UL;
|
||||
/* NOLINTNEXTLINE(runtime/int) */
|
||||
const auto u_nsec = static_cast<unsigned long>(ts.tv_nsec);
|
||||
const auto full_nsec = static_cast<uint64_t>(u_nsec) / 1000'000UL;
|
||||
return full_sec + full_nsec;
|
||||
}
|
||||
|
||||
#undef constexpr // end N3652 bug workaround
|
||||
|
||||
template <typename NativeT, typename V8T>
|
||||
constexpr void FillStatsArray(AliasedBufferBase<NativeT, V8T>* fields,
|
||||
const uv_stat_t* s,
|
||||
const size_t offset = 0) {
|
||||
fields->SetValue(offset + 0, static_cast<NativeT>(s->st_dev));
|
||||
fields->SetValue(offset + 1, static_cast<NativeT>(s->st_mode));
|
||||
fields->SetValue(offset + 2, static_cast<NativeT>(s->st_nlink));
|
||||
fields->SetValue(offset + 3, static_cast<NativeT>(s->st_uid));
|
||||
fields->SetValue(offset + 4, static_cast<NativeT>(s->st_gid));
|
||||
fields->SetValue(offset + 5, static_cast<NativeT>(s->st_rdev));
|
||||
fields->SetValue(offset + 6, static_cast<NativeT>(s->st_blksize));
|
||||
fields->SetValue(offset + 7, static_cast<NativeT>(s->st_ino));
|
||||
fields->SetValue(offset + 8, static_cast<NativeT>(s->st_size));
|
||||
fields->SetValue(offset + 9, static_cast<NativeT>(s->st_blocks));
|
||||
// Dates.
|
||||
fields->SetValue(offset + 10, ToNative<NativeT>(s->st_atim));
|
||||
fields->SetValue(offset + 11, ToNative<NativeT>(s->st_mtim));
|
||||
fields->SetValue(offset + 12, ToNative<NativeT>(s->st_ctim));
|
||||
fields->SetValue(offset + 13, ToNative<NativeT>(s->st_birthtim));
|
||||
#define SET_FIELD_WITH_STAT(stat_offset, stat) \
|
||||
fields->SetValue(offset + static_cast<size_t>(FsStatsOffset::stat_offset), \
|
||||
static_cast<NativeT>(stat))
|
||||
|
||||
#define SET_FIELD_WITH_TIME_STAT(stat_offset, stat) \
|
||||
/* NOLINTNEXTLINE(runtime/int) */ \
|
||||
SET_FIELD_WITH_STAT(stat_offset, static_cast<unsigned long>(stat))
|
||||
|
||||
SET_FIELD_WITH_STAT(kDev, s->st_dev);
|
||||
SET_FIELD_WITH_STAT(kMode, s->st_mode);
|
||||
SET_FIELD_WITH_STAT(kNlink, s->st_nlink);
|
||||
SET_FIELD_WITH_STAT(kUid, s->st_uid);
|
||||
SET_FIELD_WITH_STAT(kGid, s->st_gid);
|
||||
SET_FIELD_WITH_STAT(kRdev, s->st_rdev);
|
||||
SET_FIELD_WITH_STAT(kBlkSize, s->st_blksize);
|
||||
SET_FIELD_WITH_STAT(kIno, s->st_ino);
|
||||
SET_FIELD_WITH_STAT(kSize, s->st_size);
|
||||
SET_FIELD_WITH_STAT(kBlocks, s->st_blocks);
|
||||
|
||||
SET_FIELD_WITH_TIME_STAT(kATimeSec, s->st_atim.tv_sec);
|
||||
SET_FIELD_WITH_TIME_STAT(kATimeNsec, s->st_atim.tv_nsec);
|
||||
SET_FIELD_WITH_TIME_STAT(kMTimeSec, s->st_mtim.tv_sec);
|
||||
SET_FIELD_WITH_TIME_STAT(kMTimeNsec, s->st_mtim.tv_nsec);
|
||||
SET_FIELD_WITH_TIME_STAT(kCTimeSec, s->st_ctim.tv_sec);
|
||||
SET_FIELD_WITH_TIME_STAT(kCTimeNsec, s->st_ctim.tv_nsec);
|
||||
SET_FIELD_WITH_TIME_STAT(kBirthTimeSec, s->st_birthtim.tv_sec);
|
||||
SET_FIELD_WITH_TIME_STAT(kBirthTimeNsec, s->st_birthtim.tv_nsec);
|
||||
|
||||
#undef SET_FIELD_WITH_TIME_STAT
|
||||
#undef SET_FIELD_WITH_STAT
|
||||
}
|
||||
|
||||
inline Local<Value> FillGlobalStatsArray(Environment* env,
|
||||
const bool use_bigint,
|
||||
const uv_stat_t* s,
|
||||
const bool second = false) {
|
||||
const ptrdiff_t offset = second ? kFsStatsFieldsNumber : 0;
|
||||
const ptrdiff_t offset =
|
||||
second ? static_cast<ptrdiff_t>(FsStatsOffset::kFsStatsFieldsNumber) : 0;
|
||||
if (use_bigint) {
|
||||
auto* const arr = env->fs_stats_field_bigint_array();
|
||||
FillStatsArray(arr, s, offset);
|
||||
@ -302,7 +277,9 @@ class FSReqPromise : public FSReqBase {
|
||||
private:
|
||||
FSReqPromise(Environment* env, v8::Local<v8::Object> obj, bool use_bigint)
|
||||
: FSReqBase(env, obj, AsyncWrap::PROVIDER_FSREQPROMISE, use_bigint),
|
||||
stats_field_array_(env->isolate(), kFsStatsFieldsNumber) {}
|
||||
stats_field_array_(
|
||||
env->isolate(),
|
||||
static_cast<size_t>(FsStatsOffset::kFsStatsFieldsNumber)) {}
|
||||
|
||||
bool finished_ = false;
|
||||
AliasedBufferT stats_field_array_;
|
||||
|
@ -59,6 +59,26 @@ function verifyStats(bigintStats, numStats) {
|
||||
bigintStats.isSymbolicLink(),
|
||||
numStats.isSymbolicLink()
|
||||
);
|
||||
} else if (key.endsWith('Ms')) {
|
||||
const nsKey = key.replace('Ms', 'Ns');
|
||||
const msFromBigInt = bigintStats[key];
|
||||
const nsFromBigInt = bigintStats[nsKey];
|
||||
const msFromBigIntNs = Number(nsFromBigInt / (10n ** 6n));
|
||||
const msFromNum = numStats[key];
|
||||
// The difference between the millisecond-precision values should be
|
||||
// smaller than 2
|
||||
assert(
|
||||
Math.abs(msFromNum - Number(msFromBigInt)) < 2,
|
||||
`Number version ${key} = ${msFromNum}, ` +
|
||||
`BigInt version ${key} = ${msFromBigInt}n`);
|
||||
// The difference between the millisecond-precision value and the
|
||||
// nanosecond-precision value scaled down to milliseconds should be
|
||||
// smaller than 2
|
||||
assert(
|
||||
Math.abs(msFromNum - Number(msFromBigIntNs)) < 2,
|
||||
`Number version ${key} = ${msFromNum}, ` +
|
||||
`BigInt version ${nsKey} = ${nsFromBigInt}n` +
|
||||
` = ${msFromBigIntNs}ms`);
|
||||
} else if (Number.isSafeInteger(val)) {
|
||||
assert.strictEqual(
|
||||
bigintStats[key], BigInt(val),
|
||||
|
@ -1,14 +1,18 @@
|
||||
'use strict';
|
||||
|
||||
// Flags: --expose-internals
|
||||
|
||||
const common = require('../common');
|
||||
|
||||
const assert = require('assert');
|
||||
const { BigIntStats } = require('internal/fs/utils');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const tmpdir = require('../common/tmpdir');
|
||||
|
||||
const enoentFile = path.join(tmpdir.path, 'non-existent-file');
|
||||
const expectedStatObject = new fs.Stats(
|
||||
const expectedStatObject = new BigIntStats(
|
||||
0n, // dev
|
||||
0n, // mode
|
||||
0n, // nlink
|
||||
@ -19,10 +23,14 @@ const expectedStatObject = new fs.Stats(
|
||||
0n, // ino
|
||||
0n, // size
|
||||
0n, // blocks
|
||||
0n, // atim_msec
|
||||
0n, // mtim_msec
|
||||
0n, // ctim_msec
|
||||
0n // birthtim_msec
|
||||
0n, // atimeMs
|
||||
0n, // mtimeMs
|
||||
0n, // ctimeMs
|
||||
0n, // birthtimeMs
|
||||
0n, // atimeNs
|
||||
0n, // mtimeNs
|
||||
0n, // ctimeNs
|
||||
0n // birthtimeNs
|
||||
);
|
||||
|
||||
tmpdir.refresh();
|
||||
|
Loading…
x
Reference in New Issue
Block a user