Remove `#pragma allow_unsafe_buffers` from `base/files/file_path.cc`.

Bug: 40284755
Change-Id: I65bb0889d896a9e10be846fd043736506a709f5c
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6973923
Reviewed-by: Daniel Cheng <dcheng@chromium.org>
Commit-Queue: Ɓukasz Anforowicz <lukasza@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1521490}
diff --git a/base/files/file_path.cc b/base/files/file_path.cc
index 207f84e8..651381c 100644
--- a/base/files/file_path.cc
+++ b/base/files/file_path.cc
@@ -2,11 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifdef UNSAFE_BUFFERS_BUILD
-// TODO(crbug.com/40284755): Remove this and spanify to fix the errors.
-#pragma allow_unsafe_buffers
-#endif
-
 #include "base/files/file_path.h"
 
 #include <string.h>
@@ -16,6 +11,8 @@
 #include <string_view>
 
 #include "base/check_op.h"
+#include "base/containers/contains.h"
+#include "base/containers/span.h"
 #include "base/features.h"
 #include "base/files/safe_base_name.h"
 #include "base/numerics/safe_conversions.h"
@@ -322,13 +319,8 @@
 
 // static
 bool FilePath::IsSeparator(CharType character) {
-  for (size_t i = 0; i < kSeparatorsLength - 1; ++i) {
-    if (character == kSeparators[i]) {
-      return true;
-    }
-  }
-
-  return false;
+  span<const CharType> all_known_separators = SeparatorsAsSpan();
+  return base::Contains(all_known_separators, character);
 }
 
 std::vector<FilePath::StringType> FilePath::GetComponents() const {
@@ -814,6 +806,19 @@
   return new_path;
 }
 
