diff --git a/posix.cc b/posix.cc index 3b02dd0b..7ef6b7b9 100644 --- a/posix.cc +++ b/posix.cc @@ -53,6 +53,20 @@ #endif // _WIN32 +#if FMT_GCC_VERSION >= 407 +# define FMT_UNUSED __attribute__((unused)) +#else +# define FMT_UNUSED +#endif + +#if FMT_USE_STATIC_ASSERT +# define FMT_STATIC_ASSERT(cond, message) static_assert(cond, message) +#else +# define FMT_CONCAT_(a, b) FMT_CONCAT(a, b) +# define FMT_STATIC_ASSERT(cond, message) \ + typedef int FMT_CONCAT_(Assert, __LINE__)[(cond) ? 1 : -1] FMT_UNUSED +#endif + namespace { #ifdef _WIN32 // Return type of read and write functions. @@ -98,13 +112,13 @@ int fmt::BufferedFile::fileno() const { return fd; } -fmt::File::File(const char *path, int oflag) { +fmt::File::File(fmt::StringRef path, int oflag) { int mode = S_IRUSR | S_IWUSR; #ifdef _WIN32 fd_ = -1; - FMT_POSIX_CALL(sopen_s(&fd_, path, oflag, _SH_DENYNO, mode)); + FMT_POSIX_CALL(sopen_s(&fd_, path.c_str(), oflag, _SH_DENYNO, mode)); #else - FMT_RETRY(fd_, FMT_POSIX_CALL(open(path, oflag, mode))); + FMT_RETRY(fd_, FMT_POSIX_CALL(open(path.c_str(), oflag, mode))); #endif if (fd_ == -1) throw SystemError(errno, "cannot open file {}", path); @@ -128,6 +142,25 @@ void fmt::File::close() { throw SystemError(errno, "cannot close file"); } +fmt::LongLong fmt::File::size() const { +#ifdef _WIN32 + LARGE_INTEGER size = {}; + if (!GetFileSizeEx(_get_osfhandle(fd_), &size)) + throw WindowsError(GetLastError(), "cannot get file size"); + FMT_STATIC_ASSERT(sizeof(fmt::LongLong) >= sizeof(size.QuadPart), + "return type of File::size is not large enough"); + return size.QuadPart; +#else + typedef struct stat Stat; + Stat file_stat = Stat(); + if (fstat(fd_, &file_stat) == -1) + throw SystemError(errno, "cannot get file attributes"); + FMT_STATIC_ASSERT(sizeof(fmt::LongLong) >= sizeof(file_stat.st_size), + "return type of File::size is not large enough"); + return file_stat.st_size; +#endif +} + std::size_t fmt::File::read(void *buffer, std::size_t count) { RWResult result = 0; FMT_RETRY(result, FMT_POSIX_CALL(read(fd_, buffer, convert_rwcount(count)))); diff --git a/posix.h b/posix.h index 0b7965a4..96a4e973 100644 --- a/posix.h +++ b/posix.h @@ -206,7 +206,7 @@ class File { File() FMT_NOEXCEPT(true) : fd_(-1) {} // Opens a file and constructs a File object representing this file. - File(const char *path, int oflag); + File(fmt::StringRef path, int oflag); #if !FMT_USE_RVALUE_REFERENCES // Emulate a move constructor and a move assignment operator if rvalue @@ -277,6 +277,9 @@ class File { // Closes the file. void close(); + // Returns the file size. + fmt::LongLong size() const; + // Attempts to read count bytes from the file into the specified buffer. std::size_t read(void *buffer, std::size_t count); diff --git a/test/posix-test.cc b/test/posix-test.cc index af9f06e8..7b6c3f2e 100644 --- a/test/posix-test.cc +++ b/test/posix-test.cc @@ -194,6 +194,17 @@ TEST(FileTest, CloseNoRetry) { close_count = 0; } +TEST(FileTest, Size) { + BufferedFile bf("test", "w"); + std::string content = "top secret, destroy before reading"; + bf.print(content); + bf.close(); + File f("test", File::RDONLY); + EXPECT_EQ(content.size(), f.size()); + // TODO: test if size can handle large file sizes + // TODO: test FMT_STATIC_ASSERT +} + TEST(FileTest, ReadRetry) { File read_end, write_end; File::pipe(read_end, write_end);