def test_union_empty(self): """Test that empty intervals are well behaved.""" a = Interval() b = Interval() c = a.union(b) d = b.union(a) self.assertEqual(a, b) self.assertEqual(b, c) self.assertEqual(c, d)
class File(object): """Holds data on what parts of a file have already been used.""" __metaclass__ = invariant.EnforceInvariant def __init__(self, filename, size, subdir): # Total size of the file self.size = size # Number of bytes in the file that have been used already. self.used = 0 # Relative name of the file. self.filename = filename # Regions of the file in use. self._extents = Interval() # Subdir the file is currently in. self.subdir = subdir def _checkInvariant(self): assert self.size >= self.used assert len(self._extents) == self.used @property def free(self): return self.size - self.used @property def path(self): """Returns the path of the padfile relative to the paddir.""" return (self.subdir, self.filename) def getAllocation(self, requested): """Requests an allocation from the file of the given size. Raises OutOfPad if the file is out of space.""" if requested > self.free: raise OutOfPad("File %s has %i bytes but you requested %i." % \ (self.filename, self.free, requested)) alloc = Allocation() for (start, length) in self._extents.iterExterior(self.size): interval = Interval.fromAtom(start, min(requested - len(alloc), length)) seg = Allocation(self, interval) alloc.unionUpdate(seg) if len(alloc) >= requested: assert len(alloc) == requested break assert len(alloc) == requested return alloc def commitAllocation(self, ival): """Mark the specified interval as used. Error if overlaps with currently used area.""" self._extents = self._extents.union(ival) self.used += len(ival) def consumeEntireFile(self): """Mark the entire file as consumed. Used to prevent its use for encryption while keeping it around for decryption.""" self.used = self.size self._extents = Interval.fromAtom(0, self.size)