update_engine: Further move Extent manipulation to extent_utils.

This patch moves more Extent manipulation functions to extent_utils.
It moves NormalizeExtents() and creates a new ExtentsSublist() function
that will be used by a follow up CL.

BUG=None
TEST=Added unittests.

Change-Id: Icf0ef0af208aa45c9f44e1a73662b3efd8bbbb45
Reviewed-on: https://chromium-review.googlesource.com/275801
Reviewed-by: Alex Deymo <[email protected]>
Commit-Queue: Alex Deymo <[email protected]>
Tested-by: Alex Deymo <[email protected]>
diff --git a/payload_generator/delta_diff_generator.cc b/payload_generator/delta_diff_generator.cc
index ef8b8ff..c9259b5 100644
--- a/payload_generator/delta_diff_generator.cc
+++ b/payload_generator/delta_diff_generator.cc
@@ -1364,26 +1364,6 @@
                  extents->end());
 }
 
-void DeltaDiffGenerator::NormalizeExtents(vector<Extent>* extents) {
-  vector<Extent> new_extents;
-  for (const Extent& curr_ext : *extents) {
-    if (new_extents.empty()) {
-      new_extents.push_back(curr_ext);
-      continue;
-    }
-    Extent& last_ext = new_extents.back();
-    if (last_ext.start_block() + last_ext.num_blocks() ==
-        curr_ext.start_block()) {
-      // If the extents are touching, we want to combine them.
-      last_ext.set_num_blocks(last_ext.num_blocks() + curr_ext.num_blocks());
-    } else {
-      // Otherwise just include the extent as is.
-      new_extents.push_back(curr_ext);
-    }
-  }
-  *extents = new_extents;
-}
-
 bool DeltaDiffGenerator::FragmentOperations(
     vector<AnnotatedOperation>* aops,
     const string& target_part_path,
diff --git a/payload_generator/delta_diff_generator.h b/payload_generator/delta_diff_generator.h
index 07e26ad..06f84a3 100644
--- a/payload_generator/delta_diff_generator.h
+++ b/payload_generator/delta_diff_generator.h
@@ -233,11 +233,6 @@
   // Takes a vector of extents and removes extents that begin in a sparse hole.
   static void ClearSparseHoles(std::vector<Extent>* extents);
 
-  // Takes a vector of extents and normalizes those extents. Expects the extents
-  // to be sorted by start block. E.g. if |extents| is [(1, 2), (3, 5), (10, 2)]
-  // then |extents| will be changed to [(1, 7), (10, 2)].
-  static void NormalizeExtents(std::vector<Extent>* extents);
-
   // Takes a vector of AnnotatedOperations |aops| and fragments those operations
   // such that there is only one dst extent per operation. Sets |aops| to a
   // vector of the new fragmented operations.
diff --git a/payload_generator/delta_diff_generator_unittest.cc b/payload_generator/delta_diff_generator_unittest.cc
index 050f25b..da82d14 100644
--- a/payload_generator/delta_diff_generator_unittest.cc
+++ b/payload_generator/delta_diff_generator_unittest.cc
@@ -1076,25 +1076,6 @@
   EXPECT_EQ(extents[1], ExtentForRange(29, 1));
 }
 
-TEST_F(DeltaDiffGeneratorTest, NormalizeExtentsTest) {
-  vector<Extent> extents;
-  AddExtent(0, 3, &extents);
-  // Make sure it works when there's just one extent.
-  DeltaDiffGenerator::NormalizeExtents(&extents);
-  EXPECT_EQ(extents.size(), 1);
-  EXPECT_EQ(extents[0], ExtentForRange(0, 3));
-  AddExtent(3, 2, &extents);
-  AddExtent(5, 1, &extents);
-  AddExtent(8, 4, &extents);
-  AddExtent(13, 1, &extents);
-  AddExtent(14, 2, &extents);
-  DeltaDiffGenerator::NormalizeExtents(&extents);
-  EXPECT_EQ(extents.size(), 3);
-  EXPECT_EQ(extents[0], ExtentForRange(0, 6));
-  EXPECT_EQ(extents[1], ExtentForRange(8, 4));
-  EXPECT_EQ(extents[2], ExtentForRange(13, 3));
-}
-
 TEST_F(DeltaDiffGeneratorTest, SplitSourceCopyTest) {
   DeltaArchiveManifest_InstallOperation op;
   op.set_type(DeltaArchiveManifest_InstallOperation_Type_SOURCE_COPY);
diff --git a/payload_generator/extent_utils.cc b/payload_generator/extent_utils.cc
index dcb8bf8..6896678 100644
--- a/payload_generator/extent_utils.cc
+++ b/payload_generator/extent_utils.cc
@@ -11,6 +11,7 @@
 #include <base/logging.h>
 #include <base/macros.h>
 
+#include "update_engine/extent_ranges.h"
 #include "update_engine/payload_constants.h"
 #include "update_engine/payload_generator/annotated_operation.h"
 
@@ -46,6 +47,60 @@
   return collection.Get(index);
 }
 
