file_io.h (3942B)
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 TOOLS_FILE_IO_H_ 7 #define TOOLS_FILE_IO_H_ 8 9 #include <errno.h> 10 #include <limits.h> 11 #include <stdint.h> 12 #include <stdio.h> 13 #include <string.h> 14 #include <sys/stat.h> 15 16 #include <list> 17 #include <string> 18 #include <vector> 19 20 #include "lib/jxl/base/compiler_specific.h" 21 22 namespace jpegxl { 23 namespace tools { 24 25 // RAII, ensures files are closed even when returning early. 26 class FileWrapper { 27 public: 28 FileWrapper(const FileWrapper& other) = delete; 29 FileWrapper& operator=(const FileWrapper& other) = delete; 30 31 explicit FileWrapper(const std::string& pathname, const char* mode) 32 : file_(pathname == "-" ? (mode[0] == 'r' ? stdin : stdout) 33 : fopen(pathname.c_str(), mode)), 34 close_on_delete_(pathname != "-") { 35 #ifdef _WIN32 36 struct __stat64 s = {}; 37 const int err = _stat64(pathname.c_str(), &s); 38 const bool is_file = (s.st_mode & S_IFREG) != 0; 39 #else 40 struct stat s = {}; 41 const int err = stat(pathname.c_str(), &s); 42 const bool is_file = S_ISREG(s.st_mode); 43 #endif 44 if (err == 0 && is_file) { 45 size_ = s.st_size; 46 } 47 } 48 49 ~FileWrapper() { 50 if (file_ != nullptr && close_on_delete_) { 51 const int err = fclose(file_); 52 if (err) { 53 fprintf(stderr, 54 "Could not close file\n" 55 "Error: %s", 56 strerror(errno)); 57 } 58 } 59 } 60 61 // We intend to use FileWrapper as a replacement of FILE. 62 // NOLINTNEXTLINE(google-explicit-constructor) 63 operator FILE*() const { return file_; } 64 65 int64_t size() const { return size_; } 66 67 private: 68 FILE* const file_; 69 bool close_on_delete_ = true; 70 int64_t size_ = -1; 71 }; 72 73 template <typename ContainerType> 74 static inline bool ReadFile(FileWrapper& f, ContainerType* JXL_RESTRICT bytes) { 75 if (!f) return false; 76 77 // Get size of file in bytes 78 const int64_t size = f.size(); 79 if (size < 0) { 80 // Size is unknown, loop reading chunks until EOF. 81 bytes->clear(); 82 std::list<std::vector<uint8_t>> chunks; 83 84 size_t total_size = 0; 85 while (true) { 86 std::vector<uint8_t> chunk(16 * 1024); 87 const size_t bytes_read = fread(chunk.data(), 1, chunk.size(), f); 88 if (ferror(f) || bytes_read > chunk.size()) { 89 return false; 90 } 91 92 chunk.resize(bytes_read); 93 total_size += bytes_read; 94 if (bytes_read != 0) { 95 chunks.emplace_back(std::move(chunk)); 96 } 97 if (feof(f)) { 98 break; 99 } 100 } 101 bytes->resize(total_size); 102 size_t pos = 0; 103 for (const auto& chunk : chunks) { 104 memcpy(bytes->data() + pos, chunk.data(), chunk.size()); 105 pos += chunk.size(); 106 } 107 } else { 108 // Size is known, read the file directly. 109 bytes->resize(static_cast<size_t>(size)); 110 111 const size_t bytes_read = fread(bytes->data(), 1, bytes->size(), f); 112 if (bytes_read != static_cast<size_t>(size)) return false; 113 } 114 115 return true; 116 } 117 118 template <typename ContainerType> 119 static inline bool ReadFile(const std::string& filename, 120 ContainerType* JXL_RESTRICT bytes) { 121 FileWrapper f(filename, "rb"); 122 return ReadFile(f, bytes); 123 } 124 125 template <typename ContainerType> 126 static inline bool WriteFile(const std::string& filename, 127 const ContainerType& bytes) { 128 FileWrapper file(filename, "wb"); 129 if (!file) { 130 fprintf(stderr, 131 "Could not open %s for writing\n" 132 "Error: %s", 133 filename.c_str(), strerror(errno)); 134 return false; 135 } 136 if (fwrite(bytes.data(), 1, bytes.size(), file) != bytes.size()) { 137 fprintf(stderr, 138 "Could not write to file\n" 139 "Error: %s", 140 strerror(errno)); 141 return false; 142 } 143 return true; 144 } 145 146 } // namespace tools 147 } // namespace jpegxl 148 149 #endif // TOOLS_FILE_IO_H_