Add an additional optimization for the pathological case of editing the beginning and end of a file.

This commit is contained in:
Jennifer Taylor 2021-10-21 04:05:30 +00:00
parent e02c307004
commit 23631564cf
2 changed files with 49 additions and 4 deletions

View File

@ -9,6 +9,7 @@ class FileBytes:
def __init__(self, handle: BinaryIO) -> None: def __init__(self, handle: BinaryIO) -> None:
self.__handle: BinaryIO = handle self.__handle: BinaryIO = handle
self.__patches: Dict[int, int] = {} self.__patches: Dict[int, int] = {}
self.__regions: Set[int] = set()
self.__copies: List["FileBytes"] = [] self.__copies: List["FileBytes"] = []
self.__unsafe: bool = False self.__unsafe: bool = False
self.__lowest_patch: Optional[int] = None self.__lowest_patch: Optional[int] = None
@ -125,6 +126,7 @@ class FileBytes:
myclone.__patches = {k: v for k, v in self.__patches.items()} myclone.__patches = {k: v for k, v in self.__patches.items()}
myclone.__lowest_patch = self.__lowest_patch myclone.__lowest_patch = self.__lowest_patch
myclone.__highest_patch = self.__highest_patch myclone.__highest_patch = self.__highest_patch
myclone.__regions = self.__regions
myclone.__filelength = self.__filelength myclone.__filelength = self.__filelength
myclone.__patchlength = self.__patchlength myclone.__patchlength = self.__patchlength
myclone.__origfilelength = self.__origfilelength myclone.__origfilelength = self.__origfilelength
@ -145,6 +147,7 @@ class FileBytes:
self.__patches[loc] = change self.__patches[loc] = change
self.__lowest_patch = min(self.__lowest_patch, loc) if self.__lowest_patch is not None else loc self.__lowest_patch = min(self.__lowest_patch, loc) if self.__lowest_patch is not None else loc
self.__highest_patch = max(self.__highest_patch, loc + 1) if self.__highest_patch is not None else (loc + 1) self.__highest_patch = max(self.__highest_patch, loc + 1) if self.__highest_patch is not None else (loc + 1)
self.__regions.clear()
self.__patchlength += len(data) self.__patchlength += len(data)
@ -164,9 +167,13 @@ class FileBytes:
self.__filelength = size self.__filelength = size
# Get rid of any changes made in the truncation range. # Get rid of any changes made in the truncation range.
cleared: bool = False
for off in range(size, self.__patchlength): for off in range(size, self.__patchlength):
if off in self.__patches: if off in self.__patches:
del self.__patches[off] del self.__patches[off]
cleared = True
if cleared:
self.__regions.clear()
# Set the length of this object to the size as well so resizing will # Set the length of this object to the size as well so resizing will
# zero out the data. # zero out the data.
@ -237,6 +244,7 @@ class FileBytes:
# Now that we've serialized out the data, clean up our own representation. # Now that we've serialized out the data, clean up our own representation.
self.__handle.flush() self.__handle.flush()
self.__patches.clear() self.__patches.clear()
self.__regions.clear()
self.__lowest_patch = None self.__lowest_patch = None
self.__highest_patch = None self.__highest_patch = None
self.__filelength = self.__patchlength self.__filelength = self.__patchlength
@ -261,8 +269,9 @@ class FileBytes:
inst.__patchlength = self.__patchlength inst.__patchlength = self.__patchlength
inst.__origfilelength = self.__origfilelength inst.__origfilelength = self.__origfilelength
inst.__patches.clear() inst.__patches.clear()
self.__lowest_patch = None inst.__regions.clear()
self.__highest_patch = None inst.__lowest_patch = None
inst.__highest_patch = None
def __slice(self, key: slice) -> Tuple[int, int, int]: def __slice(self, key: slice) -> Tuple[int, int, int]:
# Determine step of slice # Determine step of slice
@ -344,7 +353,41 @@ class FileBytes:
elif self.__highest_patch is None or (start > self.__highest_patch and stop > self.__highest_patch): elif self.__highest_patch is None or (start > self.__highest_patch and stop > self.__highest_patch):
modifications = False modifications = False
else: else:
modifications = any(index in self.__patches for index in range(start, stop, step)) # Whether we should do the slow check or not.
check = False
if not self.__regions:
# Recreate the index.
last_index = -1
for iterval in sorted(self.__patches.keys()):
# Only attempt to update the region cache if we haven't already
# seen something in this section.
index = iterval // self.IO_SIZE
if index != last_index:
self.__regions.add(index)
last_index = index
if start > stop:
iterstart = stop + 1
iterend = start + 1
else:
iterstart = start
iterend = stop
iterstart //= self.IO_SIZE
iterend //= self.IO_SIZE
if iterend == iterstart:
iterend += 1
for index in range(iterstart, iterend):
if index in self.__regions:
check = True
if check:
modifications = any(index in self.__patches for index in range(start, stop, step))
else:
modifications = False
# Now see if we can do any fast loading # Now see if we can do any fast loading
if start < stop and step == 1: if start < stop and step == 1:
@ -433,6 +476,7 @@ class FileBytes:
self.__patches[key] = val self.__patches[key] = val
self.__lowest_patch = min(self.__lowest_patch, key) if self.__lowest_patch is not None else key self.__lowest_patch = min(self.__lowest_patch, key) if self.__lowest_patch is not None else key
self.__highest_patch = max(self.__highest_patch, key + 1) if self.__highest_patch is not None else (key + 1) self.__highest_patch = max(self.__highest_patch, key + 1) if self.__highest_patch is not None else (key + 1)
self.__regions.clear()
elif isinstance(key, slice): elif isinstance(key, slice):
if not isinstance(val, bytes): if not isinstance(val, bytes):
@ -467,6 +511,7 @@ class FileBytes:
self.__patches[off] = val[index] self.__patches[off] = val[index]
self.__lowest_patch = min(self.__lowest_patch, off) if self.__lowest_patch is not None else off self.__lowest_patch = min(self.__lowest_patch, off) if self.__lowest_patch is not None else off
self.__highest_patch = max(self.__highest_patch, off + 1) if self.__highest_patch is not None else (off + 1) self.__highest_patch = max(self.__highest_patch, off + 1) if self.__highest_patch is not None else (off + 1)
self.__regions.clear()
else: else:
raise NotImplementedError("Not implemented!") raise NotImplementedError("Not implemented!")

View File

@ -8,7 +8,7 @@ with open(os.path.join("arcadeutils", "README.md"), "r", encoding="utf-8") as fh
setup( setup(
name='arcadeutils', name='arcadeutils',
version='0.1.7', version='0.1.8',
description='Collection of utilities written in Python for working with various arcade binaries.', description='Collection of utilities written in Python for working with various arcade binaries.',
long_description=long_description, long_description=long_description,
long_description_content_type="text/markdown", long_description_content_type="text/markdown",