changeset 2:8884b0bf779d

Improve the efficiency.
author David Barts <n5jrn@me.com>
date Thu, 26 Dec 2019 13:18:53 -0800
parents 173e86601dbc
children 091c03f1b2e8
files workspace.py
diffstat 1 files changed, 27 insertions(+), 6 deletions(-) [+]
line wrap: on
line diff
--- a/workspace.py	Thu Dec 26 12:00:03 2019 -0800
+++ b/workspace.py	Thu Dec 26 13:18:53 2019 -0800
@@ -206,6 +206,7 @@
             raise TypeError("offset must be an int")
         self.bounds = bounds
         self.offset = offset
+        self.delta = self.offset - self.bounds.start
 
     def __repr__(self):
         return "{0}({1!r}, {2!r})".format(self.__class__.__name__, self.bounds, self.offset)
@@ -233,11 +234,10 @@
         self._length = pos
 
     def _mapped(self, index):
-        mi = self._binsch(index)
-        m = None if mi is None else self._mmap[mi]
-        if m is None:
+        mmap_index = self._binsch(index)
+        if mmap_index is None:
             raise IndexError("index {0} out of range".format(index))
-        return index - m.bounds.start + m.offset
+        return index + self._mmap[mmap_index].delta
 
     def _binsch(self, index):
         a = 0
@@ -260,13 +260,34 @@
 
     # XXX - this is sorta brute-forced and could be more efficient
     def __getitem__(self, key):
+        # Trivial cases
         if isinstance(key, int):
             return self._get1(key)
         if not isinstance(key, slice):
             raise TypeError("expecting int or slice")
+        if key.step is not None:
+            raise ValueError("__getitem__ does not support steps in slices")
+
+        # Loop up the starting segment.
+        mi = self._binsch(key.start)
+        if mi is None:
+            return ""
+        m = self._mmap[mi]
+
+        # Horray! There's only one segment, so we can optimize.
+        if key.stop <= m.bounds.stop:
+            start = key.start + m.delta
+            stop = key.stop + m.delta
+            return self.indexable[start:stop]
+
+        # The most involved (multi-segment) case.
         with io.StringIO() as buf:
-            for i in range(key.start, key.stop, key.step or 1):
-                buf.write(self._get1(i))
+            for m in self._mmap[mi:]:
+                if m.bounds.start >= key.stop:
+                    break
+                start = max(key.start, m.bounds.start) + m.delta
+                stop = min(key.stop, m.bounds.stop) + m.delta
+                buf.write(self.indexable[start:stop])
             return buf.getvalue()
 
     def __len__(self):