benchmark_file_io.cc (6875B)
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 #include "tools/benchmark/benchmark_file_io.h" 6 7 #include <errno.h> 8 #include <sys/stat.h> 9 10 #include <cstdio> 11 12 #if defined(_WIN32) || defined(_WIN64) 13 #include "third_party/dirent.h" 14 #else 15 #include <dirent.h> 16 #include <unistd.h> 17 #endif 18 19 #ifndef HAS_GLOB 20 #define HAS_GLOB 0 21 #if defined __has_include 22 // <glob.h> is included in previous APIs but glob() function is not defined 23 // until API 28. 24 #if __has_include(<glob.h>) && \ 25 (!defined(__ANDROID_API__) || __ANDROID_API__ >= 28) 26 #undef HAS_GLOB 27 #define HAS_GLOB 1 28 #endif // __has_include(<glob.h>) 29 #endif // __has_include 30 #endif // HAS_GLOB 31 32 #if HAS_GLOB 33 #include <glob.h> 34 #endif // HAS_GLOB 35 36 // There is no "user" in embedded filesystems. 37 #ifndef GLOB_TILDE 38 #define GLOB_TILDE 0 39 #endif 40 41 namespace jpegxl { 42 namespace tools { 43 44 const char kPathSeparator = '/'; 45 46 // RAII, ensures dir is closed even when returning early. 47 class DirWrapper { 48 public: 49 DirWrapper(const DirWrapper& other) = delete; 50 DirWrapper& operator=(const DirWrapper& other) = delete; 51 52 explicit DirWrapper(const std::string& pathname) 53 : dir_(opendir(pathname.c_str())) {} 54 55 ~DirWrapper() { 56 if (dir_ != nullptr) { 57 const int err = closedir(dir_); 58 JXL_CHECK(err == 0); 59 } 60 } 61 62 // NOLINTNEXTLINE(google-explicit-constructor) 63 operator DIR*() const { return dir_; } 64 65 private: 66 DIR* const dir_; 67 }; 68 69 // Checks if the file exists, either as file or as directory 70 bool PathExists(const std::string& fname) { 71 struct stat s; 72 if (stat(fname.c_str(), &s) != 0) return false; 73 return true; 74 } 75 76 // Checks if the file exists and is a regular file. 77 bool IsRegularFile(const std::string& fname) { 78 struct stat s; 79 if (stat(fname.c_str(), &s) != 0) return false; 80 return S_ISREG(s.st_mode); 81 } 82 83 // Checks if the file exists and is a directory. 84 bool IsDirectory(const std::string& fname) { 85 struct stat s; 86 if (stat(fname.c_str(), &s) != 0) return false; 87 return S_ISDIR(s.st_mode); 88 } 89 90 // Recursively makes dir, or successfully does nothing if it already exists. 91 Status MakeDir(const std::string& dirname) { 92 size_t pos = 0; 93 for (pos = dirname.size(); pos > 0; pos--) { 94 if (pos == dirname.size() || dirname[pos] == kPathSeparator) { 95 // Found existing dir or regular file, break and then start creating 96 // from here (in the latter case we'll get error below). 97 if (PathExists(dirname.substr(0, pos + 1))) { 98 pos += 1; // Skip past this existing path 99 break; 100 } 101 } 102 } 103 for (; pos <= dirname.size(); pos++) { 104 if (pos == dirname.size() || dirname[pos] == kPathSeparator) { 105 std::string subdir = dirname.substr(0, pos + 1); 106 if (mkdir(subdir.c_str(), 0777) && errno != EEXIST) { 107 return JXL_FAILURE("Failed to create directory"); 108 } 109 } 110 } 111 if (!IsDirectory(dirname)) return JXL_FAILURE("Failed to create directory"); 112 return true; // success 113 } 114 115 Status DeleteFile(const std::string& fname) { 116 if (!IsRegularFile(fname)) { 117 return JXL_FAILURE("Trying to delete non-regular file"); 118 } 119 if (std::remove(fname.c_str())) return JXL_FAILURE("Failed to delete file"); 120 return true; 121 } 122 123 std::string FileBaseName(const std::string& fname) { 124 size_t pos = fname.rfind('/'); 125 if (pos == std::string::npos) return fname; 126 return fname.substr(pos + 1); 127 } 128 129 std::string FileDirName(const std::string& fname) { 130 size_t pos = fname.rfind('/'); 131 if (pos == std::string::npos) return ""; 132 return fname.substr(0, pos); 133 } 134 135 std::string FileExtension(const std::string& fname) { 136 size_t pos = fname.rfind('.'); 137 if (pos == std::string::npos) return ""; 138 return fname.substr(pos); 139 } 140 141 std::string JoinPath(const std::string& first, const std::string& second) { 142 JXL_CHECK(second.empty() || second[0] != kPathSeparator); 143 return (!first.empty() && first.back() == kPathSeparator) 144 ? (first + second) 145 : (first + kPathSeparator + second); 146 } 147 148 // Can match a single file, or multiple files in a directory (non-recursive). 149 // With POSIX, supports glob(), otherwise supports a subset. 150 Status MatchFiles(const std::string& pattern, std::vector<std::string>* list) { 151 #if HAS_GLOB 152 glob_t g; 153 memset(&g, 0, sizeof(g)); 154 int error = glob(pattern.c_str(), GLOB_TILDE, nullptr, &g); 155 if (!error) { 156 for (size_t i = 0; i < g.gl_pathc; ++i) { 157 list->emplace_back(g.gl_pathv[i]); 158 } 159 } 160 globfree(&g); 161 if (error) return JXL_FAILURE("glob failed for %s", pattern.c_str()); 162 return true; 163 #else 164 std::string dirname = FileDirName(pattern); 165 std::string basename = FileBaseName(pattern); 166 size_t pos0 = basename.find('*'); 167 size_t pos1 = pos0 == std::string::npos ? pos0 : basename.find('*', pos0 + 1); 168 std::string prefix, middle, suffix; 169 if (pos0 != std::string::npos) { 170 prefix = basename.substr(0, pos0); 171 if (pos1 != std::string::npos) { 172 middle = basename.substr(pos0 + 1, pos1 - pos0 - 1); 173 suffix = basename.substr(pos1 + 1); 174 } else { 175 suffix = basename.substr(pos0 + 1); 176 } 177 } 178 179 if (prefix.find_first_of("*?[") != std::string::npos || 180 middle.find_first_of("*?[") != std::string::npos || 181 suffix.find_first_of("*?[") != std::string::npos || 182 dirname.find_first_of("*?[") != std::string::npos) { 183 return JXL_FAILURE( 184 "Only glob patterns with max two '*' in the basename" 185 " are supported, e.g. directory/path/*.png or" 186 " /directory/path/*heatmap*"); 187 } 188 189 if (pos0 != std::string::npos) { 190 DirWrapper dir(dirname); 191 if (!dir) return JXL_FAILURE("directory %s doesn't exist", dirname.c_str()); 192 for (;;) { 193 dirent* ent = readdir(dir); 194 if (!ent) break; 195 std::string name = ent->d_name; 196 // If there was a suffix, only add if it matches (e.g. ".png") 197 bool matches = 198 name.size() >= (prefix.size() + middle.size() + suffix.size()); 199 if (matches) { 200 if (!prefix.empty() && name.substr(0, prefix.size()) != prefix) { 201 matches = false; 202 } 203 if (!middle.empty()) { 204 size_t pos = name.find(middle, prefix.size()); 205 if (pos == std::string::npos || 206 pos + middle.size() > name.size() - suffix.size()) { 207 matches = false; 208 } 209 } 210 if (!suffix.empty() && 211 name.substr(name.size() - suffix.size()) != suffix) { 212 matches = false; 213 } 214 } 215 if (matches) { 216 std::string path = JoinPath(dirname, name); 217 218 if (IsRegularFile(path)) { 219 list->push_back(path); 220 } 221 } 222 } 223 return true; 224 } 225 // No *, so a single regular file is intended 226 if (IsRegularFile(pattern)) { 227 list->push_back(pattern); 228 } 229 return true; 230 #endif // HAS_GLOB 231 } 232 233 } // namespace tools 234 } // namespace jpegxl