xref: /aosp_15_r20/build/make/tools/releasetools/blockimgdiff.py (revision 9e94795a3d4ef5c1d47486f9a02bb378756cea8a)
1*9e94795aSAndroid Build Coastguard Worker# Copyright (C) 2014 The Android Open Source Project
2*9e94795aSAndroid Build Coastguard Worker#
3*9e94795aSAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License");
4*9e94795aSAndroid Build Coastguard Worker# you may not use this file except in compliance with the License.
5*9e94795aSAndroid Build Coastguard Worker# You may obtain a copy of the License at
6*9e94795aSAndroid Build Coastguard Worker#
7*9e94795aSAndroid Build Coastguard Worker#      http://www.apache.org/licenses/LICENSE-2.0
8*9e94795aSAndroid Build Coastguard Worker#
9*9e94795aSAndroid Build Coastguard Worker# Unless required by applicable law or agreed to in writing, software
10*9e94795aSAndroid Build Coastguard Worker# distributed under the License is distributed on an "AS IS" BASIS,
11*9e94795aSAndroid Build Coastguard Worker# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12*9e94795aSAndroid Build Coastguard Worker# See the License for the specific language governing permissions and
13*9e94795aSAndroid Build Coastguard Worker# limitations under the License.
14*9e94795aSAndroid Build Coastguard Worker
15*9e94795aSAndroid Build Coastguard Workerfrom __future__ import print_function
16*9e94795aSAndroid Build Coastguard Worker
17*9e94795aSAndroid Build Coastguard Workerimport array
18*9e94795aSAndroid Build Coastguard Workerimport copy
19*9e94795aSAndroid Build Coastguard Workerimport functools
20*9e94795aSAndroid Build Coastguard Workerimport heapq
21*9e94795aSAndroid Build Coastguard Workerimport itertools
22*9e94795aSAndroid Build Coastguard Workerimport logging
23*9e94795aSAndroid Build Coastguard Workerimport multiprocessing
24*9e94795aSAndroid Build Coastguard Workerimport os
25*9e94795aSAndroid Build Coastguard Workerimport os.path
26*9e94795aSAndroid Build Coastguard Workerimport re
27*9e94795aSAndroid Build Coastguard Workerimport sys
28*9e94795aSAndroid Build Coastguard Workerimport threading
29*9e94795aSAndroid Build Coastguard Workerimport zlib
30*9e94795aSAndroid Build Coastguard Workerfrom collections import deque, namedtuple, OrderedDict
31*9e94795aSAndroid Build Coastguard Worker
32*9e94795aSAndroid Build Coastguard Workerimport common
33*9e94795aSAndroid Build Coastguard Workerfrom images import EmptyImage
34*9e94795aSAndroid Build Coastguard Workerfrom rangelib import RangeSet
35*9e94795aSAndroid Build Coastguard Worker
36*9e94795aSAndroid Build Coastguard Worker__all__ = ["BlockImageDiff"]
37*9e94795aSAndroid Build Coastguard Worker
38*9e94795aSAndroid Build Coastguard Workerlogger = logging.getLogger(__name__)
39*9e94795aSAndroid Build Coastguard Worker
40*9e94795aSAndroid Build Coastguard Worker# The tuple contains the style and bytes of a bsdiff|imgdiff patch.
41*9e94795aSAndroid Build Coastguard WorkerPatchInfo = namedtuple("PatchInfo", ["imgdiff", "content"])
42*9e94795aSAndroid Build Coastguard Worker
43*9e94795aSAndroid Build Coastguard Worker
44*9e94795aSAndroid Build Coastguard Workerdef compute_patch(srcfile, tgtfile, imgdiff=False):
45*9e94795aSAndroid Build Coastguard Worker  """Calls bsdiff|imgdiff to compute the patch data, returns a PatchInfo."""
46*9e94795aSAndroid Build Coastguard Worker  patchfile = common.MakeTempFile(prefix='patch-')
47*9e94795aSAndroid Build Coastguard Worker
48*9e94795aSAndroid Build Coastguard Worker  cmd = ['imgdiff', '-z'] if imgdiff else ['bsdiff']
49*9e94795aSAndroid Build Coastguard Worker  cmd.extend([srcfile, tgtfile, patchfile])
50*9e94795aSAndroid Build Coastguard Worker
51*9e94795aSAndroid Build Coastguard Worker  # Don't dump the bsdiff/imgdiff commands, which are not useful for the case
52*9e94795aSAndroid Build Coastguard Worker  # here, since they contain temp filenames only.
53*9e94795aSAndroid Build Coastguard Worker  proc = common.Run(cmd, verbose=False)
54*9e94795aSAndroid Build Coastguard Worker  output, _ = proc.communicate()
55*9e94795aSAndroid Build Coastguard Worker
56*9e94795aSAndroid Build Coastguard Worker  if proc.returncode != 0:
57*9e94795aSAndroid Build Coastguard Worker    raise ValueError(output)
58*9e94795aSAndroid Build Coastguard Worker
59*9e94795aSAndroid Build Coastguard Worker  with open(patchfile, 'rb') as f:
60*9e94795aSAndroid Build Coastguard Worker    return PatchInfo(imgdiff, f.read())
61*9e94795aSAndroid Build Coastguard Worker
62*9e94795aSAndroid Build Coastguard Worker
63*9e94795aSAndroid Build Coastguard Workerclass Transfer(object):
64*9e94795aSAndroid Build Coastguard Worker  def __init__(self, tgt_name, src_name, tgt_ranges, src_ranges, tgt_sha1,
65*9e94795aSAndroid Build Coastguard Worker               src_sha1, style, by_id):
66*9e94795aSAndroid Build Coastguard Worker    self.tgt_name = tgt_name
67*9e94795aSAndroid Build Coastguard Worker    self.src_name = src_name
68*9e94795aSAndroid Build Coastguard Worker    self.tgt_ranges = tgt_ranges
69*9e94795aSAndroid Build Coastguard Worker    self.src_ranges = src_ranges
70*9e94795aSAndroid Build Coastguard Worker    self.tgt_sha1 = tgt_sha1
71*9e94795aSAndroid Build Coastguard Worker    self.src_sha1 = src_sha1
72*9e94795aSAndroid Build Coastguard Worker    self.style = style
73*9e94795aSAndroid Build Coastguard Worker
74*9e94795aSAndroid Build Coastguard Worker    # We use OrderedDict rather than dict so that the output is repeatable;
75*9e94795aSAndroid Build Coastguard Worker    # otherwise it would depend on the hash values of the Transfer objects.
76*9e94795aSAndroid Build Coastguard Worker    self.goes_before = OrderedDict()
77*9e94795aSAndroid Build Coastguard Worker    self.goes_after = OrderedDict()
78*9e94795aSAndroid Build Coastguard Worker
79*9e94795aSAndroid Build Coastguard Worker    self.stash_before = []
80*9e94795aSAndroid Build Coastguard Worker    self.use_stash = []
81*9e94795aSAndroid Build Coastguard Worker
82*9e94795aSAndroid Build Coastguard Worker    self.id = len(by_id)
83*9e94795aSAndroid Build Coastguard Worker    by_id.append(self)
84*9e94795aSAndroid Build Coastguard Worker
85*9e94795aSAndroid Build Coastguard Worker    self._patch_info = None
86*9e94795aSAndroid Build Coastguard Worker
87*9e94795aSAndroid Build Coastguard Worker  @property
88*9e94795aSAndroid Build Coastguard Worker  def patch_info(self):
89*9e94795aSAndroid Build Coastguard Worker    return self._patch_info
90*9e94795aSAndroid Build Coastguard Worker
91*9e94795aSAndroid Build Coastguard Worker  @patch_info.setter
92*9e94795aSAndroid Build Coastguard Worker  def patch_info(self, info):
93*9e94795aSAndroid Build Coastguard Worker    if info:
94*9e94795aSAndroid Build Coastguard Worker      assert self.style == "diff"
95*9e94795aSAndroid Build Coastguard Worker    self._patch_info = info
96*9e94795aSAndroid Build Coastguard Worker
97*9e94795aSAndroid Build Coastguard Worker  def NetStashChange(self):
98*9e94795aSAndroid Build Coastguard Worker    return (sum(sr.size() for (_, sr) in self.stash_before) -
99*9e94795aSAndroid Build Coastguard Worker            sum(sr.size() for (_, sr) in self.use_stash))
100*9e94795aSAndroid Build Coastguard Worker
101*9e94795aSAndroid Build Coastguard Worker  def ConvertToNew(self):
102*9e94795aSAndroid Build Coastguard Worker    assert self.style != "new"
103*9e94795aSAndroid Build Coastguard Worker    self.use_stash = []
104*9e94795aSAndroid Build Coastguard Worker    self.style = "new"
105*9e94795aSAndroid Build Coastguard Worker    self.src_ranges = RangeSet()
106*9e94795aSAndroid Build Coastguard Worker    self.patch_info = None
107*9e94795aSAndroid Build Coastguard Worker
108*9e94795aSAndroid Build Coastguard Worker  def __str__(self):
109*9e94795aSAndroid Build Coastguard Worker    return (str(self.id) + ": <" + str(self.src_ranges) + " " + self.style +
110*9e94795aSAndroid Build Coastguard Worker            " to " + str(self.tgt_ranges) + ">")
111*9e94795aSAndroid Build Coastguard Worker
112*9e94795aSAndroid Build Coastguard Worker
113*9e94795aSAndroid Build Coastguard Worker@functools.total_ordering
114*9e94795aSAndroid Build Coastguard Workerclass HeapItem(object):
115*9e94795aSAndroid Build Coastguard Worker  def __init__(self, item):
116*9e94795aSAndroid Build Coastguard Worker    self.item = item
117*9e94795aSAndroid Build Coastguard Worker    # Negate the score since python's heap is a min-heap and we want the
118*9e94795aSAndroid Build Coastguard Worker    # maximum score.
119*9e94795aSAndroid Build Coastguard Worker    self.score = -item.score
120*9e94795aSAndroid Build Coastguard Worker
121*9e94795aSAndroid Build Coastguard Worker  def clear(self):
122*9e94795aSAndroid Build Coastguard Worker    self.item = None
123*9e94795aSAndroid Build Coastguard Worker
124*9e94795aSAndroid Build Coastguard Worker  def __bool__(self):
125*9e94795aSAndroid Build Coastguard Worker    return self.item is not None
126*9e94795aSAndroid Build Coastguard Worker
127*9e94795aSAndroid Build Coastguard Worker  # Python 2 uses __nonzero__, while Python 3 uses __bool__.
128*9e94795aSAndroid Build Coastguard Worker  __nonzero__ = __bool__
129*9e94795aSAndroid Build Coastguard Worker
130*9e94795aSAndroid Build Coastguard Worker  # The rest operations are generated by functools.total_ordering decorator.
131*9e94795aSAndroid Build Coastguard Worker  def __eq__(self, other):
132*9e94795aSAndroid Build Coastguard Worker    return self.score == other.score
133*9e94795aSAndroid Build Coastguard Worker
134*9e94795aSAndroid Build Coastguard Worker  def __le__(self, other):
135*9e94795aSAndroid Build Coastguard Worker    return self.score <= other.score
136*9e94795aSAndroid Build Coastguard Worker
137*9e94795aSAndroid Build Coastguard Worker
138*9e94795aSAndroid Build Coastguard Workerclass ImgdiffStats(object):
139*9e94795aSAndroid Build Coastguard Worker  """A class that collects imgdiff stats.
140*9e94795aSAndroid Build Coastguard Worker
141*9e94795aSAndroid Build Coastguard Worker  It keeps track of the files that will be applied imgdiff while generating
142*9e94795aSAndroid Build Coastguard Worker  BlockImageDiff. It also logs the ones that cannot use imgdiff, with specific
143*9e94795aSAndroid Build Coastguard Worker  reasons. The stats is only meaningful when imgdiff not being disabled by the
144*9e94795aSAndroid Build Coastguard Worker  caller of BlockImageDiff. In addition, only files with supported types
145*9e94795aSAndroid Build Coastguard Worker  (BlockImageDiff.FileTypeSupportedByImgdiff()) are allowed to be logged.
146*9e94795aSAndroid Build Coastguard Worker  """
147*9e94795aSAndroid Build Coastguard Worker
148*9e94795aSAndroid Build Coastguard Worker  USED_IMGDIFF = "APK files diff'd with imgdiff"
149*9e94795aSAndroid Build Coastguard Worker  USED_IMGDIFF_LARGE_APK = "Large APK files split and diff'd with imgdiff"
150*9e94795aSAndroid Build Coastguard Worker
151*9e94795aSAndroid Build Coastguard Worker  # Reasons for not applying imgdiff on APKs.
152*9e94795aSAndroid Build Coastguard Worker  SKIPPED_NONMONOTONIC = "Not used imgdiff due to having non-monotonic ranges"
153*9e94795aSAndroid Build Coastguard Worker  SKIPPED_SHARED_BLOCKS = "Not used imgdiff due to using shared blocks"
154*9e94795aSAndroid Build Coastguard Worker  SKIPPED_INCOMPLETE = "Not used imgdiff due to incomplete RangeSet"
155*9e94795aSAndroid Build Coastguard Worker
156*9e94795aSAndroid Build Coastguard Worker  # The list of valid reasons, which will also be the dumped order in a report.
157*9e94795aSAndroid Build Coastguard Worker  REASONS = (
158*9e94795aSAndroid Build Coastguard Worker      USED_IMGDIFF,
159*9e94795aSAndroid Build Coastguard Worker      USED_IMGDIFF_LARGE_APK,
160*9e94795aSAndroid Build Coastguard Worker      SKIPPED_NONMONOTONIC,
161*9e94795aSAndroid Build Coastguard Worker      SKIPPED_SHARED_BLOCKS,
162*9e94795aSAndroid Build Coastguard Worker      SKIPPED_INCOMPLETE,
163*9e94795aSAndroid Build Coastguard Worker  )
164*9e94795aSAndroid Build Coastguard Worker
165*9e94795aSAndroid Build Coastguard Worker  def  __init__(self):
166*9e94795aSAndroid Build Coastguard Worker    self.stats = {}
167*9e94795aSAndroid Build Coastguard Worker
168*9e94795aSAndroid Build Coastguard Worker  def Log(self, filename, reason):
169*9e94795aSAndroid Build Coastguard Worker    """Logs why imgdiff can or cannot be applied to the given filename.
170*9e94795aSAndroid Build Coastguard Worker
171*9e94795aSAndroid Build Coastguard Worker    Args:
172*9e94795aSAndroid Build Coastguard Worker      filename: The filename string.
173*9e94795aSAndroid Build Coastguard Worker      reason: One of the reason constants listed in REASONS.
174*9e94795aSAndroid Build Coastguard Worker
175*9e94795aSAndroid Build Coastguard Worker    Raises:
176*9e94795aSAndroid Build Coastguard Worker      AssertionError: On unsupported filetypes or invalid reason.
177*9e94795aSAndroid Build Coastguard Worker    """
178*9e94795aSAndroid Build Coastguard Worker    assert BlockImageDiff.FileTypeSupportedByImgdiff(filename)
179*9e94795aSAndroid Build Coastguard Worker    assert reason in self.REASONS
180*9e94795aSAndroid Build Coastguard Worker
181*9e94795aSAndroid Build Coastguard Worker    if reason not in self.stats:
182*9e94795aSAndroid Build Coastguard Worker      self.stats[reason] = set()
183*9e94795aSAndroid Build Coastguard Worker    self.stats[reason].add(filename)
184*9e94795aSAndroid Build Coastguard Worker
185*9e94795aSAndroid Build Coastguard Worker  def Report(self):
186*9e94795aSAndroid Build Coastguard Worker    """Prints a report of the collected imgdiff stats."""
187*9e94795aSAndroid Build Coastguard Worker
188*9e94795aSAndroid Build Coastguard Worker    def print_header(header, separator):
189*9e94795aSAndroid Build Coastguard Worker      logger.info(header)
190*9e94795aSAndroid Build Coastguard Worker      logger.info('%s\n', separator * len(header))
191*9e94795aSAndroid Build Coastguard Worker
192*9e94795aSAndroid Build Coastguard Worker    print_header('  Imgdiff Stats Report  ', '=')
193*9e94795aSAndroid Build Coastguard Worker    for key in self.REASONS:
194*9e94795aSAndroid Build Coastguard Worker      if key not in self.stats:
195*9e94795aSAndroid Build Coastguard Worker        continue
196*9e94795aSAndroid Build Coastguard Worker      values = self.stats[key]
197*9e94795aSAndroid Build Coastguard Worker      section_header = ' {} (count: {}) '.format(key, len(values))
198*9e94795aSAndroid Build Coastguard Worker      print_header(section_header, '-')
199*9e94795aSAndroid Build Coastguard Worker      logger.info(''.join(['  {}\n'.format(name) for name in values]))
200*9e94795aSAndroid Build Coastguard Worker
201*9e94795aSAndroid Build Coastguard Worker
202*9e94795aSAndroid Build Coastguard Workerclass BlockImageDiff(object):
203*9e94795aSAndroid Build Coastguard Worker  """Generates the diff of two block image objects.
204*9e94795aSAndroid Build Coastguard Worker
205*9e94795aSAndroid Build Coastguard Worker  BlockImageDiff works on two image objects. An image object is anything that
206*9e94795aSAndroid Build Coastguard Worker  provides the following attributes:
207*9e94795aSAndroid Build Coastguard Worker
208*9e94795aSAndroid Build Coastguard Worker     blocksize: the size in bytes of a block, currently must be 4096.
209*9e94795aSAndroid Build Coastguard Worker
210*9e94795aSAndroid Build Coastguard Worker     total_blocks: the total size of the partition/image, in blocks.
211*9e94795aSAndroid Build Coastguard Worker
212*9e94795aSAndroid Build Coastguard Worker     care_map: a RangeSet containing which blocks (in the range [0,
213*9e94795aSAndroid Build Coastguard Worker       total_blocks) we actually care about; i.e. which blocks contain data.
214*9e94795aSAndroid Build Coastguard Worker
215*9e94795aSAndroid Build Coastguard Worker     file_map: a dict that partitions the blocks contained in care_map into
216*9e94795aSAndroid Build Coastguard Worker         smaller domains that are useful for doing diffs on. (Typically a domain
217*9e94795aSAndroid Build Coastguard Worker         is a file, and the key in file_map is the pathname.)
218*9e94795aSAndroid Build Coastguard Worker
219*9e94795aSAndroid Build Coastguard Worker     clobbered_blocks: a RangeSet containing which blocks contain data but may
220*9e94795aSAndroid Build Coastguard Worker         be altered by the FS. They need to be excluded when verifying the
221*9e94795aSAndroid Build Coastguard Worker         partition integrity.
222*9e94795aSAndroid Build Coastguard Worker
223*9e94795aSAndroid Build Coastguard Worker     ReadRangeSet(): a function that takes a RangeSet and returns the data
224*9e94795aSAndroid Build Coastguard Worker         contained in the image blocks of that RangeSet. The data is returned as
225*9e94795aSAndroid Build Coastguard Worker         a list or tuple of strings; concatenating the elements together should
226*9e94795aSAndroid Build Coastguard Worker         produce the requested data. Implementations are free to break up the
227*9e94795aSAndroid Build Coastguard Worker         data into list/tuple elements in any way that is convenient.
228*9e94795aSAndroid Build Coastguard Worker
229*9e94795aSAndroid Build Coastguard Worker     RangeSha1(): a function that returns (as a hex string) the SHA-1 hash of
230*9e94795aSAndroid Build Coastguard Worker         all the data in the specified range.
231*9e94795aSAndroid Build Coastguard Worker
232*9e94795aSAndroid Build Coastguard Worker     TotalSha1(): a function that returns (as a hex string) the SHA-1 hash of
233*9e94795aSAndroid Build Coastguard Worker         all the data in the image (ie, all the blocks in the care_map minus
234*9e94795aSAndroid Build Coastguard Worker         clobbered_blocks, or including the clobbered blocks if
235*9e94795aSAndroid Build Coastguard Worker         include_clobbered_blocks is True).
236*9e94795aSAndroid Build Coastguard Worker
237*9e94795aSAndroid Build Coastguard Worker  When creating a BlockImageDiff, the src image may be None, in which case the
238*9e94795aSAndroid Build Coastguard Worker  list of transfers produced will never read from the original image.
239*9e94795aSAndroid Build Coastguard Worker  """
240*9e94795aSAndroid Build Coastguard Worker
241*9e94795aSAndroid Build Coastguard Worker  def __init__(self, tgt, src=None, threads=None, version=4,
242*9e94795aSAndroid Build Coastguard Worker               disable_imgdiff=False):
243*9e94795aSAndroid Build Coastguard Worker    if threads is None:
244*9e94795aSAndroid Build Coastguard Worker      threads = multiprocessing.cpu_count() // 2
245*9e94795aSAndroid Build Coastguard Worker      if threads == 0:
246*9e94795aSAndroid Build Coastguard Worker        threads = 1
247*9e94795aSAndroid Build Coastguard Worker    self.threads = threads
248*9e94795aSAndroid Build Coastguard Worker    self.version = version
249*9e94795aSAndroid Build Coastguard Worker    self.transfers = []
250*9e94795aSAndroid Build Coastguard Worker    self.src_basenames = {}
251*9e94795aSAndroid Build Coastguard Worker    self.src_numpatterns = {}
252*9e94795aSAndroid Build Coastguard Worker    self._max_stashed_size = 0
253*9e94795aSAndroid Build Coastguard Worker    self.touched_src_ranges = RangeSet()
254*9e94795aSAndroid Build Coastguard Worker    self.touched_src_sha1 = None
255*9e94795aSAndroid Build Coastguard Worker    self.disable_imgdiff = disable_imgdiff
256*9e94795aSAndroid Build Coastguard Worker    self.imgdiff_stats = ImgdiffStats() if not disable_imgdiff else None
257*9e94795aSAndroid Build Coastguard Worker
258*9e94795aSAndroid Build Coastguard Worker    assert version in (3, 4)
259*9e94795aSAndroid Build Coastguard Worker
260*9e94795aSAndroid Build Coastguard Worker    self.tgt = tgt
261*9e94795aSAndroid Build Coastguard Worker    if src is None:
262*9e94795aSAndroid Build Coastguard Worker      src = EmptyImage()
263*9e94795aSAndroid Build Coastguard Worker    self.src = src
264*9e94795aSAndroid Build Coastguard Worker
265*9e94795aSAndroid Build Coastguard Worker    # The updater code that installs the patch always uses 4k blocks.
266*9e94795aSAndroid Build Coastguard Worker    assert tgt.blocksize == 4096
267*9e94795aSAndroid Build Coastguard Worker    assert src.blocksize == 4096
268*9e94795aSAndroid Build Coastguard Worker
269*9e94795aSAndroid Build Coastguard Worker    # The range sets in each filemap should comprise a partition of
270*9e94795aSAndroid Build Coastguard Worker    # the care map.
271*9e94795aSAndroid Build Coastguard Worker    self.AssertPartition(src.care_map, src.file_map.values())
272*9e94795aSAndroid Build Coastguard Worker    self.AssertPartition(tgt.care_map, tgt.file_map.values())
273*9e94795aSAndroid Build Coastguard Worker
274*9e94795aSAndroid Build Coastguard Worker  @property
275*9e94795aSAndroid Build Coastguard Worker  def max_stashed_size(self):
276*9e94795aSAndroid Build Coastguard Worker    return self._max_stashed_size
277*9e94795aSAndroid Build Coastguard Worker
278*9e94795aSAndroid Build Coastguard Worker  @staticmethod
279*9e94795aSAndroid Build Coastguard Worker  def FileTypeSupportedByImgdiff(filename):
280*9e94795aSAndroid Build Coastguard Worker    """Returns whether the file type is supported by imgdiff."""
281*9e94795aSAndroid Build Coastguard Worker    return filename.lower().endswith(('.apk', '.jar', '.zip'))
282*9e94795aSAndroid Build Coastguard Worker
283*9e94795aSAndroid Build Coastguard Worker  def CanUseImgdiff(self, name, tgt_ranges, src_ranges, large_apk=False):
284*9e94795aSAndroid Build Coastguard Worker    """Checks whether we can apply imgdiff for the given RangeSets.
285*9e94795aSAndroid Build Coastguard Worker
286*9e94795aSAndroid Build Coastguard Worker    For files in ZIP format (e.g., APKs, JARs, etc.) we would like to use
287*9e94795aSAndroid Build Coastguard Worker    'imgdiff -z' if possible. Because it usually produces significantly smaller
288*9e94795aSAndroid Build Coastguard Worker    patches than bsdiff.
289*9e94795aSAndroid Build Coastguard Worker
290*9e94795aSAndroid Build Coastguard Worker    This is permissible if all of the following conditions hold.
291*9e94795aSAndroid Build Coastguard Worker      - The imgdiff hasn't been disabled by the caller (e.g. squashfs);
292*9e94795aSAndroid Build Coastguard Worker      - The file type is supported by imgdiff;
293*9e94795aSAndroid Build Coastguard Worker      - The source and target blocks are monotonic (i.e. the data is stored with
294*9e94795aSAndroid Build Coastguard Worker        blocks in increasing order);
295*9e94795aSAndroid Build Coastguard Worker      - Both files don't contain shared blocks;
296*9e94795aSAndroid Build Coastguard Worker      - Both files have complete lists of blocks;
297*9e94795aSAndroid Build Coastguard Worker      - We haven't removed any blocks from the source set.
298*9e94795aSAndroid Build Coastguard Worker
299*9e94795aSAndroid Build Coastguard Worker    If all these conditions are satisfied, concatenating all the blocks in the
300*9e94795aSAndroid Build Coastguard Worker    RangeSet in order will produce a valid ZIP file (plus possibly extra zeros
301*9e94795aSAndroid Build Coastguard Worker    in the last block). imgdiff is fine with extra zeros at the end of the file.
302*9e94795aSAndroid Build Coastguard Worker
303*9e94795aSAndroid Build Coastguard Worker    Args:
304*9e94795aSAndroid Build Coastguard Worker      name: The filename to be diff'd.
305*9e94795aSAndroid Build Coastguard Worker      tgt_ranges: The target RangeSet.
306*9e94795aSAndroid Build Coastguard Worker      src_ranges: The source RangeSet.
307*9e94795aSAndroid Build Coastguard Worker      large_apk: Whether this is to split a large APK.
308*9e94795aSAndroid Build Coastguard Worker
309*9e94795aSAndroid Build Coastguard Worker    Returns:
310*9e94795aSAndroid Build Coastguard Worker      A boolean result.
311*9e94795aSAndroid Build Coastguard Worker    """
312*9e94795aSAndroid Build Coastguard Worker    if self.disable_imgdiff or not self.FileTypeSupportedByImgdiff(name):
313*9e94795aSAndroid Build Coastguard Worker      return False
314*9e94795aSAndroid Build Coastguard Worker
315*9e94795aSAndroid Build Coastguard Worker    if not tgt_ranges.monotonic or not src_ranges.monotonic:
316*9e94795aSAndroid Build Coastguard Worker      self.imgdiff_stats.Log(name, ImgdiffStats.SKIPPED_NONMONOTONIC)
317*9e94795aSAndroid Build Coastguard Worker      return False
318*9e94795aSAndroid Build Coastguard Worker
319*9e94795aSAndroid Build Coastguard Worker    if (tgt_ranges.extra.get('uses_shared_blocks') or
320*9e94795aSAndroid Build Coastguard Worker        src_ranges.extra.get('uses_shared_blocks')):
321*9e94795aSAndroid Build Coastguard Worker      self.imgdiff_stats.Log(name, ImgdiffStats.SKIPPED_SHARED_BLOCKS)
322*9e94795aSAndroid Build Coastguard Worker      return False
323*9e94795aSAndroid Build Coastguard Worker
324*9e94795aSAndroid Build Coastguard Worker    if tgt_ranges.extra.get('incomplete') or src_ranges.extra.get('incomplete'):
325*9e94795aSAndroid Build Coastguard Worker      self.imgdiff_stats.Log(name, ImgdiffStats.SKIPPED_INCOMPLETE)
326*9e94795aSAndroid Build Coastguard Worker      return False
327*9e94795aSAndroid Build Coastguard Worker
328*9e94795aSAndroid Build Coastguard Worker    reason = (ImgdiffStats.USED_IMGDIFF_LARGE_APK if large_apk
329*9e94795aSAndroid Build Coastguard Worker              else ImgdiffStats.USED_IMGDIFF)
330*9e94795aSAndroid Build Coastguard Worker    self.imgdiff_stats.Log(name, reason)
331*9e94795aSAndroid Build Coastguard Worker    return True
332*9e94795aSAndroid Build Coastguard Worker
333*9e94795aSAndroid Build Coastguard Worker  def Compute(self, prefix):
334*9e94795aSAndroid Build Coastguard Worker    # When looking for a source file to use as the diff input for a
335*9e94795aSAndroid Build Coastguard Worker    # target file, we try:
336*9e94795aSAndroid Build Coastguard Worker    #   1) an exact path match if available, otherwise
337*9e94795aSAndroid Build Coastguard Worker    #   2) a exact basename match if available, otherwise
338*9e94795aSAndroid Build Coastguard Worker    #   3) a basename match after all runs of digits are replaced by
339*9e94795aSAndroid Build Coastguard Worker    #      "#" if available, otherwise
340*9e94795aSAndroid Build Coastguard Worker    #   4) we have no source for this target.
341*9e94795aSAndroid Build Coastguard Worker    self.AbbreviateSourceNames()
342*9e94795aSAndroid Build Coastguard Worker    self.FindTransfers()
343*9e94795aSAndroid Build Coastguard Worker
344*9e94795aSAndroid Build Coastguard Worker    self.FindSequenceForTransfers()
345*9e94795aSAndroid Build Coastguard Worker
346*9e94795aSAndroid Build Coastguard Worker    # Ensure the runtime stash size is under the limit.
347*9e94795aSAndroid Build Coastguard Worker    if common.OPTIONS.cache_size is not None:
348*9e94795aSAndroid Build Coastguard Worker      stash_limit = (common.OPTIONS.cache_size *
349*9e94795aSAndroid Build Coastguard Worker                     common.OPTIONS.stash_threshold / self.tgt.blocksize)
350*9e94795aSAndroid Build Coastguard Worker      # Ignore the stash limit and calculate the maximum simultaneously stashed
351*9e94795aSAndroid Build Coastguard Worker      # blocks needed.
352*9e94795aSAndroid Build Coastguard Worker      _, max_stashed_blocks = self.ReviseStashSize(ignore_stash_limit=True)
353*9e94795aSAndroid Build Coastguard Worker
354*9e94795aSAndroid Build Coastguard Worker      # We cannot stash more blocks than the stash limit simultaneously. As a
355*9e94795aSAndroid Build Coastguard Worker      # result, some 'diff' commands will be converted to new; leading to an
356*9e94795aSAndroid Build Coastguard Worker      # unintended large package. To mitigate this issue, we can carefully
357*9e94795aSAndroid Build Coastguard Worker      # choose the transfers for conversion. The number '1024' can be further
358*9e94795aSAndroid Build Coastguard Worker      # tweaked here to balance the package size and build time.
359*9e94795aSAndroid Build Coastguard Worker      if max_stashed_blocks > stash_limit + 1024:
360*9e94795aSAndroid Build Coastguard Worker        self.SelectAndConvertDiffTransfersToNew(
361*9e94795aSAndroid Build Coastguard Worker            max_stashed_blocks - stash_limit)
362*9e94795aSAndroid Build Coastguard Worker        # Regenerate the sequence as the graph has changed.
363*9e94795aSAndroid Build Coastguard Worker        self.FindSequenceForTransfers()
364*9e94795aSAndroid Build Coastguard Worker
365*9e94795aSAndroid Build Coastguard Worker      # Revise the stash size again to keep the size under limit.
366*9e94795aSAndroid Build Coastguard Worker      self.ReviseStashSize()
367*9e94795aSAndroid Build Coastguard Worker
368*9e94795aSAndroid Build Coastguard Worker    # Double-check our work.
369*9e94795aSAndroid Build Coastguard Worker    self.AssertSequenceGood()
370*9e94795aSAndroid Build Coastguard Worker    self.AssertSha1Good()
371*9e94795aSAndroid Build Coastguard Worker
372*9e94795aSAndroid Build Coastguard Worker    self.ComputePatches(prefix)
373*9e94795aSAndroid Build Coastguard Worker    self.WriteTransfers(prefix)
374*9e94795aSAndroid Build Coastguard Worker
375*9e94795aSAndroid Build Coastguard Worker    # Report the imgdiff stats.
376*9e94795aSAndroid Build Coastguard Worker    if not self.disable_imgdiff:
377*9e94795aSAndroid Build Coastguard Worker      self.imgdiff_stats.Report()
378*9e94795aSAndroid Build Coastguard Worker
379*9e94795aSAndroid Build Coastguard Worker  def WriteTransfers(self, prefix):
380*9e94795aSAndroid Build Coastguard Worker    def WriteSplitTransfers(out, style, target_blocks):
381*9e94795aSAndroid Build Coastguard Worker      """Limit the size of operand in command 'new' and 'zero' to 1024 blocks.
382*9e94795aSAndroid Build Coastguard Worker
383*9e94795aSAndroid Build Coastguard Worker      This prevents the target size of one command from being too large; and
384*9e94795aSAndroid Build Coastguard Worker      might help to avoid fsync errors on some devices."""
385*9e94795aSAndroid Build Coastguard Worker
386*9e94795aSAndroid Build Coastguard Worker      assert style == "new" or style == "zero"
387*9e94795aSAndroid Build Coastguard Worker      blocks_limit = 1024
388*9e94795aSAndroid Build Coastguard Worker      total = 0
389*9e94795aSAndroid Build Coastguard Worker      while target_blocks:
390*9e94795aSAndroid Build Coastguard Worker        blocks_to_write = target_blocks.first(blocks_limit)
391*9e94795aSAndroid Build Coastguard Worker        out.append("%s %s\n" % (style, blocks_to_write.to_string_raw()))
392*9e94795aSAndroid Build Coastguard Worker        total += blocks_to_write.size()
393*9e94795aSAndroid Build Coastguard Worker        target_blocks = target_blocks.subtract(blocks_to_write)
394*9e94795aSAndroid Build Coastguard Worker      return total
395*9e94795aSAndroid Build Coastguard Worker
396*9e94795aSAndroid Build Coastguard Worker    out = []
397*9e94795aSAndroid Build Coastguard Worker    total = 0
398*9e94795aSAndroid Build Coastguard Worker
399*9e94795aSAndroid Build Coastguard Worker    # In BBOTA v3+, it uses the hash of the stashed blocks as the stash slot
400*9e94795aSAndroid Build Coastguard Worker    # id. 'stashes' records the map from 'hash' to the ref count. The stash
401*9e94795aSAndroid Build Coastguard Worker    # will be freed only if the count decrements to zero.
402*9e94795aSAndroid Build Coastguard Worker    stashes = {}
403*9e94795aSAndroid Build Coastguard Worker    stashed_blocks = 0
404*9e94795aSAndroid Build Coastguard Worker    max_stashed_blocks = 0
405*9e94795aSAndroid Build Coastguard Worker
406*9e94795aSAndroid Build Coastguard Worker    for xf in self.transfers:
407*9e94795aSAndroid Build Coastguard Worker
408*9e94795aSAndroid Build Coastguard Worker      for _, sr in xf.stash_before:
409*9e94795aSAndroid Build Coastguard Worker        sh = self.src.RangeSha1(sr)
410*9e94795aSAndroid Build Coastguard Worker        if sh in stashes:
411*9e94795aSAndroid Build Coastguard Worker          stashes[sh] += 1
412*9e94795aSAndroid Build Coastguard Worker        else:
413*9e94795aSAndroid Build Coastguard Worker          stashes[sh] = 1
414*9e94795aSAndroid Build Coastguard Worker          stashed_blocks += sr.size()
415*9e94795aSAndroid Build Coastguard Worker          self.touched_src_ranges = self.touched_src_ranges.union(sr)
416*9e94795aSAndroid Build Coastguard Worker          out.append("stash %s %s\n" % (sh, sr.to_string_raw()))
417*9e94795aSAndroid Build Coastguard Worker
418*9e94795aSAndroid Build Coastguard Worker      if stashed_blocks > max_stashed_blocks:
419*9e94795aSAndroid Build Coastguard Worker        max_stashed_blocks = stashed_blocks
420*9e94795aSAndroid Build Coastguard Worker
421*9e94795aSAndroid Build Coastguard Worker      free_string = []
422*9e94795aSAndroid Build Coastguard Worker      free_size = 0
423*9e94795aSAndroid Build Coastguard Worker
424*9e94795aSAndroid Build Coastguard Worker      #   <# blocks> <src ranges>
425*9e94795aSAndroid Build Coastguard Worker      #     OR
426*9e94795aSAndroid Build Coastguard Worker      #   <# blocks> <src ranges> <src locs> <stash refs...>
427*9e94795aSAndroid Build Coastguard Worker      #     OR
428*9e94795aSAndroid Build Coastguard Worker      #   <# blocks> - <stash refs...>
429*9e94795aSAndroid Build Coastguard Worker
430*9e94795aSAndroid Build Coastguard Worker      size = xf.src_ranges.size()
431*9e94795aSAndroid Build Coastguard Worker      src_str_buffer = [str(size)]
432*9e94795aSAndroid Build Coastguard Worker
433*9e94795aSAndroid Build Coastguard Worker      unstashed_src_ranges = xf.src_ranges
434*9e94795aSAndroid Build Coastguard Worker      mapped_stashes = []
435*9e94795aSAndroid Build Coastguard Worker      for _, sr in xf.use_stash:
436*9e94795aSAndroid Build Coastguard Worker        unstashed_src_ranges = unstashed_src_ranges.subtract(sr)
437*9e94795aSAndroid Build Coastguard Worker        sh = self.src.RangeSha1(sr)
438*9e94795aSAndroid Build Coastguard Worker        sr = xf.src_ranges.map_within(sr)
439*9e94795aSAndroid Build Coastguard Worker        mapped_stashes.append(sr)
440*9e94795aSAndroid Build Coastguard Worker        assert sh in stashes
441*9e94795aSAndroid Build Coastguard Worker        src_str_buffer.append("%s:%s" % (sh, sr.to_string_raw()))
442*9e94795aSAndroid Build Coastguard Worker        stashes[sh] -= 1
443*9e94795aSAndroid Build Coastguard Worker        if stashes[sh] == 0:
444*9e94795aSAndroid Build Coastguard Worker          free_string.append("free %s\n" % (sh,))
445*9e94795aSAndroid Build Coastguard Worker          free_size += sr.size()
446*9e94795aSAndroid Build Coastguard Worker          stashes.pop(sh)
447*9e94795aSAndroid Build Coastguard Worker
448*9e94795aSAndroid Build Coastguard Worker      if unstashed_src_ranges:
449*9e94795aSAndroid Build Coastguard Worker        src_str_buffer.insert(1, unstashed_src_ranges.to_string_raw())
450*9e94795aSAndroid Build Coastguard Worker        if xf.use_stash:
451*9e94795aSAndroid Build Coastguard Worker          mapped_unstashed = xf.src_ranges.map_within(unstashed_src_ranges)
452*9e94795aSAndroid Build Coastguard Worker          src_str_buffer.insert(2, mapped_unstashed.to_string_raw())
453*9e94795aSAndroid Build Coastguard Worker          mapped_stashes.append(mapped_unstashed)
454*9e94795aSAndroid Build Coastguard Worker          self.AssertPartition(RangeSet(data=(0, size)), mapped_stashes)
455*9e94795aSAndroid Build Coastguard Worker      else:
456*9e94795aSAndroid Build Coastguard Worker        src_str_buffer.insert(1, "-")
457*9e94795aSAndroid Build Coastguard Worker        self.AssertPartition(RangeSet(data=(0, size)), mapped_stashes)
458*9e94795aSAndroid Build Coastguard Worker
459*9e94795aSAndroid Build Coastguard Worker      src_str = " ".join(src_str_buffer)
460*9e94795aSAndroid Build Coastguard Worker
461*9e94795aSAndroid Build Coastguard Worker      # version 3+:
462*9e94795aSAndroid Build Coastguard Worker      #   zero <rangeset>
463*9e94795aSAndroid Build Coastguard Worker      #   new <rangeset>
464*9e94795aSAndroid Build Coastguard Worker      #   erase <rangeset>
465*9e94795aSAndroid Build Coastguard Worker      #   bsdiff patchstart patchlen srchash tgthash <tgt rangeset> <src_str>
466*9e94795aSAndroid Build Coastguard Worker      #   imgdiff patchstart patchlen srchash tgthash <tgt rangeset> <src_str>
467*9e94795aSAndroid Build Coastguard Worker      #   move hash <tgt rangeset> <src_str>
468*9e94795aSAndroid Build Coastguard Worker
469*9e94795aSAndroid Build Coastguard Worker      tgt_size = xf.tgt_ranges.size()
470*9e94795aSAndroid Build Coastguard Worker
471*9e94795aSAndroid Build Coastguard Worker      if xf.style == "new":
472*9e94795aSAndroid Build Coastguard Worker        assert xf.tgt_ranges
473*9e94795aSAndroid Build Coastguard Worker        assert tgt_size == WriteSplitTransfers(out, xf.style, xf.tgt_ranges)
474*9e94795aSAndroid Build Coastguard Worker        total += tgt_size
475*9e94795aSAndroid Build Coastguard Worker      elif xf.style == "move":
476*9e94795aSAndroid Build Coastguard Worker        assert xf.tgt_ranges
477*9e94795aSAndroid Build Coastguard Worker        assert xf.src_ranges.size() == tgt_size
478*9e94795aSAndroid Build Coastguard Worker        if xf.src_ranges != xf.tgt_ranges:
479*9e94795aSAndroid Build Coastguard Worker          # take into account automatic stashing of overlapping blocks
480*9e94795aSAndroid Build Coastguard Worker          if xf.src_ranges.overlaps(xf.tgt_ranges):
481*9e94795aSAndroid Build Coastguard Worker            temp_stash_usage = stashed_blocks + xf.src_ranges.size()
482*9e94795aSAndroid Build Coastguard Worker            if temp_stash_usage > max_stashed_blocks:
483*9e94795aSAndroid Build Coastguard Worker              max_stashed_blocks = temp_stash_usage
484*9e94795aSAndroid Build Coastguard Worker
485*9e94795aSAndroid Build Coastguard Worker          self.touched_src_ranges = self.touched_src_ranges.union(
486*9e94795aSAndroid Build Coastguard Worker              xf.src_ranges)
487*9e94795aSAndroid Build Coastguard Worker
488*9e94795aSAndroid Build Coastguard Worker          out.append("%s %s %s %s\n" % (
489*9e94795aSAndroid Build Coastguard Worker              xf.style,
490*9e94795aSAndroid Build Coastguard Worker              xf.tgt_sha1,
491*9e94795aSAndroid Build Coastguard Worker              xf.tgt_ranges.to_string_raw(), src_str))
492*9e94795aSAndroid Build Coastguard Worker          total += tgt_size
493*9e94795aSAndroid Build Coastguard Worker      elif xf.style in ("bsdiff", "imgdiff"):
494*9e94795aSAndroid Build Coastguard Worker        assert xf.tgt_ranges
495*9e94795aSAndroid Build Coastguard Worker        assert xf.src_ranges
496*9e94795aSAndroid Build Coastguard Worker        # take into account automatic stashing of overlapping blocks
497*9e94795aSAndroid Build Coastguard Worker        if xf.src_ranges.overlaps(xf.tgt_ranges):
498*9e94795aSAndroid Build Coastguard Worker          temp_stash_usage = stashed_blocks + xf.src_ranges.size()
499*9e94795aSAndroid Build Coastguard Worker          if temp_stash_usage > max_stashed_blocks:
500*9e94795aSAndroid Build Coastguard Worker            max_stashed_blocks = temp_stash_usage
501*9e94795aSAndroid Build Coastguard Worker
502*9e94795aSAndroid Build Coastguard Worker        self.touched_src_ranges = self.touched_src_ranges.union(xf.src_ranges)
503*9e94795aSAndroid Build Coastguard Worker
504*9e94795aSAndroid Build Coastguard Worker        out.append("%s %d %d %s %s %s %s\n" % (
505*9e94795aSAndroid Build Coastguard Worker            xf.style,
506*9e94795aSAndroid Build Coastguard Worker            xf.patch_start, xf.patch_len,
507*9e94795aSAndroid Build Coastguard Worker            xf.src_sha1,
508*9e94795aSAndroid Build Coastguard Worker            xf.tgt_sha1,
509*9e94795aSAndroid Build Coastguard Worker            xf.tgt_ranges.to_string_raw(), src_str))
510*9e94795aSAndroid Build Coastguard Worker        total += tgt_size
511*9e94795aSAndroid Build Coastguard Worker      elif xf.style == "zero":
512*9e94795aSAndroid Build Coastguard Worker        assert xf.tgt_ranges
513*9e94795aSAndroid Build Coastguard Worker        to_zero = xf.tgt_ranges.subtract(xf.src_ranges)
514*9e94795aSAndroid Build Coastguard Worker        assert WriteSplitTransfers(out, xf.style, to_zero) == to_zero.size()
515*9e94795aSAndroid Build Coastguard Worker        total += to_zero.size()
516*9e94795aSAndroid Build Coastguard Worker      else:
517*9e94795aSAndroid Build Coastguard Worker        raise ValueError("unknown transfer style '%s'\n" % xf.style)
518*9e94795aSAndroid Build Coastguard Worker
519*9e94795aSAndroid Build Coastguard Worker      if free_string:
520*9e94795aSAndroid Build Coastguard Worker        out.append("".join(free_string))
521*9e94795aSAndroid Build Coastguard Worker        stashed_blocks -= free_size
522*9e94795aSAndroid Build Coastguard Worker
523*9e94795aSAndroid Build Coastguard Worker      if common.OPTIONS.cache_size is not None:
524*9e94795aSAndroid Build Coastguard Worker        # Validation check: abort if we're going to need more stash space than
525*9e94795aSAndroid Build Coastguard Worker        # the allowed size (cache_size * threshold). There are two purposes
526*9e94795aSAndroid Build Coastguard Worker        # of having a threshold here. a) Part of the cache may have been
527*9e94795aSAndroid Build Coastguard Worker        # occupied by some recovery logs. b) It will buy us some time to deal
528*9e94795aSAndroid Build Coastguard Worker        # with the oversize issue.
529*9e94795aSAndroid Build Coastguard Worker        cache_size = common.OPTIONS.cache_size
530*9e94795aSAndroid Build Coastguard Worker        stash_threshold = common.OPTIONS.stash_threshold
531*9e94795aSAndroid Build Coastguard Worker        max_allowed = cache_size * stash_threshold
532*9e94795aSAndroid Build Coastguard Worker        assert max_stashed_blocks * self.tgt.blocksize <= max_allowed, \
533*9e94795aSAndroid Build Coastguard Worker               'Stash size %d (%d * %d) exceeds the limit %d (%d * %.2f)' % (
534*9e94795aSAndroid Build Coastguard Worker                   max_stashed_blocks * self.tgt.blocksize, max_stashed_blocks,
535*9e94795aSAndroid Build Coastguard Worker                   self.tgt.blocksize, max_allowed, cache_size,
536*9e94795aSAndroid Build Coastguard Worker                   stash_threshold)
537*9e94795aSAndroid Build Coastguard Worker
538*9e94795aSAndroid Build Coastguard Worker    self.touched_src_sha1 = self.src.RangeSha1(self.touched_src_ranges)
539*9e94795aSAndroid Build Coastguard Worker
540*9e94795aSAndroid Build Coastguard Worker    # Zero out extended blocks as a workaround for bug 20881595.
541*9e94795aSAndroid Build Coastguard Worker    if self.tgt.extended:
542*9e94795aSAndroid Build Coastguard Worker      assert (WriteSplitTransfers(out, "zero", self.tgt.extended) ==
543*9e94795aSAndroid Build Coastguard Worker              self.tgt.extended.size())
544*9e94795aSAndroid Build Coastguard Worker      total += self.tgt.extended.size()
545*9e94795aSAndroid Build Coastguard Worker
546*9e94795aSAndroid Build Coastguard Worker    # We erase all the blocks on the partition that a) don't contain useful
547*9e94795aSAndroid Build Coastguard Worker    # data in the new image; b) will not be touched by dm-verity. Out of those
548*9e94795aSAndroid Build Coastguard Worker    # blocks, we erase the ones that won't be used in this update at the
549*9e94795aSAndroid Build Coastguard Worker    # beginning of an update. The rest would be erased at the end. This is to
550*9e94795aSAndroid Build Coastguard Worker    # work around the eMMC issue observed on some devices, which may otherwise
551*9e94795aSAndroid Build Coastguard Worker    # get starving for clean blocks and thus fail the update. (b/28347095)
552*9e94795aSAndroid Build Coastguard Worker    all_tgt = RangeSet(data=(0, self.tgt.total_blocks))
553*9e94795aSAndroid Build Coastguard Worker    all_tgt_minus_extended = all_tgt.subtract(self.tgt.extended)
554*9e94795aSAndroid Build Coastguard Worker    new_dontcare = all_tgt_minus_extended.subtract(self.tgt.care_map)
555*9e94795aSAndroid Build Coastguard Worker
556*9e94795aSAndroid Build Coastguard Worker    erase_first = new_dontcare.subtract(self.touched_src_ranges)
557*9e94795aSAndroid Build Coastguard Worker    if erase_first:
558*9e94795aSAndroid Build Coastguard Worker      out.insert(0, "erase %s\n" % (erase_first.to_string_raw(),))
559*9e94795aSAndroid Build Coastguard Worker
560*9e94795aSAndroid Build Coastguard Worker    erase_last = new_dontcare.subtract(erase_first)
561*9e94795aSAndroid Build Coastguard Worker    if erase_last:
562*9e94795aSAndroid Build Coastguard Worker      out.append("erase %s\n" % (erase_last.to_string_raw(),))
563*9e94795aSAndroid Build Coastguard Worker
564*9e94795aSAndroid Build Coastguard Worker    out.insert(0, "%d\n" % (self.version,))   # format version number
565*9e94795aSAndroid Build Coastguard Worker    out.insert(1, "%d\n" % (total,))
566*9e94795aSAndroid Build Coastguard Worker    # v3+: the number of stash slots is unused.
567*9e94795aSAndroid Build Coastguard Worker    out.insert(2, "0\n")
568*9e94795aSAndroid Build Coastguard Worker    out.insert(3, str(max_stashed_blocks) + "\n")
569*9e94795aSAndroid Build Coastguard Worker
570*9e94795aSAndroid Build Coastguard Worker    with open(prefix + ".transfer.list", "w") as f:
571*9e94795aSAndroid Build Coastguard Worker      for i in out:
572*9e94795aSAndroid Build Coastguard Worker        f.write(i)
573*9e94795aSAndroid Build Coastguard Worker
574*9e94795aSAndroid Build Coastguard Worker    self._max_stashed_size = max_stashed_blocks * self.tgt.blocksize
575*9e94795aSAndroid Build Coastguard Worker    OPTIONS = common.OPTIONS
576*9e94795aSAndroid Build Coastguard Worker    if OPTIONS.cache_size is not None:
577*9e94795aSAndroid Build Coastguard Worker      max_allowed = OPTIONS.cache_size * OPTIONS.stash_threshold
578*9e94795aSAndroid Build Coastguard Worker      logger.info(
579*9e94795aSAndroid Build Coastguard Worker          "max stashed blocks: %d  (%d bytes), limit: %d bytes (%.2f%%)\n",
580*9e94795aSAndroid Build Coastguard Worker          max_stashed_blocks, self._max_stashed_size, max_allowed,
581*9e94795aSAndroid Build Coastguard Worker          self._max_stashed_size * 100.0 / max_allowed)
582*9e94795aSAndroid Build Coastguard Worker    else:
583*9e94795aSAndroid Build Coastguard Worker      logger.info(
584*9e94795aSAndroid Build Coastguard Worker          "max stashed blocks: %d  (%d bytes), limit: <unknown>\n",
585*9e94795aSAndroid Build Coastguard Worker          max_stashed_blocks, self._max_stashed_size)
586*9e94795aSAndroid Build Coastguard Worker
587*9e94795aSAndroid Build Coastguard Worker  def ReviseStashSize(self, ignore_stash_limit=False):
588*9e94795aSAndroid Build Coastguard Worker    """ Revises the transfers to keep the stash size within the size limit.
589*9e94795aSAndroid Build Coastguard Worker
590*9e94795aSAndroid Build Coastguard Worker    Iterates through the transfer list and calculates the stash size each
591*9e94795aSAndroid Build Coastguard Worker    transfer generates. Converts the affected transfers to new if we reach the
592*9e94795aSAndroid Build Coastguard Worker    stash limit.
593*9e94795aSAndroid Build Coastguard Worker
594*9e94795aSAndroid Build Coastguard Worker    Args:
595*9e94795aSAndroid Build Coastguard Worker      ignore_stash_limit: Ignores the stash limit and calculates the max
596*9e94795aSAndroid Build Coastguard Worker      simultaneous stashed blocks instead. No change will be made to the
597*9e94795aSAndroid Build Coastguard Worker      transfer list with this flag.
598*9e94795aSAndroid Build Coastguard Worker
599*9e94795aSAndroid Build Coastguard Worker    Return:
600*9e94795aSAndroid Build Coastguard Worker      A tuple of (tgt blocks converted to new, max stashed blocks)
601*9e94795aSAndroid Build Coastguard Worker    """
602*9e94795aSAndroid Build Coastguard Worker    logger.info("Revising stash size...")
603*9e94795aSAndroid Build Coastguard Worker    stash_map = {}
604*9e94795aSAndroid Build Coastguard Worker
605*9e94795aSAndroid Build Coastguard Worker    # Create the map between a stash and its def/use points. For example, for a
606*9e94795aSAndroid Build Coastguard Worker    # given stash of (raw_id, sr), stash_map[raw_id] = (sr, def_cmd, use_cmd).
607*9e94795aSAndroid Build Coastguard Worker    for xf in self.transfers:
608*9e94795aSAndroid Build Coastguard Worker      # Command xf defines (stores) all the stashes in stash_before.
609*9e94795aSAndroid Build Coastguard Worker      for stash_raw_id, sr in xf.stash_before:
610*9e94795aSAndroid Build Coastguard Worker        stash_map[stash_raw_id] = (sr, xf)
611*9e94795aSAndroid Build Coastguard Worker
612*9e94795aSAndroid Build Coastguard Worker      # Record all the stashes command xf uses.
613*9e94795aSAndroid Build Coastguard Worker      for stash_raw_id, _ in xf.use_stash:
614*9e94795aSAndroid Build Coastguard Worker        stash_map[stash_raw_id] += (xf,)
615*9e94795aSAndroid Build Coastguard Worker
616*9e94795aSAndroid Build Coastguard Worker    max_allowed_blocks = None
617*9e94795aSAndroid Build Coastguard Worker    if not ignore_stash_limit:
618*9e94795aSAndroid Build Coastguard Worker      # Compute the maximum blocks available for stash based on /cache size and
619*9e94795aSAndroid Build Coastguard Worker      # the threshold.
620*9e94795aSAndroid Build Coastguard Worker      cache_size = common.OPTIONS.cache_size
621*9e94795aSAndroid Build Coastguard Worker      stash_threshold = common.OPTIONS.stash_threshold
622*9e94795aSAndroid Build Coastguard Worker      max_allowed_blocks = cache_size * stash_threshold / self.tgt.blocksize
623*9e94795aSAndroid Build Coastguard Worker
624*9e94795aSAndroid Build Coastguard Worker    # See the comments for 'stashes' in WriteTransfers().
625*9e94795aSAndroid Build Coastguard Worker    stashes = {}
626*9e94795aSAndroid Build Coastguard Worker    stashed_blocks = 0
627*9e94795aSAndroid Build Coastguard Worker    new_blocks = 0
628*9e94795aSAndroid Build Coastguard Worker    max_stashed_blocks = 0
629*9e94795aSAndroid Build Coastguard Worker
630*9e94795aSAndroid Build Coastguard Worker    # Now go through all the commands. Compute the required stash size on the
631*9e94795aSAndroid Build Coastguard Worker    # fly. If a command requires excess stash than available, it deletes the
632*9e94795aSAndroid Build Coastguard Worker    # stash by replacing the command that uses the stash with a "new" command
633*9e94795aSAndroid Build Coastguard Worker    # instead.
634*9e94795aSAndroid Build Coastguard Worker    for xf in self.transfers:
635*9e94795aSAndroid Build Coastguard Worker      replaced_cmds = []
636*9e94795aSAndroid Build Coastguard Worker
637*9e94795aSAndroid Build Coastguard Worker      # xf.stash_before generates explicit stash commands.
638*9e94795aSAndroid Build Coastguard Worker      for stash_raw_id, sr in xf.stash_before:
639*9e94795aSAndroid Build Coastguard Worker        # Check the post-command stashed_blocks.
640*9e94795aSAndroid Build Coastguard Worker        stashed_blocks_after = stashed_blocks
641*9e94795aSAndroid Build Coastguard Worker        sh = self.src.RangeSha1(sr)
642*9e94795aSAndroid Build Coastguard Worker        if sh not in stashes:
643*9e94795aSAndroid Build Coastguard Worker          stashed_blocks_after += sr.size()
644*9e94795aSAndroid Build Coastguard Worker
645*9e94795aSAndroid Build Coastguard Worker        if max_allowed_blocks and stashed_blocks_after > max_allowed_blocks:
646*9e94795aSAndroid Build Coastguard Worker          # We cannot stash this one for a later command. Find out the command
647*9e94795aSAndroid Build Coastguard Worker          # that will use this stash and replace the command with "new".
648*9e94795aSAndroid Build Coastguard Worker          use_cmd = stash_map[stash_raw_id][2]
649*9e94795aSAndroid Build Coastguard Worker          replaced_cmds.append(use_cmd)
650*9e94795aSAndroid Build Coastguard Worker          logger.info("%10d  %9s  %s", sr.size(), "explicit", use_cmd)
651*9e94795aSAndroid Build Coastguard Worker        else:
652*9e94795aSAndroid Build Coastguard Worker          # Update the stashes map.
653*9e94795aSAndroid Build Coastguard Worker          if sh in stashes:
654*9e94795aSAndroid Build Coastguard Worker            stashes[sh] += 1
655*9e94795aSAndroid Build Coastguard Worker          else:
656*9e94795aSAndroid Build Coastguard Worker            stashes[sh] = 1
657*9e94795aSAndroid Build Coastguard Worker          stashed_blocks = stashed_blocks_after
658*9e94795aSAndroid Build Coastguard Worker          max_stashed_blocks = max(max_stashed_blocks, stashed_blocks)
659*9e94795aSAndroid Build Coastguard Worker
660*9e94795aSAndroid Build Coastguard Worker      # "move" and "diff" may introduce implicit stashes in BBOTA v3. Prior to
661*9e94795aSAndroid Build Coastguard Worker      # ComputePatches(), they both have the style of "diff".
662*9e94795aSAndroid Build Coastguard Worker      if xf.style == "diff":
663*9e94795aSAndroid Build Coastguard Worker        assert xf.tgt_ranges and xf.src_ranges
664*9e94795aSAndroid Build Coastguard Worker        if xf.src_ranges.overlaps(xf.tgt_ranges):
665*9e94795aSAndroid Build Coastguard Worker          if (max_allowed_blocks and
666*9e94795aSAndroid Build Coastguard Worker              stashed_blocks + xf.src_ranges.size() > max_allowed_blocks):
667*9e94795aSAndroid Build Coastguard Worker            replaced_cmds.append(xf)
668*9e94795aSAndroid Build Coastguard Worker            logger.info("%10d  %9s  %s", xf.src_ranges.size(), "implicit", xf)
669*9e94795aSAndroid Build Coastguard Worker          else:
670*9e94795aSAndroid Build Coastguard Worker            # The whole source ranges will be stashed for implicit stashes.
671*9e94795aSAndroid Build Coastguard Worker            max_stashed_blocks = max(max_stashed_blocks,
672*9e94795aSAndroid Build Coastguard Worker                                     stashed_blocks + xf.src_ranges.size())
673*9e94795aSAndroid Build Coastguard Worker
674*9e94795aSAndroid Build Coastguard Worker      # Replace the commands in replaced_cmds with "new"s.
675*9e94795aSAndroid Build Coastguard Worker      for cmd in replaced_cmds:
676*9e94795aSAndroid Build Coastguard Worker        # It no longer uses any commands in "use_stash". Remove the def points
677*9e94795aSAndroid Build Coastguard Worker        # for all those stashes.
678*9e94795aSAndroid Build Coastguard Worker        for stash_raw_id, sr in cmd.use_stash:
679*9e94795aSAndroid Build Coastguard Worker          def_cmd = stash_map[stash_raw_id][1]
680*9e94795aSAndroid Build Coastguard Worker          assert (stash_raw_id, sr) in def_cmd.stash_before
681*9e94795aSAndroid Build Coastguard Worker          def_cmd.stash_before.remove((stash_raw_id, sr))
682*9e94795aSAndroid Build Coastguard Worker
683*9e94795aSAndroid Build Coastguard Worker        # Add up blocks that violates space limit and print total number to
684*9e94795aSAndroid Build Coastguard Worker        # screen later.
685*9e94795aSAndroid Build Coastguard Worker        new_blocks += cmd.tgt_ranges.size()
686*9e94795aSAndroid Build Coastguard Worker        cmd.ConvertToNew()
687*9e94795aSAndroid Build Coastguard Worker
688*9e94795aSAndroid Build Coastguard Worker      # xf.use_stash may generate free commands.
689*9e94795aSAndroid Build Coastguard Worker      for _, sr in xf.use_stash:
690*9e94795aSAndroid Build Coastguard Worker        sh = self.src.RangeSha1(sr)
691*9e94795aSAndroid Build Coastguard Worker        assert sh in stashes
692*9e94795aSAndroid Build Coastguard Worker        stashes[sh] -= 1
693*9e94795aSAndroid Build Coastguard Worker        if stashes[sh] == 0:
694*9e94795aSAndroid Build Coastguard Worker          stashed_blocks -= sr.size()
695*9e94795aSAndroid Build Coastguard Worker          stashes.pop(sh)
696*9e94795aSAndroid Build Coastguard Worker
697*9e94795aSAndroid Build Coastguard Worker    num_of_bytes = new_blocks * self.tgt.blocksize
698*9e94795aSAndroid Build Coastguard Worker    logger.info(
699*9e94795aSAndroid Build Coastguard Worker        "  Total %d blocks (%d bytes) are packed as new blocks due to "
700*9e94795aSAndroid Build Coastguard Worker        "insufficient cache size. Maximum blocks stashed simultaneously: %d",
701*9e94795aSAndroid Build Coastguard Worker        new_blocks, num_of_bytes, max_stashed_blocks)
702*9e94795aSAndroid Build Coastguard Worker    return new_blocks, max_stashed_blocks
703*9e94795aSAndroid Build Coastguard Worker
704*9e94795aSAndroid Build Coastguard Worker  def ComputePatches(self, prefix):
705*9e94795aSAndroid Build Coastguard Worker    logger.info("Reticulating splines...")
706*9e94795aSAndroid Build Coastguard Worker    diff_queue = []
707*9e94795aSAndroid Build Coastguard Worker    patch_num = 0
708*9e94795aSAndroid Build Coastguard Worker    with open(prefix + ".new.dat", "wb") as new_f:
709*9e94795aSAndroid Build Coastguard Worker      for index, xf in enumerate(self.transfers):
710*9e94795aSAndroid Build Coastguard Worker        if xf.style == "zero":
711*9e94795aSAndroid Build Coastguard Worker          tgt_size = xf.tgt_ranges.size() * self.tgt.blocksize
712*9e94795aSAndroid Build Coastguard Worker          logger.info(
713*9e94795aSAndroid Build Coastguard Worker              "%10d %10d (%6.2f%%) %7s %s %s", tgt_size, tgt_size, 100.0,
714*9e94795aSAndroid Build Coastguard Worker              xf.style, xf.tgt_name, str(xf.tgt_ranges))
715*9e94795aSAndroid Build Coastguard Worker
716*9e94795aSAndroid Build Coastguard Worker        elif xf.style == "new":
717*9e94795aSAndroid Build Coastguard Worker          self.tgt.WriteRangeDataToFd(xf.tgt_ranges, new_f)
718*9e94795aSAndroid Build Coastguard Worker          tgt_size = xf.tgt_ranges.size() * self.tgt.blocksize
719*9e94795aSAndroid Build Coastguard Worker          logger.info(
720*9e94795aSAndroid Build Coastguard Worker              "%10d %10d (%6.2f%%) %7s %s %s", tgt_size, tgt_size, 100.0,
721*9e94795aSAndroid Build Coastguard Worker              xf.style, xf.tgt_name, str(xf.tgt_ranges))
722*9e94795aSAndroid Build Coastguard Worker
723*9e94795aSAndroid Build Coastguard Worker        elif xf.style == "diff":
724*9e94795aSAndroid Build Coastguard Worker          # We can't compare src and tgt directly because they may have
725*9e94795aSAndroid Build Coastguard Worker          # the same content but be broken up into blocks differently, eg:
726*9e94795aSAndroid Build Coastguard Worker          #
727*9e94795aSAndroid Build Coastguard Worker          #    ["he", "llo"]  vs  ["h", "ello"]
728*9e94795aSAndroid Build Coastguard Worker          #
729*9e94795aSAndroid Build Coastguard Worker          # We want those to compare equal, ideally without having to
730*9e94795aSAndroid Build Coastguard Worker          # actually concatenate the strings (these may be tens of
731*9e94795aSAndroid Build Coastguard Worker          # megabytes).
732*9e94795aSAndroid Build Coastguard Worker          if xf.src_sha1 == xf.tgt_sha1:
733*9e94795aSAndroid Build Coastguard Worker            # These are identical; we don't need to generate a patch,
734*9e94795aSAndroid Build Coastguard Worker            # just issue copy commands on the device.
735*9e94795aSAndroid Build Coastguard Worker            xf.style = "move"
736*9e94795aSAndroid Build Coastguard Worker            xf.patch_info = None
737*9e94795aSAndroid Build Coastguard Worker            tgt_size = xf.tgt_ranges.size() * self.tgt.blocksize
738*9e94795aSAndroid Build Coastguard Worker            if xf.src_ranges != xf.tgt_ranges:
739*9e94795aSAndroid Build Coastguard Worker              logger.info(
740*9e94795aSAndroid Build Coastguard Worker                  "%10d %10d (%6.2f%%) %7s %s %s (from %s)", tgt_size, tgt_size,
741*9e94795aSAndroid Build Coastguard Worker                  100.0, xf.style,
742*9e94795aSAndroid Build Coastguard Worker                  xf.tgt_name if xf.tgt_name == xf.src_name else (
743*9e94795aSAndroid Build Coastguard Worker                      xf.tgt_name + " (from " + xf.src_name + ")"),
744*9e94795aSAndroid Build Coastguard Worker                  str(xf.tgt_ranges), str(xf.src_ranges))
745*9e94795aSAndroid Build Coastguard Worker          else:
746*9e94795aSAndroid Build Coastguard Worker            if xf.patch_info:
747*9e94795aSAndroid Build Coastguard Worker              # We have already generated the patch (e.g. during split of large
748*9e94795aSAndroid Build Coastguard Worker              # APKs or reduction of stash size)
749*9e94795aSAndroid Build Coastguard Worker              imgdiff = xf.patch_info.imgdiff
750*9e94795aSAndroid Build Coastguard Worker            else:
751*9e94795aSAndroid Build Coastguard Worker              imgdiff = self.CanUseImgdiff(
752*9e94795aSAndroid Build Coastguard Worker                  xf.tgt_name, xf.tgt_ranges, xf.src_ranges)
753*9e94795aSAndroid Build Coastguard Worker            xf.style = "imgdiff" if imgdiff else "bsdiff"
754*9e94795aSAndroid Build Coastguard Worker            diff_queue.append((index, imgdiff, patch_num))
755*9e94795aSAndroid Build Coastguard Worker            patch_num += 1
756*9e94795aSAndroid Build Coastguard Worker
757*9e94795aSAndroid Build Coastguard Worker        else:
758*9e94795aSAndroid Build Coastguard Worker          assert False, "unknown style " + xf.style
759*9e94795aSAndroid Build Coastguard Worker
760*9e94795aSAndroid Build Coastguard Worker    patches = self.ComputePatchesForInputList(diff_queue, False)
761*9e94795aSAndroid Build Coastguard Worker
762*9e94795aSAndroid Build Coastguard Worker    offset = 0
763*9e94795aSAndroid Build Coastguard Worker    with open(prefix + ".patch.dat", "wb") as patch_fd:
764*9e94795aSAndroid Build Coastguard Worker      for index, patch_info, _ in patches:
765*9e94795aSAndroid Build Coastguard Worker        xf = self.transfers[index]
766*9e94795aSAndroid Build Coastguard Worker        xf.patch_len = len(patch_info.content)
767*9e94795aSAndroid Build Coastguard Worker        xf.patch_start = offset
768*9e94795aSAndroid Build Coastguard Worker        offset += xf.patch_len
769*9e94795aSAndroid Build Coastguard Worker        patch_fd.write(patch_info.content)
770*9e94795aSAndroid Build Coastguard Worker
771*9e94795aSAndroid Build Coastguard Worker        tgt_size = xf.tgt_ranges.size() * self.tgt.blocksize
772*9e94795aSAndroid Build Coastguard Worker        logger.info(
773*9e94795aSAndroid Build Coastguard Worker            "%10d %10d (%6.2f%%) %7s %s %s %s", xf.patch_len, tgt_size,
774*9e94795aSAndroid Build Coastguard Worker            xf.patch_len * 100.0 / tgt_size, xf.style,
775*9e94795aSAndroid Build Coastguard Worker            xf.tgt_name if xf.tgt_name == xf.src_name else (
776*9e94795aSAndroid Build Coastguard Worker                xf.tgt_name + " (from " + xf.src_name + ")"),
777*9e94795aSAndroid Build Coastguard Worker            xf.tgt_ranges, xf.src_ranges)
778*9e94795aSAndroid Build Coastguard Worker
779*9e94795aSAndroid Build Coastguard Worker  def AssertSha1Good(self):
780*9e94795aSAndroid Build Coastguard Worker    """Check the SHA-1 of the src & tgt blocks in the transfer list.
781*9e94795aSAndroid Build Coastguard Worker
782*9e94795aSAndroid Build Coastguard Worker    Double check the SHA-1 value to avoid the issue in b/71908713, where
783*9e94795aSAndroid Build Coastguard Worker    SparseImage.RangeSha1() messed up with the hash calculation in multi-thread
784*9e94795aSAndroid Build Coastguard Worker    environment. That specific problem has been fixed by protecting the
785*9e94795aSAndroid Build Coastguard Worker    underlying generator function 'SparseImage._GetRangeData()' with lock.
786*9e94795aSAndroid Build Coastguard Worker    """
787*9e94795aSAndroid Build Coastguard Worker    for xf in self.transfers:
788*9e94795aSAndroid Build Coastguard Worker      tgt_sha1 = self.tgt.RangeSha1(xf.tgt_ranges)
789*9e94795aSAndroid Build Coastguard Worker      assert xf.tgt_sha1 == tgt_sha1
790*9e94795aSAndroid Build Coastguard Worker      if xf.style == "diff":
791*9e94795aSAndroid Build Coastguard Worker        src_sha1 = self.src.RangeSha1(xf.src_ranges)
792*9e94795aSAndroid Build Coastguard Worker        assert xf.src_sha1 == src_sha1
793*9e94795aSAndroid Build Coastguard Worker
794*9e94795aSAndroid Build Coastguard Worker  def AssertSequenceGood(self):
795*9e94795aSAndroid Build Coastguard Worker    # Simulate the sequences of transfers we will output, and check that:
796*9e94795aSAndroid Build Coastguard Worker    # - we never read a block after writing it, and
797*9e94795aSAndroid Build Coastguard Worker    # - we write every block we care about exactly once.
798*9e94795aSAndroid Build Coastguard Worker
799*9e94795aSAndroid Build Coastguard Worker    # Start with no blocks having been touched yet.
800*9e94795aSAndroid Build Coastguard Worker    touched = array.array("B", b"\0" * self.tgt.total_blocks)
801*9e94795aSAndroid Build Coastguard Worker
802*9e94795aSAndroid Build Coastguard Worker    # Imagine processing the transfers in order.
803*9e94795aSAndroid Build Coastguard Worker    for xf in self.transfers:
804*9e94795aSAndroid Build Coastguard Worker      # Check that the input blocks for this transfer haven't yet been touched.
805*9e94795aSAndroid Build Coastguard Worker
806*9e94795aSAndroid Build Coastguard Worker      x = xf.src_ranges
807*9e94795aSAndroid Build Coastguard Worker      for _, sr in xf.use_stash:
808*9e94795aSAndroid Build Coastguard Worker        x = x.subtract(sr)
809*9e94795aSAndroid Build Coastguard Worker
810*9e94795aSAndroid Build Coastguard Worker      for s, e in x:
811*9e94795aSAndroid Build Coastguard Worker        # Source image could be larger. Don't check the blocks that are in the
812*9e94795aSAndroid Build Coastguard Worker        # source image only. Since they are not in 'touched', and won't ever
813*9e94795aSAndroid Build Coastguard Worker        # be touched.
814*9e94795aSAndroid Build Coastguard Worker        for i in range(s, min(e, self.tgt.total_blocks)):
815*9e94795aSAndroid Build Coastguard Worker          assert touched[i] == 0
816*9e94795aSAndroid Build Coastguard Worker
817*9e94795aSAndroid Build Coastguard Worker      # Check that the output blocks for this transfer haven't yet
818*9e94795aSAndroid Build Coastguard Worker      # been touched, and touch all the blocks written by this
819*9e94795aSAndroid Build Coastguard Worker      # transfer.
820*9e94795aSAndroid Build Coastguard Worker      for s, e in xf.tgt_ranges:
821*9e94795aSAndroid Build Coastguard Worker        for i in range(s, e):
822*9e94795aSAndroid Build Coastguard Worker          assert touched[i] == 0
823*9e94795aSAndroid Build Coastguard Worker          touched[i] = 1
824*9e94795aSAndroid Build Coastguard Worker
825*9e94795aSAndroid Build Coastguard Worker    # Check that we've written every target block.
826*9e94795aSAndroid Build Coastguard Worker    for s, e in self.tgt.care_map:
827*9e94795aSAndroid Build Coastguard Worker      for i in range(s, e):
828*9e94795aSAndroid Build Coastguard Worker        assert touched[i] == 1
829*9e94795aSAndroid Build Coastguard Worker
830*9e94795aSAndroid Build Coastguard Worker  def FindSequenceForTransfers(self):
831*9e94795aSAndroid Build Coastguard Worker    """Finds a sequence for the given transfers.
832*9e94795aSAndroid Build Coastguard Worker
833*9e94795aSAndroid Build Coastguard Worker     The goal is to minimize the violation of order dependencies between these
834*9e94795aSAndroid Build Coastguard Worker     transfers, so that fewer blocks are stashed when applying the update.
835*9e94795aSAndroid Build Coastguard Worker    """
836*9e94795aSAndroid Build Coastguard Worker
837*9e94795aSAndroid Build Coastguard Worker    # Clear the existing dependency between transfers
838*9e94795aSAndroid Build Coastguard Worker    for xf in self.transfers:
839*9e94795aSAndroid Build Coastguard Worker      xf.goes_before = OrderedDict()
840*9e94795aSAndroid Build Coastguard Worker      xf.goes_after = OrderedDict()
841*9e94795aSAndroid Build Coastguard Worker
842*9e94795aSAndroid Build Coastguard Worker      xf.stash_before = []
843*9e94795aSAndroid Build Coastguard Worker      xf.use_stash = []
844*9e94795aSAndroid Build Coastguard Worker
845*9e94795aSAndroid Build Coastguard Worker    # Find the ordering dependencies among transfers (this is O(n^2)
846*9e94795aSAndroid Build Coastguard Worker    # in the number of transfers).
847*9e94795aSAndroid Build Coastguard Worker    self.GenerateDigraph()
848*9e94795aSAndroid Build Coastguard Worker    # Find a sequence of transfers that satisfies as many ordering
849*9e94795aSAndroid Build Coastguard Worker    # dependencies as possible (heuristically).
850*9e94795aSAndroid Build Coastguard Worker    self.FindVertexSequence()
851*9e94795aSAndroid Build Coastguard Worker    # Fix up the ordering dependencies that the sequence didn't
852*9e94795aSAndroid Build Coastguard Worker    # satisfy.
853*9e94795aSAndroid Build Coastguard Worker    self.ReverseBackwardEdges()
854*9e94795aSAndroid Build Coastguard Worker    self.ImproveVertexSequence()
855*9e94795aSAndroid Build Coastguard Worker
856*9e94795aSAndroid Build Coastguard Worker  def ImproveVertexSequence(self):
857*9e94795aSAndroid Build Coastguard Worker    logger.info("Improving vertex order...")
858*9e94795aSAndroid Build Coastguard Worker
859*9e94795aSAndroid Build Coastguard Worker    # At this point our digraph is acyclic; we reversed any edges that
860*9e94795aSAndroid Build Coastguard Worker    # were backwards in the heuristically-generated sequence.  The
861*9e94795aSAndroid Build Coastguard Worker    # previously-generated order is still acceptable, but we hope to
862*9e94795aSAndroid Build Coastguard Worker    # find a better order that needs less memory for stashed data.
863*9e94795aSAndroid Build Coastguard Worker    # Now we do a topological sort to generate a new vertex order,
864*9e94795aSAndroid Build Coastguard Worker    # using a greedy algorithm to choose which vertex goes next
865*9e94795aSAndroid Build Coastguard Worker    # whenever we have a choice.
866*9e94795aSAndroid Build Coastguard Worker
867*9e94795aSAndroid Build Coastguard Worker    # Make a copy of the edge set; this copy will get destroyed by the
868*9e94795aSAndroid Build Coastguard Worker    # algorithm.
869*9e94795aSAndroid Build Coastguard Worker    for xf in self.transfers:
870*9e94795aSAndroid Build Coastguard Worker      xf.incoming = xf.goes_after.copy()
871*9e94795aSAndroid Build Coastguard Worker      xf.outgoing = xf.goes_before.copy()
872*9e94795aSAndroid Build Coastguard Worker
873*9e94795aSAndroid Build Coastguard Worker    L = []   # the new vertex order
874*9e94795aSAndroid Build Coastguard Worker
875*9e94795aSAndroid Build Coastguard Worker    # S is the set of sources in the remaining graph; we always choose
876*9e94795aSAndroid Build Coastguard Worker    # the one that leaves the least amount of stashed data after it's
877*9e94795aSAndroid Build Coastguard Worker    # executed.
878*9e94795aSAndroid Build Coastguard Worker    S = [(u.NetStashChange(), u.order, u) for u in self.transfers
879*9e94795aSAndroid Build Coastguard Worker         if not u.incoming]
880*9e94795aSAndroid Build Coastguard Worker    heapq.heapify(S)
881*9e94795aSAndroid Build Coastguard Worker
882*9e94795aSAndroid Build Coastguard Worker    while S:
883*9e94795aSAndroid Build Coastguard Worker      _, _, xf = heapq.heappop(S)
884*9e94795aSAndroid Build Coastguard Worker      L.append(xf)
885*9e94795aSAndroid Build Coastguard Worker      for u in xf.outgoing:
886*9e94795aSAndroid Build Coastguard Worker        del u.incoming[xf]
887*9e94795aSAndroid Build Coastguard Worker        if not u.incoming:
888*9e94795aSAndroid Build Coastguard Worker          heapq.heappush(S, (u.NetStashChange(), u.order, u))
889*9e94795aSAndroid Build Coastguard Worker
890*9e94795aSAndroid Build Coastguard Worker    # if this fails then our graph had a cycle.
891*9e94795aSAndroid Build Coastguard Worker    assert len(L) == len(self.transfers)
892*9e94795aSAndroid Build Coastguard Worker
893*9e94795aSAndroid Build Coastguard Worker    self.transfers = L
894*9e94795aSAndroid Build Coastguard Worker    for i, xf in enumerate(L):
895*9e94795aSAndroid Build Coastguard Worker      xf.order = i
896*9e94795aSAndroid Build Coastguard Worker
897*9e94795aSAndroid Build Coastguard Worker  def ReverseBackwardEdges(self):
898*9e94795aSAndroid Build Coastguard Worker    """Reverse unsatisfying edges and compute pairs of stashed blocks.
899*9e94795aSAndroid Build Coastguard Worker
900*9e94795aSAndroid Build Coastguard Worker    For each transfer, make sure it properly stashes the blocks it touches and
901*9e94795aSAndroid Build Coastguard Worker    will be used by later transfers. It uses pairs of (stash_raw_id, range) to
902*9e94795aSAndroid Build Coastguard Worker    record the blocks to be stashed. 'stash_raw_id' is an id that uniquely
903*9e94795aSAndroid Build Coastguard Worker    identifies each pair. Note that for the same range (e.g. RangeSet("1-5")),
904*9e94795aSAndroid Build Coastguard Worker    it is possible to have multiple pairs with different 'stash_raw_id's. Each
905*9e94795aSAndroid Build Coastguard Worker    'stash_raw_id' will be consumed by one transfer. In BBOTA v3+, identical
906*9e94795aSAndroid Build Coastguard Worker    blocks will be written to the same stash slot in WriteTransfers().
907*9e94795aSAndroid Build Coastguard Worker    """
908*9e94795aSAndroid Build Coastguard Worker
909*9e94795aSAndroid Build Coastguard Worker    logger.info("Reversing backward edges...")
910*9e94795aSAndroid Build Coastguard Worker    in_order = 0
911*9e94795aSAndroid Build Coastguard Worker    out_of_order = 0
912*9e94795aSAndroid Build Coastguard Worker    stash_raw_id = 0
913*9e94795aSAndroid Build Coastguard Worker    stash_size = 0
914*9e94795aSAndroid Build Coastguard Worker
915*9e94795aSAndroid Build Coastguard Worker    for xf in self.transfers:
916*9e94795aSAndroid Build Coastguard Worker      for u in xf.goes_before.copy():
917*9e94795aSAndroid Build Coastguard Worker        # xf should go before u
918*9e94795aSAndroid Build Coastguard Worker        if xf.order < u.order:
919*9e94795aSAndroid Build Coastguard Worker          # it does, hurray!
920*9e94795aSAndroid Build Coastguard Worker          in_order += 1
921*9e94795aSAndroid Build Coastguard Worker        else:
922*9e94795aSAndroid Build Coastguard Worker          # it doesn't, boo.  modify u to stash the blocks that it
923*9e94795aSAndroid Build Coastguard Worker          # writes that xf wants to read, and then require u to go
924*9e94795aSAndroid Build Coastguard Worker          # before xf.
925*9e94795aSAndroid Build Coastguard Worker          out_of_order += 1
926*9e94795aSAndroid Build Coastguard Worker
927*9e94795aSAndroid Build Coastguard Worker          overlap = xf.src_ranges.intersect(u.tgt_ranges)
928*9e94795aSAndroid Build Coastguard Worker          assert overlap
929*9e94795aSAndroid Build Coastguard Worker
930*9e94795aSAndroid Build Coastguard Worker          u.stash_before.append((stash_raw_id, overlap))
931*9e94795aSAndroid Build Coastguard Worker          xf.use_stash.append((stash_raw_id, overlap))
932*9e94795aSAndroid Build Coastguard Worker          stash_raw_id += 1
933*9e94795aSAndroid Build Coastguard Worker          stash_size += overlap.size()
934*9e94795aSAndroid Build Coastguard Worker
935*9e94795aSAndroid Build Coastguard Worker          # reverse the edge direction; now xf must go after u
936*9e94795aSAndroid Build Coastguard Worker          del xf.goes_before[u]
937*9e94795aSAndroid Build Coastguard Worker          del u.goes_after[xf]
938*9e94795aSAndroid Build Coastguard Worker          xf.goes_after[u] = None    # value doesn't matter
939*9e94795aSAndroid Build Coastguard Worker          u.goes_before[xf] = None
940*9e94795aSAndroid Build Coastguard Worker
941*9e94795aSAndroid Build Coastguard Worker    logger.info(
942*9e94795aSAndroid Build Coastguard Worker        "  %d/%d dependencies (%.2f%%) were violated; %d source blocks "
943*9e94795aSAndroid Build Coastguard Worker        "stashed.", out_of_order, in_order + out_of_order,
944*9e94795aSAndroid Build Coastguard Worker        (out_of_order * 100.0 / (in_order + out_of_order)) if (
945*9e94795aSAndroid Build Coastguard Worker            in_order + out_of_order) else 0.0,
946*9e94795aSAndroid Build Coastguard Worker        stash_size)
947*9e94795aSAndroid Build Coastguard Worker
948*9e94795aSAndroid Build Coastguard Worker  def FindVertexSequence(self):
949*9e94795aSAndroid Build Coastguard Worker    logger.info("Finding vertex sequence...")
950*9e94795aSAndroid Build Coastguard Worker
951*9e94795aSAndroid Build Coastguard Worker    # This is based on "A Fast & Effective Heuristic for the Feedback
952*9e94795aSAndroid Build Coastguard Worker    # Arc Set Problem" by P. Eades, X. Lin, and W.F. Smyth.  Think of
953*9e94795aSAndroid Build Coastguard Worker    # it as starting with the digraph G and moving all the vertices to
954*9e94795aSAndroid Build Coastguard Worker    # be on a horizontal line in some order, trying to minimize the
955*9e94795aSAndroid Build Coastguard Worker    # number of edges that end up pointing to the left.  Left-pointing
956*9e94795aSAndroid Build Coastguard Worker    # edges will get removed to turn the digraph into a DAG.  In this
957*9e94795aSAndroid Build Coastguard Worker    # case each edge has a weight which is the number of source blocks
958*9e94795aSAndroid Build Coastguard Worker    # we'll lose if that edge is removed; we try to minimize the total
959*9e94795aSAndroid Build Coastguard Worker    # weight rather than just the number of edges.
960*9e94795aSAndroid Build Coastguard Worker
961*9e94795aSAndroid Build Coastguard Worker    # Make a copy of the edge set; this copy will get destroyed by the
962*9e94795aSAndroid Build Coastguard Worker    # algorithm.
963*9e94795aSAndroid Build Coastguard Worker    for xf in self.transfers:
964*9e94795aSAndroid Build Coastguard Worker      xf.incoming = xf.goes_after.copy()
965*9e94795aSAndroid Build Coastguard Worker      xf.outgoing = xf.goes_before.copy()
966*9e94795aSAndroid Build Coastguard Worker      xf.score = sum(xf.outgoing.values()) - sum(xf.incoming.values())
967*9e94795aSAndroid Build Coastguard Worker
968*9e94795aSAndroid Build Coastguard Worker    # We use an OrderedDict instead of just a set so that the output
969*9e94795aSAndroid Build Coastguard Worker    # is repeatable; otherwise it would depend on the hash values of
970*9e94795aSAndroid Build Coastguard Worker    # the transfer objects.
971*9e94795aSAndroid Build Coastguard Worker    G = OrderedDict()
972*9e94795aSAndroid Build Coastguard Worker    for xf in self.transfers:
973*9e94795aSAndroid Build Coastguard Worker      G[xf] = None
974*9e94795aSAndroid Build Coastguard Worker    s1 = deque()  # the left side of the sequence, built from left to right
975*9e94795aSAndroid Build Coastguard Worker    s2 = deque()  # the right side of the sequence, built from right to left
976*9e94795aSAndroid Build Coastguard Worker
977*9e94795aSAndroid Build Coastguard Worker    heap = []
978*9e94795aSAndroid Build Coastguard Worker    for xf in self.transfers:
979*9e94795aSAndroid Build Coastguard Worker      xf.heap_item = HeapItem(xf)
980*9e94795aSAndroid Build Coastguard Worker      heap.append(xf.heap_item)
981*9e94795aSAndroid Build Coastguard Worker    heapq.heapify(heap)
982*9e94795aSAndroid Build Coastguard Worker
983*9e94795aSAndroid Build Coastguard Worker    # Use OrderedDict() instead of set() to preserve the insertion order. Need
984*9e94795aSAndroid Build Coastguard Worker    # to use 'sinks[key] = None' to add key into the set. sinks will look like
985*9e94795aSAndroid Build Coastguard Worker    # { key1: None, key2: None, ... }.
986*9e94795aSAndroid Build Coastguard Worker    sinks = OrderedDict.fromkeys(u for u in G if not u.outgoing)
987*9e94795aSAndroid Build Coastguard Worker    sources = OrderedDict.fromkeys(u for u in G if not u.incoming)
988*9e94795aSAndroid Build Coastguard Worker
989*9e94795aSAndroid Build Coastguard Worker    def adjust_score(iu, delta):
990*9e94795aSAndroid Build Coastguard Worker      iu.score += delta
991*9e94795aSAndroid Build Coastguard Worker      iu.heap_item.clear()
992*9e94795aSAndroid Build Coastguard Worker      iu.heap_item = HeapItem(iu)
993*9e94795aSAndroid Build Coastguard Worker      heapq.heappush(heap, iu.heap_item)
994*9e94795aSAndroid Build Coastguard Worker
995*9e94795aSAndroid Build Coastguard Worker    while G:
996*9e94795aSAndroid Build Coastguard Worker      # Put all sinks at the end of the sequence.
997*9e94795aSAndroid Build Coastguard Worker      while sinks:
998*9e94795aSAndroid Build Coastguard Worker        new_sinks = OrderedDict()
999*9e94795aSAndroid Build Coastguard Worker        for u in sinks:
1000*9e94795aSAndroid Build Coastguard Worker          if u not in G:
1001*9e94795aSAndroid Build Coastguard Worker            continue
1002*9e94795aSAndroid Build Coastguard Worker          s2.appendleft(u)
1003*9e94795aSAndroid Build Coastguard Worker          del G[u]
1004*9e94795aSAndroid Build Coastguard Worker          for iu in u.incoming:
1005*9e94795aSAndroid Build Coastguard Worker            adjust_score(iu, -iu.outgoing.pop(u))
1006*9e94795aSAndroid Build Coastguard Worker            if not iu.outgoing:
1007*9e94795aSAndroid Build Coastguard Worker              new_sinks[iu] = None
1008*9e94795aSAndroid Build Coastguard Worker        sinks = new_sinks
1009*9e94795aSAndroid Build Coastguard Worker
1010*9e94795aSAndroid Build Coastguard Worker      # Put all the sources at the beginning of the sequence.
1011*9e94795aSAndroid Build Coastguard Worker      while sources:
1012*9e94795aSAndroid Build Coastguard Worker        new_sources = OrderedDict()
1013*9e94795aSAndroid Build Coastguard Worker        for u in sources:
1014*9e94795aSAndroid Build Coastguard Worker          if u not in G:
1015*9e94795aSAndroid Build Coastguard Worker            continue
1016*9e94795aSAndroid Build Coastguard Worker          s1.append(u)
1017*9e94795aSAndroid Build Coastguard Worker          del G[u]
1018*9e94795aSAndroid Build Coastguard Worker          for iu in u.outgoing:
1019*9e94795aSAndroid Build Coastguard Worker            adjust_score(iu, +iu.incoming.pop(u))
1020*9e94795aSAndroid Build Coastguard Worker            if not iu.incoming:
1021*9e94795aSAndroid Build Coastguard Worker              new_sources[iu] = None
1022*9e94795aSAndroid Build Coastguard Worker        sources = new_sources
1023*9e94795aSAndroid Build Coastguard Worker
1024*9e94795aSAndroid Build Coastguard Worker      if not G:
1025*9e94795aSAndroid Build Coastguard Worker        break
1026*9e94795aSAndroid Build Coastguard Worker
1027*9e94795aSAndroid Build Coastguard Worker      # Find the "best" vertex to put next.  "Best" is the one that
1028*9e94795aSAndroid Build Coastguard Worker      # maximizes the net difference in source blocks saved we get by
1029*9e94795aSAndroid Build Coastguard Worker      # pretending it's a source rather than a sink.
1030*9e94795aSAndroid Build Coastguard Worker
1031*9e94795aSAndroid Build Coastguard Worker      while True:
1032*9e94795aSAndroid Build Coastguard Worker        u = heapq.heappop(heap)
1033*9e94795aSAndroid Build Coastguard Worker        if u and u.item in G:
1034*9e94795aSAndroid Build Coastguard Worker          u = u.item
1035*9e94795aSAndroid Build Coastguard Worker          break
1036*9e94795aSAndroid Build Coastguard Worker
1037*9e94795aSAndroid Build Coastguard Worker      s1.append(u)
1038*9e94795aSAndroid Build Coastguard Worker      del G[u]
1039*9e94795aSAndroid Build Coastguard Worker      for iu in u.outgoing:
1040*9e94795aSAndroid Build Coastguard Worker        adjust_score(iu, +iu.incoming.pop(u))
1041*9e94795aSAndroid Build Coastguard Worker        if not iu.incoming:
1042*9e94795aSAndroid Build Coastguard Worker          sources[iu] = None
1043*9e94795aSAndroid Build Coastguard Worker
1044*9e94795aSAndroid Build Coastguard Worker      for iu in u.incoming:
1045*9e94795aSAndroid Build Coastguard Worker        adjust_score(iu, -iu.outgoing.pop(u))
1046*9e94795aSAndroid Build Coastguard Worker        if not iu.outgoing:
1047*9e94795aSAndroid Build Coastguard Worker          sinks[iu] = None
1048*9e94795aSAndroid Build Coastguard Worker
1049*9e94795aSAndroid Build Coastguard Worker    # Now record the sequence in the 'order' field of each transfer,
1050*9e94795aSAndroid Build Coastguard Worker    # and by rearranging self.transfers to be in the chosen sequence.
1051*9e94795aSAndroid Build Coastguard Worker
1052*9e94795aSAndroid Build Coastguard Worker    new_transfers = []
1053*9e94795aSAndroid Build Coastguard Worker    for x in itertools.chain(s1, s2):
1054*9e94795aSAndroid Build Coastguard Worker      x.order = len(new_transfers)
1055*9e94795aSAndroid Build Coastguard Worker      new_transfers.append(x)
1056*9e94795aSAndroid Build Coastguard Worker      del x.incoming
1057*9e94795aSAndroid Build Coastguard Worker      del x.outgoing
1058*9e94795aSAndroid Build Coastguard Worker
1059*9e94795aSAndroid Build Coastguard Worker    self.transfers = new_transfers
1060*9e94795aSAndroid Build Coastguard Worker
1061*9e94795aSAndroid Build Coastguard Worker  def GenerateDigraph(self):
1062*9e94795aSAndroid Build Coastguard Worker    logger.info("Generating digraph...")
1063*9e94795aSAndroid Build Coastguard Worker
1064*9e94795aSAndroid Build Coastguard Worker    # Each item of source_ranges will be:
1065*9e94795aSAndroid Build Coastguard Worker    #   - None, if that block is not used as a source,
1066*9e94795aSAndroid Build Coastguard Worker    #   - an ordered set of transfers.
1067*9e94795aSAndroid Build Coastguard Worker    source_ranges = []
1068*9e94795aSAndroid Build Coastguard Worker    for b in self.transfers:
1069*9e94795aSAndroid Build Coastguard Worker      for s, e in b.src_ranges:
1070*9e94795aSAndroid Build Coastguard Worker        if e > len(source_ranges):
1071*9e94795aSAndroid Build Coastguard Worker          source_ranges.extend([None] * (e-len(source_ranges)))
1072*9e94795aSAndroid Build Coastguard Worker        for i in range(s, e):
1073*9e94795aSAndroid Build Coastguard Worker          if source_ranges[i] is None:
1074*9e94795aSAndroid Build Coastguard Worker            source_ranges[i] = OrderedDict.fromkeys([b])
1075*9e94795aSAndroid Build Coastguard Worker          else:
1076*9e94795aSAndroid Build Coastguard Worker            source_ranges[i][b] = None
1077*9e94795aSAndroid Build Coastguard Worker
1078*9e94795aSAndroid Build Coastguard Worker    for a in self.transfers:
1079*9e94795aSAndroid Build Coastguard Worker      intersections = OrderedDict()
1080*9e94795aSAndroid Build Coastguard Worker      for s, e in a.tgt_ranges:
1081*9e94795aSAndroid Build Coastguard Worker        for i in range(s, e):
1082*9e94795aSAndroid Build Coastguard Worker          if i >= len(source_ranges):
1083*9e94795aSAndroid Build Coastguard Worker            break
1084*9e94795aSAndroid Build Coastguard Worker          # Add all the Transfers in source_ranges[i] to the (ordered) set.
1085*9e94795aSAndroid Build Coastguard Worker          if source_ranges[i] is not None:
1086*9e94795aSAndroid Build Coastguard Worker            for j in source_ranges[i]:
1087*9e94795aSAndroid Build Coastguard Worker              intersections[j] = None
1088*9e94795aSAndroid Build Coastguard Worker
1089*9e94795aSAndroid Build Coastguard Worker      for b in intersections:
1090*9e94795aSAndroid Build Coastguard Worker        if a is b:
1091*9e94795aSAndroid Build Coastguard Worker          continue
1092*9e94795aSAndroid Build Coastguard Worker
1093*9e94795aSAndroid Build Coastguard Worker        # If the blocks written by A are read by B, then B needs to go before A.
1094*9e94795aSAndroid Build Coastguard Worker        i = a.tgt_ranges.intersect(b.src_ranges)
1095*9e94795aSAndroid Build Coastguard Worker        if i:
1096*9e94795aSAndroid Build Coastguard Worker          if b.src_name == "__ZERO":
1097*9e94795aSAndroid Build Coastguard Worker            # the cost of removing source blocks for the __ZERO domain
1098*9e94795aSAndroid Build Coastguard Worker            # is (nearly) zero.
1099*9e94795aSAndroid Build Coastguard Worker            size = 0
1100*9e94795aSAndroid Build Coastguard Worker          else:
1101*9e94795aSAndroid Build Coastguard Worker            size = i.size()
1102*9e94795aSAndroid Build Coastguard Worker          b.goes_before[a] = size
1103*9e94795aSAndroid Build Coastguard Worker          a.goes_after[b] = size
1104*9e94795aSAndroid Build Coastguard Worker
1105*9e94795aSAndroid Build Coastguard Worker  def ComputePatchesForInputList(self, diff_queue, compress_target):
1106*9e94795aSAndroid Build Coastguard Worker    """Returns a list of patch information for the input list of transfers.
1107*9e94795aSAndroid Build Coastguard Worker
1108*9e94795aSAndroid Build Coastguard Worker      Args:
1109*9e94795aSAndroid Build Coastguard Worker        diff_queue: a list of transfers with style 'diff'
1110*9e94795aSAndroid Build Coastguard Worker        compress_target: If True, compresses the target ranges of each
1111*9e94795aSAndroid Build Coastguard Worker            transfers; and save the size.
1112*9e94795aSAndroid Build Coastguard Worker
1113*9e94795aSAndroid Build Coastguard Worker      Returns:
1114*9e94795aSAndroid Build Coastguard Worker        A list of (transfer order, patch_info, compressed_size) tuples.
1115*9e94795aSAndroid Build Coastguard Worker    """
1116*9e94795aSAndroid Build Coastguard Worker
1117*9e94795aSAndroid Build Coastguard Worker    if not diff_queue:
1118*9e94795aSAndroid Build Coastguard Worker      return []
1119*9e94795aSAndroid Build Coastguard Worker
1120*9e94795aSAndroid Build Coastguard Worker    if self.threads > 1:
1121*9e94795aSAndroid Build Coastguard Worker      logger.info("Computing patches (using %d threads)...", self.threads)
1122*9e94795aSAndroid Build Coastguard Worker    else:
1123*9e94795aSAndroid Build Coastguard Worker      logger.info("Computing patches...")
1124*9e94795aSAndroid Build Coastguard Worker
1125*9e94795aSAndroid Build Coastguard Worker    diff_total = len(diff_queue)
1126*9e94795aSAndroid Build Coastguard Worker    patches = [None] * diff_total
1127*9e94795aSAndroid Build Coastguard Worker    error_messages = []
1128*9e94795aSAndroid Build Coastguard Worker
1129*9e94795aSAndroid Build Coastguard Worker    # Using multiprocessing doesn't give additional benefits, due to the
1130*9e94795aSAndroid Build Coastguard Worker    # pattern of the code. The diffing work is done by subprocess.call, which
1131*9e94795aSAndroid Build Coastguard Worker    # already runs in a separate process (not affected much by the GIL -
1132*9e94795aSAndroid Build Coastguard Worker    # Global Interpreter Lock). Using multiprocess also requires either a)
1133*9e94795aSAndroid Build Coastguard Worker    # writing the diff input files in the main process before forking, or b)
1134*9e94795aSAndroid Build Coastguard Worker    # reopening the image file (SparseImage) in the worker processes. Doing
1135*9e94795aSAndroid Build Coastguard Worker    # neither of them further improves the performance.
1136*9e94795aSAndroid Build Coastguard Worker    lock = threading.Lock()
1137*9e94795aSAndroid Build Coastguard Worker
1138*9e94795aSAndroid Build Coastguard Worker    def diff_worker():
1139*9e94795aSAndroid Build Coastguard Worker      while True:
1140*9e94795aSAndroid Build Coastguard Worker        with lock:
1141*9e94795aSAndroid Build Coastguard Worker          if not diff_queue:
1142*9e94795aSAndroid Build Coastguard Worker            return
1143*9e94795aSAndroid Build Coastguard Worker          xf_index, imgdiff, patch_index = diff_queue.pop()
1144*9e94795aSAndroid Build Coastguard Worker          xf = self.transfers[xf_index]
1145*9e94795aSAndroid Build Coastguard Worker
1146*9e94795aSAndroid Build Coastguard Worker        message = []
1147*9e94795aSAndroid Build Coastguard Worker        compressed_size = None
1148*9e94795aSAndroid Build Coastguard Worker
1149*9e94795aSAndroid Build Coastguard Worker        patch_info = xf.patch_info
1150*9e94795aSAndroid Build Coastguard Worker        if not patch_info:
1151*9e94795aSAndroid Build Coastguard Worker          src_file = common.MakeTempFile(prefix="src-")
1152*9e94795aSAndroid Build Coastguard Worker          with open(src_file, "wb") as fd:
1153*9e94795aSAndroid Build Coastguard Worker            self.src.WriteRangeDataToFd(xf.src_ranges, fd)
1154*9e94795aSAndroid Build Coastguard Worker
1155*9e94795aSAndroid Build Coastguard Worker          tgt_file = common.MakeTempFile(prefix="tgt-")
1156*9e94795aSAndroid Build Coastguard Worker          with open(tgt_file, "wb") as fd:
1157*9e94795aSAndroid Build Coastguard Worker            self.tgt.WriteRangeDataToFd(xf.tgt_ranges, fd)
1158*9e94795aSAndroid Build Coastguard Worker
1159*9e94795aSAndroid Build Coastguard Worker          try:
1160*9e94795aSAndroid Build Coastguard Worker            patch_info = compute_patch(src_file, tgt_file, imgdiff)
1161*9e94795aSAndroid Build Coastguard Worker          except ValueError as e:
1162*9e94795aSAndroid Build Coastguard Worker            message.append(
1163*9e94795aSAndroid Build Coastguard Worker                "Failed to generate %s for %s: tgt=%s, src=%s:\n%s" % (
1164*9e94795aSAndroid Build Coastguard Worker                    "imgdiff" if imgdiff else "bsdiff",
1165*9e94795aSAndroid Build Coastguard Worker                    xf.tgt_name if xf.tgt_name == xf.src_name else
1166*9e94795aSAndroid Build Coastguard Worker                    xf.tgt_name + " (from " + xf.src_name + ")",
1167*9e94795aSAndroid Build Coastguard Worker                    xf.tgt_ranges, xf.src_ranges, e.message))
1168*9e94795aSAndroid Build Coastguard Worker
1169*9e94795aSAndroid Build Coastguard Worker        if compress_target:
1170*9e94795aSAndroid Build Coastguard Worker          tgt_data = self.tgt.ReadRangeSet(xf.tgt_ranges)
1171*9e94795aSAndroid Build Coastguard Worker          try:
1172*9e94795aSAndroid Build Coastguard Worker            # Compresses with the default level
1173*9e94795aSAndroid Build Coastguard Worker            compress_obj = zlib.compressobj(6, zlib.DEFLATED, -zlib.MAX_WBITS)
1174*9e94795aSAndroid Build Coastguard Worker            compressed_data = (compress_obj.compress(b"".join(tgt_data))
1175*9e94795aSAndroid Build Coastguard Worker                               + compress_obj.flush())
1176*9e94795aSAndroid Build Coastguard Worker            compressed_size = len(compressed_data)
1177*9e94795aSAndroid Build Coastguard Worker          except zlib.error as e:
1178*9e94795aSAndroid Build Coastguard Worker            message.append(
1179*9e94795aSAndroid Build Coastguard Worker                "Failed to compress the data in target range {} for {}:\n"
1180*9e94795aSAndroid Build Coastguard Worker                "{}".format(xf.tgt_ranges, xf.tgt_name, e.message))
1181*9e94795aSAndroid Build Coastguard Worker
1182*9e94795aSAndroid Build Coastguard Worker        if message:
1183*9e94795aSAndroid Build Coastguard Worker          with lock:
1184*9e94795aSAndroid Build Coastguard Worker            error_messages.extend(message)
1185*9e94795aSAndroid Build Coastguard Worker
1186*9e94795aSAndroid Build Coastguard Worker        with lock:
1187*9e94795aSAndroid Build Coastguard Worker          patches[patch_index] = (xf_index, patch_info, compressed_size)
1188*9e94795aSAndroid Build Coastguard Worker
1189*9e94795aSAndroid Build Coastguard Worker    threads = [threading.Thread(target=diff_worker)
1190*9e94795aSAndroid Build Coastguard Worker               for _ in range(self.threads)]
1191*9e94795aSAndroid Build Coastguard Worker    for th in threads:
1192*9e94795aSAndroid Build Coastguard Worker      th.start()
1193*9e94795aSAndroid Build Coastguard Worker    while threads:
1194*9e94795aSAndroid Build Coastguard Worker      threads.pop().join()
1195*9e94795aSAndroid Build Coastguard Worker
1196*9e94795aSAndroid Build Coastguard Worker    if error_messages:
1197*9e94795aSAndroid Build Coastguard Worker      logger.error('ERROR:')
1198*9e94795aSAndroid Build Coastguard Worker      logger.error('\n'.join(error_messages))
1199*9e94795aSAndroid Build Coastguard Worker      logger.error('\n\n\n')
1200*9e94795aSAndroid Build Coastguard Worker      sys.exit(1)
1201*9e94795aSAndroid Build Coastguard Worker
1202*9e94795aSAndroid Build Coastguard Worker    return patches
1203*9e94795aSAndroid Build Coastguard Worker
1204*9e94795aSAndroid Build Coastguard Worker  def SelectAndConvertDiffTransfersToNew(self, violated_stash_blocks):
1205*9e94795aSAndroid Build Coastguard Worker    """Converts the diff transfers to reduce the max simultaneous stash.
1206*9e94795aSAndroid Build Coastguard Worker
1207*9e94795aSAndroid Build Coastguard Worker    Since the 'new' data is compressed with deflate, we can select the 'diff'
1208*9e94795aSAndroid Build Coastguard Worker    transfers for conversion by comparing its patch size with the size of the
1209*9e94795aSAndroid Build Coastguard Worker    compressed data. Ideally, we want to convert the transfers with a small
1210*9e94795aSAndroid Build Coastguard Worker    size increase, but using a large number of stashed blocks.
1211*9e94795aSAndroid Build Coastguard Worker    """
1212*9e94795aSAndroid Build Coastguard Worker    TransferSizeScore = namedtuple("TransferSizeScore",
1213*9e94795aSAndroid Build Coastguard Worker                                   "xf, used_stash_blocks, score")
1214*9e94795aSAndroid Build Coastguard Worker
1215*9e94795aSAndroid Build Coastguard Worker    logger.info("Selecting diff commands to convert to new.")
1216*9e94795aSAndroid Build Coastguard Worker    diff_queue = []
1217*9e94795aSAndroid Build Coastguard Worker    for xf in self.transfers:
1218*9e94795aSAndroid Build Coastguard Worker      if xf.style == "diff" and xf.src_sha1 != xf.tgt_sha1:
1219*9e94795aSAndroid Build Coastguard Worker        use_imgdiff = self.CanUseImgdiff(xf.tgt_name, xf.tgt_ranges,
1220*9e94795aSAndroid Build Coastguard Worker                                         xf.src_ranges)
1221*9e94795aSAndroid Build Coastguard Worker        diff_queue.append((xf.order, use_imgdiff, len(diff_queue)))
1222*9e94795aSAndroid Build Coastguard Worker
1223*9e94795aSAndroid Build Coastguard Worker    # Remove the 'move' transfers, and compute the patch & compressed size
1224*9e94795aSAndroid Build Coastguard Worker    # for the remaining.
1225*9e94795aSAndroid Build Coastguard Worker    result = self.ComputePatchesForInputList(diff_queue, True)
1226*9e94795aSAndroid Build Coastguard Worker
1227*9e94795aSAndroid Build Coastguard Worker    conversion_candidates = []
1228*9e94795aSAndroid Build Coastguard Worker    for xf_index, patch_info, compressed_size in result:
1229*9e94795aSAndroid Build Coastguard Worker      xf = self.transfers[xf_index]
1230*9e94795aSAndroid Build Coastguard Worker      if not xf.patch_info:
1231*9e94795aSAndroid Build Coastguard Worker        xf.patch_info = patch_info
1232*9e94795aSAndroid Build Coastguard Worker
1233*9e94795aSAndroid Build Coastguard Worker      size_ratio = len(xf.patch_info.content) * 100.0 / compressed_size
1234*9e94795aSAndroid Build Coastguard Worker      diff_style = "imgdiff" if xf.patch_info.imgdiff else "bsdiff"
1235*9e94795aSAndroid Build Coastguard Worker      logger.info("%s, target size: %d blocks, style: %s, patch size: %d,"
1236*9e94795aSAndroid Build Coastguard Worker                  " compression_size: %d, ratio %.2f%%", xf.tgt_name,
1237*9e94795aSAndroid Build Coastguard Worker                  xf.tgt_ranges.size(), diff_style,
1238*9e94795aSAndroid Build Coastguard Worker                  len(xf.patch_info.content), compressed_size, size_ratio)
1239*9e94795aSAndroid Build Coastguard Worker
1240*9e94795aSAndroid Build Coastguard Worker      used_stash_blocks = sum(sr.size() for _, sr in xf.use_stash)
1241*9e94795aSAndroid Build Coastguard Worker      # Convert the transfer to new if the compressed size is smaller or equal.
1242*9e94795aSAndroid Build Coastguard Worker      # We don't need to maintain the stash_before lists here because the
1243*9e94795aSAndroid Build Coastguard Worker      # graph will be regenerated later.
1244*9e94795aSAndroid Build Coastguard Worker      if len(xf.patch_info.content) >= compressed_size:
1245*9e94795aSAndroid Build Coastguard Worker        # Add the transfer to the candidate list with negative score. And it
1246*9e94795aSAndroid Build Coastguard Worker        # will be converted later.
1247*9e94795aSAndroid Build Coastguard Worker        conversion_candidates.append(TransferSizeScore(xf, used_stash_blocks,
1248*9e94795aSAndroid Build Coastguard Worker                                                       -1))
1249*9e94795aSAndroid Build Coastguard Worker      elif used_stash_blocks > 0:
1250*9e94795aSAndroid Build Coastguard Worker        # This heuristic represents the size increase in the final package to
1251*9e94795aSAndroid Build Coastguard Worker        # remove per unit of stashed data.
1252*9e94795aSAndroid Build Coastguard Worker        score = ((compressed_size - len(xf.patch_info.content)) * 100.0
1253*9e94795aSAndroid Build Coastguard Worker                 / used_stash_blocks)
1254*9e94795aSAndroid Build Coastguard Worker        conversion_candidates.append(TransferSizeScore(xf, used_stash_blocks,
1255*9e94795aSAndroid Build Coastguard Worker                                                       score))
1256*9e94795aSAndroid Build Coastguard Worker    # Transfers with lower score (i.e. less expensive to convert) will be
1257*9e94795aSAndroid Build Coastguard Worker    # converted first.
1258*9e94795aSAndroid Build Coastguard Worker    conversion_candidates.sort(key=lambda x: x.score)
1259*9e94795aSAndroid Build Coastguard Worker
1260*9e94795aSAndroid Build Coastguard Worker    # TODO(xunchang), improve the logic to find the transfers to convert, e.g.
1261*9e94795aSAndroid Build Coastguard Worker    # convert the ones that contribute to the max stash, run ReviseStashSize
1262*9e94795aSAndroid Build Coastguard Worker    # multiple times etc.
1263*9e94795aSAndroid Build Coastguard Worker    removed_stashed_blocks = 0
1264*9e94795aSAndroid Build Coastguard Worker    for xf, used_stash_blocks, _ in conversion_candidates:
1265*9e94795aSAndroid Build Coastguard Worker      logger.info("Converting %s to new", xf.tgt_name)
1266*9e94795aSAndroid Build Coastguard Worker      xf.ConvertToNew()
1267*9e94795aSAndroid Build Coastguard Worker      removed_stashed_blocks += used_stash_blocks
1268*9e94795aSAndroid Build Coastguard Worker      # Experiments show that we will get a smaller package size if we remove
1269*9e94795aSAndroid Build Coastguard Worker      # slightly more stashed blocks than the violated stash blocks.
1270*9e94795aSAndroid Build Coastguard Worker      if removed_stashed_blocks >= violated_stash_blocks:
1271*9e94795aSAndroid Build Coastguard Worker        break
1272*9e94795aSAndroid Build Coastguard Worker
1273*9e94795aSAndroid Build Coastguard Worker    logger.info("Removed %d stashed blocks", removed_stashed_blocks)
1274*9e94795aSAndroid Build Coastguard Worker
1275*9e94795aSAndroid Build Coastguard Worker  def FindTransfers(self):
1276*9e94795aSAndroid Build Coastguard Worker    """Parse the file_map to generate all the transfers."""
1277*9e94795aSAndroid Build Coastguard Worker
1278*9e94795aSAndroid Build Coastguard Worker    def AddSplitTransfersWithFixedSizeChunks(tgt_name, src_name, tgt_ranges,
1279*9e94795aSAndroid Build Coastguard Worker                                             src_ranges, style, by_id):
1280*9e94795aSAndroid Build Coastguard Worker      """Add one or multiple Transfer()s by splitting large files.
1281*9e94795aSAndroid Build Coastguard Worker
1282*9e94795aSAndroid Build Coastguard Worker      For BBOTA v3, we need to stash source blocks for resumable feature.
1283*9e94795aSAndroid Build Coastguard Worker      However, with the growth of file size and the shrink of the cache
1284*9e94795aSAndroid Build Coastguard Worker      partition source blocks are too large to be stashed. If a file occupies
1285*9e94795aSAndroid Build Coastguard Worker      too many blocks, we split it into smaller pieces by getting multiple
1286*9e94795aSAndroid Build Coastguard Worker      Transfer()s.
1287*9e94795aSAndroid Build Coastguard Worker
1288*9e94795aSAndroid Build Coastguard Worker      The downside is that after splitting, we may increase the package size
1289*9e94795aSAndroid Build Coastguard Worker      since the split pieces don't align well. According to our experiments,
1290*9e94795aSAndroid Build Coastguard Worker      1/8 of the cache size as the per-piece limit appears to be optimal.
1291*9e94795aSAndroid Build Coastguard Worker      Compared to the fixed 1024-block limit, it reduces the overall package
1292*9e94795aSAndroid Build Coastguard Worker      size by 30% for volantis, and 20% for angler and bullhead."""
1293*9e94795aSAndroid Build Coastguard Worker
1294*9e94795aSAndroid Build Coastguard Worker      pieces = 0
1295*9e94795aSAndroid Build Coastguard Worker      while (tgt_ranges.size() > max_blocks_per_transfer and
1296*9e94795aSAndroid Build Coastguard Worker             src_ranges.size() > max_blocks_per_transfer):
1297*9e94795aSAndroid Build Coastguard Worker        tgt_split_name = "%s-%d" % (tgt_name, pieces)
1298*9e94795aSAndroid Build Coastguard Worker        src_split_name = "%s-%d" % (src_name, pieces)
1299*9e94795aSAndroid Build Coastguard Worker        tgt_first = tgt_ranges.first(max_blocks_per_transfer)
1300*9e94795aSAndroid Build Coastguard Worker        src_first = src_ranges.first(max_blocks_per_transfer)
1301*9e94795aSAndroid Build Coastguard Worker
1302*9e94795aSAndroid Build Coastguard Worker        Transfer(tgt_split_name, src_split_name, tgt_first, src_first,
1303*9e94795aSAndroid Build Coastguard Worker                 self.tgt.RangeSha1(tgt_first), self.src.RangeSha1(src_first),
1304*9e94795aSAndroid Build Coastguard Worker                 style, by_id)
1305*9e94795aSAndroid Build Coastguard Worker
1306*9e94795aSAndroid Build Coastguard Worker        tgt_ranges = tgt_ranges.subtract(tgt_first)
1307*9e94795aSAndroid Build Coastguard Worker        src_ranges = src_ranges.subtract(src_first)
1308*9e94795aSAndroid Build Coastguard Worker        pieces += 1
1309*9e94795aSAndroid Build Coastguard Worker
1310*9e94795aSAndroid Build Coastguard Worker      # Handle remaining blocks.
1311*9e94795aSAndroid Build Coastguard Worker      if tgt_ranges.size() or src_ranges.size():
1312*9e94795aSAndroid Build Coastguard Worker        # Must be both non-empty.
1313*9e94795aSAndroid Build Coastguard Worker        assert tgt_ranges.size() and src_ranges.size()
1314*9e94795aSAndroid Build Coastguard Worker        tgt_split_name = "%s-%d" % (tgt_name, pieces)
1315*9e94795aSAndroid Build Coastguard Worker        src_split_name = "%s-%d" % (src_name, pieces)
1316*9e94795aSAndroid Build Coastguard Worker        Transfer(tgt_split_name, src_split_name, tgt_ranges, src_ranges,
1317*9e94795aSAndroid Build Coastguard Worker                 self.tgt.RangeSha1(tgt_ranges), self.src.RangeSha1(src_ranges),
1318*9e94795aSAndroid Build Coastguard Worker                 style, by_id)
1319*9e94795aSAndroid Build Coastguard Worker
1320*9e94795aSAndroid Build Coastguard Worker    def AddSplitTransfers(tgt_name, src_name, tgt_ranges, src_ranges, style,
1321*9e94795aSAndroid Build Coastguard Worker                          by_id):
1322*9e94795aSAndroid Build Coastguard Worker      """Find all the zip files and split the others with a fixed chunk size.
1323*9e94795aSAndroid Build Coastguard Worker
1324*9e94795aSAndroid Build Coastguard Worker      This function will construct a list of zip archives, which will later be
1325*9e94795aSAndroid Build Coastguard Worker      split by imgdiff to reduce the final patch size. For the other files,
1326*9e94795aSAndroid Build Coastguard Worker      we will plainly split them based on a fixed chunk size with the potential
1327*9e94795aSAndroid Build Coastguard Worker      patch size penalty.
1328*9e94795aSAndroid Build Coastguard Worker      """
1329*9e94795aSAndroid Build Coastguard Worker
1330*9e94795aSAndroid Build Coastguard Worker      assert style == "diff"
1331*9e94795aSAndroid Build Coastguard Worker
1332*9e94795aSAndroid Build Coastguard Worker      # Change nothing for small files.
1333*9e94795aSAndroid Build Coastguard Worker      if (tgt_ranges.size() <= max_blocks_per_transfer and
1334*9e94795aSAndroid Build Coastguard Worker          src_ranges.size() <= max_blocks_per_transfer):
1335*9e94795aSAndroid Build Coastguard Worker        Transfer(tgt_name, src_name, tgt_ranges, src_ranges,
1336*9e94795aSAndroid Build Coastguard Worker                 self.tgt.RangeSha1(tgt_ranges), self.src.RangeSha1(src_ranges),
1337*9e94795aSAndroid Build Coastguard Worker                 style, by_id)
1338*9e94795aSAndroid Build Coastguard Worker        return
1339*9e94795aSAndroid Build Coastguard Worker
1340*9e94795aSAndroid Build Coastguard Worker      # Split large APKs with imgdiff, if possible. We're intentionally checking
1341*9e94795aSAndroid Build Coastguard Worker      # file types one more time (CanUseImgdiff() checks that as well), before
1342*9e94795aSAndroid Build Coastguard Worker      # calling the costly RangeSha1()s.
1343*9e94795aSAndroid Build Coastguard Worker      if (self.FileTypeSupportedByImgdiff(tgt_name) and
1344*9e94795aSAndroid Build Coastguard Worker          self.tgt.RangeSha1(tgt_ranges) != self.src.RangeSha1(src_ranges)):
1345*9e94795aSAndroid Build Coastguard Worker        if self.CanUseImgdiff(tgt_name, tgt_ranges, src_ranges, True):
1346*9e94795aSAndroid Build Coastguard Worker          large_apks.append((tgt_name, src_name, tgt_ranges, src_ranges))
1347*9e94795aSAndroid Build Coastguard Worker          return
1348*9e94795aSAndroid Build Coastguard Worker
1349*9e94795aSAndroid Build Coastguard Worker      AddSplitTransfersWithFixedSizeChunks(tgt_name, src_name, tgt_ranges,
1350*9e94795aSAndroid Build Coastguard Worker                                           src_ranges, style, by_id)
1351*9e94795aSAndroid Build Coastguard Worker
1352*9e94795aSAndroid Build Coastguard Worker    def AddTransfer(tgt_name, src_name, tgt_ranges, src_ranges, style, by_id,
1353*9e94795aSAndroid Build Coastguard Worker                    split=False):
1354*9e94795aSAndroid Build Coastguard Worker      """Wrapper function for adding a Transfer()."""
1355*9e94795aSAndroid Build Coastguard Worker
1356*9e94795aSAndroid Build Coastguard Worker      # We specialize diff transfers only (which covers bsdiff/imgdiff/move);
1357*9e94795aSAndroid Build Coastguard Worker      # otherwise add the Transfer() as is.
1358*9e94795aSAndroid Build Coastguard Worker      if style != "diff" or not split:
1359*9e94795aSAndroid Build Coastguard Worker        Transfer(tgt_name, src_name, tgt_ranges, src_ranges,
1360*9e94795aSAndroid Build Coastguard Worker                 self.tgt.RangeSha1(tgt_ranges), self.src.RangeSha1(src_ranges),
1361*9e94795aSAndroid Build Coastguard Worker                 style, by_id)
1362*9e94795aSAndroid Build Coastguard Worker        return
1363*9e94795aSAndroid Build Coastguard Worker
1364*9e94795aSAndroid Build Coastguard Worker      # Handle .odex files specially to analyze the block-wise difference. If
1365*9e94795aSAndroid Build Coastguard Worker      # most of the blocks are identical with only few changes (e.g. header),
1366*9e94795aSAndroid Build Coastguard Worker      # we will patch the changed blocks only. This avoids stashing unchanged
1367*9e94795aSAndroid Build Coastguard Worker      # blocks while patching. We limit the analysis to files without size
1368*9e94795aSAndroid Build Coastguard Worker      # changes only. This is to avoid sacrificing the OTA generation cost too
1369*9e94795aSAndroid Build Coastguard Worker      # much.
1370*9e94795aSAndroid Build Coastguard Worker      if (tgt_name.split(".")[-1].lower() == 'odex' and
1371*9e94795aSAndroid Build Coastguard Worker          tgt_ranges.size() == src_ranges.size()):
1372*9e94795aSAndroid Build Coastguard Worker
1373*9e94795aSAndroid Build Coastguard Worker        # 0.5 threshold can be further tuned. The tradeoff is: if only very
1374*9e94795aSAndroid Build Coastguard Worker        # few blocks remain identical, we lose the opportunity to use imgdiff
1375*9e94795aSAndroid Build Coastguard Worker        # that may have better compression ratio than bsdiff.
1376*9e94795aSAndroid Build Coastguard Worker        crop_threshold = 0.5
1377*9e94795aSAndroid Build Coastguard Worker
1378*9e94795aSAndroid Build Coastguard Worker        tgt_skipped = RangeSet()
1379*9e94795aSAndroid Build Coastguard Worker        src_skipped = RangeSet()
1380*9e94795aSAndroid Build Coastguard Worker        tgt_size = tgt_ranges.size()
1381*9e94795aSAndroid Build Coastguard Worker        tgt_changed = 0
1382*9e94795aSAndroid Build Coastguard Worker        for src_block, tgt_block in zip(src_ranges.next_item(),
1383*9e94795aSAndroid Build Coastguard Worker                                        tgt_ranges.next_item()):
1384*9e94795aSAndroid Build Coastguard Worker          src_rs = RangeSet(str(src_block))
1385*9e94795aSAndroid Build Coastguard Worker          tgt_rs = RangeSet(str(tgt_block))
1386*9e94795aSAndroid Build Coastguard Worker          if self.src.ReadRangeSet(src_rs) == self.tgt.ReadRangeSet(tgt_rs):
1387*9e94795aSAndroid Build Coastguard Worker            tgt_skipped = tgt_skipped.union(tgt_rs)
1388*9e94795aSAndroid Build Coastguard Worker            src_skipped = src_skipped.union(src_rs)
1389*9e94795aSAndroid Build Coastguard Worker          else:
1390*9e94795aSAndroid Build Coastguard Worker            tgt_changed += tgt_rs.size()
1391*9e94795aSAndroid Build Coastguard Worker
1392*9e94795aSAndroid Build Coastguard Worker          # Terminate early if no clear sign of benefits.
1393*9e94795aSAndroid Build Coastguard Worker          if tgt_changed > tgt_size * crop_threshold:
1394*9e94795aSAndroid Build Coastguard Worker            break
1395*9e94795aSAndroid Build Coastguard Worker
1396*9e94795aSAndroid Build Coastguard Worker        if tgt_changed < tgt_size * crop_threshold:
1397*9e94795aSAndroid Build Coastguard Worker          assert tgt_changed + tgt_skipped.size() == tgt_size
1398*9e94795aSAndroid Build Coastguard Worker          logger.info(
1399*9e94795aSAndroid Build Coastguard Worker              '%10d %10d (%6.2f%%) %s', tgt_skipped.size(), tgt_size,
1400*9e94795aSAndroid Build Coastguard Worker              tgt_skipped.size() * 100.0 / tgt_size, tgt_name)
1401*9e94795aSAndroid Build Coastguard Worker          AddSplitTransfers(
1402*9e94795aSAndroid Build Coastguard Worker              "%s-skipped" % (tgt_name,),
1403*9e94795aSAndroid Build Coastguard Worker              "%s-skipped" % (src_name,),
1404*9e94795aSAndroid Build Coastguard Worker              tgt_skipped, src_skipped, style, by_id)
1405*9e94795aSAndroid Build Coastguard Worker
1406*9e94795aSAndroid Build Coastguard Worker          # Intentionally change the file extension to avoid being imgdiff'd as
1407*9e94795aSAndroid Build Coastguard Worker          # the files are no longer in their original format.
1408*9e94795aSAndroid Build Coastguard Worker          tgt_name = "%s-cropped" % (tgt_name,)
1409*9e94795aSAndroid Build Coastguard Worker          src_name = "%s-cropped" % (src_name,)
1410*9e94795aSAndroid Build Coastguard Worker          tgt_ranges = tgt_ranges.subtract(tgt_skipped)
1411*9e94795aSAndroid Build Coastguard Worker          src_ranges = src_ranges.subtract(src_skipped)
1412*9e94795aSAndroid Build Coastguard Worker
1413*9e94795aSAndroid Build Coastguard Worker          # Possibly having no changed blocks.
1414*9e94795aSAndroid Build Coastguard Worker          if not tgt_ranges:
1415*9e94795aSAndroid Build Coastguard Worker            return
1416*9e94795aSAndroid Build Coastguard Worker
1417*9e94795aSAndroid Build Coastguard Worker      # Add the transfer(s).
1418*9e94795aSAndroid Build Coastguard Worker      AddSplitTransfers(
1419*9e94795aSAndroid Build Coastguard Worker          tgt_name, src_name, tgt_ranges, src_ranges, style, by_id)
1420*9e94795aSAndroid Build Coastguard Worker
1421*9e94795aSAndroid Build Coastguard Worker    def ParseAndValidateSplitInfo(patch_size, tgt_ranges, src_ranges,
1422*9e94795aSAndroid Build Coastguard Worker                                  split_info):
1423*9e94795aSAndroid Build Coastguard Worker      """Parse the split_info and return a list of info tuples.
1424*9e94795aSAndroid Build Coastguard Worker
1425*9e94795aSAndroid Build Coastguard Worker      Args:
1426*9e94795aSAndroid Build Coastguard Worker        patch_size: total size of the patch file.
1427*9e94795aSAndroid Build Coastguard Worker        tgt_ranges: Ranges of the target file within the original image.
1428*9e94795aSAndroid Build Coastguard Worker        src_ranges: Ranges of the source file within the original image.
1429*9e94795aSAndroid Build Coastguard Worker        split_info format:
1430*9e94795aSAndroid Build Coastguard Worker          imgdiff version#
1431*9e94795aSAndroid Build Coastguard Worker          count of pieces
1432*9e94795aSAndroid Build Coastguard Worker          <patch_size_1> <tgt_size_1> <src_ranges_1>
1433*9e94795aSAndroid Build Coastguard Worker          ...
1434*9e94795aSAndroid Build Coastguard Worker          <patch_size_n> <tgt_size_n> <src_ranges_n>
1435*9e94795aSAndroid Build Coastguard Worker
1436*9e94795aSAndroid Build Coastguard Worker      Returns:
1437*9e94795aSAndroid Build Coastguard Worker        [patch_start, patch_len, split_tgt_ranges, split_src_ranges]
1438*9e94795aSAndroid Build Coastguard Worker      """
1439*9e94795aSAndroid Build Coastguard Worker
1440*9e94795aSAndroid Build Coastguard Worker      version = int(split_info[0])
1441*9e94795aSAndroid Build Coastguard Worker      assert version == 2
1442*9e94795aSAndroid Build Coastguard Worker      count = int(split_info[1])
1443*9e94795aSAndroid Build Coastguard Worker      assert len(split_info) - 2 == count
1444*9e94795aSAndroid Build Coastguard Worker
1445*9e94795aSAndroid Build Coastguard Worker      split_info_list = []
1446*9e94795aSAndroid Build Coastguard Worker      patch_start = 0
1447*9e94795aSAndroid Build Coastguard Worker      tgt_remain = copy.deepcopy(tgt_ranges)
1448*9e94795aSAndroid Build Coastguard Worker      # each line has the format <patch_size>, <tgt_size>, <src_ranges>
1449*9e94795aSAndroid Build Coastguard Worker      for line in split_info[2:]:
1450*9e94795aSAndroid Build Coastguard Worker        info = line.split()
1451*9e94795aSAndroid Build Coastguard Worker        assert len(info) == 3
1452*9e94795aSAndroid Build Coastguard Worker        patch_length = int(info[0])
1453*9e94795aSAndroid Build Coastguard Worker
1454*9e94795aSAndroid Build Coastguard Worker        split_tgt_size = int(info[1])
1455*9e94795aSAndroid Build Coastguard Worker        assert split_tgt_size % 4096 == 0
1456*9e94795aSAndroid Build Coastguard Worker        assert split_tgt_size // 4096 <= tgt_remain.size()
1457*9e94795aSAndroid Build Coastguard Worker        split_tgt_ranges = tgt_remain.first(split_tgt_size // 4096)
1458*9e94795aSAndroid Build Coastguard Worker        tgt_remain = tgt_remain.subtract(split_tgt_ranges)
1459*9e94795aSAndroid Build Coastguard Worker
1460*9e94795aSAndroid Build Coastguard Worker        # Find the split_src_ranges within the image file from its relative
1461*9e94795aSAndroid Build Coastguard Worker        # position in file.
1462*9e94795aSAndroid Build Coastguard Worker        split_src_indices = RangeSet.parse_raw(info[2])
1463*9e94795aSAndroid Build Coastguard Worker        split_src_ranges = RangeSet()
1464*9e94795aSAndroid Build Coastguard Worker        for r in split_src_indices:
1465*9e94795aSAndroid Build Coastguard Worker          curr_range = src_ranges.first(r[1]).subtract(src_ranges.first(r[0]))
1466*9e94795aSAndroid Build Coastguard Worker          assert not split_src_ranges.overlaps(curr_range)
1467*9e94795aSAndroid Build Coastguard Worker          split_src_ranges = split_src_ranges.union(curr_range)
1468*9e94795aSAndroid Build Coastguard Worker
1469*9e94795aSAndroid Build Coastguard Worker        split_info_list.append((patch_start, patch_length,
1470*9e94795aSAndroid Build Coastguard Worker                                split_tgt_ranges, split_src_ranges))
1471*9e94795aSAndroid Build Coastguard Worker        patch_start += patch_length
1472*9e94795aSAndroid Build Coastguard Worker
1473*9e94795aSAndroid Build Coastguard Worker      # Check that the sizes of all the split pieces add up to the final file
1474*9e94795aSAndroid Build Coastguard Worker      # size for patch and target.
1475*9e94795aSAndroid Build Coastguard Worker      assert tgt_remain.size() == 0
1476*9e94795aSAndroid Build Coastguard Worker      assert patch_start == patch_size
1477*9e94795aSAndroid Build Coastguard Worker      return split_info_list
1478*9e94795aSAndroid Build Coastguard Worker
1479*9e94795aSAndroid Build Coastguard Worker    def SplitLargeApks():
1480*9e94795aSAndroid Build Coastguard Worker      """Split the large apks files.
1481*9e94795aSAndroid Build Coastguard Worker
1482*9e94795aSAndroid Build Coastguard Worker      Example: Chrome.apk will be split into
1483*9e94795aSAndroid Build Coastguard Worker        src-0: Chrome.apk-0, tgt-0: Chrome.apk-0
1484*9e94795aSAndroid Build Coastguard Worker        src-1: Chrome.apk-1, tgt-1: Chrome.apk-1
1485*9e94795aSAndroid Build Coastguard Worker        ...
1486*9e94795aSAndroid Build Coastguard Worker
1487*9e94795aSAndroid Build Coastguard Worker      After the split, the target pieces are continuous and block aligned; and
1488*9e94795aSAndroid Build Coastguard Worker      the source pieces are mutually exclusive. During the split, we also
1489*9e94795aSAndroid Build Coastguard Worker      generate and save the image patch between src-X & tgt-X. This patch will
1490*9e94795aSAndroid Build Coastguard Worker      be valid because the block ranges of src-X & tgt-X will always stay the
1491*9e94795aSAndroid Build Coastguard Worker      same afterwards; but there's a chance we don't use the patch if we
1492*9e94795aSAndroid Build Coastguard Worker      convert the "diff" command into "new" or "move" later.
1493*9e94795aSAndroid Build Coastguard Worker      """
1494*9e94795aSAndroid Build Coastguard Worker
1495*9e94795aSAndroid Build Coastguard Worker      while True:
1496*9e94795aSAndroid Build Coastguard Worker        with transfer_lock:
1497*9e94795aSAndroid Build Coastguard Worker          if not large_apks:
1498*9e94795aSAndroid Build Coastguard Worker            return
1499*9e94795aSAndroid Build Coastguard Worker          tgt_name, src_name, tgt_ranges, src_ranges = large_apks.pop(0)
1500*9e94795aSAndroid Build Coastguard Worker
1501*9e94795aSAndroid Build Coastguard Worker        src_file = common.MakeTempFile(prefix="src-")
1502*9e94795aSAndroid Build Coastguard Worker        tgt_file = common.MakeTempFile(prefix="tgt-")
1503*9e94795aSAndroid Build Coastguard Worker        with open(src_file, "wb") as src_fd:
1504*9e94795aSAndroid Build Coastguard Worker          self.src.WriteRangeDataToFd(src_ranges, src_fd)
1505*9e94795aSAndroid Build Coastguard Worker        with open(tgt_file, "wb") as tgt_fd:
1506*9e94795aSAndroid Build Coastguard Worker          self.tgt.WriteRangeDataToFd(tgt_ranges, tgt_fd)
1507*9e94795aSAndroid Build Coastguard Worker
1508*9e94795aSAndroid Build Coastguard Worker        patch_file = common.MakeTempFile(prefix="patch-")
1509*9e94795aSAndroid Build Coastguard Worker        patch_info_file = common.MakeTempFile(prefix="split_info-")
1510*9e94795aSAndroid Build Coastguard Worker        cmd = ["imgdiff", "-z",
1511*9e94795aSAndroid Build Coastguard Worker               "--block-limit={}".format(max_blocks_per_transfer),
1512*9e94795aSAndroid Build Coastguard Worker               "--split-info=" + patch_info_file,
1513*9e94795aSAndroid Build Coastguard Worker               src_file, tgt_file, patch_file]
1514*9e94795aSAndroid Build Coastguard Worker        proc = common.Run(cmd)
1515*9e94795aSAndroid Build Coastguard Worker        imgdiff_output, _ = proc.communicate()
1516*9e94795aSAndroid Build Coastguard Worker        assert proc.returncode == 0, \
1517*9e94795aSAndroid Build Coastguard Worker            "Failed to create imgdiff patch between {} and {}:\n{}".format(
1518*9e94795aSAndroid Build Coastguard Worker                src_name, tgt_name, imgdiff_output)
1519*9e94795aSAndroid Build Coastguard Worker
1520*9e94795aSAndroid Build Coastguard Worker        with open(patch_info_file) as patch_info:
1521*9e94795aSAndroid Build Coastguard Worker          lines = patch_info.readlines()
1522*9e94795aSAndroid Build Coastguard Worker
1523*9e94795aSAndroid Build Coastguard Worker        patch_size_total = os.path.getsize(patch_file)
1524*9e94795aSAndroid Build Coastguard Worker        split_info_list = ParseAndValidateSplitInfo(patch_size_total,
1525*9e94795aSAndroid Build Coastguard Worker                                                    tgt_ranges, src_ranges,
1526*9e94795aSAndroid Build Coastguard Worker                                                    lines)
1527*9e94795aSAndroid Build Coastguard Worker        for index, (patch_start, patch_length, split_tgt_ranges,
1528*9e94795aSAndroid Build Coastguard Worker                    split_src_ranges) in enumerate(split_info_list):
1529*9e94795aSAndroid Build Coastguard Worker          with open(patch_file, 'rb') as f:
1530*9e94795aSAndroid Build Coastguard Worker            f.seek(patch_start)
1531*9e94795aSAndroid Build Coastguard Worker            patch_content = f.read(patch_length)
1532*9e94795aSAndroid Build Coastguard Worker
1533*9e94795aSAndroid Build Coastguard Worker          split_src_name = "{}-{}".format(src_name, index)
1534*9e94795aSAndroid Build Coastguard Worker          split_tgt_name = "{}-{}".format(tgt_name, index)
1535*9e94795aSAndroid Build Coastguard Worker          split_large_apks.append((split_tgt_name,
1536*9e94795aSAndroid Build Coastguard Worker                                   split_src_name,
1537*9e94795aSAndroid Build Coastguard Worker                                   split_tgt_ranges,
1538*9e94795aSAndroid Build Coastguard Worker                                   split_src_ranges,
1539*9e94795aSAndroid Build Coastguard Worker                                   patch_content))
1540*9e94795aSAndroid Build Coastguard Worker
1541*9e94795aSAndroid Build Coastguard Worker    logger.info("Finding transfers...")
1542*9e94795aSAndroid Build Coastguard Worker
1543*9e94795aSAndroid Build Coastguard Worker    large_apks = []
1544*9e94795aSAndroid Build Coastguard Worker    split_large_apks = []
1545*9e94795aSAndroid Build Coastguard Worker    cache_size = common.OPTIONS.cache_size
1546*9e94795aSAndroid Build Coastguard Worker    split_threshold = 0.125
1547*9e94795aSAndroid Build Coastguard Worker    assert cache_size is not None
1548*9e94795aSAndroid Build Coastguard Worker    max_blocks_per_transfer = int(cache_size * split_threshold /
1549*9e94795aSAndroid Build Coastguard Worker                                  self.tgt.blocksize)
1550*9e94795aSAndroid Build Coastguard Worker    empty = RangeSet()
1551*9e94795aSAndroid Build Coastguard Worker    for tgt_fn, tgt_ranges in sorted(self.tgt.file_map.items()):
1552*9e94795aSAndroid Build Coastguard Worker      if tgt_fn == "__ZERO":
1553*9e94795aSAndroid Build Coastguard Worker        # the special "__ZERO" domain is all the blocks not contained
1554*9e94795aSAndroid Build Coastguard Worker        # in any file and that are filled with zeros.  We have a
1555*9e94795aSAndroid Build Coastguard Worker        # special transfer style for zero blocks.
1556*9e94795aSAndroid Build Coastguard Worker        src_ranges = self.src.file_map.get("__ZERO", empty)
1557*9e94795aSAndroid Build Coastguard Worker        AddTransfer(tgt_fn, "__ZERO", tgt_ranges, src_ranges,
1558*9e94795aSAndroid Build Coastguard Worker                    "zero", self.transfers)
1559*9e94795aSAndroid Build Coastguard Worker        continue
1560*9e94795aSAndroid Build Coastguard Worker
1561*9e94795aSAndroid Build Coastguard Worker      elif tgt_fn == "__COPY":
1562*9e94795aSAndroid Build Coastguard Worker        # "__COPY" domain includes all the blocks not contained in any
1563*9e94795aSAndroid Build Coastguard Worker        # file and that need to be copied unconditionally to the target.
1564*9e94795aSAndroid Build Coastguard Worker        AddTransfer(tgt_fn, None, tgt_ranges, empty, "new", self.transfers)
1565*9e94795aSAndroid Build Coastguard Worker        continue
1566*9e94795aSAndroid Build Coastguard Worker
1567*9e94795aSAndroid Build Coastguard Worker      elif tgt_fn == "__HASHTREE":
1568*9e94795aSAndroid Build Coastguard Worker        continue
1569*9e94795aSAndroid Build Coastguard Worker
1570*9e94795aSAndroid Build Coastguard Worker      elif tgt_fn in self.src.file_map:
1571*9e94795aSAndroid Build Coastguard Worker        # Look for an exact pathname match in the source.
1572*9e94795aSAndroid Build Coastguard Worker        AddTransfer(tgt_fn, tgt_fn, tgt_ranges, self.src.file_map[tgt_fn],
1573*9e94795aSAndroid Build Coastguard Worker                    "diff", self.transfers, True)
1574*9e94795aSAndroid Build Coastguard Worker        continue
1575*9e94795aSAndroid Build Coastguard Worker
1576*9e94795aSAndroid Build Coastguard Worker      b = os.path.basename(tgt_fn)
1577*9e94795aSAndroid Build Coastguard Worker      if b in self.src_basenames:
1578*9e94795aSAndroid Build Coastguard Worker        # Look for an exact basename match in the source.
1579*9e94795aSAndroid Build Coastguard Worker        src_fn = self.src_basenames[b]
1580*9e94795aSAndroid Build Coastguard Worker        AddTransfer(tgt_fn, src_fn, tgt_ranges, self.src.file_map[src_fn],
1581*9e94795aSAndroid Build Coastguard Worker                    "diff", self.transfers, True)
1582*9e94795aSAndroid Build Coastguard Worker        continue
1583*9e94795aSAndroid Build Coastguard Worker
1584*9e94795aSAndroid Build Coastguard Worker      b = re.sub("[0-9]+", "#", b)
1585*9e94795aSAndroid Build Coastguard Worker      if b in self.src_numpatterns:
1586*9e94795aSAndroid Build Coastguard Worker        # Look for a 'number pattern' match (a basename match after
1587*9e94795aSAndroid Build Coastguard Worker        # all runs of digits are replaced by "#").  (This is useful
1588*9e94795aSAndroid Build Coastguard Worker        # for .so files that contain version numbers in the filename
1589*9e94795aSAndroid Build Coastguard Worker        # that get bumped.)
1590*9e94795aSAndroid Build Coastguard Worker        src_fn = self.src_numpatterns[b]
1591*9e94795aSAndroid Build Coastguard Worker        AddTransfer(tgt_fn, src_fn, tgt_ranges, self.src.file_map[src_fn],
1592*9e94795aSAndroid Build Coastguard Worker                    "diff", self.transfers, True)
1593*9e94795aSAndroid Build Coastguard Worker        continue
1594*9e94795aSAndroid Build Coastguard Worker
1595*9e94795aSAndroid Build Coastguard Worker      AddTransfer(tgt_fn, None, tgt_ranges, empty, "new", self.transfers)
1596*9e94795aSAndroid Build Coastguard Worker
1597*9e94795aSAndroid Build Coastguard Worker    transfer_lock = threading.Lock()
1598*9e94795aSAndroid Build Coastguard Worker    threads = [threading.Thread(target=SplitLargeApks)
1599*9e94795aSAndroid Build Coastguard Worker               for _ in range(self.threads)]
1600*9e94795aSAndroid Build Coastguard Worker    for th in threads:
1601*9e94795aSAndroid Build Coastguard Worker      th.start()
1602*9e94795aSAndroid Build Coastguard Worker    while threads:
1603*9e94795aSAndroid Build Coastguard Worker      threads.pop().join()
1604*9e94795aSAndroid Build Coastguard Worker
1605*9e94795aSAndroid Build Coastguard Worker    # Sort the split transfers for large apks to generate a determinate package.
1606*9e94795aSAndroid Build Coastguard Worker    split_large_apks.sort()
1607*9e94795aSAndroid Build Coastguard Worker    for (tgt_name, src_name, tgt_ranges, src_ranges,
1608*9e94795aSAndroid Build Coastguard Worker         patch) in split_large_apks:
1609*9e94795aSAndroid Build Coastguard Worker      transfer_split = Transfer(tgt_name, src_name, tgt_ranges, src_ranges,
1610*9e94795aSAndroid Build Coastguard Worker                                self.tgt.RangeSha1(tgt_ranges),
1611*9e94795aSAndroid Build Coastguard Worker                                self.src.RangeSha1(src_ranges),
1612*9e94795aSAndroid Build Coastguard Worker                                "diff", self.transfers)
1613*9e94795aSAndroid Build Coastguard Worker      transfer_split.patch_info = PatchInfo(True, patch)
1614*9e94795aSAndroid Build Coastguard Worker
1615*9e94795aSAndroid Build Coastguard Worker  def AbbreviateSourceNames(self):
1616*9e94795aSAndroid Build Coastguard Worker    for k in self.src.file_map.keys():
1617*9e94795aSAndroid Build Coastguard Worker      b = os.path.basename(k)
1618*9e94795aSAndroid Build Coastguard Worker      self.src_basenames[b] = k
1619*9e94795aSAndroid Build Coastguard Worker      b = re.sub("[0-9]+", "#", b)
1620*9e94795aSAndroid Build Coastguard Worker      self.src_numpatterns[b] = k
1621*9e94795aSAndroid Build Coastguard Worker
1622*9e94795aSAndroid Build Coastguard Worker  @staticmethod
1623*9e94795aSAndroid Build Coastguard Worker  def AssertPartition(total, seq):
1624*9e94795aSAndroid Build Coastguard Worker    """Assert that all the RangeSets in 'seq' form a partition of the
1625*9e94795aSAndroid Build Coastguard Worker    'total' RangeSet (ie, they are nonintersecting and their union
1626*9e94795aSAndroid Build Coastguard Worker    equals 'total')."""
1627*9e94795aSAndroid Build Coastguard Worker
1628*9e94795aSAndroid Build Coastguard Worker    so_far = RangeSet()
1629*9e94795aSAndroid Build Coastguard Worker    for i in seq:
1630*9e94795aSAndroid Build Coastguard Worker      assert not so_far.overlaps(i)
1631*9e94795aSAndroid Build Coastguard Worker      so_far = so_far.union(i)
1632*9e94795aSAndroid Build Coastguard Worker    assert so_far == total
1633