3 _logger = logging.getLogger('arvados.ranges')
5 # Log level below 'debug' !
9 def __init__(self, locator, range_start, range_size, segment_offset=0):
10 self.locator = locator
11 self.range_start = range_start
12 self.range_size = range_size
13 self.segment_offset = segment_offset
16 return "Range(%r, %r, %r, %r)" % (self.locator, self.range_start, self.range_size, self.segment_offset)
18 def __eq__(self, other):
19 return (self.locator == other.locator and
20 self.range_start == other.range_start and
21 self.range_size == other.range_size and
22 self.segment_offset == other.segment_offset)
24 def first_block(data_locators, range_start):
27 # range_start/block_start is the inclusive lower bound
28 # range_end/block_end is the exclusive upper bound
30 hi = len(data_locators)
32 i = int((hi + lo) / 2)
33 block_size = data_locators[i].range_size
34 block_start = data_locators[i].range_start
35 block_end = block_start + block_size
37 # perform a binary search for the first block
38 # assumes that all of the blocks are contiguous, so range_start is guaranteed
39 # to either fall into the range of a block or be outside the block range entirely
40 while not (range_start >= block_start and range_start < block_end):
42 # must be out of range, fail
44 if range_start > block_start:
48 i = int((hi + lo) / 2)
49 block_size = data_locators[i].range_size
50 block_start = data_locators[i].range_start
51 block_end = block_start + block_size
55 class LocatorAndRange(object):
56 def __init__(self, locator, block_size, segment_offset, segment_size):
57 self.locator = locator
58 self.block_size = block_size
59 self.segment_offset = segment_offset
60 self.segment_size = segment_size
62 def __eq__(self, other):
63 return (self.locator == other.locator and
64 self.block_size == other.block_size and
65 self.segment_offset == other.segment_offset and
66 self.segment_size == other.segment_size)
69 return "LocatorAndRange(%r, %r, %r, %r)" % (self.locator, self.block_size, self.segment_offset, self.segment_size)
71 def locators_and_ranges(data_locators, range_start, range_size, limit=None):
72 """Get blocks that are covered by a range.
74 Returns a list of LocatorAndRange objects.
77 list of Range objects, assumes that blocks are in order and contiguous
86 Maximum segments to return, default None (unlimited). Will truncate the
87 result if there are more segments needed to cover the range than the
94 range_end = range_start + range_size
96 i = first_block(data_locators, range_start)
100 # We should always start at the first segment due to the binary
102 while i < len(data_locators) and len(resp) != limit:
103 dl = data_locators[i]
104 block_start = dl.range_start
105 block_size = dl.range_size
106 block_end = block_start + block_size
107 _logger.log(RANGES_SPAM,
108 "%s range_start %s block_start %s range_end %s block_end %s",
109 dl.locator, range_start, block_start, range_end, block_end)
110 if range_end <= block_start:
111 # range ends before this block starts, so don't look at any more locators
114 if range_start >= block_start and range_end <= block_end:
115 # range starts and ends in this block
116 resp.append(LocatorAndRange(dl.locator, block_size, dl.segment_offset + (range_start - block_start), range_size))
117 elif range_start >= block_start and range_end > block_end:
118 # range starts in this block
119 resp.append(LocatorAndRange(dl.locator, block_size, dl.segment_offset + (range_start - block_start), block_end - range_start))
120 elif range_start < block_start and range_end > block_end:
121 # range starts in a previous block and extends to further blocks
122 resp.append(LocatorAndRange(dl.locator, block_size, dl.segment_offset, block_size))
123 elif range_start < block_start and range_end <= block_end:
124 # range starts in a previous block and ends in this block
125 resp.append(LocatorAndRange(dl.locator, block_size, dl.segment_offset, range_end - block_start))
126 block_start = block_end
130 def replace_range(data_locators, new_range_start, new_range_size, new_locator, new_segment_offset):
132 Replace a file segment range with a new segment.
135 data_locators will be updated in place
138 list of Range objects, assumes that segments are in order and contiguous
141 start of range to replace in data_locators
144 size of range to replace in data_locators
147 locator for new segment to be inserted
150 segment offset within the locator
153 if new_range_size == 0:
156 new_range_end = new_range_start + new_range_size
158 if len(data_locators) == 0:
159 data_locators.append(Range(new_locator, new_range_start, new_range_size, new_segment_offset))
162 last = data_locators[-1]
163 if (last.range_start+last.range_size) == new_range_start:
164 if last.locator == new_locator:
165 # extend last segment
166 last.range_size += new_range_size
168 data_locators.append(Range(new_locator, new_range_start, new_range_size, new_segment_offset))
171 i = first_block(data_locators, new_range_start)
175 # We should always start at the first segment due to the binary
177 while i < len(data_locators):
178 dl = data_locators[i]
179 old_segment_start = dl.range_start
180 old_segment_end = old_segment_start + dl.range_size
181 _logger.log(RANGES_SPAM,
182 "%s range_start %s segment_start %s range_end %s segment_end %s",
183 dl, new_range_start, old_segment_start, new_range_end,
185 if new_range_end <= old_segment_start:
186 # range ends before this segment starts, so don't look at any more locators
189 if old_segment_start <= new_range_start and new_range_end <= old_segment_end:
190 # new range starts and ends in old segment
191 # split segment into up to 3 pieces
192 if (new_range_start-old_segment_start) > 0:
193 data_locators[i] = Range(dl.locator, old_segment_start, (new_range_start-old_segment_start), dl.segment_offset)
194 data_locators.insert(i+1, Range(new_locator, new_range_start, new_range_size, new_segment_offset))
196 data_locators[i] = Range(new_locator, new_range_start, new_range_size, new_segment_offset)
198 if (old_segment_end-new_range_end) > 0:
199 data_locators.insert(i+2, Range(dl.locator, new_range_end, (old_segment_end-new_range_end), dl.segment_offset + (new_range_start-old_segment_start) + new_range_size))
201 elif old_segment_start <= new_range_start and new_range_end > old_segment_end:
202 # range starts in this segment
203 # split segment into 2 pieces
204 data_locators[i] = Range(dl.locator, old_segment_start, (new_range_start-old_segment_start), dl.segment_offset)
205 data_locators.insert(i+1, Range(new_locator, new_range_start, new_range_size, new_segment_offset))
207 elif new_range_start < old_segment_start and new_range_end >= old_segment_end:
208 # range starts in a previous segment and extends to further segments
209 # delete this segment
212 elif new_range_start < old_segment_start and new_range_end < old_segment_end:
213 # range starts in a previous segment and ends in this segment
214 # move the starting point of this segment up, and shrink it.
215 data_locators[i] = Range(dl.locator, new_range_end, (old_segment_end-new_range_end), dl.segment_offset + (new_range_end-old_segment_start))