1 // Copyright (c) the JPEG XL Project Authors. All rights reserved.
2 //
3 // Use of this source code is governed by a BSD-style
4 // license that can be found in the LICENSE file.
5 
6 #ifndef LIB_JXL_BASE_FILE_IO_H_
7 #define LIB_JXL_BASE_FILE_IO_H_
8 
9 // Helper functions for reading/writing files.
10 
11 #include <stdio.h>
12 #include <sys/stat.h>
13 
14 #include <string>
15 
16 #include "lib/jxl/base/compiler_specific.h"
17 #include "lib/jxl/base/padded_bytes.h"
18 #include "lib/jxl/base/status.h"
19 
20 namespace jxl {
21 
22 // Returns extension including the dot, or empty string if none. Assumes
23 // filename is not a hidden file (e.g. ".bashrc"). May be called with a pathname
24 // if the filename contains a dot and/or no other path component does.
Extension(const std::string & filename)25 static inline std::string Extension(const std::string& filename) {
26   const size_t pos = filename.rfind('.');
27   if (pos == std::string::npos) return std::string();
28   return filename.substr(pos);
29 }
30 
31 // RAII, ensures files are closed even when returning early.
32 class FileWrapper {
33  public:
34   FileWrapper(const FileWrapper& other) = delete;
35   FileWrapper& operator=(const FileWrapper& other) = delete;
36 
FileWrapper(const std::string & pathname,const char * mode)37   explicit FileWrapper(const std::string& pathname, const char* mode)
38       : file_(fopen(pathname.c_str(), mode)) {}
39 
~FileWrapper()40   ~FileWrapper() {
41     if (file_ != nullptr) {
42       const int err = fclose(file_);
43       JXL_CHECK(err == 0);
44     }
45   }
46 
47   // We intend to use FileWrapper as a replacement of FILE.
48   // NOLINTNEXTLINE(google-explicit-constructor)
49   operator FILE*() const { return file_; }
50 
51  private:
52   FILE* const file_;
53 };
54 
55 template <typename ContainerType>
ReadFile(const std::string & pathname,ContainerType * JXL_RESTRICT bytes)56 static inline Status ReadFile(const std::string& pathname,
57                               ContainerType* JXL_RESTRICT bytes) {
58   FileWrapper f(pathname, "rb");
59   if (f == nullptr) return JXL_FAILURE("Failed to open file for reading");
60 
61     // Ensure it is a regular file
62 #ifdef _WIN32
63   struct __stat64 s = {};
64   const int err = _stat64(pathname.c_str(), &s);
65   const bool is_file = (s.st_mode & S_IFREG) != 0;
66 #else
67   struct stat s = {};
68   const int err = stat(pathname.c_str(), &s);
69   const bool is_file = S_ISREG(s.st_mode);
70 #endif
71   if (err != 0) return JXL_FAILURE("Failed to obtain file status");
72   if (!is_file) return JXL_FAILURE("Not a file");
73 
74   // Get size of file in bytes
75   const int64_t size = s.st_size;
76   if (size <= 0) return JXL_FAILURE("Empty or invalid file size");
77   bytes->resize(static_cast<size_t>(size));
78 
79   size_t pos = 0;
80   while (pos < bytes->size()) {
81     // Needed in case ContainerType is std::string, whose data() is const.
82     char* bytes_writable = reinterpret_cast<char*>(&(*bytes)[0]);
83     const size_t bytes_read =
84         fread(bytes_writable + pos, 1, bytes->size() - pos, f);
85     if (bytes_read == 0) return JXL_FAILURE("Failed to read");
86     pos += bytes_read;
87   }
88   JXL_ASSERT(pos == bytes->size());
89   return true;
90 }
91 
92 template <typename ContainerType>
WriteFile(const ContainerType & bytes,const std::string & pathname)93 static inline Status WriteFile(const ContainerType& bytes,
94                                const std::string& pathname) {
95   FileWrapper f(pathname, "wb");
96   if (f == nullptr) return JXL_FAILURE("Failed to open file for writing");
97 
98   size_t pos = 0;
99   while (pos < bytes.size()) {
100     const size_t bytes_written =
101         fwrite(bytes.data() + pos, 1, bytes.size() - pos, f);
102     if (bytes_written == 0) return JXL_FAILURE("Failed to write");
103     pos += bytes_written;
104   }
105   JXL_ASSERT(pos == bytes.size());
106 
107   return true;
108 }
109 
110 }  // namespace jxl
111 
112 #endif  // LIB_JXL_BASE_FILE_IO_H_
113