Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 365e062

Browse filesBrowse files
joyeecheungBridgeAR
authored andcommitted
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: #21387 Reviewed-By: Jeremiah Senkpiel <fishrock123@rocketmail.com> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Gus Caplan <me@gus.host>
1 parent 78c9e5f commit 365e062
Copy full SHA for 365e062

File tree

Expand file treeCollapse file tree

7 files changed

+275
-129
lines changed
Open diff view settings
Filter options
Expand file treeCollapse file tree

7 files changed

+275
-129
lines changed
Open diff view settings
Collapse file

‎doc/api/fs.md‎

Copy file name to clipboardExpand all lines: doc/api/fs.md
+67-4Lines changed: 67 additions & 4 deletions
  • Display the source diff
  • Display the rich diff
Original file line numberDiff line numberDiff line change
@@ -512,7 +512,8 @@ A `fs.Stats` object provides information about a file.
512512
Objects returned from [`fs.stat()`][], [`fs.lstat()`][] and [`fs.fstat()`][] and
513513
their synchronous counterparts are of this type.
514514
If `bigint` in the `options` passed to those methods is true, the numeric values
515-
will be `bigint` instead of `number`.
515+
will be `bigint` instead of `number`, and the object will contain additional
516+
nanosecond-precision properties suffixed with `Ns`.
516517

