Skip to content

Commit 6d9ac89

Browse files
committed
Implement File#size and File#truncate
1 parent 6c5ee8f commit 6d9ac89

File tree

2 files changed

+116
-1
lines changed

2 files changed

+116
-1
lines changed

mrbgems/mruby-io/src/file.c

Lines changed: 100 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,15 @@
7070
#define LOCK_UN 8
7171
#endif
7272

73-
#define STAT(p, s) stat(p, s)
73+
#ifndef _WIN32
74+
typedef struct stat mrb_stat;
75+
# define mrb_stat(path, sb) stat(path, sb)
76+
# define mrb_fstat(fd, sb) fstat(fd, sb)
77+
#else
78+
typedef struct __stat64 mrb_stat;
79+
# define mrb_stat(path, sb) _stat64(path, sb)
80+
# define mrb_fstat(fd, sb) _fstat64(fd, sb)
81+
#endif
7482

7583
#ifdef _WIN32
7684
static int
@@ -384,6 +392,95 @@ mrb_file_flock(mrb_state *mrb, mrb_value self)
384392
return mrb_fixnum_value(0);
385393
}
386394

395+
static mrb_value
396+
mrb_file_size(mrb_state *mrb, mrb_value self)
397+
{
398+
mrb_stat st;
399+
int fd;
400+
401+
fd = (int)mrb_fixnum(mrb_io_fileno(mrb, self));
402+
if (mrb_fstat(fd, &st) == -1) {
403+
mrb_raise(mrb, E_RUNTIME_ERROR, "fstat failed");
404+
}
405+
406+
if (st.st_size > MRB_INT_MAX) {
407+
#ifdef MRB_WITHOUT_FLOAT
408+
mrb_raise(mrb, E_RUNTIME_ERROR, "File#size too large for MRB_WITHOUT_FLOAT");
409+
#else
410+
return mrb_float_value(mrb, st.st_size);
411+
#endif
412+
}
413+
414+
return mrb_fixnum_value(st.st_size);
415+
}
416+
417+
static int
418+
mrb_ftruncate(int fd, int64_t length)
419+
{
420+
#ifndef _WIN32
421+
return ftruncate(fd, (off_t)length);
422+
#else
423+
HANDLE file;
424+
__int64 cur;
425+
426+
file = (HANDLE)_get_osfhandle(fd);
427+
if (file == INVALID_HANDLE_VALUE) {
428+
return -1;
429+
}
430+
431+
cur = _lseeki64(fd, 0, SEEK_CUR);
432+
if (cur == -1) return -1;
433+
434+
if (_lseeki64(fd, (__int64)length, SEEK_SET) == -1) return -1;
435+
436+
if (!SetEndOfFile(file)) {
437+
errno = EINVAL; /* TODO: GetLastError to errno */
438+
return -1;
439+
}
440+
441+
if (_lseeki64(fd, cur, SEEK_SET) == -1) return -1;
442+
443+
return 0;
444+
#endif /* _WIN32 */
445+
}
446+
447+
static mrb_value
448+
mrb_file_truncate(mrb_state *mrb, mrb_value self)
449+
{
450+
int fd;
451+
int64_t length;
452+
mrb_value lenv;
453+
454+
fd = (int)mrb_fixnum(mrb_io_fileno(mrb, self));
455+
mrb_get_args(mrb, "o", &lenv);
456+
switch (mrb_type(lenv)) {
457+
#ifndef MRB_WITHOUT_FLOAT
458+
case MRB_TT_FLOAT:
459+
{
460+
mrb_float lenf = mrb_float(lenv);
461+
if (lenf > INT64_MAX) {
462+
mrb_raise(mrb, E_ARGUMENT_ERROR, "length too large");
463+
}
464+
length = (int64_t)lenf;
465+
}
466+
break;
467+
#endif
468+
case MRB_TT_FIXNUM:
469+
default:
470+
{
471+
mrb_int leni = mrb_int(mrb, lenv);
472+
length = (int64_t)leni;
473+
}
474+
break;
475+
}
476+
477+
if (mrb_ftruncate(fd, length) != 0) {
478+
mrb_raise(mrb, E_IO_ERROR, "ftruncate failed");
479+
}
480+
481+
return mrb_fixnum_value(0);
482+
}
483+
387484
static mrb_value
388485
mrb_file_s_symlink(mrb_state *mrb, mrb_value klass)
389486
{
@@ -490,6 +587,8 @@ mrb_init_file(mrb_state *mrb)
490587

491588
mrb_define_method(mrb, file, "flock", mrb_file_flock, MRB_ARGS_REQ(1));
492589
mrb_define_method(mrb, file, "mtime", mrb_file_mtime, MRB_ARGS_NONE());
590+
mrb_define_method(mrb, file, "size", mrb_file_size, MRB_ARGS_NONE());
591+
mrb_define_method(mrb, file, "truncate", mrb_file_truncate, MRB_ARGS_REQ(1));
493592

494593
cnst = mrb_define_module_under(mrb, file, "Constants");
495594
mrb_define_const(mrb, cnst, "LOCK_SH", mrb_fixnum_value(LOCK_SH));

mrbgems/mruby-io/test/file.rb

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,22 @@
8080
end
8181
end
8282

83+
assert('File#size and File#truncate') do
84+
fname = "#{$mrbtest_io_wfname}.resize"
85+
begin
86+
File.open(fname, 'w') do |f|
87+
assert_equal 0, f.size
88+
assert_equal 0, f.truncate(100)
89+
assert_equal 100, f.size
90+
assert_equal 0, f.pos
91+
assert_equal 0, f.truncate(5)
92+
assert_equal 5, f.size
93+
end
94+
ensure
95+
File.delete(fname)
96+
end
97+
end
98+
8399
assert('File.join') do
84100
assert_equal "", File.join()
85101
assert_equal "a", File.join("a")

0 commit comments

Comments
 (0)