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 d36adcd

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 5a7154e commit d36adcd
Copy full SHA for d36adcd

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
@@ -4976,6 +5038,7 @@ the file contents.
49765038
[`net.Socket`]: net.html#net_class_net_socket
49775039
[`stat()`]: fs.html#fs_fs_stat_path_options_callback
49785040
[`util.promisify()`]: util.html#util_util_promisify_original
5041+
[bigints]: https://tc39.github.io/proposal-bigint
49795042
[Caveats]: #fs_caveats
49805043
[Common System Errors]: errors.html#errors_common_system_errors
49815044
[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 {
@@ -16,7 +16,8 @@ const {
1616
} = require('internal/errors');
1717
const {
1818
isUint8Array,
19-
isDate
19+
isDate,
20+
isBigUint64Array
2021
} = require('internal/util/types');
2122
const { once } = require('internal/util');
2223
const { toPathIfFileURL } = require('internal/url');
@@ -230,27 +231,9 @@ function preprocessSymlinkDestination(path, type, linkPath) {
230231
}
231232
}
232233

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

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

289-
Stats.prototype.isFile = function() {
253+
StatsBase.prototype.isFile = function() {
290254
return this._checkModeProperty(S_IFREG);
291255
};
292256

293-
Stats.prototype.isBlockDevice = function() {
257+
StatsBase.prototype.isBlockDevice = function() {
294258
return this._checkModeProperty(S_IFBLK);
295259
};
296260

297-
Stats.prototype.isCharacterDevice = function() {
261+
StatsBase.prototype.isCharacterDevice = function() {
298262
return this._checkModeProperty(S_IFCHR);
299263
};
300264

301-
Stats.prototype.isSymbolicLink = function() {
265+
StatsBase.prototype.isSymbolicLink = function() {
302266
return this._checkModeProperty(S_IFLNK);
303267
};
304268

305-
Stats.prototype.isFIFO = function() {
269+
StatsBase.prototype.isFIFO = function() {
306270
return this._checkModeProperty(S_IFIFO);
307271
};
308272

309-
Stats.prototype.isSocket = function() {
273+
StatsBase.prototype.isSocket = function() {
310274
return this._checkModeProperty(S_IFSOCK);
311275
};
312276

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

323375
function stringToFlags(flags) {
@@ -453,6 +505,7 @@ function warnOnNonPortableTemplate(template) {
453505

454506
module.exports = {
455507
assertEncoding,
508+
BigIntStats, // for testing
456509
copyObject,
457510
Dirent,
458511
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
@@ -102,10 +102,32 @@ struct PackageConfig {
102102
};
103103
} // namespace loader
104104

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

110132
// PER_ISOLATE_* macros: We have a lot of per-isolate properties
111133
// 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.