Add a stat to the smoothness benchmark for avg number of missing tiles.
[chromium-blink-merge.git] / base / file_util.cc
blobeffee8af29b52c165908c00af0d10b4a4c9b2e07
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "base/file_util.h"
7 #if defined(OS_WIN)
8 #include <io.h>
9 #endif
10 #include <stdio.h>
12 #include <fstream>
14 #include "base/file_path.h"
15 #include "base/logging.h"
16 #include "base/stringprintf.h"
17 #include "base/string_piece.h"
18 #include "base/string_util.h"
19 #include "base/utf_string_conversions.h"
21 namespace {
23 const FilePath::CharType kExtensionSeparator = FILE_PATH_LITERAL('.');
25 // The maximum number of 'uniquified' files we will try to create.
26 // This is used when the filename we're trying to download is already in use,
27 // so we create a new unique filename by appending " (nnn)" before the
28 // extension, where 1 <= nnn <= kMaxUniqueFiles.
29 // Also used by code that cleans up said files.
30 static const int kMaxUniqueFiles = 100;
32 } // namespace
34 namespace file_util {
36 bool g_bug108724_debug = false;
38 bool EndsWithSeparator(const FilePath& path) {
39 FilePath::StringType value = path.value();
40 if (value.empty())
41 return false;
43 return FilePath::IsSeparator(value[value.size() - 1]);
46 bool EnsureEndsWithSeparator(FilePath* path) {
47 if (!DirectoryExists(*path))
48 return false;
50 if (EndsWithSeparator(*path))
51 return true;
53 FilePath::StringType& path_str =
54 const_cast<FilePath::StringType&>(path->value());
55 path_str.append(&FilePath::kSeparators[0], 1);
57 return true;
60 void InsertBeforeExtension(FilePath* path, const FilePath::StringType& suffix) {
61 FilePath::StringType& value =
62 const_cast<FilePath::StringType&>(path->value());
64 const FilePath::StringType::size_type last_dot =
65 value.rfind(kExtensionSeparator);
66 const FilePath::StringType::size_type last_separator =
67 value.find_last_of(FilePath::StringType(FilePath::kSeparators));
69 if (last_dot == FilePath::StringType::npos ||
70 (last_separator != std::wstring::npos && last_dot < last_separator)) {
71 // The path looks something like "C:\pics.old\jojo" or "C:\pics\jojo".
72 // We should just append the suffix to the entire path.
73 value.append(suffix);
74 return;
77 value.insert(last_dot, suffix);
80 bool ContentsEqual(const FilePath& filename1, const FilePath& filename2) {
81 // We open the file in binary format even if they are text files because
82 // we are just comparing that bytes are exactly same in both files and not
83 // doing anything smart with text formatting.
84 std::ifstream file1(filename1.value().c_str(),
85 std::ios::in | std::ios::binary);
86 std::ifstream file2(filename2.value().c_str(),
87 std::ios::in | std::ios::binary);
89 // Even if both files aren't openable (and thus, in some sense, "equal"),
90 // any unusable file yields a result of "false".
91 if (!file1.is_open() || !file2.is_open())
92 return false;
94 const int BUFFER_SIZE = 2056;
95 char buffer1[BUFFER_SIZE], buffer2[BUFFER_SIZE];
96 do {
97 file1.read(buffer1, BUFFER_SIZE);
98 file2.read(buffer2, BUFFER_SIZE);
100 if ((file1.eof() != file2.eof()) ||
101 (file1.gcount() != file2.gcount()) ||
102 (memcmp(buffer1, buffer2, file1.gcount()))) {
103 file1.close();
104 file2.close();
105 return false;
107 } while (!file1.eof() || !file2.eof());
109 file1.close();
110 file2.close();
111 return true;
114 bool TextContentsEqual(const FilePath& filename1, const FilePath& filename2) {
115 std::ifstream file1(filename1.value().c_str(), std::ios::in);
116 std::ifstream file2(filename2.value().c_str(), std::ios::in);
118 // Even if both files aren't openable (and thus, in some sense, "equal"),
119 // any unusable file yields a result of "false".
120 if (!file1.is_open() || !file2.is_open())
121 return false;
123 do {
124 std::string line1, line2;
125 getline(file1, line1);
126 getline(file2, line2);
128 // Check for mismatched EOF states, or any error state.
129 if ((file1.eof() != file2.eof()) ||
130 file1.bad() || file2.bad()) {
131 return false;
134 // Trim all '\r' and '\n' characters from the end of the line.
135 std::string::size_type end1 = line1.find_last_not_of("\r\n");
136 if (end1 == std::string::npos)
137 line1.clear();
138 else if (end1 + 1 < line1.length())
139 line1.erase(end1 + 1);
141 std::string::size_type end2 = line2.find_last_not_of("\r\n");
142 if (end2 == std::string::npos)
143 line2.clear();
144 else if (end2 + 1 < line2.length())
145 line2.erase(end2 + 1);
147 if (line1 != line2)
148 return false;
149 } while (!file1.eof() || !file2.eof());
151 return true;
154 bool ReadFileToString(const FilePath& path, std::string* contents) {
155 FILE* file = OpenFile(path, "rb");
156 if (!file) {
157 return false;
160 char buf[1 << 16];
161 size_t len;
162 while ((len = fread(buf, 1, sizeof(buf), file)) > 0) {
163 if (contents)
164 contents->append(buf, len);
166 CloseFile(file);
168 return true;
171 bool IsDirectoryEmpty(const FilePath& dir_path) {
172 FileEnumerator files(dir_path, false,
173 FileEnumerator::FILES | FileEnumerator::DIRECTORIES);
174 if (files.Next().value().empty())
175 return true;
176 return false;
179 FILE* CreateAndOpenTemporaryFile(FilePath* path) {
180 FilePath directory;
181 if (!GetTempDir(&directory))
182 return NULL;
184 return CreateAndOpenTemporaryFileInDir(directory, path);
187 bool GetFileSize(const FilePath& file_path, int64* file_size) {
188 base::PlatformFileInfo info;
189 if (!GetFileInfo(file_path, &info))
190 return false;
191 *file_size = info.size;
192 return true;
195 bool IsDot(const FilePath& path) {
196 return FILE_PATH_LITERAL(".") == path.BaseName().value();
199 bool IsDotDot(const FilePath& path) {
200 return FILE_PATH_LITERAL("..") == path.BaseName().value();
203 bool TouchFile(const FilePath& path,
204 const base::Time& last_accessed,
205 const base::Time& last_modified) {
206 int flags = base::PLATFORM_FILE_OPEN | base::PLATFORM_FILE_WRITE_ATTRIBUTES;
208 #if defined(OS_WIN)
209 // On Windows, FILE_FLAG_BACKUP_SEMANTICS is needed to open a directory.
210 if (DirectoryExists(path))
211 flags |= base::PLATFORM_FILE_BACKUP_SEMANTICS;
212 #endif // OS_WIN
214 const base::PlatformFile file =
215 base::CreatePlatformFile(path, flags, NULL, NULL);
216 if (file != base::kInvalidPlatformFileValue) {
217 bool result = base::TouchPlatformFile(file, last_accessed, last_modified);
218 base::ClosePlatformFile(file);
219 return result;
222 return false;
225 bool SetLastModifiedTime(const FilePath& path,
226 const base::Time& last_modified) {
227 return TouchFile(path, last_modified, last_modified);
230 bool CloseFile(FILE* file) {
231 if (file == NULL)
232 return true;
233 return fclose(file) == 0;
236 bool TruncateFile(FILE* file) {
237 if (file == NULL)
238 return false;
239 long current_offset = ftell(file);
240 if (current_offset == -1)
241 return false;
242 #if defined(OS_WIN)
243 int fd = _fileno(file);
244 if (_chsize(fd, current_offset) != 0)
245 return false;
246 #else
247 int fd = fileno(file);
248 if (ftruncate(fd, current_offset) != 0)
249 return false;
250 #endif
251 return true;
254 int GetUniquePathNumber(
255 const FilePath& path,
256 const FilePath::StringType& suffix) {
257 bool have_suffix = !suffix.empty();
258 if (!PathExists(path) &&
259 (!have_suffix || !PathExists(FilePath(path.value() + suffix)))) {
260 return 0;
263 FilePath new_path;
264 for (int count = 1; count <= kMaxUniqueFiles; ++count) {
265 new_path = path.InsertBeforeExtensionASCII(StringPrintf(" (%d)", count));
266 if (!PathExists(new_path) &&
267 (!have_suffix || !PathExists(FilePath(new_path.value() + suffix)))) {
268 return count;
272 return -1;
275 bool ContainsPath(const FilePath &parent, const FilePath& child) {
276 FilePath abs_parent = FilePath(parent);
277 FilePath abs_child = FilePath(child);
279 if (!file_util::AbsolutePath(&abs_parent) ||
280 !file_util::AbsolutePath(&abs_child))
281 return false;
283 #if defined(OS_WIN)
284 // file_util::AbsolutePath() does not flatten case on Windows, so we must do
285 // a case-insensitive compare.
286 if (!StartsWith(abs_child.value(), abs_parent.value(), false))
287 #else
288 if (!StartsWithASCII(abs_child.value(), abs_parent.value(), true))
289 #endif
290 return false;
292 // file_util::AbsolutePath() normalizes '/' to '\' on Windows, so we only need
293 // to check kSeparators[0].
294 if (abs_child.value().length() <= abs_parent.value().length() ||
295 abs_child.value()[abs_parent.value().length()] !=
296 FilePath::kSeparators[0])
297 return false;
299 return true;
302 int64 ComputeDirectorySize(const FilePath& root_path) {
303 int64 running_size = 0;
304 FileEnumerator file_iter(root_path, true, FileEnumerator::FILES);
305 for (FilePath current = file_iter.Next(); !current.empty();
306 current = file_iter.Next()) {
307 FileEnumerator::FindInfo info;
308 file_iter.GetFindInfo(&info);
309 #if defined(OS_WIN)
310 LARGE_INTEGER li = { info.nFileSizeLow, info.nFileSizeHigh };
311 running_size += li.QuadPart;
312 #else
313 running_size += info.stat.st_size;
314 #endif
316 return running_size;
319 int64 ComputeFilesSize(const FilePath& directory,
320 const FilePath::StringType& pattern) {
321 int64 running_size = 0;
322 FileEnumerator file_iter(directory, false, FileEnumerator::FILES, pattern);
323 for (FilePath current = file_iter.Next(); !current.empty();
324 current = file_iter.Next()) {
325 FileEnumerator::FindInfo info;
326 file_iter.GetFindInfo(&info);
327 #if defined(OS_WIN)
328 LARGE_INTEGER li = { info.nFileSizeLow, info.nFileSizeHigh };
329 running_size += li.QuadPart;
330 #else
331 running_size += info.stat.st_size;
332 #endif
334 return running_size;
337 ///////////////////////////////////////////////
338 // MemoryMappedFile
340 MemoryMappedFile::~MemoryMappedFile() {
341 CloseHandles();
344 bool MemoryMappedFile::Initialize(const FilePath& file_name) {
345 if (IsValid())
346 return false;
348 if (!MapFileToMemory(file_name)) {
349 CloseHandles();
350 return false;
353 return true;
356 bool MemoryMappedFile::Initialize(base::PlatformFile file) {
357 if (IsValid())
358 return false;
360 file_ = file;
362 if (!MapFileToMemoryInternal()) {
363 CloseHandles();
364 return false;
367 return true;
370 bool MemoryMappedFile::IsValid() const {
371 return data_ != NULL;
374 bool MemoryMappedFile::MapFileToMemory(const FilePath& file_name) {
375 file_ = base::CreatePlatformFile(
376 file_name, base::PLATFORM_FILE_OPEN | base::PLATFORM_FILE_READ,
377 NULL, NULL);
379 if (file_ == base::kInvalidPlatformFileValue) {
380 DLOG(ERROR) << "Couldn't open " << file_name.value();
381 return false;
384 return MapFileToMemoryInternal();
387 ///////////////////////////////////////////////
388 // FileEnumerator
390 // Note: the main logic is in file_util_<platform>.cc
392 bool FileEnumerator::ShouldSkip(const FilePath& path) {
393 FilePath::StringType basename = path.BaseName().value();
394 return IsDot(path) || (IsDotDot(path) && !(INCLUDE_DOT_DOT & file_type_));
397 } // namespace