implemented LogarithmicBuffer
authorChristian Herdtweck <christian.herdtweck@intra2net.com>
Tue, 12 Jan 2016 12:25:48 +0000 (13:25 +0100)
committerChristian Herdtweck <christian.herdtweck@intra2net.com>
Tue, 12 Jan 2016 12:25:48 +0000 (13:25 +0100)
buffers.py

index bda7d80..b586820 100644 (file)
@@ -22,8 +22,8 @@ buffers.py: buffers of various shapes, sizes and functionalities
 Featuring::
 
 * CircularBuffer
-* LogBuffer (saves one of the 2 last items, one of the 4 last items, ..
-             of the 8 last, 16 last, ...
+* LogarithmicBuffer: saves only last N items, and after that less and less so
+*                    very few old items are kept
 
 .. codeauthor:: Christian Herdtweck, christian.herdtweck@intra2net.com
 """
@@ -78,3 +78,62 @@ class CircularBuffer:
             result.append(self._buffer[idx])
 
         return result
+
+
+class LogarithmicBuffer:
+    """ saves only last N items, and after that less and less; old stuff is del
+
+    --> grows only logarithmically in size
+
+    has a data_new which is a CircularBuffer for the last N items
+    data_old is a list of items with ages approx 2, 4, 8, 16, ... counts
+
+    Could easily extend to other bases than 2 to make even scarcer
+    """
+
+    def __init__(self, n_new):
+        self.n_new = n_new
+        if n_new < 0:
+            raise ValueError('n_new must be non-negative!')
+        if n_new == 0:
+            self.data_new = None
+        else:
+            self.data_new = CircularBuffer(n_new)
+        self.count = 0
+        self.data_old = []
+
+    def add(self, new_item):
+        """ add a new item to buffer """
+        if self.data_new:
+            newest_old_item = data_new.add(new_item)
+            age = count - n_new
+        else:
+            newest_old_item = new_item
+            age = count
+
+        _save_old(newest_old_item, age, 0)
+        self.count += 1
+
+    def _save_old(item, age, index):
+        """ internal helper for saving (or not) items in data_old """
+
+        # determine whether we throw it away or actually save it
+        if age % 2**index != 0:
+            return
+
+        # we will save it. But before overwriting the item that was there,
+        # we determine whether that will be saved at next index
+        _save_old(data_old[index], age, index+1)
+
+        # now do save at index, possibly extending data_old
+        if len(data_old) < index:
+            data_old.append(item)
+        else:
+            data_old[index] = item
+
+    def get_all(self):
+        """ get all buffer contents in correct order """
+        if self.data_new:
+            return self.data_old[::-1] + self.data_new.get_all()
+        else:
+            return self.data_old[::-1]