Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,7 @@ They are still available on rubygems.org and can be installed with
* irb 1.18.0
* 1.16.0 to [v1.17.0][irb-v1.17.0], [v1.18.0][irb-v1.18.0]
* reline 0.7.0
* 0.6.3 to [v0.7.0][reline-v0.7.0]

### RubyGems and Bundler

Expand Down Expand Up @@ -464,3 +465,4 @@ A lot of work has gone into making Ractors more stable, performant, and usable.
[win32ole-v1.9.3]: https://github.com/ruby/win32ole/releases/tag/v1.9.3
[irb-v1.17.0]: https://github.com/ruby/irb/releases/tag/v1.17.0
[irb-v1.18.0]: https://github.com/ruby/irb/releases/tag/v1.18.0
[reline-v0.7.0]: https://github.com/ruby/reline/releases/tag/v0.7.0
97 changes: 52 additions & 45 deletions doc/file/timestamps.md
Original file line number Diff line number Diff line change
@@ -1,23 +1,56 @@
# \Filesystem Timestamps

A filesystem entry (the name of a file or directory)
has several times (called timestamps) associated with it.
has several associated times, called timestamps.

A Ruby method that returns a filesystem timestamp (as a Time object)
is actually returning "whatever the filesystem says";
the returned times may vary among filesystems, even on the same machine.

These timestamps methods are:
Each of these methods returns a Time object:

| Name | Meaning | Changes |
|:--------------------------------:|----------------------------------------|---------------------------------------|
| [`birthtime`](#birth-time) | Create time. | Never. |
| [`mtime`](#modification-time) | Modification time. | When written; see Note 1. |
| [`atime`](#access-time) | Access time. | When read; see Note 2. |
| [`ctime`](#metadata-change-time) | Metadata-change time (or create time). | See [`ctime`](#metadata-change-time). |

Notes:

1. Modification time update may be delayed by the filesystem.
2. Access time may occur immediately, later, or never, depending on filesystem settings.

Each of these methods updates the access time and modification time for an entry:

- File::utime, Pathname#utime: follow symbolic links.
- File::lutime, Pathname#lutime: do not follow symbolic links.

## \File Timestamps

| Operation | Affects<br>birthtime | Affects<br>ctime | Affects<br>mtime | Affects<br>atime |
|:------------------:|:---------------------:|:----------------:|:----------------:|:--------------------------:|
| Create | **Yes** | **Yes** | **Yes** | **Yes** |
| Write content | No | No | **Yes** | No |
| Read content | No | No | No | *Filesystem-<br>dependent* |
| Rename/Move | No | **Yes** | No | No |
| Change permissions | No | **Yes** | No | No |
| Change ownership | No | **Yes** | No | No |


## Directory Timestamps

| Operation | Affects<br>birthtime | Affects<br>ctime | Affects<br>mtime | Affects<br>atime |
|:------------------:|:--------------------:|:----------------:|:----------------:|:--------------------------:|
| Create | **Yes** | **Yes** | **Yes** | **Yes** |
| Write entries | No | No | **Yes** | No |
| Read entries | No | No | No | *Filesystem-<br>dependent* |
| Rename/Move | No | **Yes** | No | No |
| Change permissions | No | **Yes** | No | No |
| Change ownership | No | **Yes** | No | No |


| Name | Meaning | Changes |
|:--------------------------------:|----------------------------------------|---------------|
| [`birthtime`](#birth-time) | Create time. | Never. |
| [`mtime`](#modification-time) | Modification time. | When written. |
| [`atime`](#access-time) | Access time. | When read. |
| [`ctime`](#metadata-change-time) | Metadata-change time (or create time). | See below. |

A method raises an exception if the filesystem does not support
the corresponding timestamp.

## Birth \Time

Expand Down Expand Up @@ -64,6 +97,15 @@ The modification time (along with the access time) may also be updated explicitl
The access time for an entry is the time of the most recent read for the entry,
as reported by the underlying filesystem.

Depending on a filesystem's settings, reading an entry may cause the access time
to be updated immediately, later, or never.

The access time for a file is commonly the most recent time the file was read,
or if never read, the time it was created.

The access time for a directory is commonly the most recent time its entries were read,
or if never read, the time it was created.

Each of these methods returns the access time for an entry as a Time object:

- File::atime.
Expand All @@ -78,41 +120,6 @@ The access time (along with the modification time) may also be updated explicitl
- Pathname#lutime.
- Pathname#utime.

Depending on a filesystem's settings, reading an entry may cause the access time
to be updated immediately, later, or never;
thus in the tables below, some entries say "Filesystem-dependent."

### File

The access time for a file is commonly the most recent time the file was read,
or if never read, the time it was created:

| Operation | Updates Access \Time |
|:------------------:|:--------------------:|
| Create | Yes |
| Read | Filesystem-dependent |
| Write | No |
| Rename | No |
| Move | No |
| Change permissions | No |
| Change ownership | No |


### Directory

The access time for a directory is commonly the most recent time its entries were read,
or if never read, the time it was created:

| Operation | Updates Access \Time |
|:------------------:|:--------------------:|
| Create | Yes |
| Read entries | Filesystem-dependent |
| Write entries | No |
| Rename | No |
| Move | No |
| Change permissions | No |
| Change ownership | No |

## Metadata-Change \Time

The metadata-change time for an entry is the time the entry last read.
Expand Down
70 changes: 34 additions & 36 deletions hash.c
Original file line number Diff line number Diff line change
Expand Up @@ -1482,32 +1482,43 @@ compact_after_delete(VALUE hash)
}

static inline size_t
hash_slot_size(bool st)
hash_slot_size(size_t capa, bool frozen)
{
return sizeof(struct RHash) + (st ? sizeof(st_table) : sizeof(ar_table));
if (capa > RHASH_AR_TABLE_MAX_SIZE) {
return sizeof(struct RHash) + sizeof(st_table);
}

// If the hash is immutable, we can allocate a slot with exactly as much space as needed.
if (frozen) {
return sizeof(struct RHash) + offsetof(ar_table, pairs) + capa * sizeof(ar_table_pair);
}

return sizeof(struct RHash) + sizeof(ar_table);
}

static VALUE
hash_alloc_flags(VALUE klass, VALUE flags, VALUE ifnone, bool st)
hash_alloc_capa(VALUE klass, VALUE flags, VALUE ifnone, size_t size, bool frozen)
{
const size_t size = hash_slot_size(st);
VALUE hash = rb_newobj_of(klass, T_HASH | flags, size);
return rb_hash_set_ifnone(hash, ifnone);
VALUE hash = rb_newobj_of(klass, T_HASH | flags, hash_slot_size(size, frozen));
rb_hash_set_ifnone(hash, ifnone);
if (size > RHASH_AR_TABLE_MAX_SIZE) {
hash_st_table_init(hash, &objhash, size);
}
return hash;
}

static VALUE
hash_alloc(VALUE klass)
{
/* Allocate to be able to fit both st_table and ar_table. */
return hash_alloc_flags(klass, 0, Qnil, sizeof(st_table) > sizeof(ar_table));
return hash_alloc_capa(klass, 0, Qnil, 0, false);
}

#if USE_ZJIT
size_t
rb_zjit_hash_new_size(VALUE *flags_out)
{
*flags_out = T_HASH;
return hash_slot_size(sizeof(st_table) > sizeof(ar_table));
return hash_slot_size(0, false);
}
#endif

Expand Down Expand Up @@ -1537,37 +1548,19 @@ copy_compare_by_id(VALUE hash, VALUE basis)
VALUE
rb_hash_new_with_size(st_index_t size)
{
bool st = size > RHASH_AR_TABLE_MAX_SIZE;
VALUE ret = hash_alloc_flags(rb_cHash, 0, Qnil, st);

if (st) {
hash_st_table_init(ret, &objhash, size);
}

return ret;
return hash_alloc_capa(rb_cHash, 0, Qnil, size, false);
}

VALUE
rb_hash_new_capa(long capa)
{
return rb_hash_new_with_size((st_index_t)capa);
return hash_alloc_capa(rb_cHash, 0, Qnil, capa, false);
}

VALUE
rb_hash_alloc_fixed_size(VALUE klass, st_index_t size)
{
VALUE ret;
if (size > RHASH_AR_TABLE_MAX_SIZE) {
ret = hash_alloc_flags(klass, 0, Qnil, true);
hash_st_table_init(ret, &objhash, size);
}
else {
size_t slot_size = sizeof(struct RHash) + offsetof(ar_table, pairs) + size * sizeof(ar_table_pair);
ret = rb_newobj_of(klass, T_HASH, slot_size);
}

RHASH_SET_IFNONE(ret, Qnil);
return ret;
return hash_alloc_capa(klass, 0, Qnil, size, true);
}

static VALUE
Expand All @@ -1583,8 +1576,6 @@ hash_copy(VALUE ret, VALUE hash)
}
else {
st_table *tab = RHASH_ST_TABLE(ret);
st_init_existing_table_with_size(tab, &objhash, RHASH_AR_TABLE_SIZE(hash));

int bound = RHASH_AR_TABLE_BOUND(hash);
for (int i = 0; i < bound; i++) {
if (ar_cleared_entry(hash, i)) continue;
Expand All @@ -1610,7 +1601,7 @@ hash_copy(VALUE ret, VALUE hash)
static VALUE
hash_dup_with_compare_by_id(VALUE hash)
{
VALUE dup = hash_alloc_flags(rb_cHash, 0, Qnil, RHASH_ST_TABLE_P(hash));
VALUE dup = hash_alloc_capa(rb_cHash, 0, Qnil, RHASH_SIZE(hash), false);
if (RHASH_ST_TABLE_P(hash)) {
RHASH_SET_ST_FLAG(dup);
}
Expand All @@ -1624,8 +1615,8 @@ hash_dup_with_compare_by_id(VALUE hash)
static VALUE
hash_dup(VALUE hash, VALUE klass, VALUE flags)
{
return hash_copy(hash_alloc_flags(klass, flags, RHASH_IFNONE(hash), !RHASH_EMPTY_P(hash) && RHASH_ST_TABLE_P(hash)),
hash);
VALUE dup = hash_alloc_capa(klass, flags, RHASH_IFNONE(hash), RHASH_SIZE(hash), false);
return hash_copy(dup, hash);
}

VALUE
Expand Down Expand Up @@ -1655,7 +1646,7 @@ rb_zjit_hash_dup_can_fastpath(VALUE hash, size_t *alloc_size_out, VALUE *flags_o

const unsigned int bound = RHASH_AR_TABLE_BOUND(hash);

*alloc_size_out = hash_slot_size(false);
*alloc_size_out = hash_slot_size(0, false);
*flags_out = T_HASH
| ((VALUE)RHASH_AR_TABLE_SIZE(hash) << RHASH_AR_TABLE_SIZE_SHIFT)
| ((VALUE)bound << RHASH_AR_TABLE_BOUND_SHIFT);
Expand Down Expand Up @@ -3085,11 +3076,18 @@ rb_hash_replace(VALUE hash, VALUE hash2)

if (RHASH_AR_TABLE_P(hash)) {
hash_ar_free_and_clear_table(hash);
if (RHASH_SIZE(hash2) > RHASH_AR_TABLE_MAX_SIZE) {
RHASH_SET_ST_FLAG(hash);
}
}
else {
hash_st_free_and_clear_table(hash);
}

if (RHASH_ST_TABLE_P(hash)) {
st_init_existing_table_with_size(RHASH_ST_TABLE(hash), &objhash, RHASH_SIZE(hash2));
}

hash_copy(hash, hash2);

return hash;
Expand Down