Add FdFile::Compare

Also added unit test. Will be used in tests.

Bug: 63467744
Test: test-art-host-gtest-fd_file_test -j40

Change-Id: If5b81ecb587fbff3cce8e0acb9ba397a9bc654da
diff --git a/runtime/base/unix_file/fd_file.cc b/runtime/base/unix_file/fd_file.cc
index 0c73ce7..eb8ced0 100644
--- a/runtime/base/unix_file/fd_file.cc
+++ b/runtime/base/unix_file/fd_file.cc
@@ -438,4 +438,30 @@
   return true;
 }
 
+int FdFile::Compare(FdFile* other) {
+  int64_t length = GetLength();
+  int64_t length2 = other->GetLength();
+  if (length != length2) {
+    return length < length2 ? -1 : 1;
+  }
+  static const size_t kBufferSize = 4096;
+  std::unique_ptr<uint8_t[]> buffer1(new uint8_t[kBufferSize]);
+  std::unique_ptr<uint8_t[]> buffer2(new uint8_t[kBufferSize]);
+  while (length > 0) {
+    size_t len = std::min(kBufferSize, static_cast<size_t>(length));
+    if (!ReadFully(&buffer1[0], len)) {
+      return -1;
+    }
+    if (!other->ReadFully(&buffer2[0], len)) {
+      return 1;
+    }
+    int result = memcmp(&buffer1[0], &buffer2[0], len);
+    if (result != 0) {
+      return result;
+    }
+    length -= len;
+  }
+  return 0;
+}
+
 }  // namespace unix_file
diff --git a/runtime/base/unix_file/fd_file.h b/runtime/base/unix_file/fd_file.h
index e07c3fd..91b08bc 100644
--- a/runtime/base/unix_file/fd_file.h
+++ b/runtime/base/unix_file/fd_file.h
@@ -145,6 +145,11 @@
   // WARNING: Only use this when you know what you're doing!
   void MarkUnchecked();
 
+  // Compare against another file. Returns 0 if the files are equivalent, otherwise returns -1 or 1
+  // depending on if the lenghts are different. If the lengths are the same, the function returns
+  // the difference of the first byte that differs.
+  int Compare(FdFile* other);
+
  protected:
   // If the guard state indicates checking (!=kNoCheck), go to the target state "target". Print the
   // given warning if the current state is or exceeds warn_threshold.
diff --git a/runtime/base/unix_file/fd_file_test.cc b/runtime/base/unix_file/fd_file_test.cc
index 6aef348..8b1a115 100644
--- a/runtime/base/unix_file/fd_file_test.cc
+++ b/runtime/base/unix_file/fd_file_test.cc
@@ -220,4 +220,58 @@
   EXPECT_FALSE(art::OS::FileExists(filename.c_str())) << filename;
 }
 
+TEST_F(FdFileTest, Compare) {
+  std::vector<uint8_t> buffer;
+  constexpr int64_t length = 17 * art::KB;
+  for (size_t i = 0; i < length; ++i) {
+    buffer.push_back(static_cast<uint8_t>(i));
+  }
+
+  auto reset_compare = [&](art::ScratchFile& a, art::ScratchFile& b) {
+    a.GetFile()->ResetOffset();
+    b.GetFile()->ResetOffset();
+    return a.GetFile()->Compare(b.GetFile());
+  };
+
+  art::ScratchFile tmp;
+  EXPECT_TRUE(tmp.GetFile()->WriteFully(&buffer[0], length));
+  EXPECT_EQ(tmp.GetFile()->GetLength(), length);
+
+  art::ScratchFile tmp2;
+  EXPECT_TRUE(tmp2.GetFile()->WriteFully(&buffer[0], length));
+  EXPECT_EQ(tmp2.GetFile()->GetLength(), length);
+
+  // Basic equality check.
+  tmp.GetFile()->ResetOffset();
+  tmp2.GetFile()->ResetOffset();
+  // Files should be the same.
+  EXPECT_EQ(reset_compare(tmp, tmp2), 0);
+
+  // Change a byte near the start.
+  ++buffer[2];
+  art::ScratchFile tmp3;
+  EXPECT_TRUE(tmp3.GetFile()->WriteFully(&buffer[0], length));
+  --buffer[2];
+  EXPECT_NE(reset_compare(tmp, tmp3), 0);
+
+  // Change a byte near the middle.
+  ++buffer[length / 2];
+  art::ScratchFile tmp4;
+  EXPECT_TRUE(tmp4.GetFile()->WriteFully(&buffer[0], length));
+  --buffer[length / 2];
+  EXPECT_NE(reset_compare(tmp, tmp4), 0);
+
+  // Change a byte near the end.
+  ++buffer[length - 5];
+  art::ScratchFile tmp5;
+  EXPECT_TRUE(tmp5.GetFile()->WriteFully(&buffer[0], length));
+  --buffer[length - 5];
+  EXPECT_NE(reset_compare(tmp, tmp5), 0);
+
+  // Reference check
+  art::ScratchFile tmp6;
+  EXPECT_TRUE(tmp6.GetFile()->WriteFully(&buffer[0], length));
+  EXPECT_EQ(reset_compare(tmp, tmp6), 0);
+}
+
 }  // namespace unix_file