+// static
+span<const FilePath::CharType> FilePath::SeparatorsAsSpan() {
+  // The last element of `kSeparators` is a terminating NUL character.
+  // Discard it when creating the span.
+  //
+  // TODO(lukasza): We could try to avoid `UNSAFE_TODO` here.  One idea would
+  // be changing the type of `kSeparators` to `std::array<CharType, 2-or-3>`,
+  // but this idea seems incompatible with `constexpr` (and maybe the risk
+  // of changing the public API is not worth it).
+  DCHECK_EQ(kSeparators[kSeparatorsLength - 1], FILE_PATH_LITERAL('\0'));
+  return UNSAFE_TODO(span(kSeparators, kSeparatorsLength - 1));
+}
+
 bool FilePath::ReferencesParent() const {
   if (path_.find(kParentDirectory) == StringType::npos) {
     // GetComponents is quite expensive, so avoid calling it in the majority
@@ -1035,7 +1040,7 @@
 namespace {
 
 // clang-format off
-const UInt16 lower_case_table[11 * 256] = {
+const std::array<UInt16, 11*256> lower_case_table = {
   // High-byte indices ( == 0 iff no case mapping and no ignorables )
 
   /* 0 */ 0x0100, 0x0200, 0x0000, 0x0300, 0x0400, 0x0500, 0x0000, 0x0000,
@@ -1434,18 +1439,20 @@
   while (*index < length && codepoint == 0) {
     // CBU8_NEXT returns a value < 0 in error cases. For purposes of string
     // comparison, we just use that value and flag it with DCHECK.
-    CBU8_NEXT(reinterpret_cast<const uint8_t*>(string), *index, length,
-              codepoint);
+    UNSAFE_BUFFERS(CBU8_NEXT(reinterpret_cast<const uint8_t*>(string), *index,
+                             length, codepoint));
     DCHECK_GT(codepoint, 0);
 
     // Note: Here, there are no lower case conversion implemented in the
     // Supplementary Multilingual Plane (codepoint > 0xFFFF).
 
     if (codepoint > 0 && codepoint <= 0xFFFF) {
+      UInt16 unsigned_codepoint = checked_cast<UInt16>(codepoint);
       // Check if there is a subtable for this upper byte.
-      int lookup_offset = lower_case_table[codepoint >> 8];
+      UInt16 lookup_offset = lower_case_table[unsigned_codepoint >> 8];
       if (lookup_offset != 0) {
-        codepoint = lower_case_table[lookup_offset + (codepoint & 0x00FF)];
+        codepoint =
+            lower_case_table[lookup_offset + (unsigned_codepoint & 0x00FF)];
       }
       // Note: `codepoint` may be again 0 at this point if the character was
       // an ignorable.
@@ -1546,15 +1553,14 @@
     // succeed, fall back to strcmp. This can occur when the input string is
     // invalid UTF-8.
     if (!cfstring1 || !cfstring2) {
-      int comparison = memcmp(string1.data(), string2.data(),
-                              std::min(string1.length(), string2.length()));
-      if (comparison < 0) {
+      std::strong_ordering order = (string1 <=> string2);
+      if (order < 0) {
         return -1;
-      }
-      if (comparison > 0) {
+      } else if (order > 0) {
         return 1;
+      } else {
+        return 0;
       }
-      return 0;
     }
 
     return static_cast<int>(CFStringCompare(cfstring1.get(), cfstring2.get(),
@@ -1611,13 +1617,17 @@
   perfetto::WriteIntoTracedValue(std::move(context), value());
 }
 
-FilePath FilePath::NormalizePathSeparatorsTo(CharType separator) const {
+FilePath FilePath::NormalizePathSeparatorsTo(
+    CharType normalized_separator) const {
 #if defined(FILE_PATH_USES_WIN_SEPARATORS)
-  DCHECK_NE(kSeparators + kSeparatorsLength,
-            std::find(kSeparators, kSeparators + kSeparatorsLength, separator));
+  span<const CharType> all_known_separators = SeparatorsAsSpan();
+  DCHECK(base::Contains(all_known_separators, normalized_separator));
+
   StringType copy = path_;
-  for (size_t i = 0; i < kSeparatorsLength; ++i) {
-    std::replace(copy.begin(), copy.end(), kSeparators[i], separator);
+  for (CharType known_separator : all_known_separators) {
+    if (known_separator != normalized_separator) {
+      std::ranges::replace(copy, known_separator, normalized_separator);
+    }
   }
   return FilePath(copy);
 #else
diff --git a/base/files/file_path.h b/base/files/file_path.h
index a04fec3..70cbb8f 100644
--- a/base/files/file_path.h
+++ b/base/files/file_path.h
@@ -110,6 +110,7 @@
 
 #include "base/base_export.h"
 #include "base/compiler_specific.h"
+#include "base/containers/span_forward_internal.h"
 #include "base/trace_event/base_tracing_forward.h"
 #include "build/build_config.h"
 
@@ -471,7 +472,8 @@
 
   // Normalize all path separators to given type on Windows
   // (if FILE_PATH_USES_WIN_SEPARATORS is true), or do nothing on POSIX systems.
-  [[nodiscard]] FilePath NormalizePathSeparatorsTo(CharType separator) const;
+  [[nodiscard]] FilePath NormalizePathSeparatorsTo(
+      CharType normalized_separator) const;
 
   // Compare two strings in the same way the file system does.
   // Note that these always ignore case, even on file systems that are case-
@@ -539,6 +541,9 @@
   // support UNC paths on Windows.
   void StripTrailingSeparatorsInternal();
 
+  // Returns `kSeparators` as a `span` (without the terminating NUL character).
+  static span<const CharType> SeparatorsAsSpan();
+
   bool IsParentFast(const FilePath& child) const;
   bool IsParentSlow(const FilePath& child) const;
 
diff --git a/base/files/file_path_unittest.cc b/base/files/file_path_unittest.cc
index 5cc0980..48a0d5d 100644
--- a/base/files/file_path_unittest.cc
+++ b/base/files/file_path_unittest.cc
@@ -1662,6 +1662,17 @@
   EXPECT_EQ(perfetto::TracedValueToString(FilePath(FPL("foo"))), "foo");
 }
 
+TEST_F(FilePathTest, IsSeparator) {
+  EXPECT_FALSE(FilePath::IsSeparator(FILE_PATH_LITERAL('x')));
+  EXPECT_FALSE(FilePath::IsSeparator(FILE_PATH_LITERAL('\0')));
+  EXPECT_TRUE(FilePath::IsSeparator(FILE_PATH_LITERAL('/')));
+#if defined(FILE_PATH_USES_WIN_SEPARATORS)
+  EXPECT_TRUE(FilePath::IsSeparator(FILE_PATH_LITERAL('\\')));
+#else
+  EXPECT_FALSE(FilePath::IsSeparator(FILE_PATH_LITERAL('\\')));
+#endif
+}
+
 // Test GetHFSDecomposedForm should return empty result for invalid UTF-8
 // strings.
 #if BUILDFLAG(IS_APPLE)