Use StringPiece for the optional suffix in base::GetUniquePathNumber.
This removes the need to create/destroy a string when calling. This
change also adds a default value of an empty StringPiece to the suffix
so that callers who don't care about a suffix don't need to bother with
it. Finally, this change adds a unit test for the function, which it
apparently never had.
BUG=none
TBR=vasilii@chromium.org,vakh@chromium.org,eladalon@chromium.org,jamiewalch@chromium.org,carlosk@chromium.org
Change-Id: Iaf98d5237ffcc211bf9dfc2242742508f35bafac
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1752302
Commit-Queue: Greg Thompson <grt@chromium.org>
Auto-Submit: Greg Thompson <grt@chromium.org>
Reviewed-by: Daniel Cheng <dcheng@chromium.org>
Cr-Commit-Position: refs/heads/master@{#686754}
diff --git a/base/files/file_util.cc b/base/files/file_util.cc
index e6e84a2..f11f456 100644
--- a/base/files/file_util.cc
+++ b/base/files/file_util.cc
@@ -281,20 +281,34 @@
}
int GetUniquePathNumber(const FilePath& path,
- const FilePath::StringType& suffix) {
- bool have_suffix = !suffix.empty();
- if (!PathExists(path) &&
- (!have_suffix || !PathExists(FilePath(path.value() + suffix)))) {
- return 0;
- }
+ FilePath::StringPieceType suffix) {
+ // Storage for use by is_unique to reduce heap churn when looping.
+ FilePath::StringType path_with_suffix;
- FilePath new_path;
- for (int count = 1; count <= kMaxUniqueFiles; ++count) {
- new_path = path.InsertBeforeExtensionASCII(StringPrintf(" (%d)", count));
- if (!PathExists(new_path) &&
- (!have_suffix || !PathExists(FilePath(new_path.value() + suffix)))) {
- return count;
+ // A function that returns true if |candidate| is unique with and without an
+ // optional suffix.
+ const auto is_unique = [suffix,
+ &path_with_suffix](const base::FilePath& candidate) {
+ if (!PathExists(candidate)) {
+ if (suffix.empty())
+ return true;
+ path_with_suffix = candidate.value();
+ suffix.AppendToString(&path_with_suffix);
+ if (!PathExists(FilePath(path_with_suffix)))
+ return true;
}
+ return false;
+ };
+
+ if (is_unique(path))
+ return 0;
+
+ std::string number;
+ for (int count = 1; count <= kMaxUniqueFiles; ++count) {
+ StringAppendF(&number, " (%d)", count);
+ if (is_unique(path.InsertBeforeExtensionASCII(number)))
+ return count;
+ number.clear();
}
return -1;
@@ -302,7 +316,7 @@
FilePath GetUniquePath(const FilePath& path) {
FilePath unique_path = path;
- int uniquifier = GetUniquePathNumber(path, FilePath::StringType());
+ int uniquifier = GetUniquePathNumber(path);
if (uniquifier > 0) {
unique_path = unique_path.InsertBeforeExtensionASCII(
StringPrintf(" (%d)", uniquifier));
diff --git a/base/files/file_util.h b/base/files/file_util.h
index 0987b7b..fc2f443 100644
--- a/base/files/file_util.h
+++ b/base/files/file_util.h
@@ -401,8 +401,9 @@
// unique. If |path| does not exist, 0 is returned. If it fails to find such
// a number, -1 is returned. If |suffix| is not empty, also checks the
// existence of it with the given suffix.
-BASE_EXPORT int GetUniquePathNumber(const FilePath& path,
- const FilePath::StringType& suffix);
+BASE_EXPORT int GetUniquePathNumber(
+ const FilePath& path,
+ FilePath::StringPieceType suffix = FilePath::StringPieceType());
// If file at |path| already exists, modifies filename portion of |path| to
// return unique path.
diff --git a/base/files/file_util_unittest.cc b/base/files/file_util_unittest.cc
index 21cefcc4..64de4d0 100644
--- a/base/files/file_util_unittest.cc
+++ b/base/files/file_util_unittest.cc
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include "base/files/file_util.h"
+
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
@@ -23,7 +25,6 @@
#include "base/files/file.h"
#include "base/files/file_enumerator.h"
#include "base/files/file_path.h"
-#include "base/files/file_util.h"
#include "base/files/scoped_file.h"
#include "base/files/scoped_temp_dir.h"
#include "base/guid.h"
@@ -37,6 +38,7 @@
#include "base/test/test_timeouts.h"
#include "base/threading/platform_thread.h"
#include "base/threading/thread.h"
+#include "base/time/time.h"
#include "build/build_config.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "testing/multiprocess_func_list.h"
@@ -3871,6 +3873,48 @@
}
#endif
+#if !defined(OS_NACL_NONSFI)
+TEST_F(FileUtilTest, GetUniquePathNumber) {
+ static constexpr FilePath::StringPieceType kSomeFile(FPL("SomeFile.txt"));
+ static constexpr FilePath::StringPieceType kSomeFileOne(
+ FPL("SomeFile (1).txt"));
+ static constexpr FilePath::StringPieceType kSomeSuffix(FPL(".SUFFIX"));
+
+ const FilePath& temp_dir = temp_dir_.GetPath();
+
+ // The dir is empty to start with.
+ EXPECT_EQ(GetUniquePathNumber(temp_dir.Append(kSomeFile)), 0);
+ EXPECT_EQ(GetUniquePathNumber(temp_dir.Append(kSomeFile), kSomeSuffix), 0);
+
+ // Manufacture a collision with the suffixed file.
+ FilePath::StringType path = kSomeFile.as_string();
+ kSomeSuffix.AppendToString(&path);
+ ASSERT_EQ(WriteFile(temp_dir.Append(path), "hi", 2), 2);
+ // No collision unsuffixed.
+ EXPECT_EQ(GetUniquePathNumber(temp_dir.Append(kSomeFile)), 0);
+ // But there is with the suffix.
+ EXPECT_EQ(GetUniquePathNumber(temp_dir.Append(kSomeFile), kSomeSuffix), 1);
+ ASSERT_TRUE(DeleteFile(temp_dir.Append(path), false));
+
+ // Manufacture a collision with the unsuffixed file.
+ ASSERT_EQ(WriteFile(temp_dir.Append(kSomeFile), "hi", 2), 2);
+ // Both collide.
+ EXPECT_EQ(GetUniquePathNumber(temp_dir.Append(kSomeFile)), 1);
+ EXPECT_EQ(GetUniquePathNumber(temp_dir.Append(kSomeFile), kSomeSuffix), 1);
+
+ // Now with the unsuffixed collision, manufacture a suffixed collision with
+ // the number '1'.
+ path = kSomeFileOne.as_string();
+ kSomeSuffix.AppendToString(&path);
+ ASSERT_EQ(WriteFile(temp_dir.Append(path), "hi", 2), 2);
+ EXPECT_EQ(GetUniquePathNumber(temp_dir.Append(kSomeFile), kSomeSuffix), 2);
+
+ // Clean up.
+ ASSERT_TRUE(DeleteFile(temp_dir.Append(path), false));
+ ASSERT_TRUE(DeleteFile(temp_dir.Append(kSomeFile), false));
+}
+#endif // !defined(OS_NACL_NONSFI)
+
// Test that temp files obtained racily are all unique (no interference between
// threads). Mimics file operations in DoLaunchChildTestProcess() to rule out
// thread-safety issues @ https://crbug.com/826408#c17.
diff --git a/chrome/browser/media/webrtc/webrtc_event_log_manager_local.cc b/chrome/browser/media/webrtc/webrtc_event_log_manager_local.cc
index 4e99b80..b56953c5 100644
--- a/chrome/browser/media/webrtc/webrtc_event_log_manager_local.cc
+++ b/chrome/browser/media/webrtc/webrtc_event_log_manager_local.cc
@@ -184,8 +184,7 @@
// In the unlikely case that this filename is already taken, find a unique
// number to append to the filename, if possible.
- int unique_number =
- base::GetUniquePathNumber(file_path, base::FilePath::StringType());
+ int unique_number = base::GetUniquePathNumber(file_path);
if (unique_number < 0) {
return; // No available file path was found.
} else if (unique_number != 0) {
diff --git a/chrome/browser/safe_browsing/test_safe_browsing_database_helper.cc b/chrome/browser/safe_browsing/test_safe_browsing_database_helper.cc
index b52ff49..01ca698 100644
--- a/chrome/browser/safe_browsing/test_safe_browsing_database_helper.cc
+++ b/chrome/browser/safe_browsing/test_safe_browsing_database_helper.cc
@@ -59,8 +59,7 @@
if (!base::Contains(*store_map, id)) {
const base::FilePath store_path =
base_store_path.InsertBeforeExtensionASCII(base::StringPrintf(
- " (%d)", base::GetUniquePathNumber(
- base_store_path, base::FilePath::StringType())));
+ " (%d)", base::GetUniquePathNumber(base_store_path)));
(*store_map)[id] =
store_factory_->CreateV4Store(db_task_runner, store_path);
}
diff --git a/chrome/browser/web_applications/components/web_app_shortcut_win.cc b/chrome/browser/web_applications/components/web_app_shortcut_win.cc
index 21b43d9..a8fc647 100644
--- a/chrome/browser/web_applications/components/web_app_shortcut_win.cc
+++ b/chrome/browser/web_applications/components/web_app_shortcut_win.cc
@@ -232,8 +232,7 @@
continue;
}
if (shortcut_paths[i] != web_app_path) {
- int unique_number = base::GetUniquePathNumber(
- shortcut_file, base::FilePath::StringType());
+ int unique_number = base::GetUniquePathNumber(shortcut_file);
if (unique_number == -1) {
success = false;
continue;
diff --git a/components/offline_pages/core/model/offline_page_model_utils.cc b/components/offline_pages/core/model/offline_page_model_utils.cc
index 6785f1a..d0783e52 100644
--- a/components/offline_pages/core/model/offline_page_model_utils.cc
+++ b/components/offline_pages/core/model/offline_page_model_utils.cc
@@ -74,8 +74,7 @@
title, url, false /* can_save_as_complete */, kMHTMLMimeType));
// Find a unique name based on |suggested_path|.
- int uniquifier =
- base::GetUniquePathNumber(suggested_path, base::FilePath::StringType());
+ int uniquifier = base::GetUniquePathNumber(suggested_path);
base::FilePath::StringType suffix;
if (uniquifier > 0)
#if defined(OS_WIN)
diff --git a/remoting/host/file_transfer/local_file_operations.cc b/remoting/host/file_transfer/local_file_operations.cc
index b09064a..ef38677 100644
--- a/remoting/host/file_transfer/local_file_operations.cc
+++ b/remoting/host/file_transfer/local_file_operations.cc
@@ -393,7 +393,7 @@
PostTaskAndReplyWithResult(
file_task_runner_.get(), FROM_HERE,
base::BindOnce(&base::GetUniquePathNumber, temp_filepath,
- base::FilePath::StringType()),
+ base::FilePath::StringPieceType()),
base::BindOnce(&LocalFileWriter::CreateTempFile,
weak_ptr_factory_.GetWeakPtr(), std::move(callback),
temp_filepath));
@@ -492,7 +492,7 @@
base::PostTaskAndReplyWithResult(
file_task_runner_.get(), FROM_HERE,
base::BindOnce(&base::GetUniquePathNumber, destination_filepath_,
- base::FilePath::StringType()),
+ base::FilePath::StringPieceType()),
base::BindOnce(&LocalFileWriter::MoveToDestination,
weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
}