517518
```console
518519
Stats {
@@ -539,7 +540,7 @@ Stats {
539540
`bigint` version:
540541

541542
```console
542-
Stats {
543+
BigIntStats {
543544
dev: 2114n,
544545
ino: 48064969n,
545546
mode: 33188n,
@@ -554,6 +555,10 @@ Stats {
554555
mtimeMs: 1318289051000n,
555556
ctimeMs: 1318289051000n,
556557
birthtimeMs: 1318289051000n,
558+
atimeNs: 1318289051000000000n,
559+
mtimeNs: 1318289051000000000n,
560+
ctimeNs: 1318289051000000000n,
561+
birthtimeNs: 1318289051000000000n,
557562
atime: Mon, 10 Oct 2011 23:24:11 GMT,
558563
mtime: Mon, 10 Oct 2011 23:24:11 GMT,
559564
ctime: Mon, 10 Oct 2011 23:24:11 GMT,
@@ -726,6 +731,54 @@ added: v8.1.0
726731
The timestamp indicating the creation time of this file expressed in
727732
milliseconds since the POSIX Epoch.
728733

734+
### stats.atimeNs
735+
<!-- YAML
736+
added: REPLACEME
737+
-->
738+
739+
* {bigint}
740+
741+
Only present when `bigint: true` is passed into the method that generates
742+
the object.
743+
The timestamp indicating the last time this file was accessed expressed in
744+
nanoseconds since the POSIX Epoch.
745+
746+
### stats.mtimeNs
747+
<!-- YAML
748+
added: REPLACEME
749+
-->
750+
751+
* {bigint}
752+
753+
Only present when `bigint: true` is passed into the method that generates
754+
the object.
755+
The timestamp indicating the last time this file was modified expressed in
756+
nanoseconds since the POSIX Epoch.
757+
758+
### stats.ctimeNs
759+
<!-- YAML
760+
added: REPLACEME
761+
-->
762+
763+
* {bigint}
764+
765+
Only present when `bigint: true` is passed into the method that generates
766+
the object.
767+
The timestamp indicating the last time the file status was changed expressed
768+
in nanoseconds since the POSIX Epoch.
769+
770+
### stats.birthtimeNs
771+
<!-- YAML
772+
added: REPLACEME
773+
-->
774+
775+
* {bigint}
776+
777+
Only present when `bigint: true` is passed into the method that generates
778+
the object.
779+
The timestamp indicating the creation time of this file expressed in
780+
nanoseconds since the POSIX Epoch.
781+
729782
### stats.atime
730783
<!-- YAML
731784
added: v0.11.13
@@ -765,8 +818,17 @@ The timestamp indicating the creation time of this file.
765818
### Stat Time Values
766819

767820
The `atimeMs`, `mtimeMs`, `ctimeMs`, `birthtimeMs` properties are
768-
[numbers][MDN-Number] that hold the corresponding times in milliseconds. Their
769-
precision is platform specific. `atime`, `mtime`, `ctime`, and `birthtime` are
821+
numeric values that hold the corresponding times in milliseconds. Their
822+
precision is platform specific. When `bigint: true` is passed into the
823+
method that generates the object, the properties will be [bigints][],
824+
otherwise they will be [numbers][MDN-Number].
825+
826+
The `atimeNs`, `mtimeNs`, `ctimeNs`, `birthtimeNs` properties are
827+
[bigints][] that hold the corresponding times in nanoseconds. They are
828+
only present when `bigint: true` is passed into the method that generates
829+
the object. Their precision is platform specific.
830+
831+
`atime`, `mtime`, `ctime`, and `birthtime` are
770832
[`Date`][MDN-Date] object alternate representations of the various times. The
771833
`Date` and number values are not connected. Assigning a new number value, or
772834
mutating the `Date` value, will not be reflected in the corresponding alternate
@@ -5129,6 +5191,7 @@ the file contents.
51295191
[`net.Socket`]: net.html#net_class_net_socket
51305192
[`stat()`]: fs.html#fs_fs_stat_path_options_callback
51315193
[`util.promisify()`]: util.html#util_util_promisify_original
5194+
[bigints]: https://tc39.github.io/proposal-bigint
51325195
[Caveats]: #fs_caveats
51335196
[Common System Errors]: errors.html#errors_common_system_errors
51345197
[FS Constants]: #fs_fs_constants_1
Collapse file

‎lib/internal/fs/utils.js‎

Copy file name to clipboardExpand all lines: lib/internal/fs/utils.js
+108-55Lines changed: 108 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
'use strict';
22

3-
const { Reflect } = primordials;
3+
const { Object, Reflect } = primordials;
44

55
const { Buffer, kMaxLength } = require('buffer');
66
const {
@@ -17,7 +17,8 @@ const {
1717
const {
1818
isArrayBufferView,
1919
isUint8Array,
20-
isDate
20+
isDate,
21+
isBigUint64Array
2122
} = require('internal/util/types');
2223
const { once } = require('internal/util');
2324
const { toPathIfFileURL } = require('internal/url');
@@ -231,27 +232,9 @@ function preprocessSymlinkDestination(path, type, linkPath) {
231232
}
232233
}
233234

234-
function dateFromNumeric(num) {
235-
return new Date(Number(num) + 0.5);
236-
}
237-
238235
// Constructor for file stats.
239-
function Stats(
240-
dev,
241-
mode,
242-
nlink,
243-
uid,
244-
gid,
245-
rdev,
246-
blksize,
247-
ino,
248-
size,
249-
blocks,
250-
atim_msec,
251-
mtim_msec,
252-
ctim_msec,
253-
birthtim_msec
254-
) {
236+
function StatsBase(dev, mode, nlink, uid, gid, rdev, blksize,
237+
ino, size, blocks) {
255238
this.dev = dev;
256239
this.mode = mode;
257240
this.nlink = nlink;
@@ -262,63 +245,132 @@ function Stats(
262245
this.ino = ino;
263246
this.size = size;
264247
this.blocks = blocks;
265-
this.atimeMs = atim_msec;
266-
this.mtimeMs = mtim_msec;
267-
this.ctimeMs = ctim_msec;
268-
this.birthtimeMs = birthtim_msec;
269-
this.atime = dateFromNumeric(atim_msec);
270-
this.mtime = dateFromNumeric(mtim_msec);
271-
this.ctime = dateFromNumeric(ctim_msec);
272-
this.birthtime = dateFromNumeric(birthtim_msec);
273248
}
274249

275-
Stats.prototype._checkModeProperty = function(property) {
276-
if (isWindows && (property === S_IFIFO || property === S_IFBLK ||
277-
property === S_IFSOCK)) {
278-
return false; // Some types are not available on Windows
279-
}
280-
if (typeof this.mode === 'bigint') {
281-
return (this.mode & BigInt(S_IFMT)) === BigInt(property);
282-
}
283-
return (this.mode & S_IFMT) === property;
284-
};
285-
286-
Stats.prototype.isDirectory = function() {
250+
StatsBase.prototype.isDirectory = function() {
287251
return this._checkModeProperty(S_IFDIR);
288252
};
289253

290-
Stats.prototype.isFile = function() {
254+
StatsBase.prototype.isFile = function() {
291255
return this._checkModeProperty(S_IFREG);
292256
};
293257

294-
Stats.prototype.isBlockDevice = function() {
258+
StatsBase.prototype.isBlockDevice = function() {
295259
return this._checkModeProperty(S_IFBLK);
296260
};
297261

298-
Stats.prototype.isCharacterDevice = function() {
262+
StatsBase.prototype.isCharacterDevice = function() {
299263
return this._checkModeProperty(S_IFCHR);
300264
};
301265

302-
Stats.prototype.isSymbolicLink = function() {
266+
StatsBase.prototype.isSymbolicLink = function() {
303267
return this._checkModeProperty(S_IFLNK);
304268
};
305269

306-
Stats.prototype.isFIFO = function() {
270+
StatsBase.prototype.isFIFO = function() {
307271
return this._checkModeProperty(S_IFIFO);
308272
};
309273

310-
Stats.prototype.isSocket = function() {
274+
StatsBase.prototype.isSocket = function() {
311275
return this._checkModeProperty(S_IFSOCK);
312276
};
313277

278+
const kNsPerMsBigInt = 10n ** 6n;
279+
const kNsPerSecBigInt = 10n ** 9n;
280+
const kMsPerSec = 10 ** 3;
281+
const kNsPerMs = 10 ** 6;
282+
function msFromTimeSpec(sec, nsec) {
283+
return sec * kMsPerSec + nsec / kNsPerMs;
284+
}
285+
286+
function nsFromTimeSpecBigInt(sec, nsec) {
287+
return sec * kNsPerSecBigInt + nsec;
288+
}
289+
290+
function dateFromMs(ms) {
291+
return new Date(Number(ms) + 0.5);
292+
}
293+
294+
function BigIntStats(dev, mode, nlink, uid, gid, rdev, blksize,
295+
ino, size, blocks,
296+
atimeNs, mtimeNs, ctimeNs, birthtimeNs) {
297+
StatsBase.call(this, dev, mode, nlink, uid, gid, rdev, blksize,
298+
ino, size, blocks);
299+
300+
this.atimeMs = atimeNs / kNsPerMsBigInt;
301+
this.mtimeMs = mtimeNs / kNsPerMsBigInt;
302+
this.ctimeMs = ctimeNs / kNsPerMsBigInt;
303+
this.birthtimeMs = birthtimeNs / kNsPerMsBigInt;
304+
this.atimeNs = atimeNs;
305+
this.mtimeNs = mtimeNs;
306+
this.ctimeNs = ctimeNs;
307+
this.birthtimeNs = birthtimeNs;
308+
this.atime = dateFromMs(this.atimeMs);
309+
this.mtime = dateFromMs(this.mtimeMs);
310+
this.ctime = dateFromMs(this.ctimeMs);
311+
this.birthtime = dateFromMs(this.birthtimeMs);
312+
}
313+
314+
Object.setPrototypeOf(BigIntStats.prototype, StatsBase.prototype);
315+
Object.setPrototypeOf(BigIntStats, StatsBase);
316+
317+
BigIntStats.prototype._checkModeProperty = function(property) {
318+
if (isWindows && (property === S_IFIFO || property === S_IFBLK ||
319+
property === S_IFSOCK)) {
320+
return false; // Some types are not available on Windows
321+
}
322+
return (this.mode & BigInt(S_IFMT)) === BigInt(property);
323+
};
324+
325+
function Stats(dev, mode, nlink, uid, gid, rdev, blksize,
326+
ino, size, blocks,
327+
atimeMs, mtimeMs, ctimeMs, birthtimeMs) {
328+
StatsBase.call(this, dev, mode, nlink, uid, gid, rdev, blksize,
329+
ino, size, blocks);
330+
this.atimeMs = atimeMs;
331+
this.mtimeMs = mtimeMs;
332+
this.ctimeMs = ctimeMs;
333+
this.birthtimeMs = birthtimeMs;
334+
this.atime = dateFromMs(atimeMs);
335+
this.mtime = dateFromMs(mtimeMs);
336+
this.ctime = dateFromMs(ctimeMs);
337+
this.birthtime = dateFromMs(birthtimeMs);
338+
}
339+
340+
Object.setPrototypeOf(Stats.prototype, StatsBase.prototype);
341+
Object.setPrototypeOf(Stats, StatsBase);
342+
343+
Stats.prototype._checkModeProperty = function(property) {
344+
if (isWindows && (property === S_IFIFO || property === S_IFBLK ||
345+
property === S_IFSOCK)) {
346+
return false; // Some types are not available on Windows
347+
}
348+
return (this.mode & S_IFMT) === property;
349+
};
350+
314351
function getStatsFromBinding(stats, offset = 0) {
315-
return new Stats(stats[0 + offset], stats[1 + offset], stats[2 + offset],
316-
stats[3 + offset], stats[4 + offset], stats[5 + offset],
317-
stats[6 + offset], // blksize
318-
stats[7 + offset], stats[8 + offset],
319-
stats[9 + offset], // blocks
320-
stats[10 + offset], stats[11 + offset],
321-
stats[12 + offset], stats[13 + offset]);
352+
if (isBigUint64Array(stats)) {
353+
return new BigIntStats(
354+
stats[0 + offset], stats[1 + offset], stats[2 + offset],
355+
stats[3 + offset], stats[4 + offset], stats[5 + offset],
356+
stats[6 + offset], stats[7 + offset], stats[8 + offset],
357+
stats[9 + offset],
358+
nsFromTimeSpecBigInt(stats[10 + offset], stats[11 + offset]),
359+
nsFromTimeSpecBigInt(stats[12 + offset], stats[13 + offset]),
360+
nsFromTimeSpecBigInt(stats[14 + offset], stats[15 + offset]),
361+
nsFromTimeSpecBigInt(stats[16 + offset], stats[17 + offset])
362+
);
363+
}
364+
return new Stats(
365+
stats[0 + offset], stats[1 + offset], stats[2 + offset],
366+
stats[3 + offset], stats[4 + offset], stats[5 + offset],
367+
stats[6 + offset], stats[7 + offset], stats[8 + offset],
368+
stats[9 + offset],
369+
msFromTimeSpec(stats[10 + offset], stats[11 + offset]),
370+
msFromTimeSpec(stats[12 + offset], stats[13 + offset]),
371+
msFromTimeSpec(stats[14 + offset], stats[15 + offset]),
372+
msFromTimeSpec(stats[16 + offset], stats[17 + offset])
373+
);
322374
}
323375

324376
function stringToFlags(flags) {
@@ -466,6 +518,7 @@ function warnOnNonPortableTemplate(template) {
466518

467519
module.exports = {
468520
assertEncoding,
521+
BigIntStats, // for testing
469522
copyObject,
470523
Dirent,
471524
getDirents,
Collapse file

‎src/env.h‎

Copy file name to clipboardExpand all lines: src/env.h
+24-2Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -105,10 +105,32 @@ struct PackageConfig {
105105
};
106106
} // namespace loader
107107

108+
enum class FsStatsOffset {
109+
kDev = 0,
110+
kMode,
111+
kNlink,
112+
kUid,
113+
kGid,
114+
kRdev,
115+
kBlkSize,
116+
kIno,
117+
kSize,
118+
kBlocks,
119+
kATimeSec,
120+
kATimeNsec,
121+
kMTimeSec,
122+
kMTimeNsec,
123+
kCTimeSec,
124+
kCTimeNsec,
125+
kBirthTimeSec,
126+
kBirthTimeNsec,
127+
kFsStatsFieldsNumber
128+
};
129+
108130
// Stat fields buffers contain twice the number of entries in an uv_stat_t
109131
// because `fs.StatWatcher` needs room to store 2 `fs.Stats` instances.
110-
constexpr size_t kFsStatsFieldsNumber = 14;
111-
constexpr size_t kFsStatsBufferLength = kFsStatsFieldsNumber * 2;
132+
constexpr size_t kFsStatsBufferLength =
133+
static_cast<size_t>(FsStatsOffset::kFsStatsFieldsNumber) * 2;
112134

113135
// PER_ISOLATE_* macros: We have a lot of per-isolate properties
114136
// and adding and maintaining their getters and setters by hand would be
Collapse file

‎src/node_file.cc‎

Copy file name to clipboardExpand all lines: src/node_file.cc
+7-4Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2197,10 +2197,13 @@ void Initialize(Local<Object> target,
21972197

21982198
env->SetMethod(target, "mkdtemp", Mkdtemp);
21992199

2200-
target->Set(context,
2201-
FIXED_ONE_BYTE_STRING(isolate, "kFsStatsFieldsNumber"),
2202-
Integer::New(isolate, kFsStatsFieldsNumber))
2203-
.Check();
2200+
target
2201+
->Set(context,
2202+
FIXED_ONE_BYTE_STRING(isolate, "kFsStatsFieldsNumber"),
2203+
Integer::New(
2204+
isolate,
2205+
static_cast<int32_t>(FsStatsOffset::kFsStatsFieldsNumber)))
2206+
.Check();
22042207

22052208
target->Set(context,
22062209
FIXED_ONE_BYTE_STRING(isolate, "statValues"),

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.