+void NormalizeExtents(vector<Extent>* extents) {
+  vector<Extent> new_extents;
+  for (const Extent& curr_ext : *extents) {
+    if (new_extents.empty()) {
+      new_extents.push_back(curr_ext);
+      continue;
+    }
+    Extent& last_ext = new_extents.back();
+    if (last_ext.start_block() + last_ext.num_blocks() ==
+        curr_ext.start_block()) {
+      // If the extents are touching, we want to combine them.
+      last_ext.set_num_blocks(last_ext.num_blocks() + curr_ext.num_blocks());
+    } else {
+      // Otherwise just include the extent as is.
+      new_extents.push_back(curr_ext);
+    }
+  }
+  *extents = new_extents;
+}
+
+vector<Extent> ExtentsSublist(const vector<Extent>& extents,
+                              uint64_t block_offset, uint64_t block_count) {
+  vector<Extent> result;
+  uint64_t scanned_blocks = 0;
+  if (block_count == 0)
+    return result;
+  uint64_t end_block_offset = block_offset + block_count;
+  for (const Extent& extent : extents) {
+    // The loop invariant is that if |extents| has enough blocks, there's
+    // still some extent to add to |result|. This implies that at the beginning
+    // of the loop scanned_blocks < block_offset + block_count.
+    if (scanned_blocks + extent.num_blocks() > block_offset) {
+      // This case implies that |extent| has some overlapping with the requested
+      // subsequence.
+      uint64_t new_start = extent.start_block();
+      uint64_t new_num_blocks = extent.num_blocks();
+      if (scanned_blocks + new_num_blocks > end_block_offset) {
+        // Cut the end part of the extent.
+        new_num_blocks = end_block_offset - scanned_blocks;
+      }
+      if (block_offset > scanned_blocks) {
+        // Cut the begin part of the extent.
+        new_num_blocks -= block_offset - scanned_blocks;
+        new_start += block_offset - scanned_blocks;
+      }
+      result.push_back(ExtentForRange(new_start, new_num_blocks));
+    }
+    scanned_blocks += extent.num_blocks();
+    if (scanned_blocks >= end_block_offset)
+      break;
+  }
+  return result;
+}
+
 bool operator==(const Extent& a, const Extent& b) {
   return a.start_block() == b.start_block() && a.num_blocks() == b.num_blocks();
 }
diff --git a/payload_generator/extent_utils.h b/payload_generator/extent_utils.h
index 1583618..e9ab9eb 100644
--- a/payload_generator/extent_utils.h
+++ b/payload_generator/extent_utils.h
@@ -34,6 +34,18 @@
   return ret;
 }
 
+// Takes a vector of extents and normalizes those extents. Expects the extents
+// to be sorted by start block. E.g. if |extents| is [(1, 2), (3, 5), (10, 2)]
+// then |extents| will be changed to [(1, 7), (10, 2)].
+void NormalizeExtents(std::vector<Extent>* extents);
+
+// Return a subsequence of the list of blocks passed. Both the passed list of
+// blocks |extents| and the return value are expressed as a list of Extent, not
+// blocks. The returned list skips the first |block_offset| blocks from the
+// |extents| and cotains |block_count| blocks (or less if |extents| is shorter).
+std::vector<Extent> ExtentsSublist(const std::vector<Extent>& extents,
+                                   uint64_t block_offset, uint64_t block_count);
+
 bool operator==(const Extent& a, const Extent& b);
 
 }  // namespace chromeos_update_engine
diff --git a/payload_generator/extent_utils_unittest.cc b/payload_generator/extent_utils_unittest.cc
index fe1d000..6e685e0 100644
--- a/payload_generator/extent_utils_unittest.cc
+++ b/payload_generator/extent_utils_unittest.cc
@@ -11,6 +11,7 @@
 
 #include "update_engine/extent_ranges.h"
 #include "update_engine/payload_constants.h"
+#include "update_engine/test_utils.h"
 
 using std::vector;
 
@@ -61,4 +62,73 @@
   }
 }
 
