@@ -380,9 +380,15 @@ rb_str_normalize_ospath(const char *ptr, long len)
380380 const char * p = ptr ;
381381 const char * e = ptr + len ;
382382 const char * p1 = p ;
383- VALUE str = rb_str_buf_new (len );
384383 rb_encoding * enc = rb_utf8_encoding ();
385- rb_enc_associate (str , enc );
384+ VALUE str = rb_utf8_str_new (ptr , len );
385+ if (RB_LIKELY (rb_enc_str_coderange (str ) == ENC_CODERANGE_7BIT )) {
386+ return str ;
387+ }
388+ else {
389+ str = rb_str_buf_new (len );
390+ rb_enc_associate (str , enc );
391+ }
386392
387393 while (p < e ) {
388394 int l , c ;
@@ -1095,12 +1101,26 @@ static VALUE statx_birthtime(const rb_io_stat_data *st);
10951101
10961102/*
10971103 * call-seq:
1098- * stat.atime -> time
1099- *
1100- * Returns the last access time for this file as an object of class
1101- * Time.
1102- *
1103- * File.stat("testfile").atime #=> Wed Dec 31 18:00:00 CST 1969
1104+ * atime -> new_time
1105+ *
1106+ * Returns a new Time object containing the access time
1107+ * of the object represented by +self+
1108+ * at the time +self+ was created;
1109+ * see {Snapshot}[rdoc-ref:File::Stat@Snapshot]:
1110+ *
1111+ * filepath = 't.tmp'
1112+ * File.write(filepath, 'foo')
1113+ * file = File.new(filepath, 'w')
1114+ * stat = File::Stat.new(filepath)
1115+ * file.atime # => 2026-03-31 16:26:39.5913207 -0500
1116+ * stat.atime # => 2026-03-31 16:26:39.5913207 -0500
1117+ * File.write(filepath, 'bar')
1118+ * file.atime # => 2026-03-31 16:27:01.4981624 -0500 # Changed by access.
1119+ * stat.atime # => 2026-03-31 16:26:39.5913207 -0500 # Unchanged by access.
1120+ * stat = File::Stat.new(filepath)
1121+ * stat.atime # => 2026-03-31 16:27:01.4981624 -0500 # New access time.
1122+ * file.close
1123+ * File.delete(filepath)
11041124 *
11051125 */
11061126
@@ -2446,13 +2466,23 @@ rb_file_s_ftype(VALUE klass, VALUE fname)
24462466
24472467/*
24482468 * call-seq:
2449- * File.atime(file_name) -> time
2469+ * File.atime(object) -> new_time
24502470 *
2451- * Returns the last access time for the named file as a Time object.
2471+ * Returns a new Time object containing the time of the most recent
2472+ * access (read or write) to the object,
2473+ * which may be a string filepath or dirpath, or a File or Dir object:
24522474 *
2453- * _file_name_ can be an IO object.
2475+ * filepath = 't.tmp'
2476+ * File.exist?(filepath) # => false
2477+ * File.atime(filepath) # Raises Errno::ENOENT.
2478+ * File.write(filepath, 'foo')
2479+ * File.atime(filepath) # => 2026-03-31 16:39:37.9290772 -0500
2480+ * File.write(filepath, 'bar')
2481+ * File.atime(filepath) # => 2026-03-31 16:39:57.7710876 -0500
24542482 *
2455- * File.atime("testfile") #=> Wed Apr 09 08:51:48 CDT 2003
2483+ * File.atime('.') # => 2026-03-31 16:47:49.0970483 -0500
2484+ * File.atime(File.new('README.md')) # => 2026-03-31 11:15:27.8215934 -0500
2485+ * File.atime(Dir.new('.')) # => 2026-03-31 12:39:45.5910591 -0500
24562486 *
24572487 */
24582488
@@ -2471,12 +2501,20 @@ rb_file_s_atime(VALUE klass, VALUE fname)
24712501
24722502/*
24732503 * call-seq:
2474- * file. atime -> time
2504+ * atime -> new_time
24752505 *
2476- * Returns the last access time (a Time object) for <i>file</i>, or
2477- * epoch if <i>file</i> has not been accessed.
2506+ * Returns a new Time object containing the time of the most recent
2507+ * access (read or write) to the file represented by +self+:
24782508 *
2479- * File.new("testfile").atime #=> Wed Dec 31 18:00:00 CST 1969
2509+ * filepath = 't.tmp'
2510+ * file = File.new(filepath, 'a+')
2511+ * file.atime # => 2026-03-31 17:11:27.7285397 -0500
2512+ * file.write('foo')
2513+ * file.atime # => 2026-03-31 17:11:27.7285397 -0500 # Unchanged; not yet written.
2514+ * file.flush
2515+ * file.atime # => 2026-03-31 17:12:11.3408054 -0500 # Changed; now written.
2516+ * file.close
2517+ * File.delete(filename)
24802518 *
24812519 */
24822520
@@ -3707,8 +3745,9 @@ skipprefixroot(const char *path, const char *end, rb_encoding *enc)
37073745#endif
37083746}
37093747
3710- char *
3711- rb_enc_path_last_separator (const char * path , const char * end , rb_encoding * enc )
3748+
3749+ static char *
3750+ enc_path_last_separator (const char * path , const char * end , bool mb_enc , rb_encoding * enc )
37123751{
37133752 char * last = NULL ;
37143753 while (path < end ) {
@@ -3719,17 +3758,22 @@ rb_enc_path_last_separator(const char *path, const char *end, rb_encoding *enc)
37193758 last = (char * )tmp ;
37203759 }
37213760 else {
3722- Inc (path , end , true , enc );
3761+ Inc (path , end , mb_enc , enc );
37233762 }
37243763 }
37253764 return last ;
37263765}
3766+ char *
3767+ rb_enc_path_last_separator (const char * path , const char * end , rb_encoding * enc )
3768+ {
3769+ return enc_path_last_separator (path , end , true, enc );
3770+ }
37273771
37283772static inline char *
37293773strrdirsep (const char * path , const char * end , bool mb_enc , rb_encoding * enc )
37303774{
37313775 if (RB_UNLIKELY (mb_enc )) {
3732- return rb_enc_path_last_separator (path , end , enc );
3776+ return enc_path_last_separator (path , end , mb_enc , enc );
37333777 }
37343778
37353779 const char * cursor = end - 1 ;
@@ -4021,7 +4065,12 @@ rb_file_expand_path_internal(VALUE fname, VALUE dname, int abs_mode, int long_na
40214065
40224066 s = StringValuePtr (fname );
40234067 fend = s + RSTRING_LEN (fname );
4024- enc = rb_enc_get (fname );
4068+ enc = rb_str_enc_get (fname );
4069+ bool mb_enc = !rb_str_encindex_fastpath (rb_enc_to_index (enc ));
4070+ if (!mb_enc && RTEST (dname )) {
4071+ mb_enc = !rb_str_encindex_fastpath (rb_enc_to_index (rb_str_enc_get (dname )));
4072+ }
4073+
40254074 BUFINIT ();
40264075
40274076 if (s < fend && s [0 ] == '~' && abs_mode == 0 ) { /* execute only if NOT absolute_path() */
@@ -4115,7 +4164,7 @@ rb_file_expand_path_internal(VALUE fname, VALUE dname, int abs_mode, int long_na
41154164 }
41164165 else
41174166#endif /* defined DOSISH || defined __CYGWIN__ */
4118- p = chompdirsep (skiproot (buf , p ), p , true , enc );
4167+ p = chompdirsep (skiproot (buf , p ), p , mb_enc , enc );
41194168 }
41204169 else {
41214170 size_t len ;
@@ -4139,7 +4188,7 @@ rb_file_expand_path_internal(VALUE fname, VALUE dname, int abs_mode, int long_na
41394188 rb_str_set_len (result , p - buf + 1 );
41404189 BUFCHECK (bdiff + 1 >= buflen );
41414190 p [1 ] = 0 ;
4142- root = skipprefix (buf , p + 1 , true , enc );
4191+ root = skipprefix (buf , p + 1 , mb_enc , enc );
41434192
41444193 b = s ;
41454194 while (s < fend ) {
@@ -4156,7 +4205,7 @@ rb_file_expand_path_internal(VALUE fname, VALUE dname, int abs_mode, int long_na
41564205 /* We must go back to the parent */
41574206 char * n ;
41584207 * p = '\0' ;
4159- if (!(n = strrdirsep (root , p , true , enc ))) {
4208+ if (!(n = strrdirsep (root , p , mb_enc , enc ))) {
41604209 * p = '/' ;
41614210 }
41624211 else {
@@ -4219,7 +4268,7 @@ rb_file_expand_path_internal(VALUE fname, VALUE dname, int abs_mode, int long_na
42194268 }
42204269 }
42214270#endif /* __APPLE__ */
4222- Inc (s , fend , true , enc );
4271+ Inc (s , fend , mb_enc , enc );
42234272 break ;
42244273 }
42254274 }
0 commit comments