+TEST(ExtentUtilsTest, NormalizeExtentsSimpleList) {
+  // Make sure it works when there's just one extent.
+  vector<Extent> extents;
+  NormalizeExtents(&extents);
+  EXPECT_EQ(0, extents.size());
+
+  extents = { ExtentForRange(0, 3) };
+  NormalizeExtents(&extents);
+  EXPECT_EQ(1, extents.size());
+  EXPECT_EQ(ExtentForRange(0, 3), extents[0]);
+}
+
+TEST(ExtentUtilsTest, NormalizeExtentsTest) {
+  vector<Extent> extents = {
+      ExtentForRange(0, 3),
+      ExtentForRange(3, 2),
+      ExtentForRange(5, 1),
+      ExtentForRange(8, 4),
+      ExtentForRange(13, 1),
+      ExtentForRange(14, 2)
+  };
+  NormalizeExtents(&extents);
+  EXPECT_EQ(3, extents.size());
+  EXPECT_EQ(ExtentForRange(0, 6), extents[0]);
+  EXPECT_EQ(ExtentForRange(8, 4), extents[1]);
+  EXPECT_EQ(ExtentForRange(13, 3), extents[2]);
+}
+
+TEST(ExtentUtilsTest, ExtentsSublistTest) {
+  vector<Extent> extents = {
+      ExtentForRange(10, 10),
+      ExtentForRange(30, 10),
+      ExtentForRange(50, 10)
+  };
+
+  // Simple empty result cases.
+  EXPECT_EQ(vector<Extent>(),
+            ExtentsSublist(extents, 1000, 20));
+  EXPECT_EQ(vector<Extent>(),
+            ExtentsSublist(extents, 5, 0));
+  EXPECT_EQ(vector<Extent>(),
+            ExtentsSublist(extents, 30, 1));
+
+  // Normal test cases.
+  EXPECT_EQ(vector<Extent>{ ExtentForRange(13, 2) },
+            ExtentsSublist(extents, 3, 2));
+  EXPECT_EQ(vector<Extent>{ ExtentForRange(15, 5) },
+            ExtentsSublist(extents, 5, 5));
+  EXPECT_EQ((vector<Extent>{ ExtentForRange(15, 5), ExtentForRange(30, 5) }),
+            ExtentsSublist(extents, 5, 10));
+  EXPECT_EQ((vector<Extent>{
+                 ExtentForRange(13, 7),
+                 ExtentForRange(30, 10),
+                 ExtentForRange(50, 3), }),
+            ExtentsSublist(extents, 3, 20));
+
+  // Extact match case.
+  EXPECT_EQ(vector<Extent>{ ExtentForRange(30, 10) },
+            ExtentsSublist(extents, 10, 10));
+  EXPECT_EQ(vector<Extent>{ ExtentForRange(50, 10) },
+            ExtentsSublist(extents, 20, 10));
+
+  // Cases where the requested num_blocks is too big.
+  EXPECT_EQ(vector<Extent>{ ExtentForRange(53, 7) },
+            ExtentsSublist(extents, 23, 100));
+  EXPECT_EQ((vector<Extent>{ ExtentForRange(34, 6), ExtentForRange(50, 10) }),
+            ExtentsSublist(extents, 14, 100));
+}
+
 }  // namespace chromeos_update_engine
diff --git a/test_utils.cc b/test_utils.cc
index e2361a5..712df64 100644
--- a/test_utils.cc
+++ b/test_utils.cc
@@ -31,6 +31,10 @@
 
 namespace chromeos_update_engine {
 
+void PrintTo(const Extent& extent, ::std::ostream* os) {
+  *os << "(" << extent.start_block() << ", " << extent.num_blocks() << ")";
+}
+
 namespace test_utils {
 
 const char* const kMountPathTemplate = "UpdateEngineTests_mnt-XXXXXX";
diff --git a/test_utils.h b/test_utils.h
index a65b042..dddd563 100644
--- a/test_utils.h
+++ b/test_utils.h
@@ -9,6 +9,8 @@
 #include <sys/types.h>
 #include <unistd.h>
 
+// Streams used for gtest's PrintTo() functions.
+#include <iostream>  // NOLINT(readability/streams)
 #include <memory>
 #include <set>
 #include <string>
@@ -20,11 +22,17 @@
 
 #include "update_engine/action.h"
 #include "update_engine/subprocess.h"
+#include "update_engine/update_metadata.pb.h"
 #include "update_engine/utils.h"
 
 // These are some handy functions for unittests.
 
 namespace chromeos_update_engine {
+
+// PrintTo() functions are used by gtest to log these objects. These PrintTo()
+// functions must be defined in the same namespace as the first argument.
+void PrintTo(const Extent& extent, ::std::ostream* os);
+
 namespace test_utils {
 
 // 300 byte pseudo-random string. Not null